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 20 matching lines...) Expand all Loading... |
31 * licenses (the MPL or the GPL) and not to allow others to use your | 31 * licenses (the MPL or the GPL) and not to allow others to use your |
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 "platform/image-decoders/png/PNGImageReader.h" | 39 #include "platform/image-decoders/png/PNGImageReader.h" |
40 | 40 |
| 41 #include "platform/RuntimeEnabledFeatures.h" |
| 42 #include "platform/image-decoders/FastSharedBufferReader.h" |
| 43 |
41 #include "platform/image-decoders/SegmentReader.h" | 44 #include "platform/image-decoders/SegmentReader.h" |
42 #include "png.h" | 45 #include "png.h" |
43 #include "wtf/PtrUtil.h" | 46 #include "wtf/PtrUtil.h" |
| 47 #include "zlib.h" |
44 #include <memory> | 48 #include <memory> |
45 | 49 |
| 50 #define get16(p) ((p)[0] << 8 | (p)[1]) |
| 51 #define get32(p) ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) |
| 52 |
46 namespace { | 53 namespace { |
47 | 54 |
48 inline blink::PNGImageDecoder* imageDecoder(png_structp png) { | 55 inline blink::PNGImageDecoder* imageDecoder(png_structp png) { |
49 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png)); | 56 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png)); |
50 } | 57 } |
51 | 58 |
52 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) { | 59 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) { |
53 imageDecoder(png)->headerAvailable(); | 60 imageDecoder(png)->headerAvailable(); |
54 } | 61 } |
55 | 62 |
(...skipping 12 matching lines...) Expand all Loading... |
68 longjmp(JMPBUF(png), 1); | 75 longjmp(JMPBUF(png), 1); |
69 } | 76 } |
70 | 77 |
71 } // namespace | 78 } // namespace |
72 | 79 |
73 namespace blink { | 80 namespace blink { |
74 | 81 |
75 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) | 82 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) |
76 : m_decoder(decoder), | 83 : m_decoder(decoder), |
77 m_readOffset(readOffset), | 84 m_readOffset(readOffset), |
78 m_currentBufferSize(0), | 85 m_parseOffset(readOffset), |
79 m_decodingSizeOnly(false), | 86 m_decodeOffset(readOffset), |
| 87 m_infoSize(0), |
| 88 m_isAnimated(false), |
| 89 m_isParsed(false), |
| 90 m_width(0), |
| 91 m_height(0), |
| 92 m_repetitionCount(cAnimationNone), |
| 93 m_posterFrame(0), |
| 94 m_visibleFrames(1), |
80 m_hasAlpha(false) { | 95 m_hasAlpha(false) { |
81 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); | 96 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
82 m_info = png_create_info_struct(m_png); | 97 m_info = png_create_info_struct(m_png); |
83 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, | 98 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
84 pngRowAvailable, pngComplete); | 99 pngRowAvailable, pngComplete); |
| 100 memset(&m_currentFrame, 0, sizeof(m_currentFrame)); |
85 } | 101 } |
86 | 102 |
87 PNGImageReader::~PNGImageReader() { | 103 PNGImageReader::~PNGImageReader() { |
88 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); | 104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); |
89 DCHECK(!m_png && !m_info); | 105 DCHECK(!m_png && !m_info); |
90 | 106 |
91 m_readOffset = 0; | 107 m_readOffset = 0; |
92 } | 108 } |
93 | 109 |
94 bool PNGImageReader::decode(const SegmentReader& data, bool sizeOnly) { | 110 // PNG signature and IHDR size |
95 m_decodingSizeOnly = sizeOnly; | 111 static const size_t headerSize = 33; |
96 | 112 // fcTL size |
97 // We need to do the setjmp here. Otherwise bad things will happen. | 113 static const size_t frameControlSize = 34; |
| 114 |
| 115 bool PNGImageReader::parse(SegmentReader& data) { |
| 116 const unsigned long maxPNGSize = 1000000UL; |
| 117 size_t inputSize = data.size(); |
| 118 const png_byte* chunk; |
| 119 FastSharedBufferReader reader(&data); |
| 120 char readBuffer[frameControlSize]; |
| 121 |
| 122 if (m_parseOffset == m_readOffset) { |
| 123 if (m_readOffset + headerSize > inputSize) |
| 124 return false; |
| 125 |
| 126 chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData( |
| 127 m_readOffset + 8, headerSize - 8, readBuffer)); |
| 128 const png_byte* chunkId = chunk + 4; |
| 129 png_uint_32 chunkSize = get32(chunk); |
| 130 if (memcmp(chunkId, "IHDR", 4) || chunkSize != 13) |
| 131 return false; |
| 132 m_currentFrame.width = m_width = get32(chunk + 8); |
| 133 m_currentFrame.height = m_height = get32(chunk + 12); |
| 134 if (m_width > maxPNGSize || m_height > maxPNGSize) |
| 135 return false; |
| 136 png_byte bitDepth = chunk[16]; |
| 137 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && |
| 138 bitDepth != 16) |
| 139 return false; |
| 140 png_byte colorType = chunk[17]; |
| 141 if (colorType == 1 || colorType == 5 || colorType > 6) |
| 142 return false; |
| 143 png_uint_32 chunkCrc = get32(chunk + 21); |
| 144 if (crc32(crc32(0, Z_NULL, 0), chunkId, 17) != chunkCrc) |
| 145 return false; |
| 146 m_infoSize = m_parseOffset = m_readOffset + headerSize; |
| 147 } |
| 148 |
| 149 while (!m_isParsed) { |
| 150 if (m_parseOffset + 8 > inputSize) |
| 151 return false; |
| 152 |
| 153 chunk = reinterpret_cast<const png_byte*>( |
| 154 reader.getConsecutiveData(m_parseOffset, 8, readBuffer)); |
| 155 const png_byte* chunkId = chunk + 4; |
| 156 png_uint_32 chunkSize = get32(chunk); |
| 157 |
| 158 if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) { |
| 159 m_currentFrame.start = m_parseOffset; |
| 160 if (!m_decoder->setSize(m_width, m_height)) |
| 161 return false; |
| 162 png_destroy_read_struct(&m_png, &m_info, 0); |
| 163 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
| 164 m_info = png_create_info_struct(m_png); |
| 165 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
| 166 pngRowAvailable, pngComplete); |
| 167 |
| 168 if (setjmp(JMPBUF(m_png))) |
| 169 return false; |
| 170 |
| 171 processData(data, 0, m_readOffset, m_parseOffset + 8); |
| 172 } |
| 173 |
| 174 size_t chunkEnd = m_parseOffset + chunkSize + 12; |
| 175 if (chunkEnd > inputSize) |
| 176 return false; |
| 177 |
| 178 if (!memcmp(chunkId, "IEND", 4) && !chunkSize) { |
| 179 if (!m_currentFrame.start) |
| 180 return false; |
| 181 m_isParsed = true; |
| 182 m_currentFrame.finish = chunkEnd; |
| 183 m_frames.append(m_currentFrame); |
| 184 return true; |
| 185 } |
| 186 |
| 187 if (RuntimeEnabledFeatures::animatedPNGEnabled()) { |
| 188 if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated && |
| 189 !m_currentFrame.start) { |
| 190 m_currentFrame.start = m_parseOffset; |
| 191 } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 && |
| 192 !m_isAnimated && !m_currentFrame.start) { |
| 193 chunk = reinterpret_cast<const png_byte*>( |
| 194 reader.getConsecutiveData(m_parseOffset, 16, readBuffer)); |
| 195 m_isAnimated = true; |
| 196 m_posterFrame = 1; |
| 197 m_visibleFrames = 0; |
| 198 m_repetitionCount = static_cast<int>(get32(chunk + 12)) - 1; |
| 199 } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 && |
| 200 (m_isAnimated || !m_currentFrame.start)) { |
| 201 if (m_currentFrame.start) { |
| 202 m_currentFrame.finish = chunkEnd; |
| 203 m_frames.append(m_currentFrame); |
| 204 m_currentFrame.start = 0; |
| 205 } else if (m_frames.isEmpty()) { |
| 206 m_posterFrame = 0; |
| 207 } |
| 208 |
| 209 chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData( |
| 210 m_parseOffset, frameControlSize, readBuffer)); |
| 211 m_currentFrame.width = get32(chunk + 12); |
| 212 m_currentFrame.height = get32(chunk + 16); |
| 213 m_currentFrame.xOffset = get32(chunk + 20); |
| 214 m_currentFrame.yOffset = get32(chunk + 24); |
| 215 unsigned numerator = get16(chunk + 28); |
| 216 unsigned denominator = get16(chunk + 30); |
| 217 m_currentFrame.duration = !denominator |
| 218 ? numerator * 10 |
| 219 : (int)(numerator * 1000.0 / denominator); |
| 220 m_currentFrame.dispose = chunk[32]; |
| 221 m_currentFrame.blend = chunk[33]; |
| 222 |
| 223 if (m_currentFrame.width > maxPNGSize || |
| 224 m_currentFrame.height > maxPNGSize || |
| 225 m_currentFrame.xOffset > maxPNGSize || |
| 226 m_currentFrame.yOffset > maxPNGSize || |
| 227 m_currentFrame.xOffset + m_currentFrame.width > m_width || |
| 228 m_currentFrame.yOffset + m_currentFrame.height > m_height || |
| 229 m_currentFrame.dispose > 2 || m_currentFrame.blend > 1) |
| 230 return false; |
| 231 |
| 232 if (!m_visibleFrames) { |
| 233 m_currentFrame.blend = 0; |
| 234 if (m_currentFrame.dispose == 2) |
| 235 m_currentFrame.dispose = 1; |
| 236 } |
| 237 m_visibleFrames++; |
| 238 } else if (!m_currentFrame.start && m_frames.isEmpty()) { |
| 239 m_infoSize = chunkEnd; |
| 240 } |
| 241 } |
| 242 |
| 243 m_parseOffset = chunkEnd; |
| 244 } |
| 245 return true; |
| 246 } |
| 247 |
| 248 bool PNGImageReader::decode(SegmentReader& data, size_t index) { |
| 249 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; |
| 250 |
| 251 index += m_posterFrame; |
| 252 if (index && index >= m_frames.size()) |
| 253 return true; |
| 254 |
| 255 if (index || m_decodeOffset == m_readOffset) { |
| 256 png_destroy_read_struct(&m_png, &m_info, 0); |
| 257 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); |
| 258 m_info = png_create_info_struct(m_png); |
| 259 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, |
| 260 pngRowAvailable, pngComplete); |
| 261 } |
| 262 |
98 if (setjmp(JMPBUF(m_png))) | 263 if (setjmp(JMPBUF(m_png))) |
99 return m_decoder->setFailed(); | 264 return false; |
100 | 265 |
101 const char* segment; | 266 if (!index) { |
102 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) { | 267 size_t endOffset = !m_frames.isEmpty() ? m_frames[0].finish : 0; |
103 m_readOffset += segmentLength; | 268 processData(data, index, m_decodeOffset, endOffset); |
104 m_currentBufferSize = m_readOffset; | 269 if (m_decodeOffset == endOffset) |
105 png_process_data(m_png, m_info, | 270 png_process_data(m_png, m_info, dataIEND, 12); |
106 reinterpret_cast<png_bytep>(const_cast<char*>(segment)), | 271 } else { |
107 segmentLength); | 272 const png_byte* chunk; |
108 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() | 273 FastSharedBufferReader reader(&data); |
109 : m_decoder->frameIsCompleteAtIndex(0)) | 274 char readBuffer[headerSize]; |
| 275 |
| 276 m_decodeOffset = m_readOffset; |
| 277 size_t offset = m_frames[index].start; |
| 278 size_t endOffset = m_frames[index].finish; |
| 279 if (data.size() < endOffset) |
110 return true; | 280 return true; |
111 } | 281 |
112 | 282 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); |
113 return false; | 283 if (m_frames[index].width != m_width || |
| 284 m_frames[index].height != m_height) { |
| 285 unsigned char header[headerSize]; |
| 286 chunk = reinterpret_cast<const png_byte*>( |
| 287 reader.getConsecutiveData(m_readOffset, headerSize, readBuffer)); |
| 288 memcpy(&header[0], chunk, headerSize); |
| 289 png_save_uint_32(&header[16], m_frames[index].width); |
| 290 png_save_uint_32(&header[20], m_frames[index].height); |
| 291 png_process_data(m_png, m_info, header, headerSize); |
| 292 processData(data, index, m_readOffset + headerSize, m_infoSize); |
| 293 } else { |
| 294 processData(data, index, m_readOffset, m_infoSize); |
| 295 } |
| 296 |
| 297 png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84}; |
| 298 while (offset < endOffset) { |
| 299 chunk = reinterpret_cast<const png_byte*>( |
| 300 reader.getConsecutiveData(offset, 8, readBuffer)); |
| 301 png_uint_32 chunkSize = get32(chunk); |
| 302 if (!memcmp(chunk + 4, "fdAT", 4)) { |
| 303 png_save_uint_32(&dataIDAT[0], chunkSize - 4); |
| 304 png_process_data(m_png, m_info, dataIDAT, 8); |
| 305 processData(data, index, offset + 12, offset + 12 + chunkSize); |
| 306 } |
| 307 offset += chunkSize + 12; |
| 308 } |
| 309 png_process_data(m_png, m_info, dataIEND, 12); |
| 310 } |
| 311 return true; |
114 } | 312 } |
115 | 313 |
116 } // namespace blink | 314 } // namespace blink |
OLD | NEW |