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/PNGImageDecoder.h" | 39 #include "platform/image-decoders/png/PNGImageDecoder.h" |
40 | 40 |
41 #include "platform/image-decoders/png/PNGImageReader.h" | |
42 #include "png.h" | |
43 #include "wtf/PtrUtil.h" | |
44 #include <memory> | |
45 | |
46 namespace blink { | 41 namespace blink { |
47 | 42 |
48 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, | 43 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
49 const ColorBehavior& colorBehavior, | 44 const ColorBehavior& colorBehavior, |
50 size_t maxDecodedBytes, | 45 size_t maxDecodedBytes, |
51 size_t offset) | 46 size_t offset) |
52 : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes), | 47 : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes), |
53 m_offset(offset) {} | 48 m_offset(offset), |
| 49 m_currentFrame(0), |
| 50 // It would be logical to default to cAnimationNone, but BitmapImage uses |
| 51 // that as a signal to never check again, meaning the actual count will |
| 52 // never be respected. |
| 53 m_repetitionCount(cAnimationLoopOnce), |
| 54 m_hasAlphaChannel(false), |
| 55 m_currentBufferSawAlpha(false) {} |
54 | 56 |
55 PNGImageDecoder::~PNGImageDecoder() {} | 57 PNGImageDecoder::~PNGImageDecoder() {} |
56 | 58 |
| 59 bool PNGImageDecoder::setFailed() { |
| 60 m_reader.reset(); |
| 61 return ImageDecoder::setFailed(); |
| 62 } |
| 63 |
| 64 size_t PNGImageDecoder::decodeFrameCount() { |
| 65 parse(ParseQuery::MetaData); |
| 66 return failed() ? m_frameBufferCache.size() : m_reader->frameCount(); |
| 67 } |
| 68 |
| 69 void PNGImageDecoder::decode(size_t index) { |
| 70 parse(ParseQuery::MetaData); |
| 71 |
| 72 if (failed()) |
| 73 return; |
| 74 |
| 75 updateAggressivePurging(index); |
| 76 |
| 77 Vector<size_t> framesToDecode = findFramesToDecode(index); |
| 78 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); i++) { |
| 79 m_currentFrame = *i; |
| 80 if (!m_reader->decode(*m_data, *i)) { |
| 81 setFailed(); |
| 82 return; |
| 83 } |
| 84 |
| 85 // If this returns false, we need more data to continue decoding. |
| 86 if (!postDecodeProcessing(*i)) |
| 87 break; |
| 88 } |
| 89 |
| 90 // It is also a fatal error if all data is received and we have decoded all |
| 91 // frames available but the file is truncated. |
| 92 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && |
| 93 m_reader && !m_reader->parseCompleted()) |
| 94 setFailed(); |
| 95 } |
| 96 |
| 97 void PNGImageDecoder::parse(ParseQuery query) { |
| 98 if (failed() || (m_reader && m_reader->parseCompleted())) |
| 99 return; |
| 100 |
| 101 if (!m_reader) |
| 102 m_reader = WTF::makeUnique<PNGImageReader>(this, m_offset); |
| 103 |
| 104 if (!m_reader->parse(*m_data, query)) |
| 105 setFailed(); |
| 106 } |
| 107 |
| 108 void PNGImageDecoder::clearFrameBuffer(size_t index) { |
| 109 if (m_reader) |
| 110 m_reader->clearDecodeState(index); |
| 111 ImageDecoder::clearFrameBuffer(index); |
| 112 } |
| 113 |
| 114 bool PNGImageDecoder::canReusePreviousFrameBuffer(size_t index) const { |
| 115 DCHECK(index < m_frameBufferCache.size()); |
| 116 return m_frameBufferCache[index].getDisposalMethod() != |
| 117 ImageFrame::DisposeOverwritePrevious; |
| 118 } |
| 119 |
| 120 void PNGImageDecoder::setRepetitionCount(int repetitionCount) { |
| 121 m_repetitionCount = repetitionCount; |
| 122 } |
| 123 |
| 124 int PNGImageDecoder::repetitionCount() const { |
| 125 return failed() ? cAnimationLoopOnce : m_repetitionCount; |
| 126 } |
| 127 |
| 128 void PNGImageDecoder::initializeNewFrame(size_t index) { |
| 129 const PNGImageReader::FrameInfo& frameInfo = m_reader->frameInfo(index); |
| 130 ImageFrame& buffer = m_frameBufferCache[index]; |
| 131 |
| 132 DCHECK(IntRect(IntPoint(), size()).contains(frameInfo.frameRect)); |
| 133 buffer.setOriginalFrameRect(frameInfo.frameRect); |
| 134 |
| 135 buffer.setDuration(frameInfo.duration); |
| 136 buffer.setDisposalMethod(frameInfo.disposalMethod); |
| 137 buffer.setAlphaBlendSource(frameInfo.alphaBlend); |
| 138 |
| 139 size_t previousFrameIndex = findRequiredPreviousFrame(index, false); |
| 140 buffer.setRequiredPreviousFrameIndex(previousFrameIndex); |
| 141 } |
| 142 |
57 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { | 143 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { |
58 if (png_get_valid(png, info, PNG_INFO_sRGB)) | 144 if (png_get_valid(png, info, PNG_INFO_sRGB)) |
59 return SkColorSpace::MakeSRGB(); | 145 return SkColorSpace::MakeSRGB(); |
60 | 146 |
61 png_charp name; | 147 png_charp name; |
62 int compression; | 148 int compression; |
63 png_bytep profile; | 149 png_bytep profile; |
64 png_uint_32 length; | 150 png_uint_32 length; |
65 if (png_get_iCCP(png, info, &name, &compression, &profile, &length)) | 151 if (png_get_iCCP(png, info, &name, &compression, &profile, &length)) |
66 return SkColorSpace::MakeICC(profile, length); | 152 return SkColorSpace::MakeICC(profile, length); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 fn.fG = 1.0f / pngFixedToFloat(inverseGamma); | 190 fn.fG = 1.0f / pngFixedToFloat(inverseGamma); |
105 fn.fA = 1.0f; | 191 fn.fA = 1.0f; |
106 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; | 192 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; |
107 | 193 |
108 return SkColorSpace::MakeRGB(fn, toXYZD50); | 194 return SkColorSpace::MakeRGB(fn, toXYZD50); |
109 } | 195 } |
110 | 196 |
111 void PNGImageDecoder::headerAvailable() { | 197 void PNGImageDecoder::headerAvailable() { |
112 png_structp png = m_reader->pngPtr(); | 198 png_structp png = m_reader->pngPtr(); |
113 png_infop info = m_reader->infoPtr(); | 199 png_infop info = m_reader->infoPtr(); |
114 png_uint_32 width = png_get_image_width(png, info); | |
115 png_uint_32 height = png_get_image_height(png, info); | |
116 | 200 |
117 // Protect against large PNGs. See http://bugzil.la/251381 for more details. | 201 png_uint_32 width, height; |
118 const unsigned long maxPNGSize = 1000000UL; | |
119 if (width > maxPNGSize || height > maxPNGSize) { | |
120 longjmp(JMPBUF(png), 1); | |
121 return; | |
122 } | |
123 | |
124 // Set the image size now that the image header is available. | |
125 if (!setSize(width, height)) { | |
126 longjmp(JMPBUF(png), 1); | |
127 return; | |
128 } | |
129 | |
130 int bitDepth, colorType, interlaceType, compressionType; | 202 int bitDepth, colorType, interlaceType, compressionType; |
131 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, | 203 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, |
132 &interlaceType, &compressionType, nullptr); | 204 &interlaceType, &compressionType, nullptr); |
133 | 205 |
134 // The options we set here match what Mozilla does. | 206 // The options we set here match what Mozilla does. |
135 | 207 |
136 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. | 208 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. |
137 if (colorType == PNG_COLOR_TYPE_PALETTE || | 209 if (colorType == PNG_COLOR_TYPE_PALETTE || |
138 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) | 210 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) |
139 png_set_expand(png); | 211 png_set_expand(png); |
140 | 212 |
141 if (png_get_valid(png, info, PNG_INFO_tRNS)) | 213 if (png_get_valid(png, info, PNG_INFO_tRNS)) |
142 png_set_expand(png); | 214 png_set_expand(png); |
143 | 215 |
144 if (bitDepth == 16) | 216 if (bitDepth == 16) |
145 png_set_strip_16(png); | 217 png_set_strip_16(png); |
146 | 218 |
147 if (colorType == PNG_COLOR_TYPE_GRAY || | 219 if (colorType == PNG_COLOR_TYPE_GRAY || |
148 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | 220 colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
149 png_set_gray_to_rgb(png); | 221 png_set_gray_to_rgb(png); |
150 | 222 |
151 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { | 223 // Only set the size and the color space of the image once since non-first |
152 // We only support color profiles for color PALETTE and RGB[A] PNG. | 224 // frames also use this method: there is no per-frame color space, and the |
153 // TODO(msarret): Add GRAY profile support, block CYMK? | 225 // image size is determined from the header width and height. |
154 if (sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info)) | 226 if (!isDecodedSizeAvailable()) { |
155 setEmbeddedColorSpace(std::move(colorSpace)); | 227 // Protect against large PNGs. See http://bugzil.la/251381 for more details. |
| 228 const unsigned long maxPNGSize = 1000000UL; |
| 229 if (width > maxPNGSize || height > maxPNGSize) { |
| 230 longjmp(JMPBUF(png), 1); |
| 231 return; |
| 232 } |
| 233 |
| 234 // Set the image size now that the image header is available. |
| 235 if (!setSize(width, height)) { |
| 236 longjmp(JMPBUF(png), 1); |
| 237 return; |
| 238 } |
| 239 |
| 240 if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { |
| 241 // We only support color profiles for color PALETTE and RGB[A] PNG. |
| 242 // TODO(msarret): Add GRAY profile support, block CYMK? |
| 243 if (sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info)) |
| 244 setEmbeddedColorSpace(colorSpace); |
| 245 } |
156 } | 246 } |
157 | 247 |
158 if (!hasEmbeddedColorSpace()) { | 248 if (!hasEmbeddedColorSpace()) { |
159 const double inverseGamma = 0.45455; | 249 const double inverseGamma = 0.45455; |
160 const double defaultGamma = 2.2; | 250 const double defaultGamma = 2.2; |
161 double gamma; | 251 double gamma; |
162 if (!ignoresColorSpace() && png_get_gAMA(png, info, &gamma)) { | 252 if (!ignoresColorSpace() && png_get_gAMA(png, info, &gamma)) { |
163 const double maxGamma = 21474.83; | 253 const double maxGamma = 21474.83; |
164 if ((gamma <= 0.0) || (gamma > maxGamma)) { | 254 if ((gamma <= 0.0) || (gamma > maxGamma)) { |
165 gamma = inverseGamma; | 255 gamma = inverseGamma; |
166 png_set_gAMA(png, info, gamma); | 256 png_set_gAMA(png, info, gamma); |
167 } | 257 } |
168 png_set_gamma(png, defaultGamma, gamma); | 258 png_set_gamma(png, defaultGamma, gamma); |
169 } else { | 259 } else { |
170 png_set_gamma(png, defaultGamma, inverseGamma); | 260 png_set_gamma(png, defaultGamma, inverseGamma); |
171 } | 261 } |
172 } | 262 } |
173 | 263 |
| 264 DCHECK(isDecodedSizeAvailable()); |
| 265 |
174 // Tell libpng to send us rows for interlaced pngs. | 266 // Tell libpng to send us rows for interlaced pngs. |
175 if (interlaceType == PNG_INTERLACE_ADAM7) | 267 if (interlaceType == PNG_INTERLACE_ADAM7) |
176 png_set_interlace_handling(png); | 268 png_set_interlace_handling(png); |
177 | 269 |
178 // Update our info now (so we can get color channel info). | 270 // Update our info now (so we can get color channel info). |
179 png_read_update_info(png, info); | 271 png_read_update_info(png, info); |
180 | 272 |
181 int channels = png_get_channels(png, info); | 273 int channels = png_get_channels(png, info); |
182 DCHECK(channels == 3 || channels == 4); | 274 DCHECK(channels == 3 || channels == 4); |
183 m_reader->setHasAlpha(channels == 4); | 275 m_hasAlphaChannel = (channels == 4); |
184 | |
185 if (m_reader->decodingSizeOnly()) { | |
186 // If we only needed the size, halt the reader. | |
187 #if PNG_LIBPNG_VER_MAJOR > 1 || \ | |
188 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5) | |
189 // Passing '0' tells png_process_data_pause() not to cache unprocessed data. | |
190 m_reader->setReadOffset(m_reader->currentBufferSize() - | |
191 png_process_data_pause(png, 0)); | |
192 #else | |
193 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); | |
194 png->buffer_size = 0; | |
195 #endif | |
196 } | |
197 } | 276 } |
198 | 277 |
199 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, | 278 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
200 unsigned rowIndex, | 279 unsigned rowIndex, |
201 int) { | 280 int) { |
202 if (m_frameBufferCache.isEmpty()) | 281 if (m_currentFrame >= m_frameBufferCache.size()) |
203 return; | 282 return; |
204 | 283 |
205 // Initialize the framebuffer if needed. | 284 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
206 ImageFrame& buffer = m_frameBufferCache[0]; | |
207 if (buffer.getStatus() == ImageFrame::FrameEmpty) { | 285 if (buffer.getStatus() == ImageFrame::FrameEmpty) { |
208 png_structp png = m_reader->pngPtr(); | 286 png_structp png = m_reader->pngPtr(); |
209 if (!buffer.setSizeAndColorSpace(size().width(), size().height(), | 287 if (!initFrameBuffer(m_currentFrame)) { |
210 colorSpaceForSkImages())) { | |
211 longjmp(JMPBUF(png), 1); | 288 longjmp(JMPBUF(png), 1); |
212 return; | 289 return; |
213 } | 290 } |
214 | 291 |
215 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; | 292 DCHECK_EQ(ImageFrame::FramePartial, buffer.getStatus()); |
| 293 |
216 if (PNG_INTERLACE_ADAM7 == | 294 if (PNG_INTERLACE_ADAM7 == |
217 png_get_interlace_type(png, m_reader->infoPtr())) { | 295 png_get_interlace_type(png, m_reader->infoPtr())) { |
218 m_reader->createInterlaceBuffer(colorChannels * size().width() * | 296 unsigned colorChannels = m_hasAlphaChannel ? 4 : 3; |
219 size().height()); | 297 m_reader->createInterlaceBuffer(colorChannels * size().area()); |
220 if (!m_reader->interlaceBuffer()) { | 298 if (!m_reader->interlaceBuffer()) { |
221 longjmp(JMPBUF(png), 1); | 299 longjmp(JMPBUF(png), 1); |
222 return; | 300 return; |
223 } | 301 } |
224 } | 302 } |
225 | 303 |
226 buffer.setStatus(ImageFrame::FramePartial); | 304 m_currentBufferSawAlpha = false; |
227 buffer.setHasAlpha(false); | 305 } |
228 | 306 |
229 // For PNGs, the frame always fills the entire image. | 307 const IntRect& frameRect = buffer.originalFrameRect(); |
230 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); | 308 DCHECK(IntRect(IntPoint(), size()).contains(frameRect)); |
231 } | |
232 | 309 |
233 /* libpng comments (here to explain what follows). | 310 /* libpng comments (here to explain what follows). |
234 * | 311 * |
235 * this function is called for every row in the image. If the | 312 * this function is called for every row in the image. If the |
236 * image is interlacing, and you turned on the interlace handler, | 313 * image is interlacing, and you turned on the interlace handler, |
237 * this function will be called for every row in every pass. | 314 * this function will be called for every row in every pass. |
238 * Some of these rows will not be changed from the previous pass. | 315 * Some of these rows will not be changed from the previous pass. |
239 * When the row is not changed, the new_row variable will be NULL. | 316 * When the row is not changed, the new_row variable will be NULL. |
240 * The rows and passes are called in order, so you don't really | 317 * The rows and passes are called in order, so you don't really |
241 * need the row_num and pass, but I'm supplying them because it | 318 * need the row_num and pass, but I'm supplying them because it |
242 * may make your life easier. | 319 * may make your life easier. |
243 */ | 320 */ |
244 | 321 |
245 // Nothing to do if the row is unchanged, or the row is outside | 322 // Nothing to do if the row is unchanged, or the row is outside the image |
246 // the image bounds: libpng may send extra rows, ignore them to | 323 // bounds. In the case that a frame presents more data than the indicated |
247 // make our lives easier. | 324 // frame size, ignore the extra rows and use the frame size as the source |
| 325 // of truth. libpng can send extra rows: ignore them too, this to prevent |
| 326 // memory writes outside of the image bounds (security). |
248 if (!rowBuffer) | 327 if (!rowBuffer) |
249 return; | 328 return; |
250 int y = rowIndex; | 329 |
251 if (y < 0 || y >= size().height()) | 330 DCHECK_GT(frameRect.height(), 0); |
| 331 if (rowIndex >= static_cast<unsigned>(frameRect.height())) |
252 return; | 332 return; |
253 | 333 |
| 334 int y = rowIndex + frameRect.y(); |
| 335 if (y < 0) |
| 336 return; |
| 337 DCHECK_LT(y, size().height()); |
| 338 |
254 /* libpng comments (continued). | 339 /* libpng comments (continued). |
255 * | 340 * |
256 * For the non-NULL rows of interlaced images, you must call | 341 * For the non-NULL rows of interlaced images, you must call |
257 * png_progressive_combine_row() passing in the row and the | 342 * png_progressive_combine_row() passing in the row and the |
258 * old row. You can call this function for NULL rows (it will | 343 * old row. You can call this function for NULL rows (it will |
259 * just return) and for non-interlaced images (it just does the | 344 * just return) and for non-interlaced images (it just does the |
260 * memcpy for you) if it will make the code easier. Thus, you | 345 * memcpy for you) if it will make the code easier. Thus, you |
261 * can just do this for all cases: | 346 * can just do this for all cases: |
262 * | 347 * |
263 * png_progressive_combine_row(png_ptr, old_row, new_row); | 348 * png_progressive_combine_row(png_ptr, old_row, new_row); |
264 * | 349 * |
265 * where old_row is what was displayed for previous rows. Note | 350 * where old_row is what was displayed for previous rows. Note |
266 * that the first pass (pass == 0 really) will completely cover | 351 * that the first pass (pass == 0 really) will completely cover |
267 * the old row, so the rows do not have to be initialized. After | 352 * the old row, so the rows do not have to be initialized. After |
268 * the first pass (and only for interlaced images), you will have | 353 * the first pass (and only for interlaced images), you will have |
269 * to pass the current row, and the function will combine the | 354 * to pass the current row, and the function will combine the |
270 * old row and the new row. | 355 * old row and the new row. |
271 */ | 356 */ |
272 | 357 |
273 bool hasAlpha = m_reader->hasAlpha(); | 358 bool hasAlpha = m_hasAlphaChannel; |
274 png_bytep row = rowBuffer; | 359 png_bytep row = rowBuffer; |
275 | 360 |
276 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { | 361 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
277 unsigned colorChannels = hasAlpha ? 4 : 3; | 362 unsigned colorChannels = hasAlpha ? 4 : 3; |
278 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); | 363 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
279 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); | 364 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
280 } | 365 } |
281 | 366 |
282 // Write the decoded row pixels to the frame buffer. The repetitive | 367 // Write the decoded row pixels to the frame buffer. The repetitive |
283 // form of the row write loops is for speed. | 368 // form of the row write loops is for speed. |
284 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); | 369 ImageFrame::PixelData* const dstRow = buffer.getAddr(frameRect.x(), y); |
285 int width = size().width(); | 370 int width = frameRect.width(); |
286 | 371 |
287 png_bytep srcPtr = row; | 372 png_bytep srcPtr = row; |
288 if (hasAlpha) { | 373 if (hasAlpha) { |
289 // Here we apply the color space transformation to the dst space. | 374 // Here we apply the color space transformation to the dst space. |
290 // It does not really make sense to transform to a gamma-encoded | 375 // It does not really make sense to transform to a gamma-encoded |
291 // space and then immediately after, perform a linear premultiply. | 376 // space and then immediately after, perform a linear premultiply. |
292 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), | 377 // Ideally we would pass kPremul_SkAlphaType to xform->apply(), |
293 // instructing SkColorSpaceXform to perform the linear premultiply | 378 // instructing SkColorSpaceXform to perform the linear premultiply |
294 // while the pixels are a linear space. | 379 // while the pixels are a linear space. |
295 // We cannot do this because when we apply the gamma encoding after | 380 // We cannot do this because when we apply the gamma encoding after |
296 // the premultiply, we will very likely end up with valid pixels | 381 // the premultiply, we will very likely end up with valid pixels |
297 // where R, G, and/or B are greater than A. The legacy drawing | 382 // where R, G, and/or B are greater than A. The legacy drawing |
298 // pipeline does not know how to handle this. | 383 // pipeline does not know how to handle this. |
299 if (SkColorSpaceXform* xform = colorTransform()) { | 384 if (SkColorSpaceXform* xform = colorTransform()) { |
300 SkColorSpaceXform::ColorFormat colorFormat = | 385 SkColorSpaceXform::ColorFormat colorFormat = |
301 SkColorSpaceXform::kRGBA_8888_ColorFormat; | 386 SkColorSpaceXform::kRGBA_8888_ColorFormat; |
302 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), | 387 xform->apply(colorFormat, dstRow, colorFormat, srcPtr, size().width(), |
303 kUnpremul_SkAlphaType); | 388 kUnpremul_SkAlphaType); |
304 srcPtr = png_bytep(dstRow); | 389 srcPtr = png_bytep(dstRow); |
305 } | 390 } |
306 | 391 |
307 unsigned alphaMask = 255; | 392 unsigned alphaMask = 255; |
308 if (buffer.premultiplyAlpha()) { | 393 if (m_frameBufferCache[m_currentFrame].getAlphaBlendSource() == |
309 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 394 ImageFrame::BlendAtopBgcolor) { |
310 srcPtr += 4, ++dstPixel) { | 395 if (buffer.premultiplyAlpha()) { |
311 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], | 396 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
312 srcPtr[3]); | 397 dstPixel++, srcPtr += 4) { |
313 alphaMask &= srcPtr[3]; | 398 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
| 399 srcPtr[3]); |
| 400 alphaMask &= srcPtr[3]; |
| 401 } |
| 402 } else { |
| 403 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 404 dstPixel++, srcPtr += 4) { |
| 405 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
| 406 srcPtr[3]); |
| 407 alphaMask &= srcPtr[3]; |
| 408 } |
314 } | 409 } |
315 } else { | 410 } else { |
316 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 411 // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the |
317 srcPtr += 4, ++dstPixel) { | 412 // frame data of the previous frame is copied at initFrameBuffer, we can |
318 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); | 413 // blend the pixel of this frame, stored in |srcPtr|, over the previous |
319 alphaMask &= srcPtr[3]; | 414 // pixel stored in |dstPixel|. |
| 415 if (buffer.premultiplyAlpha()) { |
| 416 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 417 dstPixel++, srcPtr += 4) { |
| 418 buffer.blendRGBAPremultiplied(dstPixel, srcPtr[0], srcPtr[1], |
| 419 srcPtr[2], srcPtr[3]); |
| 420 alphaMask &= srcPtr[3]; |
| 421 } |
| 422 } else { |
| 423 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
| 424 dstPixel++, srcPtr += 4) { |
| 425 buffer.blendRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
| 426 srcPtr[3]); |
| 427 alphaMask &= srcPtr[3]; |
| 428 } |
320 } | 429 } |
321 } | 430 } |
322 | 431 |
323 if (alphaMask != 255 && !buffer.hasAlpha()) | 432 if (alphaMask != 255) |
324 buffer.setHasAlpha(true); | 433 m_currentBufferSawAlpha = true; |
325 | 434 |
326 } else { | 435 } else { |
327 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; | 436 for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
328 srcPtr += 3, ++dstPixel) { | 437 srcPtr += 3, ++dstPixel) { |
329 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); | 438 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); |
330 } | 439 } |
331 | 440 |
332 // We'll apply the color space xform to opaque pixels after they have been | 441 // We'll apply the color space xform to opaque pixels after they have been |
333 // written to the ImageFrame, purely because SkColorSpaceXform supports | 442 // written to the ImageFrame, purely because SkColorSpaceXform supports |
334 // RGBA (and not RGB). | 443 // RGBA (and not RGB). |
335 if (SkColorSpaceXform* xform = colorTransform()) { | 444 if (SkColorSpaceXform* xform = colorTransform()) { |
336 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, | 445 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, |
337 size().width(), kOpaque_SkAlphaType); | 446 size().width(), kOpaque_SkAlphaType); |
338 } | 447 } |
339 } | 448 } |
340 | 449 |
341 buffer.setPixelsChanged(true); | 450 buffer.setPixelsChanged(true); |
342 } | 451 } |
343 | 452 |
344 void PNGImageDecoder::complete() { | 453 void PNGImageDecoder::frameComplete() { |
345 if (m_frameBufferCache.isEmpty()) | 454 if (m_currentFrame >= m_frameBufferCache.size()) |
346 return; | 455 return; |
347 | 456 |
348 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); | 457 if (m_reader->interlaceBuffer()) |
| 458 m_reader->clearInterlaceBuffer(); |
| 459 |
| 460 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
| 461 if (buffer.getStatus() == ImageFrame::FrameEmpty) { |
| 462 longjmp(JMPBUF(m_reader->pngPtr()), 1); |
| 463 return; |
| 464 } |
| 465 |
| 466 if (!m_currentBufferSawAlpha) |
| 467 correctAlphaWhenFrameBufferSawNoAlpha(m_currentFrame); |
| 468 |
| 469 buffer.setStatus(ImageFrame::FrameComplete); |
349 } | 470 } |
350 | 471 |
351 inline bool isComplete(const PNGImageDecoder* decoder) { | 472 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const { |
352 return decoder->frameIsCompleteAtIndex(0); | 473 if (!isDecodedSizeAvailable()) |
| 474 return false; |
| 475 |
| 476 DCHECK(!failed() && m_reader); |
| 477 |
| 478 // For non-animated images, return whether the status of the frame is |
| 479 // ImageFrame::FrameComplete with ImageDecoder::frameIsCompleteAtIndex. |
| 480 // This matches the behavior of WEBPImageDecoder. |
| 481 if (m_reader->parseCompleted() && m_reader->frameCount() == 1) |
| 482 return ImageDecoder::frameIsCompleteAtIndex(index); |
| 483 |
| 484 return m_reader->frameIsFullyReceivedAtIndex(index); |
353 } | 485 } |
354 | 486 |
355 void PNGImageDecoder::decode(bool onlySize) { | 487 float PNGImageDecoder::frameDurationAtIndex(size_t index) const { |
356 if (failed()) | 488 if (index < m_frameBufferCache.size()) |
357 return; | 489 return m_frameBufferCache[index].duration(); |
358 | 490 return 0; |
359 if (!m_reader) | |
360 m_reader = WTF::makeUnique<PNGImageReader>(this, m_offset); | |
361 | |
362 // If we couldn't decode the image but have received all the data, decoding | |
363 // has failed. | |
364 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) | |
365 setFailed(); | |
366 | |
367 // If decoding is done or failed, we don't need the PNGImageReader anymore. | |
368 if (isComplete(this) || failed()) | |
369 m_reader.reset(); | |
370 } | 491 } |
371 | 492 |
372 } // namespace blink | 493 } // namespace blink |
OLD | NEW |