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

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.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/PNGImageReader.h"
40
41 #include <memory>
42 #include "platform/image-decoders/FastSharedBufferReader.h"
43 #include "platform/image-decoders/SegmentReader.h"
44 #include "platform/image-decoders/png/PNGImageDecoder.h"
45 #include "platform/wtf/PtrUtil.h"
46 #include "zlib.h"
47
48 namespace {
49
50 inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
51 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
52 }
53
54 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
55 imageDecoder(png)->HeaderAvailable();
56 }
57
58 void PNGAPI pngRowAvailable(png_structp png,
59 png_bytep row,
60 png_uint_32 rowIndex,
61 int state) {
62 imageDecoder(png)->RowAvailable(row, rowIndex, state);
63 }
64
65 void PNGAPI pngFrameComplete(png_structp png, png_infop) {
66 imageDecoder(png)->FrameComplete();
67 }
68
69 void PNGAPI pngFailed(png_structp png, png_const_charp) {
70 longjmp(JMPBUF(png), 1);
71 }
72
73 } // namespace
74
75 namespace blink {
76
77 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t initial_offset)
78 : width_(0),
79 height_(0),
80 decoder_(decoder),
81 initial_offset_(initial_offset),
82 read_offset_(initial_offset),
83 progressive_decode_offset_(0),
84 idat_offset_(0),
85 idat_is_part_of_animation_(false),
86 expect_idats_(true),
87 is_animated_(false),
88 parsed_signature_(false),
89 parsed_ihdr_(false),
90 parse_completed_(false),
91 reported_frame_count_(0),
92 next_sequence_number_(0),
93 fctl_needs_dat_chunk_(false),
94 ignore_animation_(false) {
95 png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
96 info_ = png_create_info_struct(png_);
97 png_set_progressive_read_fn(png_, decoder_, nullptr, pngRowAvailable,
98 pngFrameComplete);
99 }
100
101 PNGImageReader::~PNGImageReader() {
102 png_destroy_read_struct(png_ ? &png_ : 0, info_ ? &info_ : 0, 0);
103 DCHECK(!png_ && !info_);
104 }
105
106 // This method reads from the FastSharedBufferReader, starting at offset,
107 // and returns |length| bytes in the form of a pointer to a const png_byte*.
108 // This function is used to make it easy to access data from the reader in a
109 // png friendly way, and pass it to libpng for decoding.
110 //
111 // Pre-conditions before using this:
112 // - |reader|.size() >= |read_offset| + |length|
113 // - |buffer|.size() >= |length|
114 // - |length| <= |kBufferSize|
115 //
116 // The reason for the last two precondition is that currently the png signature
117 // plus IHDR chunk (8B + 25B = 33B) is the largest chunk that is read using this
118 // method. If the data is not consecutive, it is stored in |buffer|, which must
119 // have the size of (at least) |length|, but there's no need for it to be larger
120 // than |kBufferSize|.
121 static constexpr size_t kBufferSize = 33;
122 const png_byte* ReadAsConstPngBytep(const FastSharedBufferReader& reader,
123 size_t read_offset,
124 size_t length,
125 char* buffer) {
126 DCHECK(length <= kBufferSize);
127 return reinterpret_cast<const png_byte*>(
128 reader.GetConsecutiveData(read_offset, length, buffer));
129 }
130
131 bool PNGImageReader::ShouldDecodeWithNewPNG(size_t index) const {
132 if (!png_)
133 return true;
134 const bool first_frame_decode_in_progress = progressive_decode_offset_;
135 const bool frame_size_matches_ihdr =
136 frame_info_[index].frame_rect == IntRect(0, 0, width_, height_);
137 if (index)
138 return first_frame_decode_in_progress || !frame_size_matches_ihdr;
139 return !first_frame_decode_in_progress && !frame_size_matches_ihdr;
140 }
141
142 // Return false on a fatal error.
143 bool PNGImageReader::Decode(SegmentReader& data, size_t index) {
144 if (index >= frame_info_.size())
145 return true;
146
147 const FastSharedBufferReader reader(&data);
148
149 if (!is_animated_) {
150 if (setjmp(JMPBUF(png_)))
151 return false;
152 DCHECK_EQ(0u, index);
153 progressive_decode_offset_ += ProcessData(
154 reader, frame_info_[0].start_offset + progressive_decode_offset_, 0);
155 return true;
156 }
157
158 DCHECK(is_animated_);
159
160 const bool decode_with_new_png = ShouldDecodeWithNewPNG(index);
161 if (decode_with_new_png) {
162 ClearDecodeState(0);
163 png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
164 info_ = png_create_info_struct(png_);
165 png_set_progressive_read_fn(png_, decoder_, pngHeaderAvailable,
166 pngRowAvailable, pngFrameComplete);
167 }
168
169 if (setjmp(JMPBUF(png_)))
170 return false;
171
172 if (decode_with_new_png)
173 StartFrameDecoding(reader, index);
174
175 if (!index && (!FirstFrameFullyReceived() || progressive_decode_offset_)) {
176 const bool decoded_entire_frame = ProgressivelyDecodeFirstFrame(reader);
177 if (!decoded_entire_frame)
178 return true;
179 progressive_decode_offset_ = 0;
180 } else {
181 DecodeFrame(reader, index);
182 }
183
184 static png_byte iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
185 png_process_data(png_, info_, iend, 12);
186 png_destroy_read_struct(&png_, &info_, 0);
187 DCHECK(!png_ && !info_);
188
189 return true;
190 }
191
192 void PNGImageReader::StartFrameDecoding(const FastSharedBufferReader& reader,
193 size_t index) {
194 // If the frame is the size of the whole image, just re-process all header
195 // data up to the first frame.
196 const IntRect& frame_rect = frame_info_[index].frame_rect;
197 if (frame_rect == IntRect(0, 0, width_, height_)) {
198 ProcessData(reader, initial_offset_, idat_offset_);
199 return;
200 }
201
202 // Process the IHDR chunk, but change the width and height so it reflects
203 // the frame's width and height. ImageDecoder will apply the x,y offset.
204 constexpr size_t kHeaderSize = kBufferSize;
205 char read_buffer[kHeaderSize];
206 const png_byte* chunk =
207 ReadAsConstPngBytep(reader, initial_offset_, kHeaderSize, read_buffer);
208 png_byte* header = reinterpret_cast<png_byte*>(read_buffer);
209 if (chunk != header)
210 memcpy(header, chunk, kHeaderSize);
211 png_save_uint_32(header + 16, frame_rect.Width());
212 png_save_uint_32(header + 20, frame_rect.Height());
213 // IHDR has been modified, so tell libpng to ignore CRC errors.
214 png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
215 png_process_data(png_, info_, header, kHeaderSize);
216
217 // Process the rest of the header chunks.
218 ProcessData(reader, initial_offset_ + kHeaderSize,
219 idat_offset_ - kHeaderSize);
220 }
221
222 // Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
223 // - The length of |chunk| must be >= 8
224 // - The length of |tag| must be = 4
225 static inline bool IsChunk(const png_byte* chunk, const char* tag) {
226 return memcmp(chunk + 4, tag, 4) == 0;
227 }
228
229 bool PNGImageReader::ProgressivelyDecodeFirstFrame(
230 const FastSharedBufferReader& reader) {
231 size_t offset = frame_info_[0].start_offset;
232
233 // Loop while there is enough data to do progressive decoding.
234 while (reader.size() >= offset + 8) {
235 char read_buffer[8];
236 // At the beginning of each loop, the offset is at the start of a chunk.
237 const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
238 const png_uint_32 length = png_get_uint_32(chunk);
239 DCHECK(length <= PNG_UINT_31_MAX);
240
241 // When an fcTL or IEND chunk is encountered, the frame data has ended.
242 // Return true, since all frame data is decoded.
243 if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND"))
244 return true;
245
246 // If this chunk was already decoded, move on to the next.
247 if (progressive_decode_offset_ >= offset + length + 12) {
248 offset += length + 12;
249 continue;
250 }
251
252 // Three scenarios are possible here:
253 // 1) Some bytes of this chunk were already decoded in a previous call.
254 // Continue from there.
255 // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
256 // 3) This is any other chunk. Pass it to libpng for processing.
257 size_t end_offset_chunk = offset + length + 12;
258
259 if (progressive_decode_offset_ >= offset + 8) {
260 offset = progressive_decode_offset_;
261 } else if (IsChunk(chunk, "fdAT")) {
262 ProcessFdatChunkAsIdat(length);
263 // Skip the sequence number.
264 offset += 12;
265 } else {
266 png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
267 offset += 8;
268 }
269
270 size_t bytes_left_in_chunk = end_offset_chunk - offset;
271 size_t bytes_decoded = ProcessData(reader, offset, bytes_left_in_chunk);
272 progressive_decode_offset_ = offset + bytes_decoded;
273 if (bytes_decoded < bytes_left_in_chunk)
274 return false;
275 offset += bytes_decoded;
276 }
277
278 return false;
279 }
280
281 void PNGImageReader::ProcessFdatChunkAsIdat(png_uint_32 fdat_length) {
282 // An fdAT chunk is build up as follows:
283 // - |length| (4B)
284 // - fdAT tag (4B)
285 // - sequence number (4B)
286 // - frame data (|length| - 4B)
287 // - CRC (4B)
288 // Thus, to reformat this into an IDAT chunk, do the following:
289 // - write |length| - 4 as the new length, since the sequence number
290 // must be removed.
291 // - change the tag to IDAT.
292 // - omit the sequence number from the data part of the chunk.
293 png_byte chunk_idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
294 png_save_uint_32(chunk_idat, fdat_length - 4);
295 // The CRC is incorrect when applied to the modified fdAT.
296 png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
297 png_process_data(png_, info_, chunk_idat, 8);
298 }
299
300 void PNGImageReader::DecodeFrame(const FastSharedBufferReader& reader,
301 size_t index) {
302 size_t offset = frame_info_[index].start_offset;
303 size_t end_offset = offset + frame_info_[index].byte_length;
304 char read_buffer[8];
305
306 while (offset < end_offset) {
307 const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
308 const png_uint_32 length = png_get_uint_32(chunk);
309 DCHECK(length <= PNG_UINT_31_MAX);
310
311 if (IsChunk(chunk, "fdAT")) {
312 ProcessFdatChunkAsIdat(length);
313 // The frame data and the CRC span |length| bytes, so skip the
314 // sequence number and process |length| bytes to decode the frame.
315 ProcessData(reader, offset + 12, length);
316 } else {
317 png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
318 ProcessData(reader, offset + 8, length + 4);
319 }
320
321 offset += 12 + length;
322 }
323 }
324
325 // Compute the CRC and compare to the stored value.
326 static bool CheckCrc(const FastSharedBufferReader& reader,
327 size_t chunk_start,
328 size_t chunk_length) {
329 constexpr size_t kSizeNeededForfcTL = 26 + 4;
330 char read_buffer[kSizeNeededForfcTL];
331 DCHECK(chunk_length + 4 <= kSizeNeededForfcTL);
332 const png_byte* chunk = ReadAsConstPngBytep(reader, chunk_start + 4,
333 chunk_length + 4, read_buffer);
334
335 char crc_buffer[4];
336 const png_byte* crc_position = ReadAsConstPngBytep(
337 reader, chunk_start + 8 + chunk_length, 4, crc_buffer);
338 png_uint_32 crc = png_get_uint_32(crc_position);
339 return crc == crc32(crc32(0, Z_NULL, 0), chunk, chunk_length + 4);
340 }
341
342 bool PNGImageReader::CheckSequenceNumber(const png_byte* position) {
343 png_uint_32 sequence = png_get_uint_32(position);
344 if (sequence != next_sequence_number_ || sequence > PNG_UINT_31_MAX)
345 return false;
346
347 ++next_sequence_number_;
348 return true;
349 }
350
351 // Return false if there was a fatal error; true otherwise.
352 bool PNGImageReader::Parse(SegmentReader& data, ParseQuery query) {
353 if (parse_completed_)
354 return true;
355
356 const FastSharedBufferReader reader(&data);
357
358 if (!ParseSize(reader))
359 return false;
360
361 if (!decoder_->IsDecodedSizeAvailable())
362 return true;
363
364 // For non animated images (identified by no acTL chunk before the IDAT),
365 // there is no need to continue parsing.
366 if (!is_animated_) {
367 FrameInfo frame;
368 frame.start_offset = read_offset_;
369 // This should never be read in this case, but initialize just in case.
370 frame.byte_length = kFirstFrameIndicator;
371 frame.duration = 0;
372 frame.frame_rect = IntRect(0, 0, width_, height_);
373 frame.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
374 frame.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
375 DCHECK(frame_info_.IsEmpty());
376 frame_info_.push_back(frame);
377 parse_completed_ = true;
378 return true;
379 }
380
381 if (query == ParseQuery::kSize)
382 return true;
383
384 DCHECK_EQ(ParseQuery::kMetaData, query);
385 DCHECK(is_animated_);
386
387 // Loop over the data and manually register all frames. Nothing is passed to
388 // libpng for processing. A frame is registered on the next fcTL chunk or
389 // when the IEND chunk is found. This ensures that only complete frames are
390 // reported, unless there is an error in the stream.
391 char read_buffer[kBufferSize];
392 while (reader.size() >= read_offset_ + 8) {
393 const png_byte* chunk =
394 ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
395 const size_t length = png_get_uint_32(chunk);
396 if (length > PNG_UINT_31_MAX)
397 return false;
398
399 const bool idat = IsChunk(chunk, "IDAT");
400 if (idat && !expect_idats_)
401 return false;
402
403 const bool fd_at = IsChunk(chunk, "fdAT");
404 if (fd_at && expect_idats_)
405 return false;
406
407 if (fd_at || (idat && idat_is_part_of_animation_)) {
408 fctl_needs_dat_chunk_ = false;
409 if (!new_frame_.start_offset) {
410 // Beginning of a new frame's data.
411 new_frame_.start_offset = read_offset_;
412
413 if (frame_info_.IsEmpty()) {
414 // This is the first frame. Report it immediately so it can be
415 // decoded progressively.
416 new_frame_.byte_length = kFirstFrameIndicator;
417 frame_info_.push_back(new_frame_);
418 }
419 }
420
421 if (fd_at) {
422 if (reader.size() < read_offset_ + 8 + 4)
423 return true;
424 const png_byte* sequence_position =
425 ReadAsConstPngBytep(reader, read_offset_ + 8, 4, read_buffer);
426 if (!CheckSequenceNumber(sequence_position))
427 return false;
428 }
429
430 } else if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND")) {
431 // This marks the end of the previous frame.
432 if (new_frame_.start_offset) {
433 new_frame_.byte_length = read_offset_ - new_frame_.start_offset;
434 if (frame_info_[0].byte_length == kFirstFrameIndicator) {
435 frame_info_[0].byte_length = new_frame_.byte_length;
436 } else {
437 frame_info_.push_back(new_frame_);
438 if (IsChunk(chunk, "fcTL")) {
439 if (frame_info_.size() >= reported_frame_count_)
440 return false;
441 } else { // IEND
442 if (frame_info_.size() != reported_frame_count_)
443 return false;
444 }
445 }
446
447 new_frame_.start_offset = 0;
448 }
449
450 if (reader.size() < read_offset_ + 12 + length)
451 return true;
452
453 if (IsChunk(chunk, "IEND")) {
454 parse_completed_ = true;
455 return true;
456 }
457
458 if (length != 26 || !CheckCrc(reader, read_offset_, length))
459 return false;
460
461 chunk =
462 ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
463 if (!ParseFrameInfo(chunk))
464 return false;
465
466 expect_idats_ = false;
467 } else if (IsChunk(chunk, "acTL")) {
468 // There should only be one acTL chunk, and it should be before the
469 // IDAT chunk.
470 return false;
471 }
472
473 read_offset_ += 12 + length;
474 }
475 return true;
476 }
477
478 // If |length| == 0, read until the stream ends. Return number of bytes
479 // processed.
480 size_t PNGImageReader::ProcessData(const FastSharedBufferReader& reader,
481 size_t offset,
482 size_t length) {
483 const char* segment;
484 size_t total_processed_bytes = 0;
485 while (reader.size() > offset) {
486 size_t segment_length = reader.GetSomeData(segment, offset);
487 if (length > 0 && segment_length + total_processed_bytes > length)
488 segment_length = length - total_processed_bytes;
489
490 png_process_data(png_, info_,
491 reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
492 segment_length);
493 offset += segment_length;
494 total_processed_bytes += segment_length;
495 if (total_processed_bytes == length)
496 return length;
497 }
498 return total_processed_bytes;
499 }
500
501 // Process up to the start of the IDAT with libpng.
502 // Return false for a fatal error. True otherwise.
503 bool PNGImageReader::ParseSize(const FastSharedBufferReader& reader) {
504 if (decoder_->IsDecodedSizeAvailable())
505 return true;
506
507 char read_buffer[kBufferSize];
508
509 if (setjmp(JMPBUF(png_)))
510 return false;
511
512 if (!parsed_signature_) {
513 if (reader.size() < read_offset_ + 8)
514 return true;
515
516 const png_byte* chunk =
517 ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
518 png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
519 read_offset_ += 8;
520 parsed_signature_ = true;
521 new_frame_.start_offset = 0;
522 }
523
524 // Process APNG chunks manually, pass other chunks to libpng.
525 for (png_uint_32 length = 0; reader.size() >= read_offset_ + 8;
526 read_offset_ += length + 12) {
527 const png_byte* chunk =
528 ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
529 length = png_get_uint_32(chunk);
530
531 if (IsChunk(chunk, "IDAT")) {
532 // Done with header chunks.
533 idat_offset_ = read_offset_;
534 fctl_needs_dat_chunk_ = false;
535 if (ignore_animation_)
536 is_animated_ = false;
537 if (!is_animated_ || 1 == reported_frame_count_)
538 decoder_->SetRepetitionCount(kAnimationNone);
539 if (!decoder_->SetSize(width_, height_))
540 return false;
541 decoder_->SetColorSpace();
542 decoder_->HeaderAvailable();
543 return true;
544 }
545
546 // Wait until the entire chunk is available for parsing simplicity.
547 if (reader.size() < read_offset_ + length + 12)
548 break;
549
550 if (IsChunk(chunk, "acTL")) {
551 if (ignore_animation_)
552 continue;
553 if (is_animated_ || length != 8 || !parsed_ihdr_ ||
554 !CheckCrc(reader, read_offset_, 8)) {
555 ignore_animation_ = true;
556 continue;
557 }
558 chunk =
559 ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
560 reported_frame_count_ = png_get_uint_32(chunk);
561 if (!reported_frame_count_ || reported_frame_count_ > PNG_UINT_31_MAX) {
562 ignore_animation_ = true;
563 continue;
564 }
565 png_uint_32 repetition_count = png_get_uint_32(chunk + 4);
566 if (repetition_count > PNG_UINT_31_MAX) {
567 ignore_animation_ = true;
568 continue;
569 }
570 is_animated_ = true;
571 decoder_->SetRepetitionCount(static_cast<int>(repetition_count) - 1);
572 } else if (IsChunk(chunk, "fcTL")) {
573 if (ignore_animation_)
574 continue;
575 if (length != 26 || !parsed_ihdr_ ||
576 !CheckCrc(reader, read_offset_, 26)) {
577 ignore_animation_ = true;
578 continue;
579 }
580 chunk =
581 ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
582 if (!ParseFrameInfo(chunk) ||
583 new_frame_.frame_rect != IntRect(0, 0, width_, height_)) {
584 ignore_animation_ = true;
585 continue;
586 }
587 idat_is_part_of_animation_ = true;
588 } else if (IsChunk(chunk, "fdAT")) {
589 ignore_animation_ = true;
590 } else {
591 png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
592 ProcessData(reader, read_offset_ + 8, length + 4);
593 if (IsChunk(chunk, "IHDR")) {
594 parsed_ihdr_ = true;
595 width_ = png_get_image_width(png_, info_);
596 height_ = png_get_image_height(png_, info_);
597 }
598 }
599 }
600
601 // Not enough data to call HeaderAvailable.
602 return true;
603 }
604
605 void PNGImageReader::ClearDecodeState(size_t index) {
606 if (index)
607 return;
608 png_destroy_read_struct(png_ ? &png_ : nullptr, info_ ? &info_ : nullptr, 0);
609 DCHECK(!png_ && !info_);
610 progressive_decode_offset_ = 0;
611 }
612
613 const PNGImageReader::FrameInfo& PNGImageReader::GetFrameInfo(
614 size_t index) const {
615 DCHECK(index < frame_info_.size());
616 return frame_info_[index];
617 }
618
619 // Extract the fcTL frame control info and store it in new_frame_. The length
620 // check on the fcTL data has been done by the calling code.
621 bool PNGImageReader::ParseFrameInfo(const png_byte* data) {
622 if (fctl_needs_dat_chunk_)
623 return false;
624
625 png_uint_32 frame_width = png_get_uint_32(data + 4);
626 png_uint_32 frame_height = png_get_uint_32(data + 8);
627 png_uint_32 x_offset = png_get_uint_32(data + 12);
628 png_uint_32 y_offset = png_get_uint_32(data + 16);
629 png_uint_16 delay_numerator = png_get_uint_16(data + 20);
630 png_uint_16 delay_denominator = png_get_uint_16(data + 22);
631
632 if (!CheckSequenceNumber(data))
633 return false;
634 if (!frame_width || !frame_height)
635 return false;
636 if (x_offset + frame_width > width_ || y_offset + frame_height > height_)
637 return false;
638
639 new_frame_.frame_rect =
640 IntRect(x_offset, y_offset, frame_width, frame_height);
641
642 if (delay_denominator)
643 new_frame_.duration = delay_numerator * 1000 / delay_denominator;
644 else
645 new_frame_.duration = delay_numerator * 10;
646
647 enum DisposeOperations : png_byte {
648 kAPNG_DISPOSE_OP_NONE = 0,
649 kAPNG_DISPOSE_OP_BACKGROUND = 1,
650 kAPNG_DISPOSE_OP_PREVIOUS = 2,
651 };
652 const png_byte& dispose_op = data[24];
653 switch (dispose_op) {
654 case kAPNG_DISPOSE_OP_NONE:
655 new_frame_.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
656 break;
657 case kAPNG_DISPOSE_OP_BACKGROUND:
658 new_frame_.disposal_method =
659 ImageFrame::DisposalMethod::kDisposeOverwriteBgcolor;
660 break;
661 case kAPNG_DISPOSE_OP_PREVIOUS:
662 new_frame_.disposal_method =
663 ImageFrame::DisposalMethod::kDisposeOverwritePrevious;
664 break;
665 default:
666 return false;
667 }
668
669 enum BlendOperations : png_byte {
670 kAPNG_BLEND_OP_SOURCE = 0,
671 kAPNG_BLEND_OP_OVER = 1,
672 };
673 const png_byte& blend_op = data[25];
674 switch (blend_op) {
675 case kAPNG_BLEND_OP_SOURCE:
676 new_frame_.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
677 break;
678 case kAPNG_BLEND_OP_OVER:
679 new_frame_.alpha_blend =
680 ImageFrame::AlphaBlendSource::kBlendAtopPreviousFrame;
681 break;
682 default:
683 return false;
684 }
685
686 fctl_needs_dat_chunk_ = true;
687 return true;
688 }
689
690 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698