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