LCOV - code coverage report
Current view: top level - snapwebsites - snap_image.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 257 0.4 %
Date: 2019-12-15 17:13:15 Functions: 2 31 6.5 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13