Line data Source code
1 : // Snap Websites Server -- snap basic image handling
2 : // Copyright (c) 2013-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/snap_image.h"
22 :
23 :
24 : // snapwebsites lib
25 : //
26 : #include "snapwebsites/log.h"
27 :
28 :
29 : // snapdev lib
30 : //
31 : #include <snapdev/not_reached.h>
32 :
33 :
34 : // last include
35 : //
36 : #include <snapdev/poison.h>
37 :
38 :
39 :
40 : namespace snap
41 : {
42 :
43 :
44 : namespace
45 : {
46 :
47 : uint32_t chunk_name(unsigned char a, unsigned char b, unsigned char c, unsigned char d) __attribute__ ((const));
48 :
49 : /** \brief Transform 4 characters in a chunk name.
50 : *
51 : * Several different formats make use of chunk names (i.e. PNG, TIFF, IFF,
52 : * WAVE.) This macro is used to transform 4 bytes in a name that can
53 : * quickly be compared using uint32_t variables.
54 : *
55 : * Note that the characters do not need to be valid ASCII. Even '\0' is
56 : * acceptable.
57 : *
58 : * The order in which you specify the characters does not matter, although
59 : * you may want to put them in a sensical order. (i.e. the PNG format uses
60 : * a chunk name IHDR, so it makes more sense to write the characters in
61 : * that order!)
62 : *
63 : * \param[in] a The first character.
64 : * \param[in] b The second character.
65 : * \param[in] c The third character.
66 : * \param[in] d The fourth character.
67 : */
68 0 : uint32_t chunk_name(unsigned char a, unsigned char b, unsigned char c, unsigned char d)
69 : {
70 0 : return (a << 24) | (b << 16) | (c << 8) | d;
71 : }
72 :
73 : }
74 : // empty namespace
75 :
76 :
77 :
78 0 : snap_image_buffer_t::snap_image_buffer_t(snap_image *owner)
79 0 : : f_owner(owner)
80 : //, f_buffer(nullptr) -- auto-init
81 : {
82 0 : }
83 :
84 0 : QString const & snap_image_buffer_t::get_mime_type() const
85 : {
86 0 : return f_mime_type;
87 : }
88 :
89 0 : void snap_image_buffer_t::set_mime_type(QString const& mime_type)
90 : {
91 0 : f_mime_type = mime_type;
92 0 : }
93 :
94 0 : QString const& snap_image_buffer_t::get_format_version() const
95 : {
96 0 : return f_format_version;
97 : }
98 :
99 0 : void snap_image_buffer_t::set_format_version(QString const& format_version)
100 : {
101 0 : f_format_version = format_version;
102 0 : }
103 :
104 0 : QString const& snap_image_buffer_t::get_resolution_unit() const
105 : {
106 0 : return f_resolution_unit;
107 : }
108 :
109 0 : void snap_image_buffer_t::set_resolution_unit(QString const& resolution_unit)
110 : {
111 0 : f_resolution_unit = resolution_unit;
112 0 : }
113 :
114 0 : int snap_image_buffer_t::get_xres() const
115 : {
116 0 : return f_xres;
117 : }
118 :
119 0 : void snap_image_buffer_t::set_xres(int xres)
120 : {
121 0 : f_xres = xres;
122 0 : }
123 :
124 0 : int snap_image_buffer_t::get_yres() const
125 : {
126 0 : return f_yres;
127 : }
128 :
129 0 : void snap_image_buffer_t::set_yres(int yres)
130 : {
131 0 : f_yres = yres;
132 0 : }
133 :
134 0 : int snap_image_buffer_t::get_width() const
135 : {
136 0 : return f_width;
137 : }
138 :
139 0 : void snap_image_buffer_t::set_width(int width)
140 : {
141 0 : f_width = width;
142 0 : }
143 :
144 0 : int snap_image_buffer_t::get_height() const
145 : {
146 0 : return f_height;
147 : }
148 :
149 0 : void snap_image_buffer_t::set_height(int height)
150 : {
151 0 : f_height = height;
152 0 : }
153 :
154 0 : int snap_image_buffer_t::get_depth() const
155 : {
156 0 : return f_depth;
157 : }
158 :
159 0 : void snap_image_buffer_t::set_depth(int depth)
160 : {
161 0 : f_depth = depth;
162 0 : }
163 :
164 0 : int snap_image_buffer_t::get_bits() const
165 : {
166 0 : return f_bits;
167 : }
168 :
169 0 : void snap_image_buffer_t::set_bits(int bits)
170 : {
171 0 : f_bits = bits;
172 0 : }
173 :
174 0 : unsigned char * snap_image_buffer_t::get_buffer() const
175 : {
176 0 : return f_buffer.data();
177 : }
178 :
179 :
180 :
181 :
182 :
183 : /** \brief Read an JPEG header for its information.
184 : *
185 : * This function reads the JPEG header information and saves it in a buffer.
186 : *
187 : * Source: http://www.ijg.org/files/
188 : *
189 : * \param[in] s The start of the buffer.
190 : * \param[in] l The size of the buffer.
191 : * \param[in] e The end of the buffer (s + l).
192 : *
193 : * \return true if the JPEG was read successfully.
194 : */
195 : #pragma GCC diagnostic push
196 : #pragma GCC diagnostic ignored "-Wunused-parameter"
197 0 : bool snap_image::info_jpeg(unsigned char const *s, size_t l, unsigned char const *e)
198 : {
199 : // create a buffer "on the stack"; add it to the list of buffers only
200 : // if successful
201 0 : smart_snap_image_buffer_t buffer(new snap_image_buffer_t(this));
202 :
203 0 : buffer->set_mime_type("image/jpeg");
204 :
205 : // bytes 18/19 are the thumbnail and if present it follows as an RGB
206 : // color bitmap of: 3 x thumbnail(width) x thumbnail(height)
207 0 : int flags(0);
208 0 : unsigned char const *q(s + 2);
209 : for(;;)
210 : {
211 0 : if(q + 4 >= e || q[0] != 0xFF || q[1] < 0xC0)
212 : {
213 : // lost track... get out!
214 0 : return false;
215 : }
216 0 : int len(q[2] * 256 + q[3]);
217 : // XXX: determine whether C1 to CF should all be accepted here
218 0 : switch(q[1])
219 : {
220 0 : case 0xC0: // SOF0
221 : case 0xC1: // SOF1
222 : case 0xC2: // SOF2
223 : case 0xC3: // SOF3
224 : case 0xC5: // SOF5
225 : case 0xC6: // SOF6
226 : case 0xC7: // SOF7
227 : case 0xC9: // SOF9
228 : case 0xCA: // SOF10
229 : case 0xCB: // SOF11
230 : case 0xCD: // SOF13
231 : case 0xCE: // SOF14
232 : case 0xCF: // SOF15
233 0 : if(len < 10)
234 : {
235 : // we expect at least 6 bytes after the length
236 0 : return false;
237 : }
238 0 : buffer->set_bits(q[4] * q[9]); // usually 8 or 24
239 0 : buffer->set_width(q[5] * 256 + q[6]);
240 0 : buffer->set_height(q[7] * 256 + q[8]);
241 0 : buffer->set_depth(q[9]); // 1 or 3
242 0 : flags |= 1;
243 0 : break;
244 :
245 0 : case 0xE0: // APP0 (Not always present, i.e. Exif is APP1 or 0xE1)
246 : // verify length and marker name (JFIF\0)
247 0 : if(len < 16
248 0 : || q[4] != 'J' || q[5] != 'F' || q[6] != 'I' || q[7] != 'F' || q[8] != '\0')
249 : {
250 : // we expected at least 16 bytes
251 : break;
252 : }
253 : // usually 1.01 or 1.02
254 0 : buffer->set_format_version(QString("%1.%2").arg(static_cast<int>(q[9])).arg(static_cast<int>(q[10]), 2, 10, QChar('0')));
255 :
256 0 : buffer->set_resolution_unit(q[11] == 0 ? "" : (q[11] == 1 ? "inch" : "cm"));
257 0 : buffer->set_xres(q[12] * 256 + q[13]); // also called density
258 0 : buffer->set_yres(q[14] * 256 + q[15]);
259 0 : flags |= 2;
260 0 : break;
261 :
262 0 : case 0xDA: // start of data
263 : // TODO: add support to skip the image (read all the data up
264 : // to 0xFF 0xD9) and see whether another image is present
265 : // in the file (as far as I know "animated JPEG" are JPEG
266 : // images concatenated one after another)
267 0 : if((flags & 1) != 0)
268 : {
269 0 : f_buffers.push_back(buffer);
270 0 : return true;
271 : }
272 0 : return false;
273 :
274 : }
275 0 : q += 2 + len;
276 0 : }
277 : NOTREACHED();
278 : }
279 : #pragma GCC diagnostic pop
280 :
281 :
282 : /** \brief Read an ICO header for its information.
283 : *
284 : * This function reads the ICO header information and saves it in a buffer.
285 : * The ICO format is used for the favicon.
286 : *
287 : * Source: http://en.wikipedia.org/wiki/ICO_%28file_format%29
288 : * Source: https://en.wikipedia.org/wiki/BMP_file_format
289 : *
290 : * \param[in] s The start of the buffer.
291 : * \param[in] l The size of the buffer.
292 : * \param[in] e The end of the buffer (s + l).
293 : *
294 : * \return true if the ICO was read successfully.
295 : */
296 : #pragma GCC diagnostic push
297 : #pragma GCC diagnostic ignored "-Wunused-parameter"
298 0 : bool snap_image::info_ico(unsigned char const *s, size_t l, unsigned char const *e)
299 : {
300 : // number of images
301 0 : int const max_images(s[4] + s[5] * 256);
302 :
303 0 : for(int i(0); i < max_images; ++i)
304 : {
305 0 : unsigned char const *q(s + 6 + i * 16);
306 : // width and height are taken from the BMP if 0x0
307 : // also we can verify that it matches to the BMP anyway
308 0 : uint32_t width(q[0]);
309 0 : uint32_t height(q[1]);
310 : // ignore palette, planes, bits per pixel
311 0 : uint32_t size(q[8] + q[9] * 256 + q[10] * 65536 + q[11] * 16777216);
312 0 : uint32_t offset(q[12] + q[13] * 256 + q[14] * 65536 + q[15] * 16777216);
313 :
314 : #pragma GCC diagnostic push
315 : #pragma GCC diagnostic ignored "-Wstrict-overflow"
316 0 : if(offset + size > l || size < 40)
317 : {
318 : // invalid offset/size (out of bounds)
319 0 : return false;
320 : }
321 : #pragma GCC diagnostic pop
322 :
323 0 : unsigned char const *b(s + offset);
324 0 : if(b[0] == 0x89 && b[1] == 'P' && b[2] == 'N' && b[3] == 'G')
325 : {
326 : // We can get the info simply by calling info_png()!
327 0 : if(!info_png(s + offset, size, s + offset + size))
328 : {
329 0 : return false;
330 : }
331 : }
332 : else
333 : {
334 : // TBD is that bitmap info header always 40 bytes?
335 0 : if(b[0] != 0x28 || b[1] != 0 || b[2] != 0 || b[3] != 0)
336 : {
337 : // invalid BITMAPINFOHEADER size
338 0 : return false;
339 : }
340 0 : uint32_t bitmap_width(b[4] + b[5] * 256 + b[6] * 65536 + b[7] * 16777216);
341 0 : uint32_t bitmap_height(b[8] + b[9] * 256 + b[10] * 65536 + b[11] * 16777216);
342 : // Weird! The bitmap sizes may be larger or equal, just not smaller
343 0 : if((width != 0 && bitmap_width < width)
344 0 : || (height != 0 && bitmap_height < height))
345 : {
346 : // sizes between ICO and BITMAPINFOHEADER to do not correspond!
347 0 : return false;
348 : }
349 0 : smart_snap_image_buffer_t buffer(new snap_image_buffer_t(this));
350 :
351 0 : buffer->set_mime_type("image/x-icon");
352 0 : buffer->set_format_version("1.0");
353 0 : buffer->set_width(width != 0 ? width : bitmap_width);
354 0 : buffer->set_height(height != 0 ? height : bitmap_height);
355 0 : int bits(b[14] + b[15] * 256);
356 0 : buffer->set_bits(bits);
357 0 : buffer->set_resolution_unit("m");
358 0 : buffer->set_xres(b[24] + b[25] * 256 + b[26] * 65536 + b[27] * 16777216);
359 0 : buffer->set_yres(b[28] + b[29] * 256 + b[30] * 65536 + b[31] * 16777216);
360 :
361 : // TODO this is not correct, we'd need to know whether we use a palette or not
362 0 : if(bits == 8)
363 : {
364 0 : buffer->set_depth(1);
365 : }
366 0 : else if(bits == 24)
367 : {
368 0 : buffer->set_depth(3);
369 : }
370 0 : else if(bits == 32)
371 : {
372 0 : buffer->set_depth(4);
373 : }
374 :
375 0 : f_buffers.push_back(buffer);
376 : }
377 : }
378 :
379 : // got all the images successfully
380 0 : return true;
381 : }
382 : #pragma GCC diagnostic pop
383 :
384 :
385 : /** \brief Read an BMP header for its information.
386 : *
387 : * This function reads the BMP header information and saves it in a buffer.
388 : *
389 : * Source: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
390 : * Source: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
391 : * Source: https://en.wikipedia.org/wiki/BMP_file_format
392 : *
393 : * \param[in] s The start of the buffer.
394 : * \param[in] l The size of the buffer.
395 : * \param[in] e The end of the buffer (s + l).
396 : *
397 : * \return true if the BMP was read successfully.
398 : */
399 : #pragma GCC diagnostic push
400 : #pragma GCC diagnostic ignored "-Wunused-parameter"
401 0 : bool snap_image::info_bmp(unsigned char const *s, size_t l, unsigned char const *e)
402 : {
403 0 : uint32_t headerinfosize(s[14] + s[15] * 256 + s[16] * 65536 + s[17] * 16777216);
404 : #pragma GCC diagnostic push
405 : #pragma GCC diagnostic ignored "-Wstrict-overflow"
406 0 : if(headerinfosize < 40) // v1.0 is 40 bytes
407 : {
408 : #pragma GCC diagnostic pop
409 : // invalid BITMAPINFOHEADER size
410 0 : return false;
411 : }
412 :
413 0 : smart_snap_image_buffer_t buffer(new snap_image_buffer_t(this));
414 0 : buffer->set_mime_type("image/bmp");
415 :
416 0 : uint32_t width(s[18] + s[19] * 256 + s[20] * 65536 + s[21] * 16777216);
417 0 : uint32_t height(s[22] + s[23] * 256 + s[24] * 65536 + s[25] * 16777216);
418 : #ifdef DEBUG
419 0 : SNAP_LOG_TRACE() << "BMP size (" << width << "x" << height << ")";
420 : #endif
421 0 : if(width == 0 || height == 0)
422 : {
423 : // size just cannot be zero
424 0 : return false;
425 : }
426 0 : buffer->set_width(width);
427 0 : buffer->set_height(height);
428 :
429 0 : switch(headerinfosize)
430 : {
431 0 : case 40:
432 0 : buffer->set_format_version("1.0");
433 0 : break;
434 :
435 : // TBD v2 and v3 exist?
436 :
437 0 : case 108:
438 0 : buffer->set_format_version("4.0");
439 0 : break;
440 :
441 0 : case 124:
442 0 : buffer->set_format_version("5.0");
443 0 : break;
444 :
445 0 : default:
446 : // do not like others for now
447 0 : return false;
448 :
449 : }
450 0 : int bits(s[28] + s[29] * 256);
451 0 : buffer->set_bits(bits);
452 :
453 : // TODO this is not correct, we'd need to know whether we use a palette or not
454 0 : if(bits == 8)
455 : {
456 0 : buffer->set_depth(1);
457 : }
458 0 : else if(bits == 24)
459 : {
460 0 : buffer->set_depth(3);
461 : }
462 0 : else if(bits == 32)
463 : {
464 0 : buffer->set_depth(4);
465 : }
466 :
467 0 : buffer->set_resolution_unit("m");
468 0 : buffer->set_xres(s[38] + s[39] * 256 + s[40] * 65536 + s[41] * 16777216);
469 0 : buffer->set_yres(s[42] + s[43] * 256 + s[44] * 65536 + s[45] * 16777216);
470 :
471 0 : f_buffers.push_back(buffer);
472 :
473 : // got the image successfully
474 0 : return true;
475 : }
476 : #pragma GCC diagnostic pop
477 :
478 :
479 : /** \brief Read a PNG header for its information.
480 : *
481 : * This function reads the PNG header information and saves it in a buffer.
482 : *
483 : * Source: http://www.w3.org/TR/PNG/
484 : *
485 : * \param[in] s The start of the buffer.
486 : * \param[in] l The size of the buffer.
487 : * \param[in] e The end of the buffer (s + l).
488 : *
489 : * \return true if the PNG was read successfully.
490 : */
491 : #pragma GCC diagnostic push
492 : #pragma GCC diagnostic ignored "-Wunused-parameter"
493 0 : bool snap_image::info_png(unsigned char const *s, size_t l, unsigned char const *e)
494 : {
495 0 : smart_snap_image_buffer_t buffer(new snap_image_buffer_t(this));
496 0 : buffer->set_mime_type("image/png");
497 0 : buffer->set_format_version("1.0");
498 :
499 0 : int color_format(-1);
500 0 : unsigned char const *q(s + 8);
501 : for(;;)
502 : {
503 0 : if(q + 12 > e)
504 : {
505 0 : return false;
506 : }
507 0 : uint32_t size(q[0] * 16777216 + q[1] * 65536 + q[2] * 256 + q[3]);
508 : // TODO test the CRC
509 0 : uint32_t name(chunk_name(q[4], q[5], q[6], q[7]));
510 0 : if(name == chunk_name('I','H','D','R') && size == 13)
511 : {
512 0 : buffer->set_width(q[8] * 16777216 + q[9] * 65536 + q[10] * 256 + q[11]);
513 0 : buffer->set_height(q[12] * 16777216 + q[13] * 65536 + q[14] * 256 + q[15]);
514 :
515 0 : color_format = q[17];
516 0 : switch(color_format)
517 : {
518 0 : case 0: // Y
519 0 : buffer->set_bits(q[16]);
520 0 : buffer->set_depth(1);
521 0 : break;
522 :
523 0 : case 2: // RGB
524 0 : buffer->set_bits(q[16] * 3);
525 0 : buffer->set_depth(3);
526 0 : break;
527 :
528 0 : case 3: // Palettized RGB
529 0 : buffer->set_bits(q[16] * 3);
530 0 : buffer->set_depth(3);
531 0 : break;
532 :
533 0 : case 4: // YA
534 0 : buffer->set_bits(q[16] * 2);
535 0 : buffer->set_depth(2);
536 0 : break;
537 :
538 0 : case 6: // RGBA
539 0 : buffer->set_bits(q[16] * 4);
540 0 : buffer->set_depth(4);
541 0 : break;
542 :
543 0 : default:
544 : // not supported
545 0 : return false;
546 :
547 : }
548 : }
549 0 : else if(name == chunk_name('t','R','N','S') && color_format == 3)
550 : {
551 : // well there is an alpha channel, so we have Palettized RGBA
552 0 : buffer->set_bits(buffer->get_bits() / 3 * 4);
553 0 : buffer->set_depth(4);
554 : }
555 0 : else if(name == chunk_name('p','H','Y','s'))
556 : {
557 0 : buffer->set_xres(q[8] * 16777216 + q[9] * 65536 + q[10] * 256 + q[11]);
558 0 : buffer->set_yres(q[12] * 16777216 + q[13] * 65536 + q[14] * 256 + q[15]);
559 0 : buffer->set_resolution_unit(q[16] == 1 ? "m" : "");
560 : }
561 0 : else if(name == chunk_name('I','D','A','T'))
562 : {
563 0 : f_buffers.push_back(buffer);
564 :
565 : // there could be multiple images, create a new buffer
566 0 : smart_snap_image_buffer_t next_image(new snap_image_buffer_t(*buffer));
567 0 : buffer = next_image;
568 : }
569 0 : else if(name == chunk_name('I','E','N','D'))
570 : {
571 0 : return true;
572 : }
573 :
574 0 : q += size + 12;
575 0 : }
576 : }
577 : #pragma GCC diagnostic pop
578 :
579 :
580 :
581 : /** \brief Read a GIF header for its information.
582 : *
583 : * This function reads the GIF header information and saves it in a buffer.
584 : *
585 : * Source: http://www.w3.org/Graphics/GIF/spec-gif89a.txt
586 : *
587 : * \param[in] s The start of the buffer.
588 : * \param[in] l The size of the buffer.
589 : * \param[in] e The end of the buffer (s + l).
590 : *
591 : * \return true if the GIF was read successfully.
592 : */
593 : #pragma GCC diagnostic push
594 : #pragma GCC diagnostic ignored "-Wunused-parameter"
595 0 : bool snap_image::info_gif(unsigned char const *s, size_t l, unsigned char const *e)
596 : {
597 0 : smart_snap_image_buffer_t buffer(new snap_image_buffer_t(this));
598 :
599 0 : buffer->set_mime_type("image/gif");
600 0 : buffer->set_format_version(QString::fromLatin1(reinterpret_cast<char const *>(s) + 3, 3));
601 :
602 0 : buffer->set_width(s[6] + s[7] * 256);
603 0 : buffer->set_height(s[8] + s[9] * 256);
604 :
605 : //int const flags(s[10]);
606 : //int bits(((flags & 0x0E) >> 1) + 1);
607 0 : buffer->set_bits(8 * 3);
608 0 : buffer->set_depth(3); // GIFs are always RGB
609 :
610 0 : int const aspect(s[12]);
611 0 : if(aspect != 0)
612 : {
613 0 : buffer->set_xres(aspect + 15);
614 0 : buffer->set_yres(64);
615 : }
616 :
617 0 : f_buffers.push_back(buffer);
618 :
619 : // TODO create one buffer per image; unfortunately, I do not think we
620 : // can safely skip the image data without parsing it all...
621 :
622 0 : return true;
623 : }
624 : #pragma GCC diagnostic pop
625 :
626 :
627 :
628 0 : bool snap_image::get_info(QByteArray const& data)
629 : {
630 : // this function is a very fast way to detect the image MIME type
631 : // and extract the header information (mainly the width and height
632 : // parameters.)
633 0 : size_t const l(data.size());
634 0 : if(l == 0)
635 : {
636 0 : return false;
637 : }
638 0 : unsigned char const *s(reinterpret_cast<unsigned char const *>(data.constData()));
639 0 : unsigned char const *e(s + l);
640 :
641 : // PNG starts with a clearly recognizable magic
642 0 : if(l >= 30 && s[0] == 0x89 && s[1] == 'P' && s[2] == 'N' && s[3] == 'G'
643 0 : && s[4] == 0x0D && s[5] == 0x0A && s[6] == 0x1A && s[7] == 0x0A)
644 : {
645 0 : return info_png(s, l, e);
646 : }
647 :
648 : // GIF starts with a clearly recognizable magic
649 : // (support other versions than 87a and 89a?)
650 0 : if(l >= 30
651 0 : && s[0] == 'G' && s[1] == 'I' && s[2] == 'F'
652 0 : && s[3] == '8' && (s[4] == '9' || s[4] == '7') && s[5] == 'a')
653 : {
654 0 : return info_gif(s, l, e);
655 : }
656 :
657 : // JPEG start with FF D8 then other markers may be in any order
658 0 : if(l >= 30 && s[0] == 0xFF && s[1] == 0xD8) // SOI marker (start of image)
659 : {
660 0 : return info_jpeg(s, l, e);
661 : }
662 :
663 : // Microsoft Bitmaps start with BM
664 0 : if(l >= 14 + 40 && s[0] == 'B' && s[1] == 'M')
665 : {
666 0 : return info_bmp(s, l, e);
667 : }
668 :
669 : // MS-Windows ICO files do not have a real magic in their header
670 0 : if(l >= 46
671 0 : && s[0] == 0x00 && s[1] == 0x00
672 0 : && s[2] == 0x01 && s[3] == 0x00
673 0 : && (s[4] + s[5] * 256) > 0)
674 : {
675 0 : return info_ico(s, l, e);
676 : }
677 :
678 : // no match...
679 0 : return false;
680 : }
681 :
682 :
683 0 : size_t snap_image::get_size() const
684 : {
685 0 : return f_buffers.size();
686 : }
687 :
688 :
689 0 : smart_snap_image_buffer_t snap_image::get_buffer(int idx)
690 : {
691 0 : return f_buffers[idx];
692 : }
693 :
694 :
695 :
696 6 : } // namespace snap
697 :
698 : // vim: ts=4 sw=4 et
|