/* * Converts a 16-bit RGB pixel to grayscale, as specified in ITU BT.601. */ static void opng_reset_chroma16_bt601(png_bytep rgb16) { /* Use 16-bit multiplications and 32-bit additions. */ unsigned int r16 = png_get_uint_16(rgb16 + 0); unsigned int g16 = png_get_uint_16(rgb16 + 2); unsigned int b16 = png_get_uint_16(rgb16 + 4); png_uint_32 r32 = r16 * (png_uint_32)Kr_bt601; png_uint_32 g32 = g16 * (png_uint_32)Kg_bt601; png_uint_32 b32 = b16 * (png_uint_32)Kb_bt601; png_uint_32 y32 = r32 + g32 + b32; unsigned int y16 = (unsigned int)((y32 + 32767U) / 65535U); png_save_uint_16(rgb16 + 0, y16); png_save_uint_16(rgb16 + 2, y16); png_save_uint_16(rgb16 + 4, y16); }
/* * Sets the precision of a 16-bit sample to a lower value, * then scales the result back to 16 bits. */ static void opng_set_precision16(png_bytep sample_ptr, int new_precision) { unsigned int old_value = png_get_uint_16(sample_ptr); unsigned int chop_value = old_value >> (16 - new_precision); unsigned int chop_max = (1U << new_precision) - 1; unsigned int new_value = (unsigned int) (((png_uint_32)chop_value * 65535U + chop_max / 2) / chop_max); png_save_uint_16(sample_ptr, new_value); }
/* * @brief manually process a single chunk of apng data */ void apng_ani::_process_chunk() { _id = _read_chunk(_chunk); if (_id == id_acTL && !_got_IDAT && !_got_acTL) { // animation control chunk if (!_reading) { return; } nframes = png_get_uint_32(&_chunk.data[8]); plays = png_get_uint_32(&_chunk.data[12]); if (!nframes || nframes > PNG_UINT_31_MAX || plays > PNG_UINT_31_MAX) { _apng_failed("invalid apng acTL data"); } _got_acTL = true; _frames.reserve(nframes); _frame_offsets.reserve(nframes+1); // extra 1 is for EOF offset } else if (_id == id_fcTL && (!_got_IDAT || _got_acTL)) { // frame control chunk if (_reading) { uint sequence_num = png_get_uint_32(&_chunk.data[8]); if (sequence_num != _sequence_num++) { _apng_failed("invalid apng fcTL sequence number"); } } // handle frame finish in next_frame _framew = png_get_uint_32(&_chunk.data[12]); _frameh = png_get_uint_32(&_chunk.data[16]); _x_offset = png_get_uint_32(&_chunk.data[20]); _y_offset = png_get_uint_32(&_chunk.data[24]); _delay_num = png_get_uint_16(&_chunk.data[28]); _delay_den = png_get_uint_16(&_chunk.data[30]); _dispose_op = _chunk.data[32]; _blend_op = _chunk.data[33]; if (_reading && (_framew > cMaxPNGSize || _frameh > cMaxPNGSize || _x_offset > cMaxPNGSize || _y_offset > cMaxPNGSize || _x_offset + _framew > w || _y_offset + _frameh > h || _dispose_op > 2 || _blend_op > 1)) { _apng_failed("invalid apng fcTL data"); } // according to spec... if (current_frame == 0 && _dispose_op == 2) { _dispose_op = 1; } if (_delay_den == 0) _delay_den = 100; // APNG spec if (_delay_num == 0) _delay_num = 1; // arbitrary lower bound float frame_delay = static_cast<float>(_delay_num)/static_cast<float>(_delay_den); frame.delay = frame_delay; if (_reading) { anim_time+= frame_delay; _frame_offsets.push_back((int)_offset); } else { if (_got_IDAT && _processing_start()) { _apng_failed("couldn't start fdat apng frame"); } } } else if (_id == id_IDAT) { _got_IDAT = true; _processing_data(&_chunk.data[0], _chunk.size); } else if (_id == id_fdAT && _got_acTL) { if (_reading) { uint sequence_num = png_get_uint_32(&_chunk.data[8]); if (sequence_num != _sequence_num++) { _apng_failed("invalid apng fdAT sequence number"); } } if (_reading) { return; } png_save_uint_32(&_chunk.data[4], _chunk.size - 16); memcpy(&_chunk.data[8], "IDAT", 4); _processing_data(&_chunk.data[4], _chunk.size - 4); } else if (_id == id_IEND) { return; } else if (not_chunk(_chunk.data[4]) || not_chunk(_chunk.data[5]) || not_chunk(_chunk.data[6]) || not_chunk(_chunk.data[7])) { _apng_failed("unknown chunk ID found"); } else if (!_got_IDAT) { _processing_data(&_chunk.data[0], _chunk.size); _info_chunks.push_back(_chunk); } }