1 #include "image.h"
2
3 #include "core/log.h"
4 #include "graphics/graphics.h"
5 #include "graphics/screen.h"
6
7 #include <string.h>
8
9 #define FOOTPRINT_WIDTH 58
10 #define FOOTPRINT_HEIGHT 30
11
12 #define COMPONENT(c, shift) ((c >> shift) & 0xff)
13 #define MIX_RB(src, dst, alpha) ((((src & 0xff00ff) * alpha + (dst & 0xff00ff) * (256 - alpha)) >> 8) & 0xff00ff)
14 #define MIX_G(src, dst, alpha) ((((src & 0x00ff00) * alpha + (dst & 0x00ff00) * (256 - alpha)) >> 8) & 0x00ff00)
15
16 typedef enum {
17 DRAW_TYPE_SET,
18 DRAW_TYPE_AND,
19 DRAW_TYPE_NONE,
20 DRAW_TYPE_BLEND,
21 DRAW_TYPE_BLEND_ALPHA
22 } draw_type;
23
24 static const int FOOTPRINT_X_START_PER_HEIGHT[] = {
25 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0,
26 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28
27 };
28
29 static const int FOOTPRINT_OFFSET_PER_HEIGHT[] = {
30 0, 2, 8, 18, 32, 50, 72, 98, 128, 162, 200, 242, 288, 338, 392, 450,
31 508, 562, 612, 658, 700, 738, 772, 802, 828, 850, 868, 882, 892, 898
32 };
33
draw_uncompressed(const image * img,const color_t * data,int x_offset,int y_offset,color_t color,draw_type type)34 static void draw_uncompressed(
35 const image *img, const color_t *data, int x_offset, int y_offset, color_t color, draw_type type)
36 {
37 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, img->height);
38 if (!clip->is_visible) {
39 return;
40 }
41 data += img->width * clip->clipped_pixels_top;
42 for (int y = clip->clipped_pixels_top; y < img->height - clip->clipped_pixels_bottom; y++) {
43 data += clip->clipped_pixels_left;
44 color_t *dst = graphics_get_pixel(x_offset + clip->clipped_pixels_left, y_offset + y);
45 int x_max = img->width - clip->clipped_pixels_right;
46 if (type == DRAW_TYPE_NONE) {
47 if (img->draw.type == IMAGE_TYPE_WITH_TRANSPARENCY || img->draw.is_external) { // can be transparent
48 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
49 if (*data != COLOR_SG2_TRANSPARENT) {
50 *dst = *data;
51 }
52 data++;
53 }
54 } else {
55 int num_pixels = x_max - clip->clipped_pixels_left;
56 memcpy(dst, data, num_pixels * sizeof(color_t));
57 data += num_pixels;
58 }
59 } else if (type == DRAW_TYPE_SET) {
60 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
61 if (*data != COLOR_SG2_TRANSPARENT) {
62 *dst = color;
63 }
64 data++;
65 }
66 } else if (type == DRAW_TYPE_AND) {
67 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
68 if (*data != COLOR_SG2_TRANSPARENT) {
69 *dst = *data & color;
70 }
71 data++;
72 }
73 } else if (type == DRAW_TYPE_BLEND) {
74 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
75 if (*data != COLOR_SG2_TRANSPARENT) {
76 *dst &= color;
77 }
78 data++;
79 }
80 } else if (type == DRAW_TYPE_BLEND_ALPHA) {
81 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
82 if (*data != COLOR_SG2_TRANSPARENT) {
83 color_t alpha = COMPONENT(*data, 24);
84 if (alpha == 255) {
85 *dst = color;
86 } else {
87 color_t s = color;
88 color_t d = *dst;
89 *dst = MIX_RB(s, d, alpha) | MIX_G(s, d, alpha);
90 }
91 }
92 data++;
93 }
94 }
95 data += clip->clipped_pixels_right;
96 }
97 }
98
draw_compressed(const image * img,const color_t * data,int x_offset,int y_offset,int height)99 static void draw_compressed(const image *img, const color_t *data, int x_offset, int y_offset, int height)
100 {
101 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, height);
102 if (!clip->is_visible) {
103 return;
104 }
105 int unclipped = clip->clip_x == CLIP_NONE;
106
107 for (int y = 0; y < height - clip->clipped_pixels_bottom; y++) {
108 int x = 0;
109 while (x < img->width) {
110 color_t b = *data;
111 data++;
112 if (b == 255) {
113 // transparent pixels to skip
114 x += *data;
115 data++;
116 } else if (y < clip->clipped_pixels_top) {
117 data += b;
118 x += b;
119 } else {
120 // number of concrete pixels
121 const color_t *pixels = data;
122 data += b;
123 color_t *dst = graphics_get_pixel(x_offset + x, y_offset + y);
124 if (unclipped) {
125 x += b;
126 memcpy(dst, pixels, b * sizeof(color_t));
127 } else {
128 while (b) {
129 if (x >= clip->clipped_pixels_left && x < img->width - clip->clipped_pixels_right) {
130 *dst = *pixels;
131 }
132 dst++;
133 x++;
134 pixels++;
135 b--;
136 }
137 }
138 }
139 }
140 }
141 }
142
draw_compressed_set(const image * img,const color_t * data,int x_offset,int y_offset,int height,color_t color)143 static void draw_compressed_set(
144 const image *img, const color_t *data, int x_offset, int y_offset, int height, color_t color)
145 {
146 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, height);
147 if (!clip->is_visible) {
148 return;
149 }
150 int unclipped = clip->clip_x == CLIP_NONE;
151
152 for (int y = 0; y < height - clip->clipped_pixels_bottom; y++) {
153 int x = 0;
154 while (x < img->width) {
155 color_t b = *data;
156 data++;
157 if (b == 255) {
158 // transparent pixels to skip
159 x += *data;
160 data++;
161 } else if (y < clip->clipped_pixels_top) {
162 data += b;
163 x += b;
164 } else {
165 data += b;
166 color_t *dst = graphics_get_pixel(x_offset + x, y_offset + y);
167 if (unclipped) {
168 x += b;
169 while (b) {
170 *dst = color;
171 dst++;
172 b--;
173 }
174 } else {
175 while (b) {
176 if (x >= clip->clipped_pixels_left && x < img->width - clip->clipped_pixels_right) {
177 *dst = color;
178 }
179 dst++;
180 x++;
181 b--;
182 }
183 }
184 }
185 }
186 }
187 }
188
draw_compressed_and(const image * img,const color_t * data,int x_offset,int y_offset,int height,color_t color)189 static void draw_compressed_and(
190 const image *img, const color_t *data, int x_offset, int y_offset, int height, color_t color)
191 {
192 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, height);
193 if (!clip->is_visible) {
194 return;
195 }
196 int unclipped = clip->clip_x == CLIP_NONE;
197
198 for (int y = 0; y < height - clip->clipped_pixels_bottom; y++) {
199 int x = 0;
200 while (x < img->width) {
201 color_t b = *data;
202 data++;
203 if (b == 255) {
204 // transparent pixels to skip
205 x += *data;
206 data++;
207 } else if (y < clip->clipped_pixels_top) {
208 data += b;
209 x += b;
210 } else {
211 // number of concrete pixels
212 const color_t *pixels = data;
213 data += b;
214 color_t *dst = graphics_get_pixel(x_offset + x, y_offset + y);
215 if (unclipped) {
216 x += b;
217 while (b) {
218 *dst = *pixels & color;
219 dst++;
220 pixels++;
221 b--;
222 }
223 } else {
224 while (b) {
225 if (x >= clip->clipped_pixels_left && x < img->width - clip->clipped_pixels_right) {
226 *dst = *pixels & color;
227 }
228 dst++;
229 x++;
230 pixels++;
231 b--;
232 }
233 }
234 }
235 }
236 }
237 }
238
draw_compressed_blend(const image * img,const color_t * data,int x_offset,int y_offset,int height,color_t color)239 static void draw_compressed_blend(
240 const image *img, const color_t *data, int x_offset, int y_offset, int height, color_t color)
241 {
242 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, height);
243 if (!clip->is_visible) {
244 return;
245 }
246 int unclipped = clip->clip_x == CLIP_NONE;
247
248 for (int y = 0; y < height - clip->clipped_pixels_bottom; y++) {
249 int x = 0;
250 while (x < img->width) {
251 color_t b = *data;
252 data++;
253 if (b == 255) {
254 // transparent pixels to skip
255 x += *data;
256 data++;
257 } else if (y < clip->clipped_pixels_top) {
258 data += b;
259 x += b;
260 } else {
261 data += b;
262 color_t *dst = graphics_get_pixel(x_offset + x, y_offset + y);
263 if (unclipped) {
264 x += b;
265 while (b) {
266 *dst &= color;
267 dst++;
268 b--;
269 }
270 } else {
271 while (b) {
272 if (x >= clip->clipped_pixels_left && x < img->width - clip->clipped_pixels_right) {
273 *dst &= color;
274 }
275 dst++;
276 x++;
277 b--;
278 }
279 }
280 }
281 }
282 }
283 }
284
draw_compressed_blend_alpha(const image * img,const color_t * data,int x_offset,int y_offset,int height,color_t color)285 static void draw_compressed_blend_alpha(
286 const image *img, const color_t *data, int x_offset, int y_offset, int height, color_t color)
287 {
288 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, img->width, height);
289 if (!clip->is_visible) {
290 return;
291 }
292 color_t alpha = COMPONENT(color, 24);
293 if (!alpha) {
294 return;
295 }
296 if (alpha == 255) {
297 draw_compressed_set(img, data, x_offset, y_offset, height, color);
298 return;
299 }
300 color_t alpha_dst = 256 - alpha;
301 color_t src_rb = (color & 0xff00ff) * alpha;
302 color_t src_g = (color & 0x00ff00) * alpha;
303 int unclipped = clip->clip_x == CLIP_NONE;
304
305 for (int y = 0; y < height - clip->clipped_pixels_bottom; y++) {
306 int x = 0;
307 color_t *dst = graphics_get_pixel(x_offset, y_offset + y);
308 while (x < img->width) {
309 color_t b = *data;
310 data++;
311 if (b == 255) {
312 // transparent pixels to skip
313 x += *data;
314 dst += *data;
315 data++;
316 } else if (y < clip->clipped_pixels_top) {
317 data += b;
318 x += b;
319 dst += b;
320 } else {
321 data += b;
322 if (unclipped) {
323 x += b;
324 while (b) {
325 color_t d = *dst;
326 *dst = (((src_rb + (d & 0xff00ff) * alpha_dst) & 0xff00ff00) |
327 ((src_g + (d & 0x00ff00) * alpha_dst) & 0x00ff0000)) >> 8;
328 b--;
329 dst++;
330 }
331 } else {
332 while (b) {
333 if (x >= clip->clipped_pixels_left && x < img->width - clip->clipped_pixels_right) {
334 color_t d = *dst;
335 *dst = (((src_rb + (d & 0xff00ff) * alpha_dst) & 0xff00ff00) |
336 ((src_g + (d & 0x00ff00) * alpha_dst) & 0x00ff0000)) >> 8;
337 }
338 dst++;
339 x++;
340 b--;
341 }
342 }
343 }
344 }
345 }
346 }
347
draw_footprint_simple(const color_t * src,int x,int y)348 static void draw_footprint_simple(const color_t *src, int x, int y)
349 {
350 memcpy(graphics_get_pixel(x + 28, y + 0), &src[0], 2 * sizeof(color_t));
351 memcpy(graphics_get_pixel(x + 26, y + 1), &src[2], 6 * sizeof(color_t));
352 memcpy(graphics_get_pixel(x + 24, y + 2), &src[8], 10 * sizeof(color_t));
353 memcpy(graphics_get_pixel(x + 22, y + 3), &src[18], 14 * sizeof(color_t));
354 memcpy(graphics_get_pixel(x + 20, y + 4), &src[32], 18 * sizeof(color_t));
355 memcpy(graphics_get_pixel(x + 18, y + 5), &src[50], 22 * sizeof(color_t));
356 memcpy(graphics_get_pixel(x + 16, y + 6), &src[72], 26 * sizeof(color_t));
357 memcpy(graphics_get_pixel(x + 14, y + 7), &src[98], 30 * sizeof(color_t));
358 memcpy(graphics_get_pixel(x + 12, y + 8), &src[128], 34 * sizeof(color_t));
359 memcpy(graphics_get_pixel(x + 10, y + 9), &src[162], 38 * sizeof(color_t));
360 memcpy(graphics_get_pixel(x + 8, y + 10), &src[200], 42 * sizeof(color_t));
361 memcpy(graphics_get_pixel(x + 6, y + 11), &src[242], 46 * sizeof(color_t));
362 memcpy(graphics_get_pixel(x + 4, y + 12), &src[288], 50 * sizeof(color_t));
363 memcpy(graphics_get_pixel(x + 2, y + 13), &src[338], 54 * sizeof(color_t));
364 memcpy(graphics_get_pixel(x + 0, y + 14), &src[392], 58 * sizeof(color_t));
365 memcpy(graphics_get_pixel(x + 0, y + 15), &src[450], 58 * sizeof(color_t));
366 memcpy(graphics_get_pixel(x + 2, y + 16), &src[508], 54 * sizeof(color_t));
367 memcpy(graphics_get_pixel(x + 4, y + 17), &src[562], 50 * sizeof(color_t));
368 memcpy(graphics_get_pixel(x + 6, y + 18), &src[612], 46 * sizeof(color_t));
369 memcpy(graphics_get_pixel(x + 8, y + 19), &src[658], 42 * sizeof(color_t));
370 memcpy(graphics_get_pixel(x + 10, y + 20), &src[700], 38 * sizeof(color_t));
371 memcpy(graphics_get_pixel(x + 12, y + 21), &src[738], 34 * sizeof(color_t));
372 memcpy(graphics_get_pixel(x + 14, y + 22), &src[772], 30 * sizeof(color_t));
373 memcpy(graphics_get_pixel(x + 16, y + 23), &src[802], 26 * sizeof(color_t));
374 memcpy(graphics_get_pixel(x + 18, y + 24), &src[828], 22 * sizeof(color_t));
375 memcpy(graphics_get_pixel(x + 20, y + 25), &src[850], 18 * sizeof(color_t));
376 memcpy(graphics_get_pixel(x + 22, y + 26), &src[868], 14 * sizeof(color_t));
377 memcpy(graphics_get_pixel(x + 24, y + 27), &src[882], 10 * sizeof(color_t));
378 memcpy(graphics_get_pixel(x + 26, y + 28), &src[892], 6 * sizeof(color_t));
379 memcpy(graphics_get_pixel(x + 28, y + 29), &src[898], 2 * sizeof(color_t));
380 }
381
draw_footprint_tile(const color_t * data,int x_offset,int y_offset,color_t color_mask)382 static void draw_footprint_tile(const color_t *data, int x_offset, int y_offset, color_t color_mask)
383 {
384 if (!color_mask) {
385 color_mask = COLOR_MASK_NONE;
386 }
387 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, FOOTPRINT_WIDTH, FOOTPRINT_HEIGHT);
388 if (!clip->is_visible) {
389 return;
390 }
391 // If the current tile neither clipped nor color masked, just draw it normally
392 if (clip->clip_y == CLIP_NONE && clip->clip_x == CLIP_NONE && color_mask == COLOR_MASK_NONE) {
393 draw_footprint_simple(data, x_offset, y_offset);
394 return;
395 }
396 int clip_left = clip->clip_x == CLIP_LEFT || clip->clip_x == CLIP_BOTH;
397 int clip_right = clip->clip_x == CLIP_RIGHT || clip->clip_x == CLIP_BOTH;
398 const color_t *src = &data[FOOTPRINT_OFFSET_PER_HEIGHT[clip->clipped_pixels_top]];
399 for (int y = clip->clipped_pixels_top; y < clip->clipped_pixels_top + clip->visible_pixels_y; y++) {
400 int x_start = FOOTPRINT_X_START_PER_HEIGHT[y];
401 int x_max = 58 - x_start * 2;
402 int x_pixel_advance = 0;
403 if (clip_left) {
404 if (clip->clipped_pixels_left + clip->visible_pixels_x < x_start) {
405 src += x_max;
406 continue;
407 }
408 if (clip->clipped_pixels_left > x_start) {
409 int pixels_to_reduce = clip->clipped_pixels_left - x_start;
410 if (pixels_to_reduce >= x_max) {
411 src += x_max;
412 continue;
413 }
414 src += pixels_to_reduce;
415 x_max -= pixels_to_reduce;
416 x_start = clip->clipped_pixels_left;
417 }
418 }
419 if (clip_right) {
420 int clip_x = 58 - clip->clipped_pixels_right;
421 if (clip_x < x_start) {
422 src += x_max;
423 continue;
424 }
425 if (x_start + x_max > clip_x) {
426 int temp_x_max = clip_x - x_start;
427 x_pixel_advance = x_max - temp_x_max;
428 x_max = temp_x_max;
429 }
430 }
431 color_t *buffer = graphics_get_pixel(x_offset + x_start, y_offset + y);
432 if (color_mask == COLOR_MASK_NONE) {
433 memcpy(buffer, src, x_max * sizeof(color_t));
434 src += x_max + x_pixel_advance;
435 } else {
436 for (int x = 0; x < x_max; x++, buffer++, src++) {
437 *buffer = *src & color_mask;
438 }
439 src += x_pixel_advance;
440 }
441 }
442 }
443
tile_data(const color_t * data,int index)444 static const color_t *tile_data(const color_t *data, int index)
445 {
446 return &data[900 * index];
447 }
448
draw_footprint_size1(int image_id,int x,int y,color_t color_mask)449 static void draw_footprint_size1(int image_id, int x, int y, color_t color_mask)
450 {
451 const color_t *data = image_data(image_id);
452
453 draw_footprint_tile(tile_data(data, 0), x, y, color_mask);
454 }
455
draw_footprint_size2(int image_id,int x,int y,color_t color_mask)456 static void draw_footprint_size2(int image_id, int x, int y, color_t color_mask)
457 {
458 const color_t *data = image_data(image_id);
459
460 int index = 0;
461 draw_footprint_tile(tile_data(data, index++), x, y, color_mask);
462
463 draw_footprint_tile(tile_data(data, index++), x - 30, y + 15, color_mask);
464 draw_footprint_tile(tile_data(data, index++), x + 30, y + 15, color_mask);
465
466 draw_footprint_tile(tile_data(data, index++), x, y + 30, color_mask);
467 }
468
draw_footprint_size3(int image_id,int x,int y,color_t color_mask)469 static void draw_footprint_size3(int image_id, int x, int y, color_t color_mask)
470 {
471 const color_t *data = image_data(image_id);
472
473 int index = 0;
474 draw_footprint_tile(tile_data(data, index++), x, y, color_mask);
475
476 draw_footprint_tile(tile_data(data, index++), x - 30, y + 15, color_mask);
477 draw_footprint_tile(tile_data(data, index++), x + 30, y + 15, color_mask);
478
479 draw_footprint_tile(tile_data(data, index++), x - 60, y + 30, color_mask);
480 draw_footprint_tile(tile_data(data, index++), x, y + 30, color_mask);
481 draw_footprint_tile(tile_data(data, index++), x + 60, y + 30, color_mask);
482
483 draw_footprint_tile(tile_data(data, index++), x - 30, y + 45, color_mask);
484 draw_footprint_tile(tile_data(data, index++), x + 30, y + 45, color_mask);
485
486 draw_footprint_tile(tile_data(data, index++), x, y + 60, color_mask);
487 }
488
draw_footprint_size4(int image_id,int x,int y,color_t color_mask)489 static void draw_footprint_size4(int image_id, int x, int y, color_t color_mask)
490 {
491 const color_t *data = image_data(image_id);
492
493 int index = 0;
494 draw_footprint_tile(tile_data(data, index++), x, y, color_mask);
495
496 draw_footprint_tile(tile_data(data, index++), x - 30, y + 15, color_mask);
497 draw_footprint_tile(tile_data(data, index++), x + 30, y + 15, color_mask);
498
499 draw_footprint_tile(tile_data(data, index++), x - 60, y + 30, color_mask);
500 draw_footprint_tile(tile_data(data, index++), x, y + 30, color_mask);
501 draw_footprint_tile(tile_data(data, index++), x + 60, y + 30, color_mask);
502
503 draw_footprint_tile(tile_data(data, index++), x - 90, y + 45, color_mask);
504 draw_footprint_tile(tile_data(data, index++), x - 30, y + 45, color_mask);
505 draw_footprint_tile(tile_data(data, index++), x + 30, y + 45, color_mask);
506 draw_footprint_tile(tile_data(data, index++), x + 90, y + 45, color_mask);
507
508 draw_footprint_tile(tile_data(data, index++), x - 60, y + 60, color_mask);
509 draw_footprint_tile(tile_data(data, index++), x, y + 60, color_mask);
510 draw_footprint_tile(tile_data(data, index++), x + 60, y + 60, color_mask);
511
512 draw_footprint_tile(tile_data(data, index++), x - 30, y + 75, color_mask);
513 draw_footprint_tile(tile_data(data, index++), x + 30, y + 75, color_mask);
514
515 draw_footprint_tile(tile_data(data, index++), x, y + 90, color_mask);
516 }
517
draw_footprint_size5(int image_id,int x,int y,color_t color_mask)518 static void draw_footprint_size5(int image_id, int x, int y, color_t color_mask)
519 {
520 const color_t *data = image_data(image_id);
521
522 int index = 0;
523 draw_footprint_tile(tile_data(data, index++), x, y, color_mask);
524
525 draw_footprint_tile(tile_data(data, index++), x - 30, y + 15, color_mask);
526 draw_footprint_tile(tile_data(data, index++), x + 30, y + 15, color_mask);
527
528 draw_footprint_tile(tile_data(data, index++), x - 60, y + 30, color_mask);
529 draw_footprint_tile(tile_data(data, index++), x, y + 30, color_mask);
530 draw_footprint_tile(tile_data(data, index++), x + 60, y + 30, color_mask);
531
532 draw_footprint_tile(tile_data(data, index++), x - 90, y + 45, color_mask);
533 draw_footprint_tile(tile_data(data, index++), x - 30, y + 45, color_mask);
534 draw_footprint_tile(tile_data(data, index++), x + 30, y + 45, color_mask);
535 draw_footprint_tile(tile_data(data, index++), x + 90, y + 45, color_mask);
536
537 draw_footprint_tile(tile_data(data, index++), x - 120, y + 60, color_mask);
538 draw_footprint_tile(tile_data(data, index++), x - 60, y + 60, color_mask);
539 draw_footprint_tile(tile_data(data, index++), x, y + 60, color_mask);
540 draw_footprint_tile(tile_data(data, index++), x + 60, y + 60, color_mask);
541 draw_footprint_tile(tile_data(data, index++), x + 120, y + 60, color_mask);
542
543 draw_footprint_tile(tile_data(data, index++), x - 90, y + 75, color_mask);
544 draw_footprint_tile(tile_data(data, index++), x - 30, y + 75, color_mask);
545 draw_footprint_tile(tile_data(data, index++), x + 30, y + 75, color_mask);
546 draw_footprint_tile(tile_data(data, index++), x + 90, y + 75, color_mask);
547
548 draw_footprint_tile(tile_data(data, index++), x - 60, y + 90, color_mask);
549 draw_footprint_tile(tile_data(data, index++), x, y + 90, color_mask);
550 draw_footprint_tile(tile_data(data, index++), x + 60, y + 90, color_mask);
551
552 draw_footprint_tile(tile_data(data, index++), x - 30, y + 105, color_mask);
553 draw_footprint_tile(tile_data(data, index++), x + 30, y + 105, color_mask);
554
555 draw_footprint_tile(tile_data(data, index++), x, y + 120, color_mask);
556 }
557
image_draw(int image_id,int x,int y)558 void image_draw(int image_id, int x, int y)
559 {
560 const image *img = image_get(image_id);
561 const color_t *data = image_data(image_id);
562 if (!data) {
563 return;
564 }
565
566 if (img->draw.is_fully_compressed) {
567 draw_compressed(img, data, x, y, img->height);
568 } else {
569 draw_uncompressed(img, data, x, y, 0, DRAW_TYPE_NONE);
570 }
571 }
572
image_draw_enemy(int image_id,int x,int y)573 void image_draw_enemy(int image_id, int x, int y)
574 {
575 if (image_id <= 0 || image_id >= 801) {
576 return;
577 }
578 const image *img = image_get_enemy(image_id);
579 const color_t *data = image_data_enemy(image_id);
580 if (data) {
581 draw_compressed(img, data, x, y, img->height);
582 }
583 }
584
image_draw_masked(int image_id,int x,int y,color_t color_mask)585 void image_draw_masked(int image_id, int x, int y, color_t color_mask)
586 {
587 const image *img = image_get(image_id);
588 const color_t *data = image_data(image_id);
589 if (!data) {
590 return;
591 }
592
593 if (img->draw.type == IMAGE_TYPE_ISOMETRIC) {
594 log_error("use image_draw_isometric_footprint for isometric!", 0, image_id);
595 return;
596 }
597
598 if (img->draw.is_fully_compressed) {
599 if (!color_mask) {
600 draw_compressed(img, data, x, y, img->height);
601 } else {
602 draw_compressed_and(img, data, x, y, img->height, color_mask);
603 }
604 } else {
605 draw_uncompressed(img, data, x, y,
606 color_mask, color_mask ? DRAW_TYPE_AND : DRAW_TYPE_NONE);
607 }
608 }
609
image_draw_blend(int image_id,int x,int y,color_t color)610 void image_draw_blend(int image_id, int x, int y, color_t color)
611 {
612 const image *img = image_get(image_id);
613 const color_t *data = image_data(image_id);
614 if (!data) {
615 return;
616 }
617
618 if (img->draw.type == IMAGE_TYPE_ISOMETRIC) {
619 return;
620 }
621
622 if (img->draw.is_fully_compressed) {
623 draw_compressed_blend(img, data, x, y, img->height, color);
624 } else {
625 draw_uncompressed(img, data, x, y, color, DRAW_TYPE_BLEND);
626 }
627 }
628
image_draw_blend_alpha(int image_id,int x,int y,color_t color)629 void image_draw_blend_alpha(int image_id, int x, int y, color_t color)
630 {
631 const image *img = image_get(image_id);
632 const color_t *data = image_data(image_id);
633 if (!data) {
634 return;
635 }
636
637 if (img->draw.type == IMAGE_TYPE_ISOMETRIC) {
638 return;
639 }
640
641 if (img->draw.is_fully_compressed) {
642 draw_compressed_blend_alpha(img, data, x, y, img->height, color);
643 } else {
644 draw_uncompressed(img, data, x, y, color, DRAW_TYPE_BLEND_ALPHA);
645 }
646 }
647
draw_multibyte_letter(font_t font,const image * img,const color_t * data,int x,int y,color_t color)648 static void draw_multibyte_letter(font_t font, const image *img, const color_t *data, int x, int y, color_t color)
649 {
650 switch (font) {
651 case FONT_NORMAL_WHITE:
652 draw_uncompressed(img, data, x + 1, y + 1, 0x311c10, DRAW_TYPE_BLEND_ALPHA);
653 draw_uncompressed(img, data, x, y, COLOR_WHITE, DRAW_TYPE_BLEND_ALPHA);
654 break;
655 case FONT_NORMAL_RED:
656 draw_uncompressed(img, data, x + 1, y + 1, 0xe7cfad, DRAW_TYPE_BLEND_ALPHA);
657 draw_uncompressed(img, data, x, y, 0x731408, DRAW_TYPE_BLEND_ALPHA);
658 break;
659 case FONT_NORMAL_GREEN:
660 draw_uncompressed(img, data, x + 1, y + 1, 0xe7cfad, DRAW_TYPE_BLEND_ALPHA);
661 draw_uncompressed(img, data, x, y, 0x180800, DRAW_TYPE_BLEND_ALPHA);
662 break;
663 case FONT_NORMAL_BLACK:
664 case FONT_LARGE_BLACK:
665 draw_uncompressed(img, data, x + 1, y + 1, 0xcead9c, DRAW_TYPE_BLEND_ALPHA);
666 draw_uncompressed(img, data, x, y, COLOR_BLACK, DRAW_TYPE_BLEND_ALPHA);
667 break;
668 default: // Plain + brown
669 draw_uncompressed(img, data, x, y, color, DRAW_TYPE_BLEND_ALPHA);
670 break;
671 }
672 }
673
image_draw_letter(font_t font,int letter_id,int x,int y,color_t color)674 void image_draw_letter(font_t font, int letter_id, int x, int y, color_t color)
675 {
676 const image *img = image_letter(letter_id);
677 const color_t *data = image_data_letter(letter_id);
678 if (!data) {
679 return;
680 }
681 if (letter_id >= IMAGE_FONT_MULTIBYTE_OFFSET) {
682 draw_multibyte_letter(font, img, data, x, y, color);
683 return;
684 }
685
686 if (img->draw.is_fully_compressed) {
687 if (color) {
688 draw_compressed_set(img, data, x, y, img->height, color);
689 } else {
690 draw_compressed(img, data, x, y, img->height);
691 }
692 } else {
693 draw_uncompressed(img, data, x, y,
694 color, color ? DRAW_TYPE_SET : DRAW_TYPE_NONE);
695 }
696 }
697
image_draw_fullscreen_background(int image_id)698 void image_draw_fullscreen_background(int image_id)
699 {
700 int s_width = screen_width();
701 int s_height = screen_height();
702 if (s_width > 1024 || s_height > 768) {
703 graphics_clear_screen();
704 }
705 image_draw(image_id, (s_width - 1024) / 2, (s_height - 768) / 2);
706 }
707
image_draw_isometric_footprint(int image_id,int x,int y,color_t color_mask)708 void image_draw_isometric_footprint(int image_id, int x, int y, color_t color_mask)
709 {
710 const image *img = image_get(image_id);
711 if (img->draw.type != IMAGE_TYPE_ISOMETRIC) {
712 return;
713 }
714 switch (img->width) {
715 case 58:
716 draw_footprint_size1(image_id, x, y, color_mask);
717 break;
718 case 118:
719 draw_footprint_size2(image_id, x, y, color_mask);
720 break;
721 case 178:
722 draw_footprint_size3(image_id, x, y, color_mask);
723 break;
724 case 238:
725 draw_footprint_size4(image_id, x, y, color_mask);
726 break;
727 case 298:
728 draw_footprint_size5(image_id, x, y, color_mask);
729 break;
730 }
731 }
732
image_draw_isometric_footprint_from_draw_tile(int image_id,int x,int y,color_t color_mask)733 void image_draw_isometric_footprint_from_draw_tile(int image_id, int x, int y, color_t color_mask)
734 {
735 const image *img = image_get(image_id);
736 if (img->draw.type != IMAGE_TYPE_ISOMETRIC) {
737 return;
738 }
739 switch (img->width) {
740 case 58:
741 draw_footprint_size1(image_id, x, y, color_mask);
742 break;
743 case 118:
744 draw_footprint_size2(image_id, x + 30, y - 15, color_mask);
745 break;
746 case 178:
747 draw_footprint_size3(image_id, x + 60, y - 30, color_mask);
748 break;
749 case 238:
750 draw_footprint_size4(image_id, x + 90, y - 45, color_mask);
751 break;
752 case 298:
753 draw_footprint_size5(image_id, x + 120, y - 60, color_mask);
754 break;
755 }
756 }
757
image_draw_isometric_top(int image_id,int x,int y,color_t color_mask)758 void image_draw_isometric_top(int image_id, int x, int y, color_t color_mask)
759 {
760 const image *img = image_get(image_id);
761 if (img->draw.type != IMAGE_TYPE_ISOMETRIC) {
762 return;
763 }
764 if (!img->draw.has_compressed_part) {
765 return;
766 }
767 const color_t *data = &image_data(image_id)[img->draw.uncompressed_length];
768
769 int height = img->height;
770 switch (img->width) {
771 case 58:
772 y -= img->height - 30;
773 height -= 16;
774 break;
775 case 118:
776 x -= 30;
777 y -= img->height - 60;
778 height -= 31;
779 break;
780 case 178:
781 x -= 60;
782 y -= img->height - 90;
783 height -= 46;
784 break;
785 case 238:
786 x -= 90;
787 y -= img->height - 120;
788 height -= 61;
789 break;
790 case 298:
791 x -= 120;
792 y -= img->height - 150;
793 height -= 76;
794 break;
795 }
796 if (!color_mask) {
797 draw_compressed(img, data, x, y, height);
798 } else {
799 draw_compressed_and(img, data, x, y, height, color_mask);
800 }
801 }
802
image_draw_isometric_top_from_draw_tile(int image_id,int x,int y,color_t color_mask)803 void image_draw_isometric_top_from_draw_tile(int image_id, int x, int y, color_t color_mask)
804 {
805 const image *img = image_get(image_id);
806 if (img->draw.type != IMAGE_TYPE_ISOMETRIC) {
807 return;
808 }
809 if (!img->draw.has_compressed_part) {
810 return;
811 }
812 const color_t *data = &image_data(image_id)[img->draw.uncompressed_length];
813
814 int height = img->height;
815 switch (img->width) {
816 case 58:
817 y -= img->height - 30;
818 height -= 16;
819 break;
820 case 118:
821 y -= img->height - 45;
822 height -= 31;
823 break;
824 case 178:
825 y -= img->height - 60;
826 height -= 46;
827 break;
828 case 238:
829 y -= img->height - 75;
830 height -= 61;
831 break;
832 case 298:
833 y -= img->height - 90;
834 height -= 76;
835 break;
836 }
837 if (!color_mask) {
838 draw_compressed(img, data, x, y, height);
839 } else {
840 draw_compressed_and(img, data, x, y, height, color_mask);
841 }
842 }
843
color_average(const image * img,const color_t * data,int x,int y,unsigned int scale_factor)844 static color_t color_average(const image *img, const color_t *data, int x, int y, unsigned int scale_factor)
845 {
846 x *= scale_factor;
847 y *= scale_factor;
848 int rb = 0, g = 0;
849 int num_colors = 0;
850 int num_transparent = 0;
851 int max_x = x + scale_factor;
852 int max_y = y + scale_factor;
853 while (y < max_y) {
854 if (y == img->height) {
855 break;
856 }
857 int current_x = x;
858 while (current_x < max_x) {
859 if (current_x == img->width) {
860 break;
861 }
862 color_t color = data[y * img->width + current_x];
863 if (color == COLOR_SG2_TRANSPARENT) {
864 num_transparent++;
865 } else {
866 // Note: keeping the R and B channels on the same int limits scale_factor to a maximum of 16
867 rb += color & 0xff00ff;
868 g += color & 0xff00;
869 num_colors++;
870 }
871 current_x++;
872 }
873 y++;
874 }
875 if (num_transparent > num_colors) {
876 return COLOR_SG2_TRANSPARENT;
877 }
878 return ((rb / num_colors) & 0xff0000) | ((g / num_colors) & 0xff00) | ((rb & 0xffff) / num_colors);
879 }
880
image_draw_scaled_down(int image_id,int x_offset,int y_offset,unsigned int scale_factor)881 void image_draw_scaled_down(int image_id, int x_offset, int y_offset, unsigned int scale_factor)
882 {
883 const image *img = image_get(image_id);
884 const color_t *data = image_data(image_id);
885
886 if (!data || img->draw.type == IMAGE_TYPE_ISOMETRIC || img->draw.is_fully_compressed || !scale_factor) {
887 return;
888 }
889
890 int width = img->width / scale_factor;
891 int height = img->height / scale_factor;
892
893 if (!width || !height) {
894 return;
895 }
896
897 const clip_info *clip = graphics_get_clip_info(x_offset, y_offset, width, height);
898 if (!clip->is_visible) {
899 return;
900 }
901 for (int y = clip->clipped_pixels_top; y < height - clip->clipped_pixels_bottom; y++) {
902 color_t *dst = graphics_get_pixel(x_offset + clip->clipped_pixels_left, y_offset + y);
903 int x_max = width - clip->clipped_pixels_right;
904
905 for (int x = clip->clipped_pixels_left; x < x_max; x++, dst++) {
906 *dst = color_average(img, data, x, y, scale_factor);
907 }
908 }
909 }
910