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