Mercurial > repos > blastem
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]; |