Home | History | Annotate | Download | only in png
      1 /*
      2  * Copyright (C) 2006 Apple Computer, Inc.
      3  * Copyright (C) 2007-2009 Torch Mobile, Inc.
      4  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
      5  *
      6  * Portions are Copyright (C) 2001 mozilla.org
      7  *
      8  * Other contributors:
      9  *   Stuart Parmenter <stuart (at) mozilla.com>
     10  *
     11  * This library is free software; you can redistribute it and/or
     12  * modify it under the terms of the GNU Lesser General Public
     13  * License as published by the Free Software Foundation; either
     14  * version 2.1 of the License, or (at your option) any later version.
     15  *
     16  * This library is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     19  * Lesser General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU Lesser General Public
     22  * License along with this library; if not, write to the Free Software
     23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     24  *
     25  * Alternatively, the contents of this file may be used under the terms
     26  * of either the Mozilla Public License Version 1.1, found at
     27  * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
     28  * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
     29  * (the "GPL"), in which case the provisions of the MPL or the GPL are
     30  * applicable instead of those above.  If you wish to allow use of your
     31  * version of this file only under the terms of one of those two
     32  * licenses (the MPL or the GPL) and not to allow others to use your
     33  * version of this file under the LGPL, indicate your decision by
     34  * deletingthe provisions above and replace them with the notice and
     35  * other provisions required by the MPL or the GPL, as the case may be.
     36  * If you do not delete the provisions above, a recipient may use your
     37  * version of this file under any of the LGPL, the MPL or the GPL.
     38  */
     39 
     40 #include "config.h"
     41 #include "PNGImageDecoder.h"
     42 #include "png.h"
     43 
     44 namespace WebCore {
     45 
     46 // Gamma constants.
     47 const double cMaxGamma = 21474.83;
     48 const double cDefaultGamma = 2.2;
     49 const double cInverseGamma = 0.45455;
     50 
     51 // Protect against large PNGs. See Mozilla's bug #251381 for more info.
     52 const unsigned long cMaxPNGSize = 1000000UL;
     53 
     54 // Called if the decoding of the image fails.
     55 static void PNGAPI decodingFailed(png_structp png_ptr, png_const_charp error_msg);
     56 
     57 // Callbacks given to the read struct.  The first is for warnings (we want to treat a particular warning
     58 // as an error, which is why we have to register this callback.
     59 static void PNGAPI decodingWarning(png_structp png_ptr, png_const_charp warning_msg);
     60 
     61 // Called when we have obtained the header information (including the size).
     62 static void PNGAPI headerAvailable(png_structp png_ptr, png_infop info_ptr);
     63 
     64 // Called when a row is ready.
     65 static void PNGAPI rowAvailable(png_structp png_ptr, png_bytep new_row,
     66                                 png_uint_32 row_num, int pass);
     67 
     68 // Called when we have completely finished decoding the image.
     69 static void PNGAPI pngComplete(png_structp png_ptr, png_infop info_ptr);
     70 
     71 class PNGImageReader
     72 {
     73 public:
     74     PNGImageReader(PNGImageDecoder* decoder)
     75         : m_readOffset(0)
     76         , m_decodingSizeOnly(false)
     77         , m_interlaceBuffer(0)
     78         , m_hasAlpha(0)
     79         , m_hasFinishedDecoding(false)
     80         , m_currentBufferSize(0)
     81     {
     82         m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning);
     83         m_info = png_create_info_struct(m_png);
     84         png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete);
     85     }
     86 
     87     ~PNGImageReader()
     88     {
     89         close();
     90     }
     91 
     92     void close() {
     93         if (m_png && m_info)
     94             png_destroy_read_struct(&m_png, &m_info, 0);  // Will zero the pointers.
     95         delete []m_interlaceBuffer;
     96         m_interlaceBuffer = 0;
     97         m_readOffset = 0;
     98         m_hasFinishedDecoding = false;
     99     }
    100 
    101     unsigned currentBufferSize() const { return m_currentBufferSize; }
    102 
    103     void setComplete() { m_hasFinishedDecoding = true; }
    104 
    105     void decode(const SharedBuffer& data, bool sizeOnly)
    106     {
    107         m_decodingSizeOnly = sizeOnly;
    108 
    109         // We need to do the setjmp here. Otherwise bad things will happen
    110         if (setjmp(m_png->jmpbuf)) {
    111             close();
    112             return;
    113         }
    114 
    115         PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png));
    116         const char* segment;
    117         while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) {
    118             m_readOffset += segmentLength;
    119             m_currentBufferSize = m_readOffset;
    120             png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength);
    121             if ((sizeOnly && decoder->isSizeAvailable()) || m_hasFinishedDecoding)
    122                 return;
    123         }
    124         if (!m_hasFinishedDecoding && decoder->isAllDataReceived())
    125             decoder->pngComplete();
    126     }
    127 
    128     bool decodingSizeOnly() const { return m_decodingSizeOnly; }
    129     png_structp pngPtr() const { return m_png; }
    130     png_infop infoPtr() const { return m_info; }
    131     png_bytep interlaceBuffer() const { return m_interlaceBuffer; }
    132     bool hasAlpha() const { return m_hasAlpha; }
    133 
    134     void setReadOffset(unsigned offset) { m_readOffset = offset; }
    135     void setHasAlpha(bool b) { m_hasAlpha = b; }
    136 
    137     void createInterlaceBuffer(int size) {
    138         m_interlaceBuffer = new png_byte[size];
    139     }
    140 
    141 private:
    142     unsigned m_readOffset;
    143     bool m_decodingSizeOnly;
    144     png_structp m_png;
    145     png_infop m_info;
    146     png_bytep m_interlaceBuffer;
    147     bool m_hasAlpha;
    148     bool m_hasFinishedDecoding;
    149     unsigned m_currentBufferSize;
    150 };
    151 
    152 PNGImageDecoder::PNGImageDecoder()
    153 {
    154 }
    155 
    156 PNGImageDecoder::~PNGImageDecoder()
    157 {
    158 }
    159 
    160 // Take the data and store it.
    161 void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
    162 {
    163     if (m_failed)
    164         return;
    165 
    166     // Cache our new data.
    167     ImageDecoder::setData(data, allDataReceived);
    168 
    169     // Create the PNG reader.
    170     if (!m_reader && !m_failed)
    171         m_reader.set(new PNGImageReader(this));
    172 }
    173 
    174 // Whether or not the size information has been decoded yet.
    175 bool PNGImageDecoder::isSizeAvailable()
    176 {
    177     if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader)
    178          decode(true);
    179 
    180     return ImageDecoder::isSizeAvailable();
    181 }
    182 
    183 RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index)
    184 {
    185     if (index)
    186         return 0;
    187 
    188     if (m_frameBufferCache.isEmpty())
    189         m_frameBufferCache.resize(1);
    190 
    191     RGBA32Buffer& frame = m_frameBufferCache[0];
    192     if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
    193         // Decode this frame.
    194         decode();
    195     return &frame;
    196 }
    197 
    198 // Feed data to the PNG reader.
    199 void PNGImageDecoder::decode(bool sizeOnly)
    200 {
    201     if (m_failed)
    202         return;
    203 
    204     m_reader->decode(*m_data, sizeOnly);
    205 
    206     if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete))
    207         m_reader.clear();
    208 }
    209 
    210 void decodingFailed(png_structp png, png_const_charp errorMsg)
    211 {
    212     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed();
    213     longjmp(png->jmpbuf, 1);
    214 }
    215 
    216 void decodingWarning(png_structp png, png_const_charp warningMsg)
    217 {
    218   // Mozilla did this, so we will too.
    219   // Convert a tRNS warning to be an error (documented in bugzilla.mozilla.org bug #251381)
    220   if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24))
    221       png_error(png, warningMsg);
    222 }
    223 
    224 void headerAvailable(png_structp png, png_infop info)
    225 {
    226     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable();
    227 }
    228 
    229 void PNGImageDecoder::decodingFailed()
    230 {
    231     m_failed = true;
    232 }
    233 
    234 void PNGImageDecoder::headerAvailable()
    235 {
    236     png_structp png = m_reader->pngPtr();
    237     png_infop info = m_reader->infoPtr();
    238     png_uint_32 width = png->width;
    239     png_uint_32 height = png->height;
    240 
    241     // Protect against large images.
    242     if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) {
    243         m_failed = true;
    244         longjmp(png->jmpbuf, 1);
    245         return;
    246     }
    247 
    248     // We can fill in the size now that the header is available.
    249     if (!ImageDecoder::isSizeAvailable()) {
    250         if (!setSize(width, height)) {
    251             // Size unreasonable, bail out.
    252             longjmp(png->jmpbuf, 1);
    253             return;
    254         }
    255         prepareScaleDataIfNecessary();
    256     }
    257 
    258     int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
    259     png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
    260                  &interlaceType, &compressionType, &filterType);
    261 
    262     // The options we set here match what Mozilla does.
    263 
    264     // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
    265     if (colorType == PNG_COLOR_TYPE_PALETTE ||
    266         (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
    267         png_set_expand(png);
    268 
    269     png_bytep trns = 0;
    270     int trnsCount = 0;
    271     if (png_get_valid(png, info, PNG_INFO_tRNS)) {
    272         png_get_tRNS(png, info, &trns, &trnsCount, 0);
    273         png_set_expand(png);
    274     }
    275 
    276     if (bitDepth == 16)
    277         png_set_strip_16(png);
    278 
    279     if (colorType == PNG_COLOR_TYPE_GRAY ||
    280         colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
    281         png_set_gray_to_rgb(png);
    282 
    283     // Deal with gamma and keep it under our control.
    284     double gamma;
    285     if (png_get_gAMA(png, info, &gamma)) {
    286         if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
    287             gamma = cInverseGamma;
    288             png_set_gAMA(png, info, gamma);
    289         }
    290         png_set_gamma(png, cDefaultGamma, gamma);
    291     }
    292     else
    293         png_set_gamma(png, cDefaultGamma, cInverseGamma);
    294 
    295     // Tell libpng to send us rows for interlaced pngs.
    296     if (interlaceType == PNG_INTERLACE_ADAM7)
    297         png_set_interlace_handling(png);
    298 
    299     // Update our info now
    300     png_read_update_info(png, info);
    301     channels = png_get_channels(png, info);
    302     ASSERT(channels == 3 || channels == 4);
    303 
    304     m_reader->setHasAlpha(channels == 4);
    305 
    306     if (m_reader->decodingSizeOnly()) {
    307         // If we only needed the size, halt the reader.
    308         m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
    309         png->buffer_size = 0;
    310     }
    311 }
    312 
    313 void rowAvailable(png_structp png, png_bytep rowBuffer,
    314                   png_uint_32 rowIndex, int interlacePass)
    315 {
    316     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass);
    317 }
    318 
    319 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
    320 {
    321     if (m_frameBufferCache.isEmpty())
    322         return;
    323 
    324     // Initialize the framebuffer if needed.
    325     RGBA32Buffer& buffer = m_frameBufferCache[0];
    326     if (buffer.status() == RGBA32Buffer::FrameEmpty) {
    327         if (!buffer.setSize(scaledSize().width(), scaledSize().height())) {
    328             static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_reader->pngPtr()))->decodingFailed();
    329             longjmp(m_reader->pngPtr()->jmpbuf, 1);
    330             return;
    331         }
    332         buffer.setStatus(RGBA32Buffer::FramePartial);
    333         buffer.setHasAlpha(false);
    334 
    335         // For PNGs, the frame always fills the entire image.
    336         buffer.setRect(IntRect(IntPoint(), size()));
    337 
    338         if (m_reader->pngPtr()->interlaced)
    339             m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height());
    340     }
    341 
    342     if (rowBuffer == 0)
    343         return;
    344 
    345    /* libpng comments (pasted in here to explain what follows)
    346     *
    347     * this function is called for every row in the image.  If the
    348     * image is interlacing, and you turned on the interlace handler,
    349     * this function will be called for every row in every pass.
    350     * Some of these rows will not be changed from the previous pass.
    351     * When the row is not changed, the new_row variable will be NULL.
    352     * The rows and passes are called in order, so you don't really
    353     * need the row_num and pass, but I'm supplying them because it
    354     * may make your life easier.
    355     *
    356     * For the non-NULL rows of interlaced images, you must call
    357     * png_progressive_combine_row() passing in the row and the
    358     * old row.  You can call this function for NULL rows (it will
    359     * just return) and for non-interlaced images (it just does the
    360     * memcpy for you) if it will make the code easier.  Thus, you
    361     * can just do this for all cases:
    362     *
    363     *    png_progressive_combine_row(png_ptr, old_row, new_row);
    364     *
    365     * where old_row is what was displayed for previous rows.  Note
    366     * that the first pass (pass == 0 really) will completely cover
    367     * the old row, so the rows do not have to be initialized.  After
    368     * the first pass (and only for interlaced images), you will have
    369     * to pass the current row, and the function will combine the
    370     * old row and the new row.
    371     */
    372 
    373     png_structp png = m_reader->pngPtr();
    374     bool hasAlpha = m_reader->hasAlpha();
    375     unsigned colorChannels = hasAlpha ? 4 : 3;
    376     png_bytep row;
    377     png_bytep interlaceBuffer = m_reader->interlaceBuffer();
    378     if (interlaceBuffer) {
    379         row = interlaceBuffer + (rowIndex * colorChannels * size().width());
    380         png_progressive_combine_row(png, row, rowBuffer);
    381     }
    382     else
    383         row = rowBuffer;
    384 
    385     // Copy the data into our buffer.
    386     int width = scaledSize().width();
    387     int destY = scaledY(rowIndex);
    388     if (destY < 0)
    389         return;
    390     bool sawAlpha = buffer.hasAlpha();
    391     for (int x = 0; x < width; x++) {
    392         png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels;
    393         unsigned alpha = hasAlpha ? pixel[3] : 255;
    394         buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha);
    395         if (!sawAlpha && alpha < 255) {
    396             sawAlpha = true;
    397             buffer.setHasAlpha(true);
    398         }
    399     }
    400 }
    401 
    402 void pngComplete(png_structp png, png_infop info)
    403 {
    404     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete();
    405 }
    406 
    407 void PNGImageDecoder::pngComplete()
    408 {
    409     m_reader->setComplete();
    410 
    411     if (m_frameBufferCache.isEmpty())
    412         return;
    413 
    414     // Hand back an appropriately sized buffer, even if the image ended up being empty.
    415     RGBA32Buffer& buffer = m_frameBufferCache[0];
    416     buffer.setStatus(RGBA32Buffer::FrameComplete);
    417 }
    418 
    419 } // namespace WebCore
    420