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
PNGImageDecoder(const sk_sp<SkData> & data)557 PNGImageDecoder::PNGImageDecoder(const sk_sp<SkData> &data) : data_(data)
558 {
559 }
560
~PNGImageDecoder()561 PNGImageDecoder::~PNGImageDecoder()
562 {
563 if (pngInfo_) {
564 free(pngInfo_);
565 }
566 }
567
IsApngSource(const std::string & src)568 bool PNGImageDecoder::IsApngSource(const std::string &src)
569 {
570 const uint32_t FileSuffixLen = 4;
571 const uint32_t APngFileSuffixLen = 5;
572 // 4 is the length of ".svg". or apng
573 return (src.size() > FileSuffixLen && src.substr(src.size() - FileSuffixLen) == ".png") ||
574 (src.size() > APngFileSuffixLen && src.substr(src.size() - APngFileSuffixLen) == ".apng");
575 }
576
577 /**
578 * With image header judge whether is a apng file
579 * use for split png and apng file
580 * @return
581 */
isApng()582 bool PNGImageDecoder::isApng()
583 {
584 if (!data_) {
585 return false;
586 }
587
588 if (dataCheck_) {
589 return isApng_;
590 }
591
592 dataCheck_ = true;
593 const uint8_t *byteDatas = data_->bytes();
594 const int headSize = PngHeadLength;
595 uint32_t length = data_->size();
596 png_byte buffer[headSize] = {0};
597
598 if (!byteDatas || length <= 0) {
599 return false;
600 }
601
602 memcpy_s(buffer, headSize, byteDatas, headSize);
603
604 // check if is not png image
605 if (png_sig_compare((png_bytep) buffer, (png_size_t) 0, headSize)) {
606 LOGE("<<< not a png format");
607 return false;
608 }
609
610 // check if is apng
611 uint32_t chunk_realloc_num = 16;
612 PngChunkInfo *chunks = (PngChunkInfo *) malloc(sizeof(PngChunkInfo) * chunk_realloc_num);
613 if (!chunks) {
614 return false;
615 }
616
617 uint32_t offset = PngHeadLength;
618 uint32_t chunkNum = 0;
619 uint32_t chunk_capacity = chunk_realloc_num;
620 uint32_t apngLoopNum = 0;
621 int32_t apng_sequence_index = -1;
622 int32_t apng_frame_index = 0;
623 int32_t apng_frame_number = -1;
624 bool apng_chunk_error = false;
625
626 // loop get all chunk headers
627 do {
628 if (chunkNum >= chunk_capacity) {
629 PngChunkInfo *new_chunks = (PngChunkInfo *) realloc(chunks, sizeof(PngChunkInfo) *
630 (chunk_capacity + chunk_realloc_num));
631 if (!new_chunks) {
632 free(chunks);
633 return false;
634 }
635
636 chunks = new_chunks;
637 chunk_capacity += chunk_realloc_num;
638 }
639
640 PngChunkInfo *chunk = chunks + chunkNum;
641 const uint8_t *chunk_data = byteDatas + offset;
642 chunk->offset = offset;
643 chunk->length = swap_endian_uint32(*((uint32_t *) chunk_data));
644 if ((uint64_t) chunk->offset + (uint64_t) chunk->length + Byte12 > length) {
645 free(chunks);
646 return false;
647 }
648
649 chunk->fourcc = *((uint32_t *) (chunk_data + PngFOURCCLen));
650 if ((uint64_t) chunk->offset + PngFOURCCLen + chunk->length + PngFOURCCLen > (uint64_t) length) {
651 break;
652 }
653
654 chunk->crc32 = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength + chunk->length)));
655 chunkNum++;
656 offset += Byte12 + chunk->length;
657
658 switch (chunk->fourcc) {
659 case FOUR_CC('a', 'c', 'T', 'L') : {
660 if (chunk->length == PngHeadLength) {
661 apng_frame_number = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
662 apngLoopNum = swap_endian_uint32(*((uint32_t *) (chunk_data + Byte12)));
663 } else {
664 apng_chunk_error = true;
665 }
666
667 break;
668 }
669 case FOUR_CC('f', 'c', 'T', 'L') :
670 case FOUR_CC('f', 'd', 'A', 'T') : {
671 if (chunk->fourcc == FOUR_CC('f', 'c', 'T', 'L')) {
672 if (chunk->length != PngFcTLLen) {
673 apng_chunk_error = true;
674 } else {
675 apng_frame_index++;
676 }
677 }
678 if (chunk->length > Byte25) {
679 uint32_t sequence = swap_endian_uint32(*((uint32_t *) (chunk_data + PngHeadLength)));
680 if (apng_sequence_index + 1 == sequence) {
681 apng_sequence_index++;
682 } else {
683 apng_chunk_error = true;
684 }
685 } else {
686 apng_chunk_error = true;
687 }
688
689 break;
690 }
691 case FOUR_CC('I', 'E', 'N', 'D') : {
692 offset = length; // end, break do-while loop
693 break;
694 }
695 }
696 } while (offset + Byte12 <= length);
697
698 free(chunks);
699
700 if (!apng_chunk_error && apng_frame_number > 1) {
701 isApng_ = true;
702 return true;
703 }
704
705 isApng_ = false;
706 return false;
707 }
708
709 /**
710 * Get apng header info and all frames information
711 * @return
712 */
GetApngInfo()713 PngInfo *PNGImageDecoder::GetApngInfo()
714 {
715 if (!data_) {
716 return nullptr;
717 }
718 if (!data_->bytes() || data_->size() <= 0) {
719 return nullptr;
720 }
721
722 if (pngInfo_) {
723 return pngInfo_;
724 }
725
726 auto pngInfo = png_info_create(data_->bytes(), (uint32_t) data_->size());
727 pngInfo_ = pngInfo;
728 return pngInfo;
729 }
730
DecodeImage()731 bool PNGImageDecoder::DecodeImage()
732 {
733 return GetApngInfo();
734 }
735
GetImageSize()736 Size PNGImageDecoder::GetImageSize()
737 {
738 Size imageSize;
739 if (!pngInfo_) {
740 DecodeImage();
741 }
742
743 if (pngInfo_) {
744 imageSize.SetWidth(pngInfo_->header.width);
745 imageSize.SetHeight(pngInfo_->header.height);
746 }
747
748 return imageSize;
749 }
750
GetFrameCount()751 uint32_t PNGImageDecoder::GetFrameCount()
752 {
753 if (!pngInfo_) {
754 DecodeImage();
755 }
756
757 if (pngInfo_) {
758 return pngInfo_->apngFrameNum;
759 }
760
761 return 0;
762 }
763
764 /**
765 * Get frame image data
766 * when render this image need to get this data to decode to raw image data
767 * i: undecoded image data
768 * @param index
769 * @param size : return data size
770 * @return
771 */
GetFrameData(uint32_t index,uint32_t * size,bool oldWay)772 uint8_t *PNGImageDecoder::GetFrameData(uint32_t index, uint32_t *size, bool oldWay)
773 {
774 if (!data_ || !pngInfo_ || index >= pngInfo_->apngFrameNum) {
775 return nullptr;
776 }
777
778 PngFrameInfo *frameInfo = pngInfo_->apngFrames + index;
779 if (!frameInfo) {
780 return nullptr;
781 }
782
783 uint32_t frameRemuxSize = PngHeadLength + pngInfo_->apngSharedChunkSize + frameInfo->chunkSize;
784
785 if (!(pngInfo_->apngFirstFrameIsCover && index == 0)) {
786 // remove fdAT sequence number
787 frameRemuxSize -= frameInfo->chunkNum * Byte4;
788 }
789
790 const uint8_t *data = data_->bytes();
791 if (!data) {
792 return nullptr;
793 }
794
795 if (oldWay) {
796 return png_copy_frame_data_at_index(data, pngInfo_, index, size);
797 }
798
799 uint8_t *frameData = (uint8_t *) malloc(frameRemuxSize);
800 if (!frameData) {
801 return nullptr;
802 }
803
804 *size = frameRemuxSize;
805
806 uint32_t dataOffset = 0;
807 bool inserted = false;
808 // PNG File Header
809 memcpy_s(frameData, PngHeadLength, data, PngHeadLength);
810 dataOffset += PngHeadLength;
811
812 for (uint32_t i = 0; i < pngInfo_->apngSharedChunkNum; i++) {
813 uint32_t sharedChunkIndex = pngInfo_->apngSharedChunkIndexs[i];
814 PngChunkInfo *sharedChunkInfo = pngInfo_->chunks + sharedChunkIndex;
815 if (!sharedChunkInfo) {
816 free(frameData);
817 return nullptr;
818 }
819
820 // replace IDAT with fdAT
821 if (sharedChunkIndex >= pngInfo_->apngSharedInsertIndex && !inserted) {
822 inserted = true;
823 for (uint32_t c = 0; c < frameInfo->chunkNum; c++) {
824 PngChunkInfo *insertChunkInfo = pngInfo_->chunks + frameInfo->chunkIndex + c;
825 if (insertChunkInfo->fourcc == FOUR_CC('f', 'd', 'A', 'T')) {
826 *((uint32_t *) (frameData + dataOffset)) = swap_endian_uint32(
827 insertChunkInfo->length - PngFOURCCLen);
828 *((uint32_t *) (frameData + dataOffset + PngFOURCCLen)) = FOUR_CC('I', 'D', 'A', 'T');
829 memcpy_s(frameData + dataOffset + PngHeadLength, insertChunkInfo->length - PngFOURCCLen,
830 data + insertChunkInfo->offset + Byte12, insertChunkInfo->length - PngFOURCCLen);
831 uint32_t crc = (uint32_t) crc32(0, frameData + dataOffset + PngFOURCCLen,
832 insertChunkInfo->length);
833 *((uint32_t *) (frameData + dataOffset + insertChunkInfo->length +
834 PngFOURCCLen)) = swap_endian_uint32(crc);
835 dataOffset += insertChunkInfo->length + PngHeadLength;
836 } else { // IDAT
837 memcpy_s(frameData + dataOffset, insertChunkInfo->length + Byte12,
838 data + insertChunkInfo->offset, insertChunkInfo->length + Byte12);
839 dataOffset += insertChunkInfo->length + Byte12;
840 }
841 }
842 }
843
844 if (sharedChunkInfo->fourcc == FOUR_CC('I', 'H', 'D', 'R')) {
845 uint8_t tmp[Byte25] = {0};
846 memcpy_s(tmp, Byte25, data + sharedChunkInfo->offset, Byte25);
847 PngChunkIHDR IHDR = pngInfo_->header;
848 IHDR.width = frameInfo->frameControl.width;
849 IHDR.height = frameInfo->frameControl.height;
850 png_chunk_IHDR_write(&IHDR, tmp + PngHeadLength);
851 *((uint32_t *) (tmp + Byte21)) = swap_endian_uint32((uint32_t) crc32(0, tmp + PngFOURCCLen, Byte17));
852 memcpy_s(frameData + dataOffset, Byte25, tmp, Byte25);
853 dataOffset += Byte25;
854 } else {
855 memcpy_s(frameData + dataOffset, sharedChunkInfo->length + Byte12,
856 data + sharedChunkInfo->offset, sharedChunkInfo->length + Byte12);
857 dataOffset += sharedChunkInfo->length + Byte12;
858 }
859 }
860
861 return frameData;
862 }
863 } // namespace OHOS::Ace
864