Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pret
GitHub Repository: pret/pokered
Path: blob/master/tools/gfx.c
1270 views
1
#define PROGRAM_NAME "gfx"
2
#define USAGE_OPTS "[-h|--help] [--trim-whitespace] [--remove-whitespace] [--interleave] [--remove-duplicates [--keep-whitespace]] [--remove-xflip] [--remove-yflip] [--preserve indexes] [-d|--depth depth] [-p|--png filename.png] [-o|--out outfile] infile"
3
4
#include "common.h"
5
6
struct Options {
7
bool trim_whitespace;
8
bool remove_whitespace;
9
bool interleave;
10
bool remove_duplicates;
11
bool keep_whitespace;
12
bool remove_xflip;
13
bool remove_yflip;
14
int *preserved;
15
int num_preserved;
16
int depth;
17
char *png_file;
18
char *outfile;
19
};
20
21
struct Options options = {.depth = 2};
22
23
void parse_args(int argc, char *argv[]) {
24
struct option long_options[] = {
25
{"remove-whitespace", no_argument, 0, 'R'},
26
{"trim-whitespace", no_argument, 0, 'T'},
27
{"interleave", no_argument, 0, 'I'},
28
{"remove-duplicates", no_argument, 0, 'D'},
29
{"keep-whitespace", no_argument, 0, 'W'},
30
{"remove-xflip", no_argument, 0, 'X'},
31
{"remove-yflip", no_argument, 0, 'Y'},
32
{"preserve", required_argument, 0, 'r'},
33
{"png", required_argument, 0, 'p'},
34
{"depth", required_argument, 0, 'd'},
35
{"out", required_argument, 0, 'o'},
36
{"help", no_argument, 0, 'h'},
37
{0}
38
};
39
for (int opt; (opt = getopt_long(argc, argv, "d:o:p:h", long_options)) != -1;) {
40
switch (opt) {
41
case 'R':
42
options.remove_whitespace = true;
43
break;
44
case 'T':
45
options.trim_whitespace = true;
46
break;
47
case 'I':
48
options.interleave = true;
49
break;
50
case 'D':
51
options.remove_duplicates = true;
52
break;
53
case 'W':
54
options.keep_whitespace = true;
55
break;
56
case 'X':
57
options.remove_xflip = true;
58
break;
59
case 'Y':
60
options.remove_yflip = true;
61
break;
62
case 'r':
63
for (char *token = strtok(optarg, ","); token; token = strtok(NULL, ",")) {
64
options.preserved = xrealloc(options.preserved, ++options.num_preserved * sizeof(*options.preserved));
65
options.preserved[options.num_preserved-1] = strtoul(token, NULL, 0);
66
}
67
break;
68
case 'd':
69
options.depth = strtoul(optarg, NULL, 0);
70
break;
71
case 'p':
72
options.png_file = optarg;
73
break;
74
case 'o':
75
options.outfile = optarg;
76
break;
77
case 'h':
78
usage_exit(0);
79
break;
80
default:
81
usage_exit(1);
82
}
83
}
84
}
85
86
struct Graphic {
87
uint8_t *data;
88
long size;
89
};
90
91
bool is_preserved(int index) {
92
for (int i = 0; i < options.num_preserved; i++) {
93
if (options.preserved[i] == index) {
94
return true;
95
}
96
}
97
return false;
98
}
99
100
void shift_preserved(int removed_index) {
101
for (int i = 0; i < options.num_preserved; i++) {
102
if (options.preserved[i] >= removed_index) {
103
options.preserved[i]--;
104
}
105
}
106
}
107
108
bool is_whitespace(const uint8_t *tile, int tile_size) {
109
for (int i = 0; i < tile_size; i++) {
110
if (tile[i] != 0) {
111
return false;
112
}
113
}
114
return true;
115
}
116
117
void trim_whitespace(struct Graphic *graphic) {
118
int tile_size = options.depth * 8;
119
for (int i = graphic->size - tile_size; i > 0; i -= tile_size) {
120
if (is_whitespace(&graphic->data[i], tile_size) && !is_preserved(i / tile_size)) {
121
graphic->size = i;
122
} else {
123
break;
124
}
125
}
126
}
127
128
int get_tile_size(void) {
129
return options.depth * (options.interleave ? 16 : 8);
130
}
131
132
void remove_whitespace(struct Graphic *graphic) {
133
int tile_size = get_tile_size();
134
graphic->size &= ~(tile_size - 1);
135
int i = 0;
136
for (int j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
137
for (; j < graphic->size && is_whitespace(&graphic->data[j], tile_size) && !is_preserved(j / tile_size - d); j += tile_size, d++) {
138
shift_preserved(j / tile_size - d);
139
}
140
if (j >= graphic->size) {
141
break;
142
} else if (j > i) {
143
memcpy(&graphic->data[i], &graphic->data[j], tile_size);
144
}
145
}
146
graphic->size = i;
147
}
148
149
bool tile_exists(const uint8_t *tile, const uint8_t *tiles, int tile_size, int num_tiles) {
150
for (int i = 0; i < num_tiles; i++) {
151
bool match = true;
152
for (int j = 0; j < tile_size; j++) {
153
if (tile[j] != tiles[i * tile_size + j]) {
154
match = false;
155
break;
156
}
157
}
158
if (match) {
159
return true;
160
}
161
}
162
return false;
163
}
164
165
void remove_duplicates(struct Graphic *graphic) {
166
int tile_size = get_tile_size();
167
graphic->size &= ~(tile_size - 1);
168
int num_tiles = 0;
169
for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
170
for (; j < graphic->size && tile_exists(&graphic->data[j], graphic->data, tile_size, num_tiles); j += tile_size, d++) {
171
if ((options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) {
172
break;
173
}
174
shift_preserved(j / tile_size - d);
175
}
176
if (j >= graphic->size) {
177
break;
178
}
179
if (j > i) {
180
memcpy(&graphic->data[i], &graphic->data[j], tile_size);
181
}
182
num_tiles++;
183
}
184
graphic->size = num_tiles * tile_size;
185
}
186
187
// for (int i = 0; i < 256; i++)
188
// for (int bit = 0; bit < 8; bit++) {
189
// flipped[i] |= ((i >> bit) & 1) << (7 - bit);
190
const uint8_t flipped[256] = {
191
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
192
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
193
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
194
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
195
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
196
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
197
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
198
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
199
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
200
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
201
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
202
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
203
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
204
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
205
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
206
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
207
};
208
209
bool flip_exists(const uint8_t *tile, const uint8_t *tiles, int tile_size, int num_tiles, bool xflip, bool yflip) {
210
uint8_t flip[tile_size]; // VLA
211
memset(flip, 0, tile_size);
212
int half_size = tile_size / 2;
213
for (int i = 0; i < tile_size; i++) {
214
int j = yflip ? (options.interleave && i < half_size ? half_size : tile_size) - 1 - (i ^ 1) : i;
215
flip[j] = xflip ? flipped[tile[i]] : tile[i];
216
}
217
return tile_exists(flip, tiles, tile_size, num_tiles);
218
}
219
220
void remove_flip(struct Graphic *graphic, bool xflip, bool yflip) {
221
int tile_size = get_tile_size();
222
graphic->size &= ~(tile_size - 1);
223
int num_tiles = 0;
224
for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
225
for (; j < graphic->size && flip_exists(&graphic->data[j], graphic->data, tile_size, num_tiles, xflip, yflip); j += tile_size, d++) {
226
if ((options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) {
227
break;
228
}
229
shift_preserved(j / tile_size - d);
230
}
231
if (j >= graphic->size) {
232
break;
233
}
234
if (j > i) {
235
memcpy(&graphic->data[i], &graphic->data[j], tile_size);
236
}
237
num_tiles++;
238
}
239
graphic->size = num_tiles * tile_size;
240
}
241
242
void interleave(struct Graphic *graphic, int width) {
243
int tile_size = options.depth * 8;
244
int width_tiles = width / 8;
245
int num_tiles = graphic->size / tile_size;
246
uint8_t *interleaved = xmalloc(graphic->size);
247
for (int i = 0; i < num_tiles; i++) {
248
int row = i / width_tiles;
249
int tile = i * 2 - (row % 2 ? width_tiles * (row + 1) - 1 : width_tiles * row);
250
memcpy(&interleaved[tile * tile_size], &graphic->data[i * tile_size], tile_size);
251
}
252
graphic->size = num_tiles * tile_size;
253
memcpy(graphic->data, interleaved, graphic->size);
254
free(interleaved);
255
}
256
257
int main(int argc, char *argv[]) {
258
parse_args(argc, argv);
259
260
argc -= optind;
261
argv += optind;
262
if (argc < 1) {
263
usage_exit(1);
264
}
265
266
struct Graphic graphic;
267
graphic.data = read_u8(argv[0], &graphic.size);
268
269
if (options.trim_whitespace) {
270
trim_whitespace(&graphic);
271
}
272
if (options.interleave) {
273
if (!options.png_file) {
274
error_exit("--interleave needs --png to infer dimensions");
275
}
276
int width = read_png_width(options.png_file);
277
interleave(&graphic, width);
278
}
279
if (options.remove_duplicates) {
280
remove_duplicates(&graphic);
281
}
282
if (options.remove_xflip) {
283
remove_flip(&graphic, true, false);
284
}
285
if (options.remove_yflip) {
286
remove_flip(&graphic, false, true);
287
}
288
if (options.remove_xflip && options.remove_yflip) {
289
remove_flip(&graphic, true, true);
290
}
291
if (options.remove_whitespace) {
292
remove_whitespace(&graphic);
293
}
294
if (options.outfile) {
295
write_u8(options.outfile, graphic.data, graphic.size);
296
}
297
298
free(graphic.data);
299
return 0;
300
}
301
302