Mercurial > repos > blastem
diff png.c @ 2295:eb45ad9d8a3f
WIP "video" recording in APNG format
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 10 Feb 2023 23:17:43 -0800 |
parents | 81eebbe6b2e3 |
children | 0111c8344477 |
line wrap: on
line diff
--- a/png.c Fri Feb 10 21:37:59 2023 -0800 +++ b/png.c Fri Feb 10 23:17:43 2023 -0800 @@ -3,12 +3,16 @@ #include <stdio.h> #include <string.h> #include "zlib/zlib.h" +#include "png.h" static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'}; static const char ihdr[] = {'I', 'H', 'D', 'R'}; static const char plte[] = {'P', 'L', 'T', 'E'}; static const char idat[] = {'I', 'D', 'A', 'T'}; static const char iend[] = {'I', 'E', 'N', 'D'}; +static const char actl[] = {'a', 'c', 'T', 'L'}; +static const char fctl[] = {'f', 'c', 'T', 'L'}; +static const char fdat[] = {'f', 'd', 'A', 'T'}; enum { COLOR_GRAY, @@ -56,7 +60,7 @@ write_chunk(f, ihdr, chunk, sizeof(chunk)); } -void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch) +void save_png24_frame(FILE *f, uint32_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch) { uint32_t idat_size = (1 + width*3) * height; uint8_t *idat_buffer = malloc(idat_size); @@ -76,14 +80,78 @@ } pixel = start + pitch / sizeof(uint32_t); } - write_header(f, width, height, COLOR_TRUE); + uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3; + uint32_t offset = 0; + if (apng) { + uint8_t chunk[26] = { + apng->sequence_number >> 24, apng->sequence_number >> 16, + apng->sequence_number >> 8, apng->sequence_number, + width >> 24, width >> 16, width >> 8, width, + height >> 24, height >> 16, height >> 8, height, + 0, 0, 0, 0, //x offset + 0, 0, 0, 0, //y offset + apng->delay_num >> 8, apng->delay_num, + apng->delay_den >> 8, apng->delay_den, + 0, 0 //dispose and blend ops + }; + write_chunk(f, fctl, chunk, sizeof(chunk)); + apng->sequence_number++; + apng->num_frames++; + if (apng->sequence_number > 1) { + offset = sizeof(uint32_t); + compress_buffer_size += offset; + } + } uint8_t *compressed = malloc(compress_buffer_size); - compress(compressed, &compress_buffer_size, idat_buffer, idat_size); + compress_buffer_size -= offset; + compress(compressed + offset, &compress_buffer_size, idat_buffer, idat_size); free(idat_buffer); - write_chunk(f, idat, compressed, compress_buffer_size); + if (offset) { + cur = compressed; + *(cur++) = apng->sequence_number >> 24; + *(cur++) = apng->sequence_number >> 16; + *(cur++) = apng->sequence_number >> 8; + *(cur++) = apng->sequence_number; + apng->sequence_number++; + } + write_chunk(f, offset ? fdat : idat, compressed, compress_buffer_size + offset); + free(compressed); +} + +apng_state* start_apng(FILE *f, uint32_t width, uint32_t height, float frame_rate) +{ + write_header(f, width, height, COLOR_TRUE); + apng_state *apng = calloc(1, sizeof(apng_state)); + uint8_t chunk[] = { + 0, 0, 0, 0, + 0, 0, 0, 1 + }; + apng->num_frame_offset = ftell(f) + 8; + write_chunk(f, actl, chunk, sizeof(chunk)); + apng->delay_num = 65535.0f / frame_rate; + apng->delay_den = frame_rate * apng->delay_num; + return apng; +} + +void end_apng(FILE *f, apng_state *apng) +{ write_chunk(f, iend, NULL, 0); - free(compressed); + fseek(f, apng->num_frame_offset, SEEK_SET); + uint8_t bytes[] = { + apng->num_frames >> 24, apng->num_frames >> 16, + apng->num_frames >> 8, apng->num_frames + }; + fwrite(bytes, 1, sizeof(bytes), f); + fclose(f); + free(apng); +} + +void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch) +{ + write_header(f, width, height, COLOR_TRUE); + save_png24_frame(f, buffer, NULL, width, height, pitch); + write_chunk(f, iend, NULL, 0); } void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)