diff options
author | Bad Diode <bd@badd10de.dev> | 2021-04-20 10:32:31 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-04-20 10:32:31 +0200 |
commit | c2080e499ab119c70f3fc7c6122bdf0751153986 (patch) | |
tree | 3c25392a371ec78408ea969946c7d4ba01690fd7 | |
parent | ca98cb0c5d3e38b8e12e2d8a3aa66b4cbe0cca3d (diff) | |
download | gba-dev-tools-c2080e499ab119c70f3fc7c6122bdf0751153986.tar.gz gba-dev-tools-c2080e499ab119c70f3fc7c6122bdf0751153986.zip |
Clean up code with more structured data
-rw-r--r-- | src/main.c | 158 |
1 files changed, 96 insertions, 62 deletions
@@ -20,14 +20,33 @@ rgb15(u32 red, u32 green, u32 blue ) { | |||
20 | // TODO: Write more documentation about this. | 20 | // TODO: Write more documentation about this. |
21 | typedef u32 Tile[TILE_SIZE]; | 21 | typedef u32 Tile[TILE_SIZE]; |
22 | 22 | ||
23 | typedef struct Tiles { | ||
24 | Tile *data; | ||
25 | size_t size; | ||
26 | size_t capacity; | ||
27 | } Tiles; | ||
28 | |||
29 | typedef struct Palette { | ||
30 | Color *data; | ||
31 | size_t size; | ||
32 | size_t capacity; | ||
33 | } Palette; | ||
34 | |||
35 | typedef struct Image { | ||
36 | unsigned char *data; | ||
37 | int width; | ||
38 | int height; | ||
39 | int n_channels; | ||
40 | } Image; | ||
41 | |||
23 | void | 42 | void |
24 | export_c_file(Tile *tiles, Color *palette, size_t n_tiles, size_t palette_size) { | 43 | export_c_file(Tiles *tiles, Palette *palette) { |
25 | // Output tiles. | 44 | // Output tiles. |
26 | printf("u32 tiles[][%u] = {\n", TILE_SIZE); | 45 | printf("u32 tiles[%u][%u] = {\n", tiles->capacity, TILE_SIZE); |
27 | for (size_t i = 0; i < n_tiles; ++i) { | 46 | for (size_t i = 0; i < tiles->capacity; ++i) { |
28 | printf(" {"); | 47 | printf(" {"); |
29 | for (size_t j = 0; j < TILE_SIZE; ++j) { | 48 | for (size_t j = 0; j < TILE_SIZE; ++j) { |
30 | printf("0x%08x", tiles[i][j]); | 49 | printf("0x%08x", tiles->data[i][j]); |
31 | if (j != (TILE_SIZE - 1)) { | 50 | if (j != (TILE_SIZE - 1)) { |
32 | printf(", "); | 51 | printf(", "); |
33 | } | 52 | } |
@@ -38,15 +57,15 @@ export_c_file(Tile *tiles, Color *palette, size_t n_tiles, size_t palette_size) | |||
38 | printf("\n"); | 57 | printf("\n"); |
39 | 58 | ||
40 | // Output palette. | 59 | // Output palette. |
41 | printf("u16 palette[%u] = {\n", palette_size); | 60 | printf("u16 palette[%u] = {\n", palette->capacity); |
42 | printf(" "); | 61 | printf(" "); |
43 | size_t counter = 0; | 62 | size_t counter = 0; |
44 | for (size_t i = 0; i < palette_size; ++i, ++counter) { | 63 | for (size_t i = 0; i < palette->capacity; ++i, ++counter) { |
45 | if (counter == 4) { | 64 | if (counter == 4) { |
46 | counter = 0; | 65 | counter = 0; |
47 | printf("\n "); | 66 | printf("\n "); |
48 | } | 67 | } |
49 | printf("0x%04x,", palette[i]); | 68 | printf("0x%04x,", palette->data[i]); |
50 | if (counter < 3) { | 69 | if (counter < 3) { |
51 | printf(" "); | 70 | printf(" "); |
52 | } | 71 | } |
@@ -64,6 +83,47 @@ print_usage(void) { | |||
64 | // TODO: Print valid options as a suggestion. | 83 | // TODO: Print valid options as a suggestion. |
65 | } | 84 | } |
66 | 85 | ||
86 | void | ||
87 | extract_tile(Tiles *tiles, Palette *palette, Image *img, | ||
88 | size_t offset_x, size_t offset_y) { | ||
89 | for (size_t j = 0; j < TILE_SIZE; ++j) { | ||
90 | u32 col_index = 0x00000000; | ||
91 | for (size_t i = 0; i < TILE_SIZE; ++i) { | ||
92 | size_t pal_index = palette->size; | ||
93 | // Find the memory index for this pixel. | ||
94 | int idx = (i + offset_x) + (j + offset_y) * img->width; | ||
95 | idx *= img->n_channels; | ||
96 | |||
97 | int red = img->data[idx]; | ||
98 | int green = img->data[idx + 1]; | ||
99 | int blue = img->data[idx + 2]; | ||
100 | |||
101 | // Quantize to 5 bits per channel. | ||
102 | red = (red * 31 / 255); | ||
103 | green = (green * 31 / 255); | ||
104 | blue = (blue * 31 / 255); | ||
105 | |||
106 | Color clr = rgb15(red, green, blue); | ||
107 | bool found = false; | ||
108 | // TODO: If the palette is full, find the closest perceived | ||
109 | // color instead. | ||
110 | for (size_t p = 0; p < palette->capacity; ++p) { | ||
111 | if (clr == palette->data[p]) { | ||
112 | pal_index = p; | ||
113 | found = true; | ||
114 | break; | ||
115 | } | ||
116 | } | ||
117 | if (!found) { | ||
118 | palette->data[palette->size++] = clr; | ||
119 | } | ||
120 | col_index |= pal_index << i * 4; // TODO: FlipH? | ||
121 | } | ||
122 | tiles->data[tiles->size][j] = col_index; | ||
123 | } | ||
124 | tiles->size++; | ||
125 | } | ||
126 | |||
67 | int | 127 | int |
68 | main(int argc, char *argv[]) { | 128 | main(int argc, char *argv[]) { |
69 | u16 background_color = DEFAULT_BG_COLOR; | 129 | u16 background_color = DEFAULT_BG_COLOR; |
@@ -122,85 +182,59 @@ main(int argc, char *argv[]) { | |||
122 | char *file_name = argv[optind]; | 182 | char *file_name = argv[optind]; |
123 | 183 | ||
124 | // Fill the palette with the background color if one was given. | 184 | // Fill the palette with the background color if one was given. |
125 | Color *palette = malloc(palette_size * sizeof(Color)); | 185 | Palette palette = { |
186 | .data = malloc(palette_size * sizeof(Color)), | ||
187 | .size = 1, | ||
188 | .capacity = palette_size, | ||
189 | }; | ||
126 | for (size_t i = 0; i < palette_size; ++i) { | 190 | for (size_t i = 0; i < palette_size; ++i) { |
127 | palette[i] = background_color; | 191 | palette.data[i] = background_color; |
128 | } | 192 | } |
129 | 193 | ||
130 | int x; | 194 | Image img = {0}; |
131 | int y; | ||
132 | int n; | ||
133 | unsigned char *data; | ||
134 | 195 | ||
135 | // Open the given file. | 196 | // Open the given file. |
136 | data = stbi_load(file_name, &x, &y, &n, 0); | 197 | img.data = stbi_load(file_name, &img.width, &img.height, &img.n_channels, 0); |
137 | if (data == NULL) { | 198 | if (img.data == NULL) { |
138 | fprintf(stderr, "Error: can't open the given file.\n"); | 199 | fprintf(stderr, "Error: can't open the given file.\n"); |
139 | return EXIT_FAILURE; | 200 | return EXIT_FAILURE; |
140 | } | 201 | } |
141 | 202 | ||
142 | // TODO: Implement support for different file inputs. | 203 | // TODO: Implement support for different file inputs. |
143 | if (n != 3) { | 204 | if (img.n_channels != 3) { |
144 | fprintf(stderr, "File format not supported. Only 3 channel files for now.\n"); | 205 | fprintf(stderr, "File format not supported. Only 3 channel files for now.\n"); |
145 | return EXIT_FAILURE; | 206 | return EXIT_FAILURE; |
146 | } | 207 | } |
147 | 208 | ||
148 | int n_tiles_x = x / TILE_SIZE; | 209 | int n_tiles_x = img.width / TILE_SIZE; |
149 | int n_tiles_y = y / TILE_SIZE; | 210 | int n_tiles_y = img.height / TILE_SIZE; |
150 | int n_tiles = n_tiles_x * n_tiles_y; | 211 | int n_tiles = n_tiles_x * n_tiles_y; |
151 | 212 | ||
152 | // Allocate memory for the tiles in this file. | 213 | // Allocate memory for the tiles in this file, with zero-initialization. |
153 | Tile *tiles = malloc(n_tiles * sizeof(Tile)); | 214 | Tiles tiles = { |
215 | .data = calloc(n_tiles, sizeof(Tile)), | ||
216 | .size = 0, | ||
217 | .capacity = n_tiles, | ||
218 | }; | ||
154 | 219 | ||
155 | // NOTE: We are going to brute-force this for now. Checking if the color is | 220 | // NOTE: We are going to brute-force this for now. Checking if the color is |
156 | // in the palette and add it to the next slot if possible. In the future we | 221 | // in the palette and add it to the next slot if possible. In the future we |
157 | // may want to use a hash-table instead. | 222 | // may want to use a hash-table instead. |
158 | int pal_idx = 1; | 223 | int pal_idx = 1; |
159 | int tile_offset_x = 0; | 224 | int offset_x = 0; |
160 | int tile_offset_y = 0; | 225 | int offset_y = 0; |
161 | for (size_t k = 0; k < n_tiles; ++k) { | 226 | for (size_t k = 0; k < n_tiles; ++k) { |
162 | for (size_t j = 0; j < TILE_SIZE; ++j) { | 227 | extract_tile(&tiles, &palette, &img, offset_x, offset_y); |
163 | u32 col_index = 0x00000000; | 228 | offset_x += TILE_SIZE; |
164 | for (size_t i = 0; i < TILE_SIZE; ++i) { | 229 | if (offset_x >= img.width) { |
165 | // Find the memory index for this pixel. | 230 | offset_x = 0; |
166 | int idx = (i + tile_offset_x) + (j + tile_offset_y) * x; | 231 | offset_y += TILE_SIZE; |
167 | idx *= n; | ||
168 | |||
169 | int red = data[idx]; | ||
170 | int green = data[idx + 1]; | ||
171 | int blue = data[idx + 2]; | ||
172 | |||
173 | // Quantize to 5 bits per channel. | ||
174 | red = (red * 31 / 255); | ||
175 | green = (green * 31 / 255); | ||
176 | blue = (blue * 31 / 255); | ||
177 | |||
178 | Color clr = rgb15(red, green, blue); | ||
179 | bool found = false; | ||
180 | // TODO: If the palette is full, find the closest perceived | ||
181 | // color instead. | ||
182 | for (size_t p = 0; p < palette_size; ++p) { | ||
183 | if (clr == palette[p]) { | ||
184 | col_index |= (p & 0xF) << ((i) * 4) ; | ||
185 | found = true; | ||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | if (!found) { | ||
190 | col_index |= pal_idx << (i) * 4; // TODO: FlipH? | ||
191 | palette[pal_idx++] = clr; | ||
192 | } | ||
193 | } | ||
194 | tiles[k][j] = col_index; | ||
195 | } | ||
196 | tile_offset_x += TILE_SIZE; | ||
197 | if (tile_offset_x >= x) { | ||
198 | tile_offset_x = 0; | ||
199 | tile_offset_y += TILE_SIZE; | ||
200 | } | 232 | } |
201 | } | 233 | } |
202 | 234 | ||
203 | export_c_file(tiles, palette, n_tiles, palette_size); | 235 | export_c_file(&tiles, &palette); |
204 | stbi_image_free(data); | 236 | |
237 | // TODO: Cleanup other resources. | ||
238 | stbi_image_free(img.data); | ||
205 | return EXIT_SUCCESS; | 239 | return EXIT_SUCCESS; |
206 | } | 240 | } |