comparison 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
comparison
equal deleted inserted replaced
2294:7e995fb948c3 2295:eb45ad9d8a3f
1 #include <stdint.h> 1 #include <stdint.h>
2 #include <stdlib.h> 2 #include <stdlib.h>
3 #include <stdio.h> 3 #include <stdio.h>
4 #include <string.h> 4 #include <string.h>
5 #include "zlib/zlib.h" 5 #include "zlib/zlib.h"
6 #include "png.h"
6 7
7 static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'}; 8 static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'};
8 static const char ihdr[] = {'I', 'H', 'D', 'R'}; 9 static const char ihdr[] = {'I', 'H', 'D', 'R'};
9 static const char plte[] = {'P', 'L', 'T', 'E'}; 10 static const char plte[] = {'P', 'L', 'T', 'E'};
10 static const char idat[] = {'I', 'D', 'A', 'T'}; 11 static const char idat[] = {'I', 'D', 'A', 'T'};
11 static const char iend[] = {'I', 'E', 'N', 'D'}; 12 static const char iend[] = {'I', 'E', 'N', 'D'};
13 static const char actl[] = {'a', 'c', 'T', 'L'};
14 static const char fctl[] = {'f', 'c', 'T', 'L'};
15 static const char fdat[] = {'f', 'd', 'A', 'T'};
12 16
13 enum { 17 enum {
14 COLOR_GRAY, 18 COLOR_GRAY,
15 COLOR_TRUE = 2, 19 COLOR_TRUE = 2,
16 COLOR_INDEXED, 20 COLOR_INDEXED,
54 fputs("Error writing PNG magic\n", stderr); 58 fputs("Error writing PNG magic\n", stderr);
55 } 59 }
56 write_chunk(f, ihdr, chunk, sizeof(chunk)); 60 write_chunk(f, ihdr, chunk, sizeof(chunk));
57 } 61 }
58 62
59 void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch) 63 void save_png24_frame(FILE *f, uint32_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch)
60 { 64 {
61 uint32_t idat_size = (1 + width*3) * height; 65 uint32_t idat_size = (1 + width*3) * height;
62 uint8_t *idat_buffer = malloc(idat_size); 66 uint8_t *idat_buffer = malloc(idat_size);
63 uint32_t *pixel = buffer; 67 uint32_t *pixel = buffer;
64 uint8_t *cur = idat_buffer; 68 uint8_t *cur = idat_buffer;
74 *(cur++) = value >> 8; 78 *(cur++) = value >> 8;
75 *(cur++) = value; 79 *(cur++) = value;
76 } 80 }
77 pixel = start + pitch / sizeof(uint32_t); 81 pixel = start + pitch / sizeof(uint32_t);
78 } 82 }
83
84 uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3;
85 uint32_t offset = 0;
86 if (apng) {
87 uint8_t chunk[26] = {
88 apng->sequence_number >> 24, apng->sequence_number >> 16,
89 apng->sequence_number >> 8, apng->sequence_number,
90 width >> 24, width >> 16, width >> 8, width,
91 height >> 24, height >> 16, height >> 8, height,
92 0, 0, 0, 0, //x offset
93 0, 0, 0, 0, //y offset
94 apng->delay_num >> 8, apng->delay_num,
95 apng->delay_den >> 8, apng->delay_den,
96 0, 0 //dispose and blend ops
97 };
98 write_chunk(f, fctl, chunk, sizeof(chunk));
99 apng->sequence_number++;
100 apng->num_frames++;
101 if (apng->sequence_number > 1) {
102 offset = sizeof(uint32_t);
103 compress_buffer_size += offset;
104 }
105 }
106 uint8_t *compressed = malloc(compress_buffer_size);
107 compress_buffer_size -= offset;
108 compress(compressed + offset, &compress_buffer_size, idat_buffer, idat_size);
109 free(idat_buffer);
110 if (offset) {
111 cur = compressed;
112 *(cur++) = apng->sequence_number >> 24;
113 *(cur++) = apng->sequence_number >> 16;
114 *(cur++) = apng->sequence_number >> 8;
115 *(cur++) = apng->sequence_number;
116 apng->sequence_number++;
117 }
118 write_chunk(f, offset ? fdat : idat, compressed, compress_buffer_size + offset);
119 free(compressed);
120 }
121
122 apng_state* start_apng(FILE *f, uint32_t width, uint32_t height, float frame_rate)
123 {
79 write_header(f, width, height, COLOR_TRUE); 124 write_header(f, width, height, COLOR_TRUE);
80 uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3; 125 apng_state *apng = calloc(1, sizeof(apng_state));
81 uint8_t *compressed = malloc(compress_buffer_size); 126 uint8_t chunk[] = {
82 compress(compressed, &compress_buffer_size, idat_buffer, idat_size); 127 0, 0, 0, 0,
83 free(idat_buffer); 128 0, 0, 0, 1
84 write_chunk(f, idat, compressed, compress_buffer_size); 129 };
130 apng->num_frame_offset = ftell(f) + 8;
131 write_chunk(f, actl, chunk, sizeof(chunk));
132 apng->delay_num = 65535.0f / frame_rate;
133 apng->delay_den = frame_rate * apng->delay_num;
134 return apng;
135 }
136
137 void end_apng(FILE *f, apng_state *apng)
138 {
85 write_chunk(f, iend, NULL, 0); 139 write_chunk(f, iend, NULL, 0);
86 free(compressed); 140 fseek(f, apng->num_frame_offset, SEEK_SET);
141 uint8_t bytes[] = {
142 apng->num_frames >> 24, apng->num_frames >> 16,
143 apng->num_frames >> 8, apng->num_frames
144 };
145 fwrite(bytes, 1, sizeof(bytes), f);
146 fclose(f);
147 free(apng);
148 }
149
150 void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
151 {
152 write_header(f, width, height, COLOR_TRUE);
153 save_png24_frame(f, buffer, NULL, width, height, pitch);
154 write_chunk(f, iend, NULL, 0);
87 } 155 }
88 156
89 void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch) 157 void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
90 { 158 {
91 uint32_t palette[256]; 159 uint32_t palette[256];