Ejemplo n.º 1
0
/*
 * Output handler
 */
static void opng_write_data(png_structp png_ptr, png_bytep data, size_t length)
{
    struct opng_codec_context * context = (struct opng_codec_context *)png_get_io_ptr(png_ptr);
    struct opng_encoding_stats * stats = context->stats;
    FILE * stream = context->stream;

    unsigned io_state = png_get_io_state(png_ptr);
    unsigned io_state_loc = io_state & PNG_IO_MASK_LOC;
    OPNG_ASSERT((io_state & PNG_IO_WRITING) && (io_state_loc != 0), "Incorrect info in png_ptr->io_state");

    /* Handle the optipng-specific events. */
    if (io_state_loc == PNG_IO_CHUNK_HDR)
    {
        OPNG_ASSERT(length == 8, "Writing chunk header, expecting 8 bytes");
        png_bytep chunk_sig = data + 4;
        context->crt_chunk_is_allowed = opng_allow_chunk(context, chunk_sig);
        if (memcmp(chunk_sig, opng_sig_IDAT, 4) == 0)
        {
            context->crt_chunk_is_idat = 1;
            stats->idat_size += png_get_uint_32(data);
        }
        else  /* not IDAT */
        {
            context->crt_chunk_is_idat = 0;
        }
    }
    if (context->no_write) {
        return;
    }

    /* Continue only if the current chunk type is allowed. */
    if (io_state_loc != PNG_IO_SIGNATURE && !context->crt_chunk_is_allowed)
        return;

    /* Here comes an elaborate way of writing the data, in which all IDATs
     * are joined into a single chunk.
     * Normally, the user-supplied I/O routines are not so complicated.
     */
    switch (io_state_loc)
    {
    case PNG_IO_CHUNK_HDR:
        if (context->crt_chunk_is_idat)
        {
            if (context->crt_idat_offset == 0)
            {
                /* This is the header of the first IDAT. */
                context->crt_idat_offset = ftell(stream);
                context->crt_idat_size = length;
                png_save_uint_32(data, (png_uint_32)context->crt_idat_size);
                /* Start computing the CRC of the final IDAT. */
                context->crt_idat_crc = crc32(0, opng_sig_IDAT, 4);
            }
            else
            {
                /* This is not the first IDAT. Do not write its header. */
                return;
            }
        }
        else
        {
            if (context->crt_idat_offset != 0)
            {
                png_byte buf[4];
                /* This is the header of the first chunk after IDAT.
                 * Finalize IDAT before resuming the normal operation.
                 */
                png_save_uint_32(buf, context->crt_idat_crc);
                fwrite(buf, 1, 4, stream);
                if (stats->idat_size != context->crt_idat_size)
                {
                    /* The IDAT size, unknown at the start of encoding,
                     * has not been guessed correctly.
                     * It must be updated in a non-streamable way.
                     */
                    png_save_uint_32(buf, (png_uint_32)stats->idat_size);
                    fpos_t pos;
                    if (fgetpos(stream, &pos) != 0 || fflush(stream) != 0 || (fseek(stream, context->crt_idat_offset, SEEK_SET) != 0) ||
                            (fwrite(buf, 1, 4, stream)!=4) || (fflush(stream) != 0) || (fsetpos(stream, &pos) != 0)) {
                        io_state = 0;
                    }
                }
                if (io_state == 0)
                    png_error(png_ptr, "Can't finalize IDAT");
                context->crt_idat_offset = 0;
            }
        }
        break;
    case PNG_IO_CHUNK_DATA:
        if (context->crt_chunk_is_idat)
            context->crt_idat_crc = crc32(context->crt_idat_crc, data, length);
        break;
    case PNG_IO_CHUNK_CRC:
        if (context->crt_chunk_is_idat)
            return;  /* defer writing until the first non-IDAT occurs */
        break;
    }

    /* Write the data. */
    if (fwrite(data, 1, length, stream) != length)
        png_error(png_ptr, "Can't write file");
}
Ejemplo n.º 2
0
/*
 * @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);
	}
}