• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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