OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006 Apple Computer, Inc. | 2 * Copyright (C) 2006 Apple Computer, Inc. |
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. | 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. |
4 * | 4 * |
5 * Portions are Copyright (C) 2001 mozilla.org | 5 * Portions are Copyright (C) 2001 mozilla.org |
6 * | 6 * |
7 * Other contributors: | 7 * Other contributors: |
8 * Stuart Parmenter <stuart@mozilla.com> | 8 * Stuart Parmenter <stuart@mozilla.com> |
9 * | 9 * |
10 * This library is free software; you can redistribute it and/or | 10 * This library is free software; you can redistribute it and/or |
(...skipping 21 matching lines...) Expand all Loading... |
32 * version of this file under the LGPL, indicate your decision by | 32 * version of this file under the LGPL, indicate your decision by |
33 * deletingthe provisions above and replace them with the notice and | 33 * deletingthe provisions above and replace them with the notice and |
34 * other provisions required by the MPL or the GPL, as the case may be. | 34 * other provisions required by the MPL or the GPL, as the case may be. |
35 * If you do not delete the provisions above, a recipient may use your | 35 * If you do not delete the provisions above, a recipient may use your |
36 * version of this file under any of the LGPL, the MPL or the GPL. | 36 * version of this file under any of the LGPL, the MPL or the GPL. |
37 */ | 37 */ |
38 | 38 |
39 #include "config.h" | 39 #include "config.h" |
40 #include "platform/image-decoders/png/PNGImageDecoder.h" | 40 #include "platform/image-decoders/png/PNGImageDecoder.h" |
41 | 41 |
| 42 #include "platform/RuntimeEnabledFeatures.h" |
42 #include "wtf/PassOwnPtr.h" | 43 #include "wtf/PassOwnPtr.h" |
43 | 44 |
44 #include "png.h" | 45 #include "png.h" |
45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) | 46 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) |
46 #error version error: compile against a versioned libpng. | 47 #error version error: compile against a versioned libpng. |
47 #endif | 48 #endif |
48 #if USE(QCMSLIB) | 49 #if USE(QCMSLIB) |
49 #include "qcms.h" | 50 #include "qcms.h" |
50 #endif | 51 #endif |
51 | 52 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
85 } // anonymous | 86 } // anonymous |
86 | 87 |
87 namespace blink { | 88 namespace blink { |
88 | 89 |
89 class PNGImageReader { | 90 class PNGImageReader { |
90 WTF_MAKE_FAST_ALLOCATED(PNGImageReader); | 91 WTF_MAKE_FAST_ALLOCATED(PNGImageReader); |
91 public: | 92 public: |
92 PNGImageReader(PNGImageDecoder* decoder) | 93 PNGImageReader(PNGImageDecoder* decoder) |
93 : m_decoder(decoder) | 94 : m_decoder(decoder) |
94 , m_readOffset(0) | 95 , m_readOffset(0) |
95 , m_currentBufferSize(0) | 96 , m_parseOffset(0) |
96 , m_decodingSizeOnly(false) | 97 , m_infoSize(0) |
| 98 , m_isAnimated(false) |
| 99 , m_isParsed(false) |
| 100 , m_width(0) |
| 101 , m_height(0) |
| 102 , m_repetitionCount(cAnimationNone) |
| 103 , m_posterFrame(0) |
| 104 , m_visibleFrames(1) |
97 , m_hasAlpha(false) | 105 , m_hasAlpha(false) |
98 #if USE(QCMSLIB) | 106 #if USE(QCMSLIB) |
99 , m_transform(0) | 107 , m_transform(0) |
100 , m_rowBuffer() | 108 , m_rowBuffer() |
101 #endif | 109 #endif |
102 { | 110 { |
103 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); | 111 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
104 m_info = png_create_info_struct(m_png); | 112 m_info = png_create_info_struct(m_png); |
105 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pngRow
Available, pngComplete); | 113 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pngRow
Available, pngComplete); |
| 114 memset(&m_currentFrame, 0, sizeof(m_currentFrame)); |
106 } | 115 } |
107 | 116 |
108 ~PNGImageReader() | 117 ~PNGImageReader() |
109 { | 118 { |
110 close(); | 119 close(); |
111 } | 120 } |
112 | 121 |
| 122 struct FrameInfo { |
| 123 unsigned start; |
| 124 unsigned finish; |
| 125 unsigned width; |
| 126 unsigned height; |
| 127 unsigned xOffset; |
| 128 unsigned yOffset; |
| 129 unsigned duration; |
| 130 unsigned dispose; |
| 131 unsigned blend; |
| 132 }; |
| 133 |
| 134 static const unsigned headerSize = 33; |
| 135 |
113 void close() | 136 void close() |
114 { | 137 { |
115 if (m_png && m_info) | 138 if (m_png && m_info) |
116 // This will zero the pointers. | 139 // This will zero the pointers. |
117 png_destroy_read_struct(&m_png, &m_info, 0); | 140 png_destroy_read_struct(&m_png, &m_info, 0); |
118 #if USE(QCMSLIB) | 141 #if USE(QCMSLIB) |
119 clearColorTransform(); | 142 clearColorTransform(); |
120 #endif | 143 #endif |
121 m_readOffset = 0; | 144 m_readOffset = 0; |
| 145 m_parseOffset = 0; |
122 } | 146 } |
123 | 147 |
124 bool decode(const SharedBuffer& data, bool sizeOnly) | 148 bool parse(const SharedBuffer& data) |
125 { | 149 { |
126 m_decodingSizeOnly = sizeOnly; | 150 if (data.size() < headerSize) |
| 151 return false; |
127 | 152 |
128 // We need to do the setjmp here. Otherwise bad things will happen. | 153 const unsigned long maxPNGSize = 1000000UL; |
129 if (setjmp(JMPBUF(m_png))) | 154 png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>(data
.data())); |
130 return m_decoder->setFailed(); | 155 unsigned inputSize = data.size(); |
131 | 156 |
132 const char* segment; | 157 if (!m_parseOffset) { |
133 while (unsigned segmentLength = data.getSomeData(segment, m_readOffset))
{ | 158 if (memcmp(data.data() + 12, "IHDR", 4)) |
134 m_readOffset += segmentLength; | 159 return false; |
135 m_currentBufferSize = m_readOffset; | 160 m_currentFrame.width = m_width = png_get_uint_32(inputData + 16); |
136 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_ca
st<char*>(segment)), segmentLength); | 161 m_currentFrame.height = m_height = png_get_uint_32(inputData + 20); |
137 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() : m_decoder->fram
eIsCompleteAtIndex(0)) | 162 if (m_width > maxPNGSize || m_height > maxPNGSize) |
138 return true; | 163 return false; |
| 164 png_byte bitDepth = inputData[24]; |
| 165 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8
&& bitDepth != 16) |
| 166 return false; |
| 167 png_byte colorType = inputData[25]; |
| 168 if (colorType == 1 || colorType == 5 || colorType > 6) |
| 169 return false; |
| 170 if (crc32(crc32(0, Z_NULL, 0), inputData + 12, 17) != png_get_uint_3
2(inputData + 29)) |
| 171 return false; |
| 172 |
| 173 m_infoSize = m_parseOffset = headerSize; |
139 } | 174 } |
140 | 175 |
141 return false; | 176 while (!m_isParsed) { |
| 177 if (inputSize < m_parseOffset + 12) |
| 178 return false; |
| 179 |
| 180 png_bytep chunk = inputData + m_parseOffset; |
| 181 png_bytep chunkId = chunk + 4; |
| 182 unsigned chunkSize = png_get_uint_32(chunk); |
| 183 unsigned chunkEnd = m_parseOffset + chunkSize + 12; |
| 184 |
| 185 if (!memcmp(chunkId, "IEND", 4) && !chunkSize) { |
| 186 if (!m_currentFrame.start) |
| 187 return false; |
| 188 m_isParsed = true; |
| 189 m_currentFrame.finish = chunkEnd; |
| 190 m_frames.append(m_currentFrame); |
| 191 return true; |
| 192 } |
| 193 if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) { |
| 194 m_currentFrame.start = m_parseOffset; |
| 195 if (!m_decoder->setSize(m_width, m_height)) |
| 196 return false; |
| 197 } |
| 198 |
| 199 if (inputSize < chunkEnd) |
| 200 return false; |
| 201 |
| 202 if (RuntimeEnabledFeatures::animatedPNGEnabled()) { |
| 203 if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated && !m_currentFra
me.start) { |
| 204 m_currentFrame.start = m_parseOffset; |
| 205 } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 && !m_i
sAnimated && !m_currentFrame.start) { |
| 206 m_isAnimated = true; |
| 207 m_posterFrame = 1; |
| 208 m_visibleFrames = 0; |
| 209 m_repetitionCount = static_cast<int>(png_get_uint_32(chunk +
12)) - 1; |
| 210 } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 && (m_
isAnimated || !m_currentFrame.start)) { |
| 211 if (m_currentFrame.start) { |
| 212 m_currentFrame.finish = chunkEnd; |
| 213 m_frames.append(m_currentFrame); |
| 214 m_currentFrame.start = 0; |
| 215 } else if (m_frames.isEmpty()) { |
| 216 m_posterFrame = 0; |
| 217 } |
| 218 |
| 219 m_currentFrame.width = png_get_uint_32(chunk + 12); |
| 220 m_currentFrame.height = png_get_uint_32(chunk + 16); |
| 221 m_currentFrame.xOffset = png_get_uint_32(chunk + 20); |
| 222 m_currentFrame.yOffset = png_get_uint_32(chunk + 24); |
| 223 unsigned numerator = png_get_uint_16(chunk + 28); |
| 224 unsigned denominator = png_get_uint_16(chunk + 30); |
| 225 m_currentFrame.duration = (!denominator) ? numerator * 10 :
(int)(numerator * 1000.0 / denominator); |
| 226 m_currentFrame.dispose = chunk[32]; |
| 227 m_currentFrame.blend = chunk[33]; |
| 228 |
| 229 if (m_currentFrame.width > maxPNGSize || m_currentFrame.heig
ht > maxPNGSize |
| 230 || m_currentFrame.xOffset > maxPNGSize || m_currentFrame
.yOffset > maxPNGSize |
| 231 || m_currentFrame.xOffset + m_currentFrame.width > m_wid
th |
| 232 || m_currentFrame.yOffset + m_currentFrame.height > m_he
ight |
| 233 || m_currentFrame.dispose > 2 || m_currentFrame.blend >
1) |
| 234 return false; |
| 235 |
| 236 if (!m_visibleFrames) { |
| 237 m_currentFrame.blend = 0; |
| 238 if (m_currentFrame.dispose == 2) |
| 239 m_currentFrame.dispose = 1; |
| 240 } |
| 241 m_visibleFrames++; |
| 242 } else if (!m_currentFrame.start && m_frames.isEmpty()) { |
| 243 m_infoSize = chunkEnd; |
| 244 } |
| 245 } |
| 246 |
| 247 m_parseOffset = chunkEnd; |
| 248 } |
| 249 return true; |
| 250 } |
| 251 |
| 252 bool decode(const SharedBuffer& data, size_t index) |
| 253 { |
| 254 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96,
130}; |
| 255 |
| 256 index += m_posterFrame; |
| 257 if (index && index >= m_frames.size()) |
| 258 return true; |
| 259 |
| 260 if (index || !m_readOffset) { |
| 261 png_destroy_read_struct(&m_png, &m_info, 0); |
| 262 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed,
0); |
| 263 m_info = png_create_info_struct(m_png); |
| 264 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pn
gRowAvailable, pngComplete); |
| 265 } |
| 266 |
| 267 if (setjmp(JMPBUF(m_png))) |
| 268 return false; |
| 269 |
| 270 if (!index) { |
| 271 const char* segment; |
| 272 unsigned endOffset = (!m_frames.isEmpty()) ? m_frames[0].finish : 0; |
| 273 while (unsigned segmentLength = data.getSomeData(segment, m_readOffs
et)) { |
| 274 if (endOffset && m_readOffset + segmentLength > endOffset) |
| 275 segmentLength = endOffset - m_readOffset; |
| 276 |
| 277 m_readOffset += segmentLength; |
| 278 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(cons
t_cast<char*>(segment)), segmentLength); |
| 279 |
| 280 if (m_readOffset == endOffset) { |
| 281 png_process_data(m_png, m_info, dataIEND, 12); |
| 282 break; |
| 283 } |
| 284 } |
| 285 } else { |
| 286 m_readOffset = 0; |
| 287 png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>(
data.data())); |
| 288 png_bytep chunk = inputData + m_frames[index].start; |
| 289 png_bytep finish = inputData + m_frames[index].finish; |
| 290 if (inputData + data.size() < finish) |
| 291 return true; |
| 292 |
| 293 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); |
| 294 if (m_frames[index].width != m_width || m_frames[index].height != m_
height) { |
| 295 unsigned char header[headerSize]; |
| 296 memcpy(&header[0], inputData, headerSize); |
| 297 png_save_uint_32(&header[16], m_frames[index].width); |
| 298 png_save_uint_32(&header[20], m_frames[index].height); |
| 299 png_process_data(m_png, m_info, header, headerSize); |
| 300 png_process_data(m_png, m_info, inputData + headerSize, m_infoSi
ze - headerSize); |
| 301 } else { |
| 302 png_process_data(m_png, m_info, inputData, m_infoSize); |
| 303 } |
| 304 |
| 305 png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84}; |
| 306 while (chunk < finish) { |
| 307 unsigned size = png_get_uint_32(chunk); |
| 308 if (!memcmp(chunk + 4, "fdAT", 4)) { |
| 309 png_save_uint_32(&dataIDAT[0], size - 4); |
| 310 png_process_data(m_png, m_info, dataIDAT, 8); |
| 311 png_process_data(m_png, m_info, chunk + 12, size); |
| 312 } |
| 313 chunk += size + 12; |
| 314 } |
| 315 png_process_data(m_png, m_info, dataIEND, 12); |
| 316 } |
| 317 |
| 318 return true; |
142 } | 319 } |
143 | 320 |
144 png_structp pngPtr() const { return m_png; } | 321 png_structp pngPtr() const { return m_png; } |
145 png_infop infoPtr() const { return m_info; } | 322 png_infop infoPtr() const { return m_info; } |
146 | 323 |
147 void setReadOffset(unsigned offset) { m_readOffset = offset; } | 324 int repetitionCount() const { return m_repetitionCount; } |
148 unsigned currentBufferSize() const { return m_currentBufferSize; } | 325 size_t imagesCount() const { return m_frames.isEmpty() ? 1 : m_frames.size()
- m_posterFrame; } |
149 bool decodingSizeOnly() const { return m_decodingSizeOnly; } | 326 const FrameInfo * frameAtIndex(size_t index) const { return m_frames.isEmpty
() ? &m_currentFrame : &m_frames[index + m_posterFrame]; } |
150 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } | 327 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } |
151 bool hasAlpha() const { return m_hasAlpha; } | 328 bool hasAlpha() const { return m_hasAlpha; } |
152 | 329 |
153 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } | 330 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } |
154 void createInterlaceBuffer(int size) { m_interlaceBuffer = adoptArrayPtr(new
png_byte[size]); } | 331 void createInterlaceBuffer(int size) { m_interlaceBuffer = adoptArrayPtr(new
png_byte[size]); } |
155 #if USE(QCMSLIB) | 332 #if USE(QCMSLIB) |
156 png_bytep rowBuffer() const { return m_rowBuffer.get(); } | 333 png_bytep rowBuffer() const { return m_rowBuffer.get(); } |
157 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si
ze]); } | 334 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si
ze]); } |
158 qcms_transform* colorTransform() const { return m_transform; } | 335 qcms_transform* colorTransform() const { return m_transform; } |
159 | 336 |
(...skipping 27 matching lines...) Expand all Loading... |
187 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf
ile, dataFormat, QCMS_INTENT_PERCEPTUAL); | 364 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf
ile, dataFormat, QCMS_INTENT_PERCEPTUAL); |
188 qcms_profile_release(inputProfile); | 365 qcms_profile_release(inputProfile); |
189 } | 366 } |
190 #endif | 367 #endif |
191 | 368 |
192 private: | 369 private: |
193 png_structp m_png; | 370 png_structp m_png; |
194 png_infop m_info; | 371 png_infop m_info; |
195 PNGImageDecoder* m_decoder; | 372 PNGImageDecoder* m_decoder; |
196 unsigned m_readOffset; | 373 unsigned m_readOffset; |
197 unsigned m_currentBufferSize; | 374 unsigned m_parseOffset; |
198 bool m_decodingSizeOnly; | 375 unsigned m_infoSize; |
| 376 bool m_isAnimated; |
| 377 bool m_isParsed; |
| 378 unsigned m_width; |
| 379 unsigned m_height; |
| 380 int m_repetitionCount; |
| 381 size_t m_posterFrame; |
| 382 size_t m_visibleFrames; |
| 383 FrameInfo m_currentFrame; |
| 384 Vector<FrameInfo> m_frames; |
199 bool m_hasAlpha; | 385 bool m_hasAlpha; |
200 OwnPtr<png_byte[]> m_interlaceBuffer; | 386 OwnPtr<png_byte[]> m_interlaceBuffer; |
201 #if USE(QCMSLIB) | 387 #if USE(QCMSLIB) |
202 qcms_transform* m_transform; | 388 qcms_transform* m_transform; |
203 OwnPtr<png_byte[]> m_rowBuffer; | 389 OwnPtr<png_byte[]> m_rowBuffer; |
204 #endif | 390 #endif |
205 }; | 391 }; |
206 | 392 |
207 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp
tion colorOptions, size_t maxDecodedBytes) | 393 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp
tion colorOptions, size_t maxDecodedBytes) |
208 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) | 394 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
209 , m_hasColorProfile(false) | 395 , m_hasColorProfile(false) |
| 396 , m_currentFrame(0) |
210 { | 397 { |
211 } | 398 } |
212 | 399 |
213 PNGImageDecoder::~PNGImageDecoder() | 400 PNGImageDecoder::~PNGImageDecoder() |
214 { | 401 { |
215 } | 402 } |
216 | 403 |
| 404 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const |
| 405 { |
| 406 if (!m_reader || failed()) |
| 407 return false; |
| 408 if (m_frameBufferCache.size() <= 1) |
| 409 return ImageDecoder::frameIsCompleteAtIndex(index); |
| 410 bool frameIsLoadedAtIndex = index < m_frameBufferCache.size(); |
| 411 return frameIsLoadedAtIndex; |
| 412 } |
| 413 |
| 414 float PNGImageDecoder::frameDurationAtIndex(size_t index) const |
| 415 { |
| 416 return (index < m_frameBufferCache.size()) ? m_frameBufferCache[index].durat
ion() : 0; |
| 417 } |
| 418 |
| 419 int PNGImageDecoder::repetitionCount() const |
| 420 { |
| 421 return (!m_reader || failed()) ? cAnimationNone : m_reader->repetitionCount(
); |
| 422 } |
| 423 |
| 424 size_t PNGImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame) |
| 425 { |
| 426 if (clearExceptFrame >= m_frameBufferCache.size() || m_frameBufferCache.size
() <= 1) |
| 427 return 0; |
| 428 |
| 429 size_t prevFrame = clearExceptFrame; |
| 430 if (m_frameBufferCache[clearExceptFrame].disposalMethod() == ImageFrame::Dis
poseOverwritePrevious) |
| 431 prevFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIn
dex(); |
| 432 |
| 433 while (clearExceptFrame != kNotFound && m_frameBufferCache[clearExceptFrame]
.status() != ImageFrame::FrameComplete) |
| 434 clearExceptFrame = m_frameBufferCache[clearExceptFrame].requiredPrevious
FrameIndex(); |
| 435 |
| 436 size_t frameBytesCleared = 0; |
| 437 for (size_t i = 0; i < m_frameBufferCache.size(); ++i) { |
| 438 if (i != prevFrame && i != clearExceptFrame) { |
| 439 frameBytesCleared += frameBytesAtIndex(i); |
| 440 clearFrameBuffer(i); |
| 441 } |
| 442 } |
| 443 return frameBytesCleared; |
| 444 } |
| 445 |
217 #if USE(QCMSLIB) | 446 #if USE(QCMSLIB) |
218 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color
Profile, bool& sRGB) | 447 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color
Profile, bool& sRGB) |
219 { | 448 { |
220 #ifdef PNG_iCCP_SUPPORTED | 449 #ifdef PNG_iCCP_SUPPORTED |
221 ASSERT(colorProfile.isEmpty()); | 450 ASSERT(colorProfile.isEmpty()); |
222 if (png_get_valid(png, info, PNG_INFO_sRGB)) { | 451 if (png_get_valid(png, info, PNG_INFO_sRGB)) { |
223 sRGB = true; | 452 sRGB = true; |
224 return; | 453 return; |
225 } | 454 } |
226 | 455 |
(...skipping 21 matching lines...) Expand all Loading... |
248 if (!ignoreProfile) | 477 if (!ignoreProfile) |
249 colorProfile.append(profileData, profileLength); | 478 colorProfile.append(profileData, profileLength); |
250 #endif | 479 #endif |
251 } | 480 } |
252 #endif | 481 #endif |
253 | 482 |
254 void PNGImageDecoder::headerAvailable() | 483 void PNGImageDecoder::headerAvailable() |
255 { | 484 { |
256 png_structp png = m_reader->pngPtr(); | 485 png_structp png = m_reader->pngPtr(); |
257 png_infop info = m_reader->infoPtr(); | 486 png_infop info = m_reader->infoPtr(); |
258 png_uint_32 width = png_get_image_width(png, info); | 487 png_uint_32 width, height; |
259 png_uint_32 height = png_get_image_height(png, info); | |
260 | |
261 // Protect against large PNGs. See http://bugzil.la/251381 for more details. | |
262 const unsigned long maxPNGSize = 1000000UL; | |
263 if (width > maxPNGSize || height > maxPNGSize) { | |
264 longjmp(JMPBUF(png), 1); | |
265 return; | |
266 } | |
267 | |
268 // Set the image size now that the image header is available. | |
269 if (!setSize(width, height)) { | |
270 longjmp(JMPBUF(png), 1); | |
271 return; | |
272 } | |
273 | |
274 int bitDepth, colorType, interlaceType, compressionType, filterType, channel
s; | 488 int bitDepth, colorType, interlaceType, compressionType, filterType, channel
s; |
275 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceTy
pe, &compressionType, &filterType); | 489 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceTy
pe, &compressionType, &filterType); |
276 | 490 |
277 // The options we set here match what Mozilla does. | 491 // The options we set here match what Mozilla does. |
278 | 492 |
279 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. | 493 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. |
280 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY
&& bitDepth < 8)) | 494 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY
&& bitDepth < 8)) |
281 png_set_expand(png); | 495 png_set_expand(png); |
282 | 496 |
283 png_bytep trns = 0; | 497 png_bytep trns = 0; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 // Tell libpng to send us rows for interlaced pngs. | 544 // Tell libpng to send us rows for interlaced pngs. |
331 if (interlaceType == PNG_INTERLACE_ADAM7) | 545 if (interlaceType == PNG_INTERLACE_ADAM7) |
332 png_set_interlace_handling(png); | 546 png_set_interlace_handling(png); |
333 | 547 |
334 // Update our info now. | 548 // Update our info now. |
335 png_read_update_info(png, info); | 549 png_read_update_info(png, info); |
336 channels = png_get_channels(png, info); | 550 channels = png_get_channels(png, info); |
337 ASSERT(channels == 3 || channels == 4); | 551 ASSERT(channels == 3 || channels == 4); |
338 | 552 |
339 m_reader->setHasAlpha(channels == 4); | 553 m_reader->setHasAlpha(channels == 4); |
340 | |
341 if (m_reader->decodingSizeOnly()) { | |
342 // If we only needed the size, halt the reader. | |
343 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN
OR >= 5) | |
344 // '0' argument to png_process_data_pause means: Do not cache unprocesse
d data. | |
345 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data
_pause(png, 0)); | |
346 #else | |
347 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size
); | |
348 png->buffer_size = 0; | |
349 #endif | |
350 } | |
351 } | 554 } |
352 | 555 |
353 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
int) | 556 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
int) |
354 { | 557 { |
355 if (m_frameBufferCache.isEmpty()) | 558 if (m_frameBufferCache.isEmpty()) |
356 return; | 559 return; |
357 | 560 |
358 // Initialize the framebuffer if needed. | 561 // Initialize the framebuffer if needed. |
359 ImageFrame& buffer = m_frameBufferCache[0]; | 562 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
360 if (buffer.status() == ImageFrame::FrameEmpty) { | 563 if (buffer.status() == ImageFrame::FrameEmpty) { |
361 png_structp png = m_reader->pngPtr(); | 564 png_structp png = m_reader->pngPtr(); |
362 if (!buffer.setSize(size().width(), size().height())) { | 565 if (!buffer.setSize(size().width(), size().height())) { |
363 longjmp(JMPBUF(png), 1); | 566 longjmp(JMPBUF(png), 1); |
364 return; | 567 return; |
365 } | 568 } |
366 | 569 |
367 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; | 570 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; |
368 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr
())) { | 571 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr
()) |
369 m_reader->createInterlaceBuffer(colorChannels * size().width() * siz
e().height()); | 572 || m_currentFrame) { |
| 573 if (!m_reader->interlaceBuffer()) |
| 574 m_reader->createInterlaceBuffer(colorChannels * size().width() *
size().height()); |
370 if (!m_reader->interlaceBuffer()) { | 575 if (!m_reader->interlaceBuffer()) { |
371 longjmp(JMPBUF(png), 1); | 576 longjmp(JMPBUF(png), 1); |
372 return; | 577 return; |
373 } | 578 } |
374 } | 579 } |
375 | 580 |
376 #if USE(QCMSLIB) | 581 #if USE(QCMSLIB) |
377 if (m_reader->colorTransform()) { | 582 if (m_reader->colorTransform()) { |
378 m_reader->createRowBuffer(colorChannels * size().width()); | 583 if (!m_reader->rowBuffer()) |
| 584 m_reader->createRowBuffer(colorChannels * size().width()); |
379 if (!m_reader->rowBuffer()) { | 585 if (!m_reader->rowBuffer()) { |
380 longjmp(JMPBUF(png), 1); | 586 longjmp(JMPBUF(png), 1); |
381 return; | 587 return; |
382 } | 588 } |
383 } | 589 } |
384 #endif | 590 #endif |
385 buffer.setStatus(ImageFrame::FramePartial); | 591 buffer.setStatus(ImageFrame::FramePartial); |
386 buffer.setHasAlpha(false); | 592 buffer.setHasAlpha(false); |
387 | 593 |
388 // For PNGs, the frame always fills the entire image. | 594 if (!initFrameBuffer()) { |
389 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 595 longjmp(JMPBUF(png), 1); |
| 596 return; |
| 597 } |
390 } | 598 } |
391 | 599 |
392 /* libpng comments (here to explain what follows). | 600 /* libpng comments (here to explain what follows). |
393 * | 601 * |
394 * this function is called for every row in the image. If the | 602 * this function is called for every row in the image. If the |
395 * image is interlacing, and you turned on the interlace handler, | 603 * image is interlacing, and you turned on the interlace handler, |
396 * this function will be called for every row in every pass. | 604 * this function will be called for every row in every pass. |
397 * Some of these rows will not be changed from the previous pass. | 605 * Some of these rows will not be changed from the previous pass. |
398 * When the row is not changed, the new_row variable will be NULL. | 606 * When the row is not changed, the new_row variable will be NULL. |
399 * The rows and passes are called in order, so you don't really | 607 * The rows and passes are called in order, so you don't really |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 | 639 |
432 bool hasAlpha = m_reader->hasAlpha(); | 640 bool hasAlpha = m_reader->hasAlpha(); |
433 png_bytep row = rowBuffer; | 641 png_bytep row = rowBuffer; |
434 | 642 |
435 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 643 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
436 unsigned colorChannels = hasAlpha ? 4 : 3; | 644 unsigned colorChannels = hasAlpha ? 4 : 3; |
437 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 645 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
438 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 646 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
439 } | 647 } |
440 | 648 |
| 649 // Only do incremental image display for the first frame. |
| 650 if (m_currentFrame) |
| 651 return; |
| 652 |
441 #if USE(QCMSLIB) | 653 #if USE(QCMSLIB) |
442 if (qcms_transform* transform = m_reader->colorTransform()) { | 654 if (qcms_transform* transform = m_reader->colorTransform()) { |
443 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width(
)); | 655 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width(
)); |
444 row = m_reader->rowBuffer(); | 656 row = m_reader->rowBuffer(); |
445 } | 657 } |
446 #endif | 658 #endif |
447 | 659 |
448 // Write the decoded row pixels to the frame buffer. The repetitive | 660 // Write the decoded row pixels to the frame buffer. The repetitive |
449 // form of the row write loops is for speed. | 661 // form of the row write loops is for speed. |
450 ImageFrame::PixelData* address = buffer.getAddr(0, y); | 662 ImageFrame::PixelData* address = buffer.getAddr(0, y); |
(...skipping 23 matching lines...) Expand all Loading... |
474 buffer.setHasAlpha(true); | 686 buffer.setHasAlpha(true); |
475 | 687 |
476 buffer.setPixelsChanged(true); | 688 buffer.setPixelsChanged(true); |
477 } | 689 } |
478 | 690 |
479 void PNGImageDecoder::complete() | 691 void PNGImageDecoder::complete() |
480 { | 692 { |
481 if (m_frameBufferCache.isEmpty()) | 693 if (m_frameBufferCache.isEmpty()) |
482 return; | 694 return; |
483 | 695 |
484 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); | 696 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
| 697 buffer.setStatus(ImageFrame::FrameComplete); |
| 698 |
| 699 if (!m_currentFrame) |
| 700 return; |
| 701 |
| 702 const IntRect& rect = buffer.originalFrameRect(); |
| 703 bool hasAlpha = m_reader->hasAlpha(); |
| 704 unsigned colorChannels = hasAlpha ? 4 : 3; |
| 705 unsigned alphaMask = 255; |
| 706 ImageFrame::AlphaBlendSource blendMethod = buffer.alphaBlendSource(); |
| 707 |
| 708 if (blendMethod == ImageFrame::BlendAtopPreviousFrame && !hasAlpha) |
| 709 blendMethod = ImageFrame::BlendAtopBgcolor; |
| 710 |
| 711 png_bytep row = m_reader->interlaceBuffer(); |
| 712 for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().w
idth()) { |
| 713 png_bytep pixel = row; |
| 714 #if USE(QCMSLIB) |
| 715 if (qcms_transform* transform = m_reader->colorTransform()) { |
| 716 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().wi
dth()); |
| 717 pixel = m_reader->rowBuffer(); |
| 718 } |
| 719 #endif |
| 720 ImageFrame::PixelData* address = buffer.getAddr(rect.x(), y); |
| 721 if (hasAlpha) { |
| 722 if (blendMethod == ImageFrame::BlendAtopBgcolor) { |
| 723 if (buffer.premultiplyAlpha()) { |
| 724 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) { |
| 725 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1],
pixel[2], pixel[3]); |
| 726 alphaMask &= pixel[3]; |
| 727 } |
| 728 } else { |
| 729 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) { |
| 730 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2
], pixel[3]); |
| 731 alphaMask &= pixel[3]; |
| 732 } |
| 733 } |
| 734 } else { |
| 735 if (buffer.premultiplyAlpha()) { |
| 736 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) { |
| 737 buffer.overRGBAPremultiply(address++, pixel[0], pixel[1]
, pixel[2], pixel[3]); |
| 738 alphaMask &= pixel[3]; |
| 739 } |
| 740 } else { |
| 741 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) { |
| 742 buffer.overRGBARaw(address++, pixel[0], pixel[1], pixel[
2], pixel[3]); |
| 743 alphaMask &= pixel[3]; |
| 744 } |
| 745 } |
| 746 } |
| 747 } else { |
| 748 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 3) { |
| 749 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); |
| 750 } |
| 751 } |
| 752 } |
| 753 |
| 754 if (alphaMask == 255) { |
| 755 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) { |
| 756 buffer.setHasAlpha(false); |
| 757 } else { |
| 758 size_t frameIndex = m_currentFrame; |
| 759 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; |
| 760 while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::Di
sposeOverwritePrevious)) |
| 761 prevBuffer = &m_frameBufferCache[--frameIndex]; |
| 762 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgc
olor) |
| 763 && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contain
s(prevBuffer->originalFrameRect())) |
| 764 buffer.setHasAlpha(false); |
| 765 } |
| 766 } else if (blendMethod == ImageFrame::BlendAtopBgcolor && !buffer.hasAlpha()
) { |
| 767 buffer.setHasAlpha(true); |
| 768 } |
485 } | 769 } |
486 | 770 |
487 inline bool isComplete(const PNGImageDecoder* decoder) | 771 size_t PNGImageDecoder::decodeFrameCount() |
488 { | 772 { |
489 return decoder->frameIsCompleteAtIndex(0); | 773 return parse() ? m_reader->imagesCount() : m_frameBufferCache.size(); |
490 } | 774 } |
491 | 775 |
492 void PNGImageDecoder::decode(bool onlySize) | 776 void PNGImageDecoder::decode(size_t index) |
| 777 { |
| 778 if (!m_reader || failed()) |
| 779 return; |
| 780 |
| 781 Vector<size_t> framesToDecode; |
| 782 size_t frameToDecode = index; |
| 783 do { |
| 784 framesToDecode.append(frameToDecode); |
| 785 frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameI
ndex(); |
| 786 } while (frameToDecode != kNotFound && m_frameBufferCache[frameToDecode].sta
tus() != ImageFrame::FrameComplete); |
| 787 |
| 788 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) { |
| 789 m_currentFrame = *i; |
| 790 if (!m_reader->decode(*m_data, m_currentFrame)) { |
| 791 setFailed(); |
| 792 break; |
| 793 } |
| 794 |
| 795 // We need more data to continue decoding. |
| 796 if (m_frameBufferCache[m_currentFrame].status() != ImageFrame::FrameComp
lete) |
| 797 break; |
| 798 } |
| 799 } |
| 800 |
| 801 void PNGImageDecoder::initializeNewFrame(size_t index) |
| 802 { |
| 803 ImageFrame* buffer = &m_frameBufferCache[index]; |
| 804 bool frameRectIsOpaque = (buffer->status() == ImageFrame::FrameComplete) ? !
m_reader->hasAlpha() : false; |
| 805 buffer->setRequiredPreviousFrameIndex(findRequiredPreviousFrame(index, frame
RectIsOpaque)); |
| 806 const PNGImageReader::FrameInfo* frame = m_reader->frameAtIndex(index); |
| 807 IntRect frameRect(frame->xOffset, frame->yOffset, frame->width, frame->heigh
t); |
| 808 buffer->setOriginalFrameRect(intersection(frameRect, IntRect(IntPoint(), siz
e()))); |
| 809 buffer->setDuration(frame->duration); |
| 810 buffer->setDisposalMethod((frame->dispose == 2) ? ImageFrame::DisposeOverwri
tePrevious : (frame->dispose == 1) ? ImageFrame::DisposeOverwriteBgcolor : Image
Frame::DisposeKeep); |
| 811 buffer->setAlphaBlendSource((frame->blend == 1) ? ImageFrame::BlendAtopPrevi
ousFrame : ImageFrame::BlendAtopBgcolor); |
| 812 } |
| 813 |
| 814 bool PNGImageDecoder::initFrameBuffer() |
| 815 { |
| 816 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
| 817 |
| 818 const size_t requiredPreviousFrameIndex = buffer.requiredPreviousFrameIndex(
); |
| 819 if (requiredPreviousFrameIndex == kNotFound) { |
| 820 // This frame doesn't rely on any previous data. |
| 821 buffer.zeroFillPixelData(); |
| 822 if (!m_currentFrame) |
| 823 buffer.setHasAlpha(false); |
| 824 } else { |
| 825 const ImageFrame& prevBuffer = m_frameBufferCache[requiredPreviousFrameI
ndex]; |
| 826 ASSERT(prevBuffer.status() == ImageFrame::FrameComplete); |
| 827 |
| 828 // Preserve the last frame as the starting state for this frame. |
| 829 if (!buffer.copyBitmapData(prevBuffer)) |
| 830 return setFailed(); |
| 831 |
| 832 if (prevBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor)
{ |
| 833 // We want to clear the previous frame to transparent, without |
| 834 // affecting pixels in the image outside of the frame. |
| 835 const IntRect& prevRect = prevBuffer.originalFrameRect(); |
| 836 ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); |
| 837 buffer.zeroFillFrameRect(prevRect); |
| 838 } |
| 839 } |
| 840 return true; |
| 841 } |
| 842 |
| 843 bool PNGImageDecoder::parse() |
493 { | 844 { |
494 if (failed()) | 845 if (failed()) |
495 return; | 846 return false; |
496 | 847 |
497 if (!m_reader) | 848 if (!m_reader) |
498 m_reader = adoptPtr(new PNGImageReader(this)); | 849 m_reader = adoptPtr(new PNGImageReader(this)); |
499 | 850 |
500 // If we couldn't decode the image but have received all the data, decoding | 851 // If we couldn't decode the image but have received all the data, decoding |
501 // has failed. | 852 // has failed. |
502 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | 853 if (!m_reader->parse(*m_data) && isAllDataReceived()) |
503 setFailed(); | 854 setFailed(); |
504 | 855 |
505 // If decoding is done or failed, we don't need the PNGImageReader anymore. | 856 if (failed()) { |
506 if (isComplete(this) || failed()) | |
507 m_reader.clear(); | 857 m_reader.clear(); |
| 858 return false; |
| 859 } |
| 860 return true; |
508 } | 861 } |
509 | 862 |
510 } // namespace blink | 863 } // namespace blink |
OLD | NEW |