1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/image/apng/apng_image_decoder.h"
17 #include <cstdlib>
18 #include <cstdio>
19 #include <securec.h>
20 #include <string>
21 #include <cmath>
22 #include "png.h"
23 #include "zlib.h"
24 #include "base/utils/string_utils.h"
25
26 #if !defined(WINDOWS_PLATFORM) and !defined(MAC_PLATFORM) and !defined(IOS_PLATFORM)
27 #include <malloc.h>
28 #endif
29
30 namespace OHOS::Ace {
31 constexpr uint32_t PNGSize22 = 22;
32 constexpr uint32_t PNGSize24 = 24;
33 constexpr uint32_t PNGSize25 = 25;
34 constexpr uint32_t PNGHeadHeight = 4;
35 constexpr uint32_t PNGHeadBitDepth = 8;
36 constexpr uint32_t PNGHeadColorType = 9;
37 constexpr uint32_t PNGHeadCompMethod = 10;
38 constexpr uint32_t PNGHeadFilterMethod = 11;
39 constexpr uint32_t PNGHeadInterMethod = 12;
40
41 constexpr uint32_t MiniChunkNum = 3;
42 constexpr uint32_t ChunkHeadLength = 13;
43 constexpr uint32_t PngHeadLength = 8;
44 constexpr uint32_t PngFOURCCLen = 4;
45 constexpr uint32_t PngFcTLLen = 26;
46 constexpr uint32_t Byte2 = 2;
47 constexpr uint32_t Byte4 = 4;
48 constexpr uint32_t Byte8 = 8;
49 constexpr uint32_t Byte12 = 12;
50 constexpr uint32_t Byte16 = 16;
51 constexpr uint32_t Byte17 = 17;
52 constexpr uint32_t Byte20 = 20;
53 constexpr uint32_t Byte21 = 21;
54 constexpr uint32_t Byte24 = 24;
55 constexpr uint32_t Byte25 = 25;
56 constexpr uint32_t Byte32 = 32;
57
58 #define FOUR_CC(c1, c2, c3, c4) (static_cast<uint32_t>(((c4) << Byte24) | ((c3) << Byte16) | ((c2) << Byte8) | (c1)))
59 #define TWO_CC(c1, c2) (static_cast<uint16_t>(((c2) << Byte8) | (c1)))
60
swap_endian_uint16(uint16_t value)61 static inline uint16_t swap_endian_uint16(uint16_t value)
62 {
63 return
64 static_cast<uint16_t>((value & 0x00FF) << Byte8) |
65 static_cast<uint16_t>((value & 0xFF00) >> Byte8);
66 }
67
swap_endian_uint32(uint32_t value)68 static inline uint32_t swap_endian_uint32(uint32_t value)
69 {
70 return
71 static_cast<uint32_t>((value & 0x000000FFU) << Byte24) |
72 static_cast<uint32_t>((value & 0x0000FF00U) << Byte8) |
73 static_cast<uint32_t>((value & 0x00FF0000U) >> Byte8) |
74 static_cast<uint32_t>((value & 0xFF000000U) >> Byte24);
75 }
76
png_chunk_IHDR_read(PngChunkIHDR * IHDR,const uint8_t * data)77 static void png_chunk_IHDR_read(PngChunkIHDR *IHDR, const uint8_t *data)
78 {
79 IHDR->width = swap_endian_uint32(*((uint32_t *) (data)));
80 IHDR->height = swap_endian_uint32(*((uint32_t *) (data + PNGHeadHeight)));
81 IHDR->bitDepth = data[PNGHeadBitDepth];
82 IHDR->colorType = data[PNGHeadColorType];
83 IHDR->compressionMethod = data[PNGHeadCompMethod];
84 IHDR->filterMethod = data[PNGHeadFilterMethod];
85 IHDR->interlaceMethod = data[PNGHeadInterMethod];
86 }
87
png_chunk_IHDR_write(PngChunkIHDR * IHDR,uint8_t * data)88 static void png_chunk_IHDR_write(PngChunkIHDR *IHDR, uint8_t *data)
89 {
90 *((uint32_t *) (data)) = swap_endian_uint32(IHDR->width);
91 *((uint32_t *) (data + PNGHeadHeight)) = swap_endian_uint32(IHDR->height);
92 data[PNGHeadBitDepth] = IHDR->bitDepth;
93 data[PNGHeadColorType] = IHDR->colorType;
94 data[PNGHeadCompMethod] = IHDR->compressionMethod;
95 data[PNGHeadFilterMethod] = IHDR->filterMethod;
96 data[PNGHeadInterMethod] = IHDR->interlaceMethod;
97 }
98
png_sig_compare(png_const_bytep sig,size_t start,size_t num_to_check)99 static int png_sig_compare(png_const_bytep sig, size_t start, size_t num_to_check)
100 {
101 const png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
102
103 if (num_to_check > Byte8) {
104 num_to_check = Byte8;
105 } else if (num_to_check < 1) {
106 return (-1);
107 }
108
109 if (start > Byte8 - 1) {
110 return (-1);
111 }
112
113 if (start + num_to_check > Byte8) {
114 num_to_check = Byte8 - start;
115 }
116
117 return ((int) (memcmp(&sig[start], &png_signature[start], num_to_check)));
118 }
119
png_chunk_fcTL_read(PngChunkfcTL * fcTL,const uint8_t * data)120 static void png_chunk_fcTL_read(PngChunkfcTL *fcTL, const uint8_t *data)
121 {
122 fcTL->sequenceNumber = swap_endian_uint32(*((uint32_t *) (data)));
123 fcTL->width = swap_endian_uint32(*((uint32_t *) (data + Byte4)));
124 fcTL->height = swap_endian_uint32(*((uint32_t *) (data + Byte8)));
125 fcTL->xOffset = swap_endian_uint32(*((uint32_t *) (data + Byte12)));
126 fcTL->yOffset = swap_endian_uint32(*((uint32_t *) (data + Byte16)));
127 fcTL->delayNum = swap_endian_uint16(*((uint16_t *) (data + Byte20)));
128 fcTL->delayDen = swap_endian_uint16(*((uint16_t *) (data + PNGSize22)));
129 fcTL->disposeOp = data[PNGSize24];
130 fcTL->blendOp = data[PNGSize25];
131 }
132
133 /**
134 * validate frame chunk order
135 * @param chunks : input
136 * @param chunkNum : chunkNum
137 * @param first_idat_index : output
138 * @param first_frame_is_cover : output
139 * @return
140 */
png_validate_animation_chunk_order(PngChunkInfo * chunks,uint32_t chunkNum,uint32_t * first_idat_index,bool * first_frame_is_cover)141 static bool png_validate_animation_chunk_order(PngChunkInfo *chunks,
142 uint32_t chunkNum,
143 uint32_t *first_idat_index,
144 bool *first_frame_is_cover)
145 {
146 /*
147 PNG at least contains 3 chunks: IHDR, IDAT, IEND.
148 `IHDR` must appear first.
149 `IDAT` must appear consecutively.
150 `IEND` must appear end.
151
152 APNG must contains one `acTL` and at least one 'fcTL' and `fdAT`.
153 `fdAT` must appear consecutively.
154 `fcTL` must appear before `IDAT` or `fdAT`.
155 */
156 if (chunkNum <= Byte2) {
157 return false;
158 }
159
160 if (chunks->fourcc != FOUR_CC('I', 'H', 'D', 'R')) {
161 return false;
162 }
163
164 if ((chunks + chunkNum - 1)->fourcc != FOUR_CC('I', 'E', 'N', 'D')) {
165 return false;
166 }
167
168 uint32_t prev_fourcc = 0;
169 uint32_t IHDR_num = 0;
170 uint32_t IDAT_num = 0;
171 uint32_t acTL_num = 0;
172 uint32_t fcTL_num = 0;
173 uint32_t first_IDAT = 0;
174 bool first_frame_cover = false;
175 for (uint32_t i = 0; i < chunkNum; i++) {
176 PngChunkInfo *chunk = chunks + i;
177 switch (chunk->fourcc) {
178 case FOUR_CC('I', 'H', 'D', 'R'): {
179 if (i != 0) {
180 return false;
181 }
182 if (IHDR_num > 0) {
183 return false;
184 }
185 IHDR_num++;
186 break;
187 }
188 case FOUR_CC('I', 'D', 'A', 'T'): {
189 if (prev_fourcc != FOUR_CC('I', 'D', 'A', 'T')) {
190 if (IDAT_num == 0) {
191 first_IDAT = i;
192 } else {
193 return false;
194 }
195 }
196
197 IDAT_num++;
198 break;
199 }
200 case FOUR_CC('a', 'c', 'T', 'L'): {
201 if (acTL_num > 0) {
202 return false;
203 }
204
205 acTL_num++;
206 break;
207 }
208 case FOUR_CC('f', 'c', 'T', 'L'): {
209 if (i + 1 == chunkNum) {
210 return false;
211 }
212 if ((chunk + 1)->fourcc != FOUR_CC('f', 'd', 'A', 'T') &&
213 (chunk + 1)->fourcc != FOUR_CC('I', 'D', 'A', 'T')) {
214 return false;
215 }
216
217 if (fcTL_num == 0) {
218 if ((chunk + 1)->fourcc == FOUR_CC('I', 'D', 'A', 'T')) {
219 first_frame_cover = true;
220 }
221 }
222
223 fcTL_num++;
224 break;
225 }
226 case FOUR_CC('f', 'd', 'A', 'T'): {
227 if (prev_fourcc != FOUR_CC('f', 'd', 'A', 'T') &&
228 prev_fourcc != FOUR_CC('f', 'c', 'T', 'L')) {
229 return false;
230 }
231 break;
232 }
233 }
234 prev_fourcc = chunk->fourcc;
235 }
236
237 if (IHDR_num != 1) {
238 return false;
239 }
240 if (IDAT_num == 0) {
241 return false;
242 }
243 if (acTL_num != 1) {
244 return false;
245 }
246 if (fcTL_num < acTL_num) {
247 return false;
248 }
249 *first_idat_index = first_IDAT;
250 *first_frame_is_cover = first_frame_cover;
251 return true;
252 }
253
png_info_release(PngInfo * info)254 static void png_info_release(PngInfo *info)
255 {
256 if (info) {
257 if (info->chunks) {
258 free(info->chunks);
259 }
260 if (info->apngFrames) {
261 free(info->apngFrames);
262 }
263 if (info->apngSharedChunkIndexs) {
264 free(info->apngSharedChunkIndexs);
265 }
266 free(info);
267 }
268 }
269
270 /**
271 Create a png info from a png file. See struct PngInfo for more information.
272
273 @param data png/apng file data.
274 @param length the data's length in bytes.
275 @return A png info object, you may call png_info_release() to release it.
276 Returns NULL if an error occurs.
277 */
png_info_create(const uint8_t * data,uint32_t length)278 static PngInfo *png_info_create(const uint8_t *data, uint32_t length)
279 {
280 if (!data || length < Byte32) {
281 return nullptr;
282 }
283
284 if(png_sig_compare((png_const_bytep)data, 0, PngHeadLength)) {
285 LOGE("image not a apng file");
286 return nullptr;
287 }
288
289 uint32_t chunk_realloc_num = 16;
290 PngChunkInfo *chunks = (PngChunkInfo *) malloc(sizeof(PngChunkInfo) * chunk_realloc_num);
291 if (!chunks) {
292 LOGE("malloc memory failed!");
293 return nullptr;
294 }
295
296 // parse png chunks
297 uint32_t offset = PngHeadLength;
298 uint32_t chunkNum = 0;
299 uint32_t chunk_capacity = chunk_realloc_num;
300 uint32_t apngLoopNum = 0;
301 int32_t apng_sequence_index = -1;
302 int32_t apng_frame_index = 0;
303 int32_t apng_frame_number = -1;
304 bool apng_chunk_error = false;
305 do {
306 if (chunkNum >= chunk_capacity) {
307 PngChunkInfo *new_chunks = (PngChunkInfo *) realloc(chunks, sizeof(PngChunkInfo) *
308 (chunk_capacity + chunk_realloc_num));
309 if (!new_chunks) {
310 free(chunks);
311 return nullptr;
312 }
313
314 chunks = new_chunks;
315 chunk_capacity += chunk_realloc_num;
316 }
317
318 PngChunkInfo *chunk = chunks + chunkNum;
319 const uint8_t *chunk_data = data + offset;
320 chunk->offset = offset;
321 chunk->length = swap_endian_uint32(*((uint32_t *) chunk_data));
322 if ((uint64_t) chunk->offset + (uint64_t) chunk->length + Byte12 > length) {
323 free(chunks);
324 return nullptr;
325 }
326
327 chunk->fourcc = *((uint32_t *) (chunk_data + PngFOURCCLen));
328 if ((uint64_t) chunk->offset + PngFOURCCLen + chunk->length + PngFOURCCLen > (uint64_t) length) {
329 break;
330 }
331
332 chunk->crc32 = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength + chunk->length)));
333 chunkNum++;
334 offset += Byte12 + chunk->length;
335
336 switch (chunk->fourcc) {
337 case FOUR_CC('a', 'c', 'T', 'L') : {
338 if (chunk->length == PngHeadLength) {
339 apng_frame_number = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
340 apngLoopNum = swap_endian_uint32(*((uint32_t *) (chunk_data + Byte12)));
341 } else {
342 apng_chunk_error = true;
343 }
344
345 break;
346 }
347 case FOUR_CC('f', 'c', 'T', 'L') :
348 case FOUR_CC('f', 'd', 'A', 'T') : {
349 if (chunk->fourcc == FOUR_CC('f', 'c', 'T', 'L')) {
350 if (chunk->length != PngFcTLLen) {
351 apng_chunk_error = true;
352 } else {
353 apng_frame_index++;
354 }
355 }
356
357 if (chunk->length > PngFOURCCLen) {
358 uint32_t sequence = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
359 if (apng_sequence_index + 1 == sequence) {
360 apng_sequence_index++;
361 } else {
362 apng_chunk_error = true;
363 }
364 } else {
365 apng_chunk_error = true;
366 }
367
368 break;
369 }
370 case FOUR_CC('I', 'E', 'N', 'D') : {
371 offset = length; // end, break do-while loop
372 break;
373 }
374 }
375 } while (offset + Byte12 <= length);
376
377 if (chunkNum < MiniChunkNum ||
378 chunks->fourcc != FOUR_CC('I', 'H', 'D', 'R') ||
379 chunks->length != ChunkHeadLength) {
380 free(chunks);
381 return nullptr;
382 }
383
384 // png info
385 PngInfo *info = (PngInfo *) calloc(1, sizeof(PngInfo));
386 if (!info) {
387 free(chunks);
388 return nullptr;
389 }
390
391 info->chunks = chunks;
392 info->chunkNum = chunkNum;
393 png_chunk_IHDR_read(&info->header, data + chunks->offset + PngHeadLength);
394
395 // apng info
396 if (!apng_chunk_error && apng_frame_number == apng_frame_index && apng_frame_number >= 1) {
397 bool first_frame_is_cover = false;
398 uint32_t first_IDAT_index = 0;
399 if (!png_validate_animation_chunk_order(info->chunks, info->chunkNum, &first_IDAT_index,
400 &first_frame_is_cover)) {
401 return info; // ignore apng chunk
402 }
403
404 info->apngLoopNum = apngLoopNum;
405 info->apngFrameNum = apng_frame_number;
406 info->apngFirstFrameIsCover = first_frame_is_cover;
407 info->apngSharedInsertIndex = first_IDAT_index;
408 info->apngFrames = (PngFrameInfo *) calloc(apng_frame_number, sizeof(PngFrameInfo));
409 if (!info->apngFrames) {
410 png_info_release(info);
411 return nullptr;
412 }
413
414 info->apngSharedChunkIndexs = (uint32_t *) calloc(info->chunkNum, sizeof(uint32_t));
415 if (!info->apngSharedChunkIndexs) {
416 png_info_release(info);
417 return nullptr;
418 }
419
420 int32_t frame_index = -1;
421 uint32_t *shared_chunk_index = info->apngSharedChunkIndexs;
422 for (int32_t i = 0; i < info->chunkNum; i++) {
423 PngChunkInfo *chunk = info->chunks + i;
424 switch (chunk->fourcc) {
425 case FOUR_CC('I', 'D', 'A', 'T'): {
426 if (info->apngSharedInsertIndex == 0) {
427 info->apngSharedInsertIndex = i;
428 }
429
430 if (first_frame_is_cover) {
431 PngFrameInfo *frame = info->apngFrames + frame_index;
432 frame->chunkNum++;
433 frame->chunkSize += chunk->length + Byte12;
434 }
435
436 break;
437 }
438 case FOUR_CC('a', 'c', 'T', 'L'): {
439 break;
440 }
441 case FOUR_CC('f', 'c', 'T', 'L'): {
442 frame_index++;
443 PngFrameInfo *frame = info->apngFrames + frame_index;
444 frame->chunkIndex = i + 1;
445 png_chunk_fcTL_read(&frame->frameControl, data + chunk->offset + PngHeadLength);
446 break;
447 }
448 case FOUR_CC('f', 'd', 'A', 'T'): {
449 PngFrameInfo *frame = info->apngFrames + frame_index;
450 frame->chunkNum++;
451 frame->chunkSize += chunk->length + Byte12;
452 break;
453 }
454 default: {
455 *shared_chunk_index = i;
456 shared_chunk_index++;
457 info->apngSharedChunkSize += chunk->length + Byte12;
458 info->apngSharedChunkNum++;
459 break;
460 }
461 }
462 }
463 }
464
465 return info;
466 }
467
468 /**
469 Copy a png frame data from an apng file.
470
471 @param data apng file data
472 @param info png info
473 @param index frame index (zero-based)
474 @param size output, the size of the frame data
475 @return A frame data (single-frame png file), call free() to release the data.
476 Returns NULL if an error occurs.
477 */
png_copy_frame_data_at_index(const uint8_t * data,const PngInfo * info,const uint32_t index,uint32_t * size)478 static uint8_t *png_copy_frame_data_at_index(const uint8_t *data,
479 const PngInfo *info,
480 const uint32_t index,
481 uint32_t *size)
482 {
483 if (index >= info->apngFrameNum) {
484 return nullptr;
485 }
486
487 if (!data) {
488 return nullptr;
489 }
490
491 PngFrameInfo *frame_info = info->apngFrames + index;
492 uint32_t frame_remux_size = PngHeadLength + info->apngSharedChunkSize + frame_info->chunkSize;
493 if (!(info->apngFirstFrameIsCover && index == 0)) {
494 frame_remux_size -= frame_info->chunkNum * Byte4; // remove fdAT sequence number
495 }
496
497 uint8_t *frame_data = (uint8_t *) malloc(frame_remux_size);
498 if (!frame_data) {
499 return nullptr;
500 }
501
502 *size = frame_remux_size;
503
504 uint32_t data_offset = 0;
505 bool inserted = false;
506 memcpy_s(frame_data, PngHeadLength, data, PngHeadLength); // PNG File Header
507 data_offset += PngHeadLength;
508 for (uint32_t i = 0; i < info->apngSharedChunkNum; i++) {
509 uint32_t shared_chunk_index = info->apngSharedChunkIndexs[i];
510 PngChunkInfo *shared_chunk_info = info->chunks + shared_chunk_index;
511
512 // replace IDAT with fdAT
513 if (shared_chunk_index >= info->apngSharedInsertIndex && !inserted) {
514 inserted = true;
515 for (uint32_t c = 0; c < frame_info->chunkNum; c++) {
516 PngChunkInfo *insert_chunk_info = info->chunks + frame_info->chunkIndex + c;
517 if (insert_chunk_info->fourcc == FOUR_CC('f', 'd', 'A', 'T')) {
518 *((uint32_t *) (frame_data + data_offset)) = swap_endian_uint32(
519 insert_chunk_info->length - PngFOURCCLen);
520 *((uint32_t *) (frame_data + data_offset + PngFOURCCLen)) = FOUR_CC('I', 'D', 'A', 'T');
521 memcpy_s(frame_data + data_offset + PngHeadLength, insert_chunk_info->length - PngFOURCCLen,
522 data + insert_chunk_info->offset + Byte12, insert_chunk_info->length - PngFOURCCLen);
523 uint32_t crc = (uint32_t) crc32(0,
524 frame_data + data_offset + PngFOURCCLen,
525 insert_chunk_info->length);
526 *((uint32_t *) (frame_data + data_offset + insert_chunk_info->length + PngFOURCCLen)) =
527 swap_endian_uint32(crc);
528 data_offset += insert_chunk_info->length + PngHeadLength;
529 } else { // IDAT
530 memcpy_s(frame_data + data_offset, insert_chunk_info->length + Byte12,
531 data + insert_chunk_info->offset, insert_chunk_info->length + Byte12);
532 data_offset += insert_chunk_info->length + Byte12;
533 }
534 }
535 }
536
537 if (shared_chunk_info->fourcc == FOUR_CC('I', 'H', 'D', 'R')) {
538 uint8_t tmp[Byte25] = {0};
539 memcpy_s(tmp, Byte25, data + shared_chunk_info->offset, Byte25);
540 PngChunkIHDR IHDR = info->header;
541 IHDR.width = frame_info->frameControl.width;
542 IHDR.height = frame_info->frameControl.height;
543 png_chunk_IHDR_write(&IHDR, tmp + PngHeadLength);
544 *((uint32_t *) (tmp + Byte21)) = swap_endian_uint32((uint32_t) crc32(0, tmp + PngFOURCCLen, Byte17));
545 memcpy_s(frame_data + data_offset, Byte25, tmp, Byte25);
546 data_offset += Byte25;
547 } else {
548 memcpy_s(frame_data + data_offset, shared_chunk_info->length + Byte12,
549 data + shared_chunk_info->offset, shared_chunk_info->length + Byte12);
550 data_offset += shared_chunk_info->length + Byte12;
551 }
552 }
553
554 return frame_data;
555 }
556
557 #ifndef USE_ROSEN_DRAWING
PNGImageDecoder(const sk_sp<SkData> & data)558 PNGImageDecoder::PNGImageDecoder(const sk_sp<SkData> &data) : data_(data)
559 #else
560 PNGImageDecoder::PNGImageDecoder(const std::shared_ptr<RSData> &data) : data_(data)
561 #endif
562 {
563 }
564
~PNGImageDecoder()565 PNGImageDecoder::~PNGImageDecoder()
566 {
567 if (pngInfo_) {
568 free(pngInfo_);
569 }
570 }
571
IsApngSource(const std::string & src)572 bool PNGImageDecoder::IsApngSource(const std::string &src)
573 {
574 const uint32_t FileSuffixLen = 4;
575 const uint32_t APngFileSuffixLen = 5;
576 // 4 is the length of ".svg". or apng
577 return (src.size() > FileSuffixLen && src.substr(src.size() - FileSuffixLen) == ".png") ||
578 (src.size() > APngFileSuffixLen && src.substr(src.size() - APngFileSuffixLen) == ".apng");
579 }
580
581 /**
582 * With image header judge whether is a apng file
583 * use for split png and apng file
584 * @return
585 */
isApng()586 bool PNGImageDecoder::isApng()
587 {
588 if (!data_) {
589 return false;
590 }
591
592 if (dataCheck_) {
593 return isApng_;
594 }
595
596 dataCheck_ = true;
597 const uint8_t *byteDatas = data_->bytes();
598 const int headSize = PngHeadLength;
599 uint32_t length = data_->size();
600 png_byte buffer[headSize] = {0};
601
602 if (!byteDatas || length <= 0) {
603 return false;
604 }
605
606 memcpy_s(buffer, headSize, byteDatas, headSize);
607
608 // check if is not png image
609 if (png_sig_compare((png_bytep) buffer, (png_size_t) 0, headSize)) {
610 LOGE("<<< not a png format");
611 return false;
612 }
613
614 // check if is apng
615 uint32_t chunk_realloc_num = 16;
616 PngChunkInfo *chunks = (PngChunkInfo *) malloc(sizeof(PngChunkInfo) * chunk_realloc_num);
617 if (!chunks) {
618 return false;
619 }
620
621 uint32_t offset = PngHeadLength;
622 uint32_t chunkNum = 0;
623 uint32_t chunk_capacity = chunk_realloc_num;
624 uint32_t apngLoopNum = 0;
625 int32_t apng_sequence_index = -1;
626 int32_t apng_frame_index = 0;
627 int32_t apng_frame_number = -1;
628 bool apng_chunk_error = false;
629
630 // loop get all chunk headers
631 do {
632 if (chunkNum >= chunk_capacity) {
633 PngChunkInfo *new_chunks = (PngChunkInfo *) realloc(chunks, sizeof(PngChunkInfo) *
634 (chunk_capacity + chunk_realloc_num));
635 if (!new_chunks) {
636 free(chunks);
637 return false;
638 }
639
640 chunks = new_chunks;
641 chunk_capacity += chunk_realloc_num;
642 }
643
644 PngChunkInfo *chunk = chunks + chunkNum;
645 const uint8_t *chunk_data = byteDatas + offset;
646 chunk->offset = offset;
647 chunk->length = swap_endian_uint32(*((uint32_t *) chunk_data));
648 if ((uint64_t) chunk->offset + (uint64_t) chunk->length + Byte12 > length) {
649 free(chunks);
650 return false;
651 }
652
653 chunk->fourcc = *((uint32_t *) (chunk_data + PngFOURCCLen));
654 if ((uint64_t) chunk->offset + PngFOURCCLen + chunk->length + PngFOURCCLen > (uint64_t) length) {
655 break;
656 }
657
658 chunk->crc32 = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength + chunk->length)));
659 chunkNum++;
660 offset += Byte12 + chunk->length;
661
662 switch (chunk->fourcc) {
663 case FOUR_CC('a', 'c', 'T', 'L') : {
664 if (chunk->length == PngHeadLength) {
665 apng_frame_number = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
666 apngLoopNum = swap_endian_uint32(*((uint32_t *) (chunk_data + Byte12)));
667 } else {
668 apng_chunk_error = true;
669 }
670
671 break;
672 }
673 case FOUR_CC('f', 'c', 'T', 'L') :
674 case FOUR_CC('f', 'd', 'A', 'T') : {
675 if (chunk->fourcc == FOUR_CC('f', 'c', 'T', 'L')) {
676 if (chunk->length != PngFcTLLen) {
677 apng_chunk_error = true;
678 } else {
679 apng_frame_index++;
680 }
681 }
682 if (chunk->length > Byte25) {
683 uint32_t sequence = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
684 if (apng_sequence_index + 1 == sequence) {
685 apng_sequence_index++;
686 } else {
687 apng_chunk_error = true;
688 }
689 } else {
690 apng_chunk_error = true;
691 }
692
693 break;
694 }
695 case FOUR_CC('I', 'E', 'N', 'D') : {
696 offset = length; // end, break do-while loop
697 break;
698 }
699 }
700 } while (offset + Byte12 <= length);
701
702 free(chunks);
703
704 if (!apng_chunk_error && apng_frame_number > 1) {
705 isApng_ = true;
706 return true;
707 }
708
709 isApng_ = false;
710 return false;
711 }
712
713 /**
714 * Get apng header info and all frames information
715 * @return
716 */
GetApngInfo()717 PngInfo *PNGImageDecoder::GetApngInfo()
718 {
719 if (!data_) {
720 return nullptr;
721 }
722 if (!data_->bytes() || data_->size() <= 0) {
723 return nullptr;
724 }
725
726 if (pngInfo_) {
727 return pngInfo_;
728 }
729
730 auto pngInfo = png_info_create(data_->bytes(), (uint32_t) data_->size());
731 pngInfo_ = pngInfo;
732 return pngInfo;
733 }
734
DecodeImage()735 bool PNGImageDecoder::DecodeImage()
736 {
737 return GetApngInfo();
738 }
739
GetImageSize()740 Size PNGImageDecoder::GetImageSize()
741 {
742 Size imageSize;
743 if (!pngInfo_) {
744 DecodeImage();
745 }
746
747 if (pngInfo_) {
748 imageSize.SetWidth(pngInfo_->header.width);
749 imageSize.SetHeight(pngInfo_->header.height);
750 }
751
752 return imageSize;
753 }
754
GetFrameCount()755 uint32_t PNGImageDecoder::GetFrameCount()
756 {
757 if (!pngInfo_) {
758 DecodeImage();
759 }
760
761 if (pngInfo_) {
762 return pngInfo_->apngFrameNum;
763 }
764
765 return 0;
766 }
767
768 /**
769 * Get frame image data
770 * when render this image need to get this data to decode to raw image data
771 * i: undecoded image data
772 * @param index
773 * @param size : return data size
774 * @return
775 */
GetFrameData(uint32_t index,uint32_t * size,bool oldWay)776 uint8_t *PNGImageDecoder::GetFrameData(uint32_t index, uint32_t *size, bool oldWay)
777 {
778 if (!data_ || !pngInfo_ || index >= pngInfo_->apngFrameNum) {
779 return nullptr;
780 }
781
782 PngFrameInfo *frameInfo = pngInfo_->apngFrames + index;
783 if (!frameInfo) {
784 return nullptr;
785 }
786
787 uint32_t frameRemuxSize = PngHeadLength + pngInfo_->apngSharedChunkSize + frameInfo->chunkSize;
788
789 if (!(pngInfo_->apngFirstFrameIsCover && index == 0)) {
790 // remove fdAT sequence number
791 frameRemuxSize -= frameInfo->chunkNum * Byte4;
792 }
793
794 const uint8_t *data = data_->bytes();
795 if (!data) {
796 return nullptr;
797 }
798
799 if (oldWay) {
800 return png_copy_frame_data_at_index(data, pngInfo_, index, size);
801 }
802
803 uint8_t *frameData = (uint8_t *) malloc(frameRemuxSize);
804 if (!frameData) {
805 return nullptr;
806 }
807
808 *size = frameRemuxSize;
809
810 uint32_t dataOffset = 0;
811 bool inserted = false;
812 // PNG File Header
813 memcpy_s(frameData, PngHeadLength, data, PngHeadLength);
814 dataOffset += PngHeadLength;
815
816 for (uint32_t i = 0; i < pngInfo_->apngSharedChunkNum; i++) {
817 uint32_t sharedChunkIndex = pngInfo_->apngSharedChunkIndexs[i];
818 PngChunkInfo *sharedChunkInfo = pngInfo_->chunks + sharedChunkIndex;
819 if (!sharedChunkInfo) {
820 free(frameData);
821 return nullptr;
822 }
823
824 // replace IDAT with fdAT
825 if (sharedChunkIndex >= pngInfo_->apngSharedInsertIndex && !inserted) {
826 inserted = true;
827 for (uint32_t c = 0; c < frameInfo->chunkNum; c++) {
828 PngChunkInfo *insertChunkInfo = pngInfo_->chunks + frameInfo->chunkIndex + c;
829 if (insertChunkInfo->fourcc == FOUR_CC('f', 'd', 'A', 'T')) {
830 *((uint32_t *) (frameData + dataOffset)) =
831 swap_endian_uint32(insertChunkInfo->length - PngFOURCCLen);
832 *((uint32_t *) (frameData + dataOffset + PngFOURCCLen)) = FOUR_CC('I', 'D', 'A', 'T');
833 memcpy_s(frameData + dataOffset + PngHeadLength, insertChunkInfo->length - PngFOURCCLen,
834 data + insertChunkInfo->offset + Byte12, insertChunkInfo->length - PngFOURCCLen);
835 uint32_t crc = (uint32_t) crc32(0, frameData + dataOffset + PngFOURCCLen,
836 insertChunkInfo->length);
837 *((uint32_t *) (frameData + dataOffset + insertChunkInfo->length +
838 PngFOURCCLen)) = swap_endian_uint32(crc);
839 dataOffset += insertChunkInfo->length + PngHeadLength;
840 } else { // IDAT
841 memcpy_s(frameData + dataOffset, insertChunkInfo->length + Byte12,
842 data + insertChunkInfo->offset, insertChunkInfo->length + Byte12);
843 dataOffset += insertChunkInfo->length + Byte12;
844 }
845 }
846 }
847
848 if (sharedChunkInfo->fourcc == FOUR_CC('I', 'H', 'D', 'R')) {
849 uint8_t tmp[Byte25] = {0};
850 memcpy_s(tmp, Byte25, data + sharedChunkInfo->offset, Byte25);
851 PngChunkIHDR IHDR = pngInfo_->header;
852 IHDR.width = frameInfo->frameControl.width;
853 IHDR.height = frameInfo->frameControl.height;
854 png_chunk_IHDR_write(&IHDR, tmp + PngHeadLength);
855 *((uint32_t *) (tmp + Byte21)) = swap_endian_uint32((uint32_t) crc32(0, tmp + PngFOURCCLen, Byte17));
856 memcpy_s(frameData + dataOffset, Byte25, tmp, Byte25);
857 dataOffset += Byte25;
858 } else {
859 memcpy_s(frameData + dataOffset, sharedChunkInfo->length + Byte12,
860 data + sharedChunkInfo->offset, sharedChunkInfo->length + Byte12);
861 dataOffset += sharedChunkInfo->length + Byte12;
862 }
863 }
864
865 return frameData;
866 }
867 } // namespace OHOS::Ace
868