Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp

Issue 2930513004: [WIP] Move ImageDecoders to SkCodec
Patch Set: Adding check for decoder creation Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006 Apple Computer, Inc.
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4 *
5 * Portions are Copyright (C) 2001 mozilla.org
6 *
7 * Other contributors:
8 * Stuart Parmenter <stuart@mozilla.com>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 * Alternatively, the contents of this file may be used under the terms
25 * of either the Mozilla Public License Version 1.1, found at
26 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
27 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
28 * (the "GPL"), in which case the provisions of the MPL or the GPL are
29 * applicable instead of those above. If you wish to allow use of your
30 * version of this file only under the terms of one of those two
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
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.
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.
37 */
38
39 #include "platform/image-decoders/png/PNGImageDecoder.h"
40
41 namespace blink {
42
43 PNGImageDecoder::PNGImageDecoder(AlphaOption alpha_option,
44 const ColorBehavior& color_behavior,
45 size_t max_decoded_bytes,
46 size_t offset)
47 : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
48 offset_(offset),
49 current_frame_(0),
50 // It would be logical to default to kAnimationNone, but BitmapImage uses
51 // that as a signal to never check again, meaning the actual count will
52 // never be respected.
53 repetition_count_(kAnimationLoopOnce),
54 has_alpha_channel_(false),
55 current_buffer_saw_alpha_(false) {}
56
57 PNGImageDecoder::~PNGImageDecoder() {}
58
59 bool PNGImageDecoder::SetFailed() {
60 reader_.reset();
61 return ImageDecoder::SetFailed();
62 }
63
64 size_t PNGImageDecoder::DecodeFrameCount() {
65 Parse(ParseQuery::kMetaData);
66 return Failed() ? frame_buffer_cache_.size() : reader_->FrameCount();
67 }
68
69 void PNGImageDecoder::Decode(size_t index) {
70 Parse(ParseQuery::kMetaData);
71
72 if (Failed())
73 return;
74
75 UpdateAggressivePurging(index);
76
77 Vector<size_t> frames_to_decode = FindFramesToDecode(index);
78 for (auto i = frames_to_decode.rbegin(); i != frames_to_decode.rend(); i++) {
79 current_frame_ = *i;
80 if (!reader_->Decode(*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 >= frame_buffer_cache_.size() - 1 && IsAllDataReceived() &&
93 reader_ && !reader_->ParseCompleted())
94 SetFailed();
95 }
96
97 void PNGImageDecoder::Parse(ParseQuery query) {
98 if (Failed() || (reader_ && reader_->ParseCompleted()))
99 return;
100
101 if (!reader_)
102 reader_ = WTF::MakeUnique<PNGImageReader>(this, offset_);
103
104 if (!reader_->Parse(*data_, query))
105 SetFailed();
106 }
107
108 void PNGImageDecoder::ClearFrameBuffer(size_t index) {
109 if (reader_)
110 reader_->ClearDecodeState(index);
111 ImageDecoder::ClearFrameBuffer(index);
112 }
113
114 bool PNGImageDecoder::CanReusePreviousFrameBuffer(size_t index) const {
115 DCHECK(index < frame_buffer_cache_.size());
116 return frame_buffer_cache_[index].GetDisposalMethod() !=
117 ImageFrame::kDisposeOverwritePrevious;
118 }
119
120 void PNGImageDecoder::SetRepetitionCount(int repetition_count) {
121 repetition_count_ = repetition_count;
122 }
123
124 int PNGImageDecoder::RepetitionCount() const {
125 return Failed() ? kAnimationLoopOnce : repetition_count_;
126 }
127
128 void PNGImageDecoder::InitializeNewFrame(size_t index) {
129 const PNGImageReader::FrameInfo& frame_info = reader_->GetFrameInfo(index);
130 ImageFrame& buffer = frame_buffer_cache_[index];
131
132 DCHECK(IntRect(IntPoint(), Size()).Contains(frame_info.frame_rect));
133 buffer.SetOriginalFrameRect(frame_info.frame_rect);
134
135 buffer.SetDuration(frame_info.duration);
136 buffer.SetDisposalMethod(frame_info.disposal_method);
137 buffer.SetAlphaBlendSource(frame_info.alpha_blend);
138
139 size_t previous_frame_index = FindRequiredPreviousFrame(index, false);
140 buffer.SetRequiredPreviousFrameIndex(previous_frame_index);
141 }
142
143 inline sk_sp<SkColorSpace> ReadColorSpace(png_structp png, png_infop info) {
144 if (png_get_valid(png, info, PNG_INFO_sRGB))
145 return SkColorSpace::MakeSRGB();
146
147 png_charp name;
148 int compression;
149 png_bytep profile;
150 png_uint_32 length;
151 if (png_get_iCCP(png, info, &name, &compression, &profile, &length))
152 return SkColorSpace::MakeICC(profile, length);
153
154 png_fixed_point chrm[8];
155 if (!png_get_cHRM_fixed(png, info, &chrm[0], &chrm[1], &chrm[2], &chrm[3],
156 &chrm[4], &chrm[5], &chrm[6], &chrm[7]))
157 return nullptr;
158
159 png_fixed_point inverse_gamma;
160 if (!png_get_gAMA_fixed(png, info, &inverse_gamma))
161 return nullptr;
162
163 // cHRM and gAMA tags are both present. The PNG spec states that cHRM is
164 // valid even without gAMA but we cannot apply the cHRM without guessing
165 // a gAMA. Color correction is not a guessing game: match the behavior
166 // of Safari and Firefox instead (compat).
167
168 struct pngFixedToFloat {
169 explicit pngFixedToFloat(png_fixed_point value)
170 : float_value(.00001f * value) {}
171 operator float() { return float_value; }
172 float float_value;
173 };
174
175 SkColorSpacePrimaries primaries;
176 primaries.fRX = pngFixedToFloat(chrm[2]);
177 primaries.fRY = pngFixedToFloat(chrm[3]);
178 primaries.fGX = pngFixedToFloat(chrm[4]);
179 primaries.fGY = pngFixedToFloat(chrm[5]);
180 primaries.fBX = pngFixedToFloat(chrm[6]);
181 primaries.fBY = pngFixedToFloat(chrm[7]);
182 primaries.fWX = pngFixedToFloat(chrm[0]);
183 primaries.fWY = pngFixedToFloat(chrm[1]);
184
185 SkMatrix44 to_xyzd50(SkMatrix44::kUninitialized_Constructor);
186 if (!primaries.toXYZD50(&to_xyzd50))
187 return nullptr;
188
189 SkColorSpaceTransferFn fn;
190 fn.fG = 1.0f / pngFixedToFloat(inverse_gamma);
191 fn.fA = 1.0f;
192 fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
193
194 return SkColorSpace::MakeRGB(fn, to_xyzd50);
195 }
196
197 void PNGImageDecoder::SetColorSpace() {
198 if (IgnoresColorSpace())
199 return;
200 png_structp png = reader_->PngPtr();
201 png_infop info = reader_->InfoPtr();
202 const int color_type = png_get_color_type(png, info);
203 if (!(color_type & PNG_COLOR_MASK_COLOR))
204 return;
205 // We only support color profiles for color PALETTE and RGB[A] PNG.
206 // TODO(msarett): Add GRAY profile support, block CYMK?
207 sk_sp<SkColorSpace> color_space = ReadColorSpace(png, info);
208 if (color_space)
209 SetEmbeddedColorSpace(color_space);
210 }
211
212 bool PNGImageDecoder::SetSize(unsigned width, unsigned height) {
213 DCHECK(!IsDecodedSizeAvailable());
214 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
215 const unsigned long kMaxPNGSize = 1000000UL;
216 return (width <= kMaxPNGSize) && (height <= kMaxPNGSize) &&
217 ImageDecoder::SetSize(width, height);
218 }
219
220 void PNGImageDecoder::HeaderAvailable() {
221 DCHECK(IsDecodedSizeAvailable());
222
223 png_structp png = reader_->PngPtr();
224 png_infop info = reader_->InfoPtr();
225
226 png_uint_32 width, height;
227 int bit_depth, color_type, interlace_type, compression_type;
228 png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type,
229 &interlace_type, &compression_type, nullptr);
230
231 // The options we set here match what Mozilla does.
232
233 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
234 if (color_type == PNG_COLOR_TYPE_PALETTE ||
235 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
236 png_set_expand(png);
237
238 if (png_get_valid(png, info, PNG_INFO_tRNS))
239 png_set_expand(png);
240
241 if (bit_depth == 16)
242 png_set_strip_16(png);
243
244 if (color_type == PNG_COLOR_TYPE_GRAY ||
245 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
246 png_set_gray_to_rgb(png);
247
248 if (!HasEmbeddedColorSpace()) {
249 const double kInverseGamma = 0.45455;
250 const double kDefaultGamma = 2.2;
251 double gamma;
252 if (!IgnoresColorSpace() && png_get_gAMA(png, info, &gamma)) {
253 const double kMaxGamma = 21474.83;
254 if ((gamma <= 0.0) || (gamma > kMaxGamma)) {
255 gamma = kInverseGamma;
256 png_set_gAMA(png, info, gamma);
257 }
258 png_set_gamma(png, kDefaultGamma, gamma);
259 } else {
260 png_set_gamma(png, kDefaultGamma, kInverseGamma);
261 }
262 }
263
264 // Tell libpng to send us rows for interlaced pngs.
265 if (interlace_type == PNG_INTERLACE_ADAM7)
266 png_set_interlace_handling(png);
267
268 // Update our info now (so we can get color channel info).
269 png_read_update_info(png, info);
270
271 int channels = png_get_channels(png, info);
272 DCHECK(channels == 3 || channels == 4);
273 has_alpha_channel_ = (channels == 4);
274 }
275
276 void PNGImageDecoder::RowAvailable(unsigned char* row_buffer,
277 unsigned row_index,
278 int) {
279 if (current_frame_ >= frame_buffer_cache_.size())
280 return;
281
282 ImageFrame& buffer = frame_buffer_cache_[current_frame_];
283 if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
284 png_structp png = reader_->PngPtr();
285 if (!InitFrameBuffer(current_frame_)) {
286 longjmp(JMPBUF(png), 1);
287 return;
288 }
289
290 DCHECK_EQ(ImageFrame::kFramePartial, buffer.GetStatus());
291
292 if (PNG_INTERLACE_ADAM7 ==
293 png_get_interlace_type(png, reader_->InfoPtr())) {
294 unsigned color_channels = has_alpha_channel_ ? 4 : 3;
295 reader_->CreateInterlaceBuffer(color_channels * Size().Area());
296 if (!reader_->InterlaceBuffer()) {
297 longjmp(JMPBUF(png), 1);
298 return;
299 }
300 }
301
302 current_buffer_saw_alpha_ = false;
303 }
304
305 const IntRect& frame_rect = buffer.OriginalFrameRect();
306 DCHECK(IntRect(IntPoint(), Size()).Contains(frame_rect));
307
308 /* libpng comments (here to explain what follows).
309 *
310 * this function is called for every row in the image. If the
311 * image is interlacing, and you turned on the interlace handler,
312 * this function will be called for every row in every pass.
313 * Some of these rows will not be changed from the previous pass.
314 * When the row is not changed, the new_row variable will be NULL.
315 * The rows and passes are called in order, so you don't really
316 * need the row_num and pass, but I'm supplying them because it
317 * may make your life easier.
318 */
319
320 // Nothing to do if the row is unchanged, or the row is outside the image
321 // bounds. In the case that a frame presents more data than the indicated
322 // frame size, ignore the extra rows and use the frame size as the source
323 // of truth. libpng can send extra rows: ignore them too, this to prevent
324 // memory writes outside of the image bounds (security).
325 if (!row_buffer)
326 return;
327
328 DCHECK_GT(frame_rect.Height(), 0);
329 if (row_index >= static_cast<unsigned>(frame_rect.Height()))
330 return;
331
332 int y = row_index + frame_rect.Y();
333 if (y < 0)
334 return;
335 DCHECK_LT(y, Size().Height());
336
337 /* libpng comments (continued).
338 *
339 * For the non-NULL rows of interlaced images, you must call
340 * png_progressive_combine_row() passing in the row and the
341 * old row. You can call this function for NULL rows (it will
342 * just return) and for non-interlaced images (it just does the
343 * memcpy for you) if it will make the code easier. Thus, you
344 * can just do this for all cases:
345 *
346 * png_progressive_combine_row(png_ptr, old_row, new_row);
347 *
348 * where old_row is what was displayed for previous rows. Note
349 * that the first pass (pass == 0 really) will completely cover
350 * the old row, so the rows do not have to be initialized. After
351 * the first pass (and only for interlaced images), you will have
352 * to pass the current row, and the function will combine the
353 * old row and the new row.
354 */
355
356 bool has_alpha = has_alpha_channel_;
357 png_bytep row = row_buffer;
358
359 if (png_bytep interlace_buffer = reader_->InterlaceBuffer()) {
360 unsigned color_channels = has_alpha ? 4 : 3;
361 row = interlace_buffer + (row_index * color_channels * Size().Width());
362 png_progressive_combine_row(reader_->PngPtr(), row, row_buffer);
363 }
364
365 // Write the decoded row pixels to the frame buffer. The repetitive
366 // form of the row write loops is for speed.
367 ImageFrame::PixelData* const dst_row = buffer.GetAddr(frame_rect.X(), y);
368 const int width = frame_rect.Width();
369
370 png_bytep src_ptr = row;
371 if (has_alpha) {
372 // Here we apply the color space transformation to the dst space.
373 // It does not really make sense to transform to a gamma-encoded
374 // space and then immediately after, perform a linear premultiply.
375 // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
376 // instructing SkColorSpaceXform to perform the linear premultiply
377 // while the pixels are a linear space.
378 // We cannot do this because when we apply the gamma encoding after
379 // the premultiply, we will very likely end up with valid pixels
380 // where R, G, and/or B are greater than A. The legacy drawing
381 // pipeline does not know how to handle this.
382 if (SkColorSpaceXform* xform = ColorTransform()) {
383 SkColorSpaceXform::ColorFormat color_format =
384 SkColorSpaceXform::kRGBA_8888_ColorFormat;
385 xform->apply(color_format, dst_row, color_format, src_ptr, width,
386 kUnpremul_SkAlphaType);
387 src_ptr = png_bytep(dst_row);
388 }
389
390 unsigned alpha_mask = 255;
391 if (frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
392 ImageFrame::kBlendAtopBgcolor) {
393 if (buffer.PremultiplyAlpha()) {
394 for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
395 dst_pixel++, src_ptr += 4) {
396 ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
397 src_ptr[2], src_ptr[3]);
398 alpha_mask &= src_ptr[3];
399 }
400 } else {
401 for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
402 dst_pixel++, src_ptr += 4) {
403 ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
404 src_ptr[3]);
405 alpha_mask &= src_ptr[3];
406 }
407 }
408 } else {
409 // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the
410 // frame data of the previous frame is copied at InitFrameBuffer, we can
411 // blend the pixel of this frame, stored in |src_ptr|, over the previous
412 // pixel stored in |dst_pixel|.
413 if (buffer.PremultiplyAlpha()) {
414 for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
415 dst_pixel++, src_ptr += 4) {
416 ImageFrame::BlendRGBAPremultiplied(dst_pixel, src_ptr[0], src_ptr[1],
417 src_ptr[2], src_ptr[3]);
418 alpha_mask &= src_ptr[3];
419 }
420 } else {
421 for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
422 dst_pixel++, src_ptr += 4) {
423 ImageFrame::BlendRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
424 src_ptr[2], src_ptr[3]);
425 alpha_mask &= src_ptr[3];
426 }
427 }
428 }
429
430 if (alpha_mask != 255)
431 current_buffer_saw_alpha_ = true;
432
433 } else {
434 for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
435 src_ptr += 3, ++dst_pixel) {
436 ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
437 255);
438 }
439
440 // We'll apply the color space xform to opaque pixels after they have been
441 // written to the ImageFrame, purely because SkColorSpaceXform supports
442 // RGBA (and not RGB).
443 if (SkColorSpaceXform* xform = ColorTransform()) {
444 xform->apply(XformColorFormat(), dst_row, XformColorFormat(), dst_row,
445 width, kOpaque_SkAlphaType);
446 }
447 }
448
449 buffer.SetPixelsChanged(true);
450 }
451
452 void PNGImageDecoder::FrameComplete() {
453 if (current_frame_ >= frame_buffer_cache_.size())
454 return;
455
456 if (reader_->InterlaceBuffer())
457 reader_->ClearInterlaceBuffer();
458
459 ImageFrame& buffer = frame_buffer_cache_[current_frame_];
460 if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
461 longjmp(JMPBUF(reader_->PngPtr()), 1);
462 return;
463 }
464
465 if (!current_buffer_saw_alpha_)
466 CorrectAlphaWhenFrameBufferSawNoAlpha(current_frame_);
467
468 buffer.SetStatus(ImageFrame::kFrameComplete);
469 }
470
471 bool PNGImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
472 if (!IsDecodedSizeAvailable())
473 return false;
474
475 DCHECK(!Failed() && reader_);
476
477 // For non-animated images, return whether the status of the frame is
478 // ImageFrame::FrameComplete with ImageDecoder::FrameIsCompleteAtIndex.
479 // This matches the behavior of WEBPImageDecoder.
480 if (reader_->ParseCompleted() && reader_->FrameCount() == 1)
481 return ImageDecoder::FrameIsCompleteAtIndex(index);
482
483 return reader_->FrameIsReceivedAtIndex(index);
484 }
485
486 float PNGImageDecoder::FrameDurationAtIndex(size_t index) const {
487 if (index < frame_buffer_cache_.size())
488 return frame_buffer_cache_[index].Duration();
489 return 0;
490 }
491
492 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698