in iOS/WAStickersThirdParty/YYImageCoder.m [309:383]
static bool yy_png_validate_animation_chunk_order(yy_png_chunk_info *chunks, /* input */
uint32_t chunk_num, /* input */
uint32_t *first_idat_index, /* output */
bool *first_frame_is_cover /* output */) {
/*
PNG at least contains 3 chunks: IHDR, IDAT, IEND.
`IHDR` must appear first.
`IDAT` must appear consecutively.
`IEND` must appear end.
APNG must contains one `acTL` and at least one 'fcTL' and `fdAT`.
`fdAT` must appear consecutively.
`fcTL` must appear before `IDAT` or `fdAT`.
*/
if (chunk_num <= 2) return false;
if (chunks->fourcc != YY_FOUR_CC('I', 'H', 'D', 'R')) return false;
if ((chunks + chunk_num - 1)->fourcc != YY_FOUR_CC('I', 'E', 'N', 'D')) return false;
uint32_t prev_fourcc = 0;
uint32_t IHDR_num = 0;
uint32_t IDAT_num = 0;
uint32_t acTL_num = 0;
uint32_t fcTL_num = 0;
uint32_t first_IDAT = 0;
bool first_frame_cover = false;
for (uint32_t i = 0; i < chunk_num; i++) {
yy_png_chunk_info *chunk = chunks + i;
switch (chunk->fourcc) {
case YY_FOUR_CC('I', 'H', 'D', 'R'): { // png header
if (i != 0) return false;
if (IHDR_num > 0) return false;
IHDR_num++;
} break;
case YY_FOUR_CC('I', 'D', 'A', 'T'): { // png data
if (prev_fourcc != YY_FOUR_CC('I', 'D', 'A', 'T')) {
if (IDAT_num == 0)
first_IDAT = i;
else
return false;
}
IDAT_num++;
} break;
case YY_FOUR_CC('a', 'c', 'T', 'L'): { // apng control
if (acTL_num > 0) return false;
acTL_num++;
} break;
case YY_FOUR_CC('f', 'c', 'T', 'L'): { // apng frame control
if (i + 1 == chunk_num) return false;
if ((chunk + 1)->fourcc != YY_FOUR_CC('f', 'd', 'A', 'T') &&
(chunk + 1)->fourcc != YY_FOUR_CC('I', 'D', 'A', 'T')) {
return false;
}
if (fcTL_num == 0) {
if ((chunk + 1)->fourcc == YY_FOUR_CC('I', 'D', 'A', 'T')) {
first_frame_cover = true;
}
}
fcTL_num++;
} break;
case YY_FOUR_CC('f', 'd', 'A', 'T'): { // apng data
if (prev_fourcc != YY_FOUR_CC('f', 'd', 'A', 'T') && prev_fourcc != YY_FOUR_CC('f', 'c', 'T', 'L')) {
return false;
}
} break;
}
prev_fourcc = chunk->fourcc;
}
if (IHDR_num != 1) return false;
if (IDAT_num == 0) return false;
if (acTL_num != 1) return false;
if (fcTL_num < acTL_num) return false;
*first_idat_index = first_IDAT;
*first_frame_is_cover = first_frame_cover;
return true;
}