root/trunk/BAL/ImageDecoder/WebCore/PNG/WK/BCPNGImageDecoderWK.cpp

Revision 1387, 13.5 kB (checked in by gpenin, 7 months ago)

merge with webkit revision 55226

Line 
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@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, png_const_charp)
56 {
57     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed();
58     longjmp(png->jmpbuf, 1);
59 }
60
61 // Callbacks given to the read struct.  The first is for warnings (we want to
62 // treat a particular warning as an error, which is why we have to register this
63 // callback).
64 static void PNGAPI decodingWarning(png_structp png, png_const_charp warningMsg)
65 {
66     // Mozilla did this, so we will too.
67     // Convert a tRNS warning to be an error (see
68     // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 )
69     if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24))
70         png_error(png, warningMsg);
71 }
72
73 // Called when we have obtained the header information (including the size).
74 static void PNGAPI headerAvailable(png_structp png, png_infop)
75 {
76     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable();
77 }
78
79 // Called when a row is ready.
80 static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass)
81 {
82     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass);
83 }
84
85 // Called when we have completely finished decoding the image.
86 static void PNGAPI pngComplete(png_structp png, png_infop)
87 {
88     static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete();
89 }
90
91 class PNGImageReader
92 {
93 public:
94     PNGImageReader(PNGImageDecoder* decoder)
95         : m_readOffset(0)
96         , m_decodingSizeOnly(false)
97         , m_interlaceBuffer(0)
98         , m_hasAlpha(false)
99         , m_hasFinishedDecoding(false)
100         , m_currentBufferSize(0)
101     {
102         m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning);
103         m_info = png_create_info_struct(m_png);
104         png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete);
105     }
106
107     ~PNGImageReader()
108     {
109         close();
110     }
111
112     void close()
113     {
114         if (m_png && m_info)
115             // This will zero the pointers.
116             png_destroy_read_struct(&m_png, &m_info, 0);
117         delete[] m_interlaceBuffer;
118         m_interlaceBuffer = 0;
119         m_readOffset = 0;
120         m_hasFinishedDecoding = false;
121     }
122
123     unsigned currentBufferSize() const { return m_currentBufferSize; }
124
125     void setComplete() { m_hasFinishedDecoding = true; }
126
127     void decode(const SharedBuffer& data, bool sizeOnly)
128     {
129         m_decodingSizeOnly = sizeOnly;
130
131         // We need to do the setjmp here. Otherwise bad things will happen.
132         if (setjmp(m_png->jmpbuf)) {
133             close();
134             return;
135         }
136
137         PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png));
138         const char* segment;
139         while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) {
140             m_readOffset += segmentLength;
141             m_currentBufferSize = m_readOffset;
142             png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength);
143             // We explicitly specify the superclass isSizeAvailable() because we
144             // merely want to check if we've managed to set the size, not
145             // (recursively) trigger additional decoding if we haven't.
146             if ((sizeOnly && decoder->ImageDecoder::isSizeAvailable()) || m_hasFinishedDecoding)
147                 return;
148         }
149         if (!m_hasFinishedDecoding && decoder->isAllDataReceived())
150             decoder->pngComplete();
151     }
152
153     bool decodingSizeOnly() const { return m_decodingSizeOnly; }
154     png_structp pngPtr() const { return m_png; }
155     png_infop infoPtr() const { return m_info; }
156     png_bytep interlaceBuffer() const { return m_interlaceBuffer; }
157     bool hasAlpha() const { return m_hasAlpha; }
158
159     void setReadOffset(unsigned offset) { m_readOffset = offset; }
160     void setHasAlpha(bool b) { m_hasAlpha = b; }
161
162     void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; }
163
164 private:
165     unsigned m_readOffset;
166     bool m_decodingSizeOnly;
167     png_structp m_png;
168     png_infop m_info;
169     png_bytep m_interlaceBuffer;
170     bool m_hasAlpha;
171     bool m_hasFinishedDecoding;
172     unsigned m_currentBufferSize;
173 };
174
175 PNGImageDecoder::PNGImageDecoder()
176 {
177 }
178
179 PNGImageDecoder::~PNGImageDecoder()
180 {
181 }
182
183 void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
184 {
185     if (m_failed)
186         return;
187
188     ImageDecoder::setData(data, allDataReceived);
189
190     if (!m_reader && !m_failed)
191         m_reader.set(new PNGImageReader(this));
192 }
193 bool PNGImageDecoder::isSizeAvailable()
194 {
195     if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader)
196          decode(true);
197
198     return ImageDecoder::isSizeAvailable();
199 }
200
201 RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index)
202 {
203     if (index)
204         return 0;
205
206     if (m_frameBufferCache.isEmpty())
207         m_frameBufferCache.resize(1);
208
209     RGBA32Buffer& frame = m_frameBufferCache[0];
210     if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
211         // Decode this frame.
212         decode(false);
213     return &frame;
214 }
215
216 void PNGImageDecoder::decodingFailed()
217 {
218     m_failed = true;
219 }
220
221 void PNGImageDecoder::headerAvailable()
222 {
223     png_structp png = m_reader->pngPtr();
224     png_infop info = m_reader->infoPtr();
225     png_uint_32 width = png->width;
226     png_uint_32 height = png->height;
227    
228     // Protect against large images.
229     if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) {
230         m_failed = true;
231         longjmp(png->jmpbuf, 1);
232         return;
233     }
234    
235     // We can fill in the size now that the header is available.
236     if (!ImageDecoder::isSizeAvailable()) {
237         if (!setSize(width, height)) {
238             // Size unreasonable, bail out.
239             longjmp(png->jmpbuf, 1);
240             return;
241         }
242         prepareScaleDataIfNecessary();
243     }
244
245     int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
246     png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);
247
248     // The options we set here match what Mozilla does.
249
250     // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
251     if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
252         png_set_expand(png);
253    
254     png_bytep trns = 0;
255     int trnsCount = 0;
256     if (png_get_valid(png, info, PNG_INFO_tRNS)) {
257         png_get_tRNS(png, info, &trns, &trnsCount, 0);
258         png_set_expand(png);
259     }
260
261     if (bitDepth == 16)
262         png_set_strip_16(png);
263
264     if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
265         png_set_gray_to_rgb(png);
266
267     // Deal with gamma and keep it under our control.
268     double gamma;
269     if (png_get_gAMA(png, info, &gamma)) {
270         if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
271             gamma = cInverseGamma;
272             png_set_gAMA(png, info, gamma);
273         }
274         png_set_gamma(png, cDefaultGamma, gamma);
275     } else
276         png_set_gamma(png, cDefaultGamma, cInverseGamma);
277
278     // Tell libpng to send us rows for interlaced pngs.
279     if (interlaceType == PNG_INTERLACE_ADAM7)
280         png_set_interlace_handling(png);
281
282     // Update our info now.
283     png_read_update_info(png, info);
284     channels = png_get_channels(png, info);
285     ASSERT(channels == 3 || channels == 4);
286
287     m_reader->setHasAlpha(channels == 4);
288
289     if (m_reader->decodingSizeOnly()) {
290         // If we only needed the size, halt the reader.     
291         m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
292         png->buffer_size = 0;
293     }
294 }
295
296 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
297 {
298     if (m_frameBufferCache.isEmpty())
299         return;
300
301     // Initialize the framebuffer if needed.
302     RGBA32Buffer& buffer = m_frameBufferCache[0];
303     if (buffer.status() == RGBA32Buffer::FrameEmpty) {
304         if (!buffer.setSize(scaledSize().width(), scaledSize().height())) {
305             static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_reader->pngPtr()))->decodingFailed();
306             longjmp(m_reader->pngPtr()->jmpbuf, 1);
307             return;
308         }
309         buffer.setStatus(RGBA32Buffer::FramePartial);
310         buffer.setHasAlpha(false);
311
312         // For PNGs, the frame always fills the entire image.
313         buffer.setRect(IntRect(IntPoint(), size()));
314
315         if (m_reader->pngPtr()->interlaced)
316             m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height());
317     }
318
319     if (!rowBuffer)
320         return;
321
322     // libpng comments (pasted in here to explain what follows)
323     /*
324      * this function is called for every row in the image.  If the
325      * image is interlacing, and you turned on the interlace handler,
326      * this function will be called for every row in every pass.
327      * Some of these rows will not be changed from the previous pass.
328      * When the row is not changed, the new_row variable will be NULL.
329      * The rows and passes are called in order, so you don't really
330      * need the row_num and pass, but I'm supplying them because it
331      * may make your life easier.
332      *
333      * For the non-NULL rows of interlaced images, you must call
334      * png_progressive_combine_row() passing in the row and the
335      * old row.  You can call this function for NULL rows (it will
336      * just return) and for non-interlaced images (it just does the
337      * memcpy for you) if it will make the code easier.  Thus, you
338      * can just do this for all cases:
339      *
340      *    png_progressive_combine_row(png_ptr, old_row, new_row);
341      *
342      * where old_row is what was displayed for previous rows.  Note
343      * that the first pass (pass == 0 really) will completely cover
344      * the old row, so the rows do not have to be initialized.  After
345      * the first pass (and only for interlaced images), you will have
346      * to pass the current row, and the function will combine the
347      * old row and the new row.
348      */
349
350     png_structp png = m_reader->pngPtr();
351     bool hasAlpha = m_reader->hasAlpha();
352     unsigned colorChannels = hasAlpha ? 4 : 3;
353     png_bytep row;
354     png_bytep interlaceBuffer = m_reader->interlaceBuffer();
355     if (interlaceBuffer) {
356         row = interlaceBuffer + (rowIndex * colorChannels * size().width());
357         png_progressive_combine_row(png, row, rowBuffer);
358     } else
359         row = rowBuffer;
360
361     // Copy the data into our buffer.
362     int width = scaledSize().width();
363     int destY = scaledY(rowIndex);
364     if (destY < 0)
365         return;
366     bool sawAlpha = buffer.hasAlpha();
367     for (int x = 0; x < width; ++x) {
368         png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels;
369         unsigned alpha = hasAlpha ? pixel[3] : 255;
370         buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha);
371         if (!sawAlpha && alpha < 255) {
372             sawAlpha = true;
373             buffer.setHasAlpha(true);
374         }
375     }
376 }
377
378 void PNGImageDecoder::pngComplete()
379 {
380     m_reader->setComplete();
381
382     if (!m_frameBufferCache.isEmpty())
383         m_frameBufferCache.first().setStatus(RGBA32Buffer::FrameComplete);
384 }
385
386 void PNGImageDecoder::decode(bool onlySize)
387 {
388     if (m_failed)
389         return;
390
391     m_reader->decode(*m_data, onlySize);
392    
393     if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete))
394         m_reader.clear();
395 }
396
397 } // namespace WebCore
398
Note: See TracBrowser for help on using the browser.