GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/display_extensions.c
Date: 2025-08-08 18:10:24
Exec Total Coverage
Lines: 1543 1640 94.1%
Functions: 70 74 94.6%
Branches: 1026 1279 80.2%

Line Branch Exec Source
1 /*
2 Copyright 2023 - 2025 Benjamin Vedder benjamin@vedder.se
3 Copyright 2023 - 2025 Joel Svensson svenssonjoel@yahoo.se
4 Copyright 2023 Rasmus Söderhielm rasmus.soderhielm@gmail.com
5 Copyright 2025 Joakim Lundborg joakim.lundborg@gmail.com
6
7 This file is part of LispBM. (Originally a part of the vesc_express FW)
8
9 LispBM is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 LispBM is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "sys/types.h"
24 #include "tjpgd.h"
25
26 #include <math.h>
27
28 #include <extensions/display_extensions.h>
29 #include <lbm_utils.h>
30 #include <lbm_defrag_mem.h>
31
32 #ifdef LBM_OPT_DISPLAY_EXTENSIONS_SIZE
33 #pragma GCC optimize ("-Os")
34 #endif
35 #ifdef LBM_OPT_DISPLAY_EXTENSIONS_SIZE_AGGRESSIVE
36 #pragma GCC optimize ("-Oz")
37 #endif
38
39
40 #define MAX_WIDTH 32000
41 #define MAX_HEIGHT 32000
42
43 // a single quadrant...
44 static const uint8_t cos_tab_128[] =
45 {
46 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, // 0 - 9
47 251, 250, 250, 249, 248, 246, 245, 244, 243, 241, //10 - 19
48 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, //20 - 29
49 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, //30 - 39
50 198, 196, 193, 190, 188, 185, 182, 179, 176, 173, //40 - 49
51 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, //50 - 59
52 140, 137, 134, 131, 127, 124, 121, 118, 115, 112, //60 - 69
53 109, 106, 103, 100, 97, 93, 90, 88, 85, 82, //70 - 79
54 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, //80 - 89
55 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, //90 - 99
56 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, //100 - 109
57 12, 11, 10, 9, 7, 6, 5, 5, 4, 3, //110 - 119
58 2, 2, 1, 1, 1, 0, 0, 0 //120 - 127
59 };
60
61 1124 uint32_t lbm_display_rgb888_from_color(color_t color, int x, int y) {
62
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 1124 times.
✗ Branch 2 not taken.
1124 switch (color.type) {
63 case COLOR_REGULAR:
64 return (uint32_t)color.color1;
65
66 1124 case COLOR_GRADIENT_X:
67 case COLOR_GRADIENT_Y: {
68 uint32_t res;
69 1124 uint32_t r1 = (uint32_t)color.color1 >> 16;
70 1124 uint32_t g1 = (uint32_t)color.color1 >> 8 & 0xFF;
71 1124 uint32_t b1 = (uint32_t)color.color1 & 0xff;
72
73 1124 uint32_t r2 = (uint32_t)color.color2 >> 16;
74 1124 uint32_t g2 = (uint32_t)color.color2 >> 8 & 0xFF;
75 1124 uint32_t b2 = (uint32_t)color.color2 & 0xff;
76
77
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1074 times.
1124 int used_len = color.mirrored ? 256 : 128;
78
79
2/2
✓ Branch 0 taken 1094 times.
✓ Branch 1 taken 30 times.
1124 int pos = color.type == COLOR_GRADIENT_X ? x : y;
80 // int tab_pos = ((pos * 256) / color.param1 + color.param2) % 256;
81 1124 int tab_pos = (((pos - color.param2) * 256) / color.param1 / 2) % used_len;
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1124 times.
1124 if (tab_pos < 0) {
83 tab_pos += used_len;
84 }
85
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1124 times.
1124 uint32_t tab_val = (uint32_t)cos_tab_128[tab_pos <= 127 ? tab_pos : 128 - (tab_pos - 127)];
87
88 1124 uint32_t r = (r1 * tab_val + r2 * (255 - tab_val)) / 255;
89 1124 uint32_t g = (g1 * tab_val + g2 * (255 - tab_val)) / 255;
90 1124 uint32_t b = (b1 * tab_val + b2 * (255 - tab_val)) / 255;
91
92 1124 res = r << 16 | g << 8 | b;
93 1124 return res;
94 }
95
96 default:
97 return 0;
98 }
99 }
100
101 static lbm_uint symbol_indexed2 = 0;
102 static lbm_uint symbol_indexed4 = 0;
103 static lbm_uint symbol_indexed16 = 0;
104 static lbm_uint symbol_rgb332 = 0;
105 static lbm_uint symbol_rgb565 = 0;
106 static lbm_uint symbol_rgb888 = 0;
107
108 static lbm_uint symbol_thickness = 0;
109 static lbm_uint symbol_filled = 0;
110 static lbm_uint symbol_rounded = 0;
111 static lbm_uint symbol_dotted = 0;
112 static lbm_uint symbol_scale = 0;
113 static lbm_uint symbol_rotate = 0;
114 static lbm_uint symbol_resolution = 0;
115 static lbm_uint symbol_tile = 0;
116 static lbm_uint symbol_clip = 0;
117
118
119 static lbm_uint symbol_regular = 0;
120 static lbm_uint symbol_gradient_x = 0;
121 static lbm_uint symbol_gradient_y = 0;
122 static lbm_uint symbol_gradient_x_pre = 0;
123 static lbm_uint symbol_gradient_y_pre = 0;
124 static lbm_uint symbol_repeat = 0;
125 static lbm_uint symbol_mirrored = 0;
126
127 static lbm_uint symbol_color_0 = 0;
128 static lbm_uint symbol_color_1 = 0;
129 static lbm_uint symbol_width = 0;
130 static lbm_uint symbol_offset = 0;
131 static lbm_uint symbol_repeat_type = 0;
132
133 static lbm_uint symbol_down = 0;
134 static lbm_uint symbol_up = 0;
135
136 24 bool display_is_symbol_up(lbm_value v) {
137
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (lbm_is_symbol(v)) {
138 24 lbm_uint s = lbm_dec_sym(v);
139 24 return (s == symbol_up);
140 }
141 return false;
142 }
143
144 24 bool display_is_symbol_down(lbm_value v) {
145
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (lbm_is_symbol(v)) {
146 24 lbm_uint s = lbm_dec_sym(v);
147 24 return (s == symbol_down);
148 }
149 return false;
150 }
151
152 91 color_format_t sym_to_color_format(lbm_value v) {
153 91 lbm_uint s = lbm_dec_sym(v);
154
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 65 times.
91 if (s == symbol_indexed2) return indexed2;
155
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 34 times.
65 if (s == symbol_indexed4) return indexed4;
156
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 19 times.
34 if (s == symbol_indexed16) return indexed16;
157
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
19 if (s == symbol_rgb332) return rgb332;
158
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 11 times.
15 if (s == symbol_rgb565) return rgb565;
159
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
11 if (s == symbol_rgb888) return rgb888;
160 3 return format_not_supported;
161 }
162
163 2808 uint32_t image_dims_to_size_bytes(color_format_t fmt, uint16_t width, uint16_t height) {
164 2808 uint32_t num_pix = (uint32_t)width * (uint32_t)height;
165
7/7
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 2627 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 4 times.
2808 switch(fmt) {
166 111 case indexed2:
167
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 54 times.
111 if (num_pix % 8 != 0) return (num_pix / 8) + 1;
168 54 else return (num_pix / 8);
169 break;
170 2627 case indexed4:
171
2/2
✓ Branch 0 taken 733 times.
✓ Branch 1 taken 1894 times.
2627 if (num_pix % 4 != 0) return (num_pix / 4) + 1;
172 1894 else return (num_pix / 4);
173 break;
174 28 case indexed16: // Two pixels per byte
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (num_pix % 2 != 0) return (num_pix / 2) + 1;
176 28 else return (num_pix / 2);
177 8 case rgb332:
178 8 return num_pix;
179 break;
180 6 case rgb565:
181 6 return num_pix * 2;
182 break;
183 24 case rgb888:
184 24 return num_pix * 3;
185 4 default:
186 4 return 0;
187 }
188 }
189
190 70 static lbm_value image_buffer_lift(uint8_t *buf, color_format_t fmt, uint16_t width, uint16_t height) {
191 70 lbm_value res = ENC_SYM_MERROR;
192 70 lbm_uint size = image_dims_to_size_bytes(fmt, width, height);
193
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 if ( lbm_lift_array(&res, (char*)buf, IMAGE_BUFFER_HEADER_SIZE + size)) {
194 70 buf[0] = (uint8_t)(width >> 8);
195 70 buf[1] = (uint8_t)width;
196 70 buf[2] = (uint8_t)(height >> 8);
197 70 buf[3] = (uint8_t)height;
198 70 buf[4] = color_format_to_byte(fmt);
199 }
200 70 return res;
201 }
202
203 147 static inline bool is_color_sized(lbm_uint size) {
204 147 size_t color_size = sizeof(color_t);
205 147 return (size == color_size);
206 }
207
208 71 static inline bool is_color(uint8_t *data, lbm_uint size) {
209 71 bool res = false;
210
1/2
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
71 if (is_color_sized(size)) {
211 71 color_t *color = (color_t*)data;
212 71 res = (color->magic == COLOR_MAGIC);
213 }
214 71 return res;
215 }
216
217
218 26 static lbm_value color_allocate(COLOR_TYPE type, int32_t color1, int32_t color2, uint16_t param1, uint16_t param2, bool mirrored) {
219 26 color_t *color = lbm_malloc(sizeof(color_t));
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (!color) {
221 return ENC_SYM_MERROR;
222 }
223
224 26 uint32_t *pre = 0;
225
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 21 times.
26 if (type == COLOR_PRE_X || type == COLOR_PRE_Y) {
226 5 pre = lbm_malloc(COLOR_PRECALC_LEN * sizeof(uint32_t));
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!pre) {
228 lbm_free(color);
229 return ENC_SYM_MERROR;
230 }
231 }
232
233 26 lbm_value res = ENC_SYM_MERROR;
234
235
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if (lbm_lift_array(&res, (char*)color, sizeof(color_t))) {
236 26 color->magic = COLOR_MAGIC;
237 26 color->type = type;
238 26 color->color1 = color1;
239 26 color->color2 = color2;
240 26 color->param1 = param1;
241 26 color->param2 = param2;
242 26 color->mirrored = mirrored;
243 26 color->precalc = pre;
244
245
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 21 times.
26 if (pre) {
246 5 COLOR_TYPE type_old = color->type;
247
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (type == COLOR_PRE_X) {
248 4 color->type = COLOR_GRADIENT_X;
249
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 } else if (type == COLOR_PRE_Y) {
250 1 color->type = COLOR_GRADIENT_Y;
251 }
252
253
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (color->param1 > COLOR_PRECALC_LEN) {
254 1 color->param1 = COLOR_PRECALC_LEN;
255 }
256
257
2/2
✓ Branch 0 taken 1124 times.
✓ Branch 1 taken 5 times.
1129 for (int i = 0;i < color->param1;i++) {
258 1124 pre[i] = lbm_display_rgb888_from_color(*color, i + color->param2, i + color->param2);
259 }
260
261 5 color->type = type_old;
262 }
263 } else {
264 lbm_free(pre);
265 lbm_free(color);
266 }
267
268 26 return res;
269 }
270
271 70 static lbm_value image_buffer_allocate(color_format_t fmt, uint16_t width, uint16_t height) {
272 70 uint32_t size_bytes = image_dims_to_size_bytes(fmt, width, height);
273
274 70 uint8_t *buf = lbm_malloc(IMAGE_BUFFER_HEADER_SIZE + size_bytes);
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!buf) {
276 return ENC_SYM_MERROR;
277 }
278 70 memset(buf, 0, size_bytes + IMAGE_BUFFER_HEADER_SIZE);
279 70 lbm_value res = image_buffer_lift(buf, fmt, width, height);
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (lbm_is_symbol(res)) { /* something is wrong, free */
281 lbm_free(buf);
282 }
283 70 return res;
284 }
285
286 1 static lbm_value image_buffer_allocate_dm(lbm_uint *dm, color_format_t fmt, uint16_t width, uint16_t height) {
287 1 uint32_t size_bytes = image_dims_to_size_bytes(fmt, width, height);
288
289 1 lbm_value res = lbm_defrag_mem_alloc(dm, IMAGE_BUFFER_HEADER_SIZE + size_bytes);
290 1 lbm_array_header_t *arr = lbm_dec_array_r(res);
291
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (arr) {
292 1 uint8_t *buf = (uint8_t*)arr->data;
293 1 buf[0] = (uint8_t)(width >> 8);
294 1 buf[1] = (uint8_t)width;
295 1 buf[2] = (uint8_t)(height >> 8);
296 1 buf[3] = (uint8_t)height;
297 1 buf[4] = color_format_to_byte(fmt);
298 }
299 1 return res;
300 }
301
302 // Exported interface
303 bool display_is_color(lbm_value v) {
304 lbm_array_header_t *array = lbm_dec_array_r(v);
305 bool res = false;
306 if (array && is_color_sized(array->size)) {
307 res = (is_color((uint8_t*)array->data, array->size));
308 }
309 return res;
310 }
311
312 77 static color_t *get_color(lbm_value v) {
313 77 color_t *res = NULL;
314 77 lbm_array_header_t *array = lbm_dec_array_r(v);
315
4/4
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 5 times.
77 if (array && is_color_sized(array->size)
316
1/2
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
71 && (is_color((uint8_t*)array->data, array->size))) {
317 71 res = (color_t*)array->data;
318 }
319 77 return res;
320 }
321
322
323 // Register symbols
324
325 411 static bool register_symbols(void) {
326 411 bool res = true;
327
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("indexed2", &symbol_indexed2);
328
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("indexed4", &symbol_indexed4);
329
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("indexed16", &symbol_indexed16);
330
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("rgb332", &symbol_rgb332);
331
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("rgb565", &symbol_rgb565);
332
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("rgb888", &symbol_rgb888);
333
334
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("thickness", &symbol_thickness);
335
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("filled", &symbol_filled);
336
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("rounded", &symbol_rounded);
337
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("dotted", &symbol_dotted);
338
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("scale", &symbol_scale);
339
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("rotate", &symbol_rotate);
340
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("resolution", &symbol_resolution);
341
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("tile", &symbol_tile);
342
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("clip", &symbol_clip);
343
344
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("regular", &symbol_regular);
345
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("gradient_x", &symbol_gradient_x);
346
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("gradient_y", &symbol_gradient_y);
347
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("gradient_x_pre", &symbol_gradient_x_pre);
348
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("gradient_y_pre", &symbol_gradient_y_pre);
349
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("mirrored", &symbol_mirrored);
350
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("repeat", &symbol_repeat);
351
352
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("color-0", &symbol_color_0);
353
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("color-1", &symbol_color_1);
354
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("width", &symbol_width);
355
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("offset", &symbol_offset);
356
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("repeat-type", &symbol_repeat_type);
357
358
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("down", &symbol_down);
359
2/4
✓ Branch 0 taken 411 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 411 times.
✗ Branch 3 not taken.
411 res = res && lbm_add_symbol_const("up", &symbol_up);
360
361 411 return res;
362 }
363
364 // Internal functions
365
366 975723 static int sign(int v) {
367
2/2
✓ Branch 0 taken 454833 times.
✓ Branch 1 taken 520890 times.
975723 if (v > 0) {
368 454833 return 1;
369
2/2
✓ Branch 0 taken 374010 times.
✓ Branch 1 taken 146880 times.
520890 } else if (v < 0) {
370 374010 return -1;
371 } else {
372 146880 return 0;
373 }
374 }
375
376 // Geometry utility functions
377
378 // Checks if a point is past a line formed by the given end and start points.
379 // The returned value is 1 if it is past, -1 if it's on the other side of the
380 // line, or 0 if it's exactly on the line.
381 // Don't ask me what is considered the "positive" side of the line ;)
382 //
383 // It would probably be more logical if the sign of the result was flipped...
384 202230 static int point_past_line(int x, int y, int line_start_x, int line_start_y, int line_end_x, int line_end_y) {
385 // source: https://stackoverflow.com/a/11908158/15507414
386
387 // this is not really a cross product, but whatever...
388 202230 int cross_prod = (x - line_start_x) * (line_end_y - line_start_y)
389 202230 - (y - line_start_y) * (line_end_x - line_start_x);
390
391
2/2
✓ Branch 0 taken 86633 times.
✓ Branch 1 taken 115597 times.
202230 if (cross_prod > 0) {
392 86633 return 1;
393
2/2
✓ Branch 0 taken 111636 times.
✓ Branch 1 taken 3961 times.
115597 } else if (cross_prod < 0) {
394 111636 return -1;
395 } else {
396 3961 return 0;
397 }
398 }
399
400 151294 static bool points_same_quadrant(int x0, int y0, int x1, int y1) {
401
4/4
✓ Branch 0 taken 90941 times.
✓ Branch 1 taken 3548 times.
✓ Branch 2 taken 30404 times.
✓ Branch 3 taken 60537 times.
245783 return (sign(x0) == sign(x1) || sign(x0) == 0 || sign(x1) == 0)
402
8/8
✓ Branch 0 taken 94489 times.
✓ Branch 1 taken 56805 times.
✓ Branch 2 taken 47572 times.
✓ Branch 3 taken 43185 times.
✓ Branch 4 taken 42631 times.
✓ Branch 5 taken 4941 times.
✓ Branch 6 taken 18510 times.
✓ Branch 7 taken 24121 times.
245783 && (sign(y0) == sign(y1) || sign(y0) == 0 || sign(y1) == 0);
403 }
404
405 24 static inline void norm_angle(float *angle) {
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 while (*angle < -M_PI) { *angle += 2.0f * (float)M_PI; }
407
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 24 times.
34 while (*angle >= M_PI) { *angle -= 2.0f * (float)M_PI; }
408 24 }
409
410 1032 static inline void norm_angle_0_2pi(float *angle) {
411
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1032 times.
1043 while (*angle < 0) { *angle += 2.0f * (float)M_PI; }
412
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1032 times.
1064 while (*angle >= 2.0 * M_PI) { *angle -= 2.0f * (float)M_PI; }
413 1032 }
414
415 1737 static uint8_t rgb888to332(uint32_t rgb) {
416 1737 uint8_t r = (uint8_t)(rgb >> (16 + 5));
417 1737 uint8_t g = (uint8_t)(rgb >> (8 + 5));
418 1737 uint8_t b = (uint8_t)(rgb >> 6);
419 1737 r = (r & 0x7) << 5;
420 1737 g = (g & 0x7) << 2;
421 1737 b = (b & 0x3);
422 1737 uint8_t res_rgb332 = r | g | b;
423 1737 return res_rgb332;
424 }
425
426 1949 static uint16_t rgb888to565(uint32_t rgb) {
427 1949 uint16_t r = (uint16_t)(rgb >> (16 + 3));
428 1949 uint16_t g = (uint16_t)(rgb >> (8 + 2));
429 1949 uint16_t b = (uint16_t)(rgb >> 3);
430 1949 r = r << 11;
431 1949 g = (g & 0x3F) << 5;
432 1949 b = (b & 0x1F);
433 1949 uint16_t res_rgb565 = r | g | b;
434 1949 return res_rgb565;
435 }
436
437 // One problem with rgb332 is that
438 // if you take 3 most significant bits of 255 you get 7.
439 // There is no whole number that you can multiply 7 with to get 255.
440 // This is fundamental for any conversion from RGB888 that just uses the
441 // N < 8 most significant bits. And it means that conversion to this format
442 // and then back to rgb888 will not (without tricks) map highest intensity
443 // back to highest intensity.
444 //
445 // Another issue is that 2 bits (the blue channel) yields steps of 85 (255 / 3)
446 // while 3 bits yields steps of 36.4 (255 / 7)
447 //
448 // 36.4 72.8 109.3 145.7 182.1 218.6 254.99
449 // 85 170 255
450 //
451 // The multiples of 85 never coincide with the multiples of 36.4 except
452 // for at 0 and 255
453 16 static uint32_t rgb332to888(uint8_t rgb) {
454 16 uint32_t r = (uint32_t)((rgb>>5) & 0x7);
455 16 uint32_t g = (uint32_t)((rgb>>2) & 0x7);
456
457 // turn 2 bits into 3 having value 0 3 5 or 7
458 // so that 4 points match up when doing greyscale.
459 16 uint32_t b = (uint32_t)(rgb & 0x3);
460
461
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
16 b = (b > 0) ? (2 * b) + 1 : 0;
462
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 r = (r == 7) ? 255 : 36 * r; // 36 is an approximation (36.4)
463
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 g = (g == 7) ? 255 : 36 * g;
464
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 b = (b == 7) ? 255 : 36 * b;
465 16 uint32_t res_rgb888 = r << 16 | g << 8 | b;
466 16 return res_rgb888;
467 }
468
469 // RGB 565
470 // 2^5 = 32
471 // 2^6 = 64
472 // 255 / 31 = 8.226
473 // 255 / 63 = 4.18
474 // 0 1 2 3 4 5 6 7 8 ... 31 63
475 // 5 bits 0 8.226 16.45 24.67 32.9 41.13 49.35 57.58 65.81 ... 254.9
476 // 6 bits 0 4.047 8.09 12.14 16.19 20.24 24.29 28.33 32.38 ... 254.9
477 //
478 // For RGB 565 the 6 and 5 bit channels match up very nicely such
479 // index i in the 5 bit channel is equal to index (2 * i) in the 6 bit channel.
480 // RGB 565 will have nice grayscales.
481
482 22 static uint32_t rgb565to888(uint16_t rgb) {
483 22 uint32_t r = (uint32_t)(rgb >> 11);
484 22 uint32_t g = (uint32_t)((rgb >> 5) & 0x3F);
485 22 uint32_t b = (uint32_t)(rgb & 0x1F);
486 22 uint32_t res_rgb888 = r << (16 + 3) | g << (8 + 2) | b << 3;
487 22 return res_rgb888;
488 }
489
490 54 void image_buffer_clear(image_buffer_t *img, uint32_t cc) {
491 54 color_format_t fmt = img->fmt;
492 54 uint32_t w = img->width;
493 54 uint32_t h = img->height;
494 54 uint32_t img_size = w * h;
495 54 uint8_t *data = img->data;
496
6/7
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
54 switch (fmt) {
497 4 case indexed2: {
498
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 uint32_t bytes = (img_size / 8) + (img_size % 8 ? 1 : 0);
499
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 uint8_t c8 = (uint8_t)((cc & 1) ? 0xFF : 0x0);
500 4 memset(data, c8, bytes);
501 }
502 4 break;
503 12 case indexed4: {
504 static const uint8_t index4_table[4] = {0x00, 0x55, 0xAA, 0xFF};
505
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 uint32_t bytes = (img_size / 4) + (img_size % 4 ? 1 : 0);
506 12 uint8_t ix = (uint8_t)(cc & 0x3);
507 12 memset(data, index4_table[ix], bytes);
508 }
509 12 break;
510 18 case indexed16: {
511 18 uint32_t bytes = (img_size / 2) + (img_size % 2 ? 1 : 0);
512 18 uint8_t ix = (uint8_t)(cc & 0xF);
513 18 uint8_t color = (uint8_t)(ix | ix << 4); // create a color based on duplication of index
514 18 memset(data, color, bytes);
515 }
516 18 break;
517 7 case rgb332: {
518 7 memset(data, rgb888to332(cc), img_size);
519 }
520 7 break;
521 6 case rgb565: {
522 6 uint16_t c = rgb888to565(cc);
523 6 uint8_t *dp = (uint8_t*)data;
524
2/2
✓ Branch 0 taken 45000 times.
✓ Branch 1 taken 6 times.
45006 for (unsigned int i = 0; i < img_size/2; i +=2) {
525 45000 dp[i] = (uint8_t)(c >> 8);
526 45000 dp[i+1] = (uint8_t)c;
527 }
528 }
529 6 break;
530 7 case rgb888: {
531 7 uint8_t *dp = (uint8_t*)data;
532
2/2
✓ Branch 0 taken 200000 times.
✓ Branch 1 taken 7 times.
200007 for (unsigned int i = 0; i < img_size * 3; i+= 3) {
533 200000 dp[i] = (uint8_t)(cc >> 16);
534 200000 dp[i+1] = (uint8_t)(cc >> 8);
535 200000 dp[i+2] = (uint8_t)cc;
536 }
537 }
538 7 break;
539 default:
540 break;
541 }
542 54 }
543
544 static const uint8_t indexed4_mask[4] = {0x03, 0x0C, 0x30, 0xC0};
545 static const uint8_t indexed4_shift[4] = {0, 2, 4, 6};
546 static const uint8_t indexed16_mask[4] = {0x0F, 0xF0};
547 static const uint8_t indexed16_shift[4] = {0, 4};
548
549
550 22528621 void putpixel(image_buffer_t* img, int x_i, int y_i, uint32_t c) {
551 22528621 uint16_t w = img->width;
552 22528621 uint16_t h = img->height;
553 22528621 uint16_t x = (uint16_t)x_i; // negative numbers become really large.
554 22528621 uint16_t y = (uint16_t)y_i;
555
556
4/4
✓ Branch 0 taken 22490915 times.
✓ Branch 1 taken 37706 times.
✓ Branch 2 taken 19971133 times.
✓ Branch 3 taken 2519782 times.
22528621 if (x < w && y < h) {
557 19971133 color_format_t fmt = img->fmt;
558 19971133 uint8_t *data = img->data;
559
6/7
✓ Branch 0 taken 75266 times.
✓ Branch 1 taken 17820783 times.
✓ Branch 2 taken 2047644 times.
✓ Branch 3 taken 1730 times.
✓ Branch 4 taken 1943 times.
✓ Branch 5 taken 23767 times.
✗ Branch 6 not taken.
19971133 switch(fmt) {
560 75266 case indexed2: {
561 75266 uint32_t pos = (uint32_t)y * (uint32_t)w + (uint32_t)x;
562 75266 uint32_t byte = pos >> 3;
563 75266 uint32_t bit = 7 - (pos & 0x7);
564
2/2
✓ Branch 0 taken 6255 times.
✓ Branch 1 taken 69011 times.
75266 if (c) {
565 6255 data[byte] |= (uint8_t)(1 << bit);
566 } else {
567 69011 data[byte] &= (uint8_t)~(1 << bit);
568 }
569 75266 break;
570 }
571 17820783 case indexed4: {
572 17820783 uint32_t pos = (uint32_t)y*w + x;
573 17820783 uint32_t byte = pos >> 2;
574 17820783 uint32_t ix = 3 - (pos & 0x3);
575 17820783 data[byte] = (uint8_t)((uint8_t)(data[byte] & ~indexed4_mask[ix]) | (uint8_t)(c << indexed4_shift[ix]));
576 17820783 break;
577 }
578 2047644 case indexed16: {
579 2047644 uint32_t pos = (uint32_t)y*w + x;
580 2047644 uint32_t byte = pos >> 1;
581 2047644 uint32_t ix = 1 - (pos & 0x1);
582 2047644 data[byte] = (uint8_t)((uint8_t)(data[byte] & ~indexed16_mask[ix]) | (uint8_t)(c << indexed16_shift[ix]));
583 2047644 break;
584 }
585 1730 case rgb332: {
586 1730 int pos = y*w + x;
587 1730 data[pos] = rgb888to332(c);
588 1730 break;
589 }
590 1943 case rgb565: {
591 1943 int pos = y*(w<<1) + (x<<1) ;
592 1943 uint16_t color = rgb888to565(c);
593 1943 data[pos] = (uint8_t)(color >> 8);
594 1943 data[pos+1] = (uint8_t)color;
595 1943 break;
596 }
597 23767 case rgb888: {
598 23767 int pos = y*(w*3) + (x*3);
599 23767 data[pos] = (uint8_t)(c>>16);
600 23767 data[pos+1] = (uint8_t)(c>>8);
601 23767 data[pos+2] = (uint8_t)c;
602 23767 break;
603 }
604 default:
605 break;
606 }
607 }
608 22528621 }
609
610 1103765 uint32_t getpixel(image_buffer_t* img, int x_i, int y_i) {
611 1103765 uint16_t w = img->width;
612 1103765 uint16_t h = img->height;
613 1103765 uint16_t x = (uint16_t)x_i;
614 1103765 uint16_t y = (uint16_t)y_i;
615
616
3/4
✓ Branch 0 taken 1103753 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1103753 times.
✗ Branch 3 not taken.
1103765 if (x < w && y < h) {
617 1103753 color_format_t fmt = img->fmt;
618 1103753 uint8_t *data = img->data;
619
6/7
✓ Branch 0 taken 61017 times.
✓ Branch 1 taken 1042640 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
1103753 switch(fmt) {
620 61017 case indexed2: {
621 61017 uint32_t pos = (uint32_t)y * w + x;
622 61017 uint32_t byte = pos >> 3;
623 61017 uint32_t bit = 7 - (pos & 0x7);
624 61017 return (uint32_t)(data[byte] >> bit) & 0x1;
625 }
626 1042640 case indexed4: {
627 1042640 uint32_t pos = (uint32_t)y*w + x;
628 1042640 uint32_t byte = pos >> 2;
629 1042640 uint32_t ix = 3 - (pos & 0x3);
630 1042640 return (uint32_t)((data[byte] & indexed4_mask[ix]) >> indexed4_shift[ix]);
631 }
632 30 case indexed16: {
633 30 uint32_t pos = (uint32_t)y*w + x;
634 30 uint32_t byte = pos >> 1;
635 30 uint32_t ix = 1 - (pos & 0x1);
636 30 return (uint32_t)((data[byte] & indexed16_mask[ix]) >> indexed16_shift[ix]);
637 }
638 16 case rgb332: {
639 16 int pos = y*w + x;
640 16 return rgb332to888(data[pos]);
641 }
642 22 case rgb565: {
643 22 int pos = y*(w<<1) + (x<<1);
644 22 uint16_t c = (uint16_t)(((uint16_t)data[pos] << 8) | (uint16_t)data[pos+1]);
645 22 return rgb565to888(c);
646 }
647 28 case rgb888: {
648 28 int pos = y*(w*3) + (x*3);
649 28 uint32_t r = data[pos];
650 28 uint32_t g = data[pos+1];
651 28 uint32_t b = data[pos+2];
652 28 return (r << 16 | g << 8 | b);
653 }
654 default:
655 break;
656 }
657 }
658 12 return 0;
659 }
660
661 342289 static void h_line(image_buffer_t* img, int x, int y, int len, uint32_t c) {
662
2/2
✓ Branch 0 taken 21197947 times.
✓ Branch 1 taken 342289 times.
21540236 for (int i = 0; i < len; i ++) {
663 21197947 putpixel(img, x+i, y, c);
664 }
665 342289 }
666
667 88 static void v_line(image_buffer_t* img, int x, int y, int len, uint32_t c) {
668
2/2
✓ Branch 0 taken 2840 times.
✓ Branch 1 taken 88 times.
2928 for (int i = 0; i < len; i ++) {
669 2840 putpixel(img, x, y+i, c);
670 }
671 88 }
672
673 17192 static void fill_circle(image_buffer_t *img, int x, int y, int radius, uint32_t color) {
674
6/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 2324 times.
✓ Branch 3 taken 3141 times.
✓ Branch 4 taken 2795 times.
✓ Branch 5 taken 8877 times.
17192 switch (radius) {
675 18 case 0:
676 18 break;
677
678 37 case 1:
679 37 putpixel(img, x - 1, y - 1, color);
680 37 putpixel(img, x, y - 1, color);
681 37 putpixel(img, x - 1, y, color);
682 37 putpixel(img, x, y, color);
683 37 break;
684
685 2324 case 2:
686 2324 h_line(img, x - 1, y - 2, 2, color);
687 2324 h_line(img, x - 2, y - 1, 4, color);
688 2324 h_line(img, x - 2, y, 4, color);
689 2324 h_line(img, x - 1, y + 1, 2, color);
690 2324 break;
691
692 3141 case 3:
693 3141 h_line(img, x - 2, y - 3, 4, color);
694 3141 h_line(img, x - 3, y - 2, 6, color);
695 3141 h_line(img, x - 3, y - 1, 6, color);
696 3141 h_line(img, x - 3, y, 6, color);
697 3141 h_line(img, x - 3, y + 1, 6, color);
698 3141 h_line(img, x - 2, y + 2, 4, color);
699 3141 break;
700
701 2795 case 4:
702 2795 h_line(img, x - 2, y - 4, 4, color);
703 2795 h_line(img, x - 3, y - 3, 6, color);
704 2795 h_line(img, x - 4, y - 2, 8, color);
705 2795 h_line(img, x - 4, y - 1, 8, color);
706 2795 h_line(img, x - 4, y, 8, color);
707 2795 h_line(img, x - 4, y + 1, 8, color);
708 2795 h_line(img, x - 3, y + 2, 6, color);
709 2795 h_line(img, x - 2, y + 3, 4, color);
710 2795 break;
711
712 8877 default: {
713 8877 int r_sq = radius * radius;
714
2/2
✓ Branch 0 taken 260341 times.
✓ Branch 1 taken 8877 times.
269218 for (int y1 = -radius; y1 <= radius; y1++) {
715
1/2
✓ Branch 0 taken 3291357 times.
✗ Branch 1 not taken.
3291357 for (int x1 = -radius; x1 <= radius; x1++) {
716
2/2
✓ Branch 0 taken 260341 times.
✓ Branch 1 taken 3031016 times.
3291357 if (x1 * x1 + y1 * y1 <= r_sq) {
717 // Compute the start and end position for x axis
718 260341 int x_left = x1;
719
4/4
✓ Branch 0 taken 20283832 times.
✓ Branch 1 taken 8877 times.
✓ Branch 2 taken 20032368 times.
✓ Branch 3 taken 251464 times.
20292709 while ((x1 + 1) <= radius && ((x1 + 1) * (x1 + 1) + y1 * y1) <= r_sq) {
720 20032368 x1++;
721 }
722 260341 int x_right = x1;
723
724 // Draw line at this level y
725 260341 int length = x_right - x_left + 1;
726 260341 h_line(img, x + x_left, y + y1, length, color);
727
728 // Break out of innter loop for this level y
729 260341 break;
730 }
731 }
732 }
733 8877 } break;
734 }
735 17192 }
736
737 // Circle helper function, to draw a circle with an inner and outer radius.
738 // Draws the slice at the given outer radius point.
739 2332 static void handle_circle_slice(int outer_x, int outer_y, image_buffer_t *img, int c_x, int c_y, int radius_inner, uint32_t color, int radius_inner_dbl_sq) {
740 int width;
741
742 bool slice_filled;
743
2/2
✓ Branch 0 taken 1166 times.
✓ Branch 1 taken 1166 times.
2332 if (outer_y < 0) {
744 1166 slice_filled = -outer_y > radius_inner;
745 } else {
746 1166 slice_filled = outer_y >= radius_inner;
747 }
748
749
2/2
✓ Branch 0 taken 716 times.
✓ Branch 1 taken 1616 times.
2332 if (slice_filled) {
750
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 358 times.
716 if (outer_x < 0) {
751 358 width = -outer_x;
752 } else {
753 358 width = outer_x + 1;
754 358 outer_x = 0;
755 }
756 } else {
757 1616 int cur_x = outer_x;
758
2/2
✓ Branch 0 taken 808 times.
✓ Branch 1 taken 808 times.
1616 int delta = outer_x > 0 ? -1 : 1;
759
760 // TODO: this could probably be binary searched
761 1616 int y_dbl_off = outer_y * 2 + 1;
762 1616 int y_dbl_off_sq = y_dbl_off * y_dbl_off;
763 6848 while (true) {
764 8464 cur_x += delta;
765 8464 int x_dbl_off = cur_x * 2 + 1;
766
2/2
✓ Branch 0 taken 6848 times.
✓ Branch 1 taken 1616 times.
8464 if (x_dbl_off * x_dbl_off + y_dbl_off_sq <= radius_inner_dbl_sq
767
1/2
✓ Branch 0 taken 6848 times.
✗ Branch 1 not taken.
6848 || abs(cur_x) > 2000) { // failsafe
768 break;
769 }
770 }
771 1616 width = abs(cur_x - outer_x);
772
2/2
✓ Branch 0 taken 808 times.
✓ Branch 1 taken 808 times.
1616 if (outer_x > 0) {
773 808 outer_x = cur_x + 1;
774 }
775 }
776
777 2332 h_line(img, outer_x + c_x, outer_y + c_y, width, color);
778 2332 }
779
780 // thickness extends inwards from the given radius circle
781 78 static void circle(image_buffer_t *img, int x, int y, int radius, int thickness, uint32_t color) {
782
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 30 times.
78 if (thickness <= 0) {
783 48 int x0 = 0;
784 48 int y0 = radius;
785 48 int d = 5 - 4*radius;
786 48 int da = 12;
787 48 int db = 20 - 8*radius;
788
789
2/2
✓ Branch 0 taken 385 times.
✓ Branch 1 taken 48 times.
433 while (x0 < y0) {
790 385 putpixel(img, x + x0, y + y0, color);
791 385 putpixel(img, x + x0, y - y0, color);
792 385 putpixel(img, x - x0, y + y0, color);
793 385 putpixel(img, x - x0, y - y0, color);
794 385 putpixel(img, x + y0, y + x0, color);
795 385 putpixel(img, x + y0, y - x0, color);
796 385 putpixel(img, x - y0, y + x0, color);
797 385 putpixel(img, x - y0, y - x0, color);
798
2/2
✓ Branch 0 taken 215 times.
✓ Branch 1 taken 170 times.
385 if (d < 0) { d = d + da; db = db+8; }
799 170 else { y0 = y0 - 1; d = d+db; db = db + 16; }
800 385 x0 = x0+1;
801 385 da = da + 8;
802 }
803 } else {
804 30 int radius_inner = radius - thickness;
805
806 30 int radius_outer_dbl_sq = radius * radius * 4;
807 30 int radius_inner_dbl_sq = radius_inner * radius_inner * 4;
808
809
2/2
✓ Branch 0 taken 583 times.
✓ Branch 1 taken 30 times.
613 for (int y0 = 0; y0 < radius; y0++) {
810 583 int y_dbl_offs = 2 * y0 + 1;
811 583 int y_dbl_offs_sq = y_dbl_offs * y_dbl_offs;
812
813
1/2
✓ Branch 0 taken 4572 times.
✗ Branch 1 not taken.
4572 for (int x0 = -radius; x0 <= 0; x0++) {
814 4572 int x_dbl_offs = 2 * x0 + 1;
815
2/2
✓ Branch 0 taken 583 times.
✓ Branch 1 taken 3989 times.
4572 if (x_dbl_offs * x_dbl_offs + y_dbl_offs_sq <= radius_outer_dbl_sq) {
816 // This is horrible...
817 583 handle_circle_slice(x0, y0,
818 img, x, y, radius_inner, color, radius_inner_dbl_sq);
819 583 handle_circle_slice(-x0 - 1, y0,
820 img, x, y, radius_inner, color, radius_inner_dbl_sq);
821 583 handle_circle_slice(x0, -y0 - 1,
822 img, x, y, radius_inner, color, radius_inner_dbl_sq);
823 583 handle_circle_slice(-x0 - 1, -y0 - 1,
824 img, x, y, radius_inner, color, radius_inner_dbl_sq);
825 583 break;
826 }
827 }
828 }
829 }
830 78 }
831
832 // Thickness extends outwards and inwards from the given line equally, resulting
833 // in double the total thickness.
834 // TODO: This should be more efficient
835 // http://homepages.enterprise.net/murphy/thickline/index.html
836 // https://github.com/ArminJo/STMF3-Discovery-Demos/blob/master/lib/BlueDisplay/LocalGUI/ThickLine.hpp
837 1030 static void line(image_buffer_t *img, int x0, int y0, int x1, int y1, int thickness, int dot1, int dot2, uint32_t c) {
838 1030 int dx = abs(x1 - x0);
839
2/2
✓ Branch 0 taken 356 times.
✓ Branch 1 taken 674 times.
1030 int sx = x0 < x1 ? 1 : -1;
840 1030 int dy = -abs(y1 - y0);
841
2/2
✓ Branch 0 taken 365 times.
✓ Branch 1 taken 665 times.
1030 int sy = y0 < y1 ? 1 : -1;
842 1030 int error = dx + dy;
843
844
2/2
✓ Branch 0 taken 547 times.
✓ Branch 1 taken 483 times.
1030 if (dot1 > 0) {
845 // These are used to deal with consecutive calls with
846 // possibly overlapping pixels.
847 static int dotcnt = 0;
848 static int x_last = 0;
849 static int y_last = 0;
850
851 while (true) {
852
2/2
✓ Branch 0 taken 1704 times.
✓ Branch 1 taken 621 times.
2325 if (dotcnt <= dot1) {
853
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 1559 times.
1704 if (thickness > 1) {
854 145 fill_circle(img, x0, y0, thickness, c);
855 } else {
856 1559 putpixel(img, x0, y0, c);
857 }
858 }
859
860
4/4
✓ Branch 0 taken 647 times.
✓ Branch 1 taken 1678 times.
✓ Branch 2 taken 249 times.
✓ Branch 3 taken 398 times.
2325 if (x0 != x_last || y0 != y_last) {
861 1927 dotcnt++;
862 }
863
864 2325 x_last = x0;
865 2325 y_last = y0;
866
867
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 2063 times.
2325 if (dotcnt >= (dot1 + dot2)) {
868 262 dotcnt = 0;
869 }
870
871
4/4
✓ Branch 0 taken 667 times.
✓ Branch 1 taken 1658 times.
✓ Branch 2 taken 418 times.
✓ Branch 3 taken 249 times.
2325 if (x0 == x1 && y0 == y1) {
872 418 break;
873 }
874
2/2
✓ Branch 0 taken 1658 times.
✓ Branch 1 taken 249 times.
1907 if ((error * 2) >= dy) {
875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1658 times.
1658 if (x0 == x1) {
876 break;
877 }
878 1658 error += dy;
879 1658 x0 += sx;
880 }
881
2/2
✓ Branch 0 taken 751 times.
✓ Branch 1 taken 1156 times.
1907 if ((error * 2) <= dx) {
882
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 686 times.
751 if (y0 == y1) {
883 65 break;
884 }
885 686 error += dx;
886 686 y0 += sy;
887 }
888 }
889 } else {
890 while (true) {
891
2/2
✓ Branch 0 taken 16926 times.
✓ Branch 1 taken 19254 times.
36180 if (thickness > 1) {
892 16926 fill_circle(img, x0, y0, thickness, c);
893 } else {
894 19254 putpixel(img, x0, y0, c);
895 }
896
897
4/4
✓ Branch 0 taken 11094 times.
✓ Branch 1 taken 25086 times.
✓ Branch 2 taken 526 times.
✓ Branch 3 taken 10568 times.
36180 if (x0 == x1 && y0 == y1) {
898 526 break;
899 }
900
2/2
✓ Branch 0 taken 23890 times.
✓ Branch 1 taken 11764 times.
35654 if ((error * 2) >= dy) {
901
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23890 times.
23890 if (x0 == x1) {
902 break;
903 }
904 23890 error += dy;
905 23890 x0 += sx;
906 }
907
2/2
✓ Branch 0 taken 15156 times.
✓ Branch 1 taken 20498 times.
35654 if ((error * 2) <= dx) {
908
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 15135 times.
15156 if (y0 == y1) {
909 21 break;
910 }
911 15135 error += dx;
912 15135 y0 += sy;
913 }
914 }
915 }
916 1030 }
917
918 // thickness extends inwards from the given rectangle edge.
919 90 static void rectangle(image_buffer_t *img, int x, int y, int width, int height,
920 bool fill, int thickness, int dot1, int dot2, uint32_t color) {
921 90 thickness /= 2;
922
923
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 68 times.
90 if (fill) {
924
2/2
✓ Branch 0 taken 760 times.
✓ Branch 1 taken 22 times.
782 for (int i = y; i < (y + height);i++) {
925 760 h_line(img, x, i, width, color);
926 }
927 } else {
928
3/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
68 if (thickness <= 0 && dot1 == 0) {
929 44 h_line(img, x, y, width, color);
930 44 h_line(img, x, y + height, width, color);
931 44 v_line(img, x, y, height, color);
932 44 v_line(img, x + width, y, height, color);
933 } else {
934 24 x += thickness;
935 24 y += thickness;
936 24 width -= thickness * 2;
937 24 height -= thickness * 2;
938 // top
939 24 line(img, x, y, x + width, y, thickness, dot1, dot2, color);
940 // bottom
941 24 line(img, x, y + height, x + width, y + height, thickness, dot1, dot2, color);
942 // left
943 24 line(img, x, y, x, y + height, thickness, dot1, dot2, color);
944 // right
945 24 line(img, x + width, y, x + width, y + height, thickness, dot1, dot2, color);
946 }
947 }
948 90 }
949
950 #define NMIN(a, b) ((a) < (b) ? (a) : (b))
951 #define NMAX(a, b) ((a) > (b) ? (a) : (b))
952
953 2 static void fill_triangle(image_buffer_t *img, int x0, int y0,
954 int x1, int y1, int x2, int y2, uint32_t color) {
955 2 int x_min = NMIN(x0, NMIN(x1, x2));
956 2 int x_max = NMAX(x0, NMAX(x1, x2));
957 2 int y_min = NMIN(y0, NMIN(y1, y2));
958 2 int y_max = NMAX(y0, NMAX(y1, y2));
959
960
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 2 times.
84 for (int y = y_min;y <= y_max;y++) {
961
2/2
✓ Branch 0 taken 5822 times.
✓ Branch 1 taken 82 times.
5904 for (int x = x_min;x <= x_max;x++) {
962 5822 int w0 = point_past_line(x, y, x1, y1, x2, y2);
963 5822 int w1 = point_past_line(x, y, x2, y2, x0, y0);
964 5822 int w2 = point_past_line(x, y, x0, y0, x1, y1);
965
966
5/6
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 5680 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 140 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
5822 if ((w0 >= 0 && w1 >= 0 && w2 >= 0)
967
5/6
✓ Branch 0 taken 5822 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4372 times.
✓ Branch 3 taken 1450 times.
✓ Branch 4 taken 2922 times.
✓ Branch 5 taken 1450 times.
5822 || (w0 <= 0 && w1 <= 0 && w2 <= 0)) {
968 2922 putpixel(img, x, y, color);
969 }
970 }
971 }
972 2 }
973
974 12 static void generic_arc(image_buffer_t *img, int x, int y, int rad, float ang_start, float ang_end,
975 int thickness, int dot1, int dot2, int res, bool sector, bool segment, uint32_t color) {
976 12 ang_start *= (float)M_PI / 180.0f;
977 12 ang_end *= (float)M_PI / 180.0f;
978
979 12 norm_angle(&ang_start);
980 12 norm_angle(&ang_end);
981
982 12 float ang_range = ang_end - ang_start;
983
984
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (ang_range < 0.0) {
985 6 ang_range += 2.0f * (float)M_PI;
986 }
987
988
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (res <= 0) {
989 12 res = 80;
990 }
991
992 12 float steps = ceilf((float)res * ang_range * (0.5f / (float)M_PI));
993
994 12 float ang_step = ang_range / steps;
995 12 float sa = sinf(ang_step);
996 12 float ca = cosf(ang_step);
997
998 12 float px_start = cosf(ang_start) * (float)rad;
999 12 float py_start = sinf(ang_start) * (float)rad;
1000
1001
1002 12 float px = px_start;
1003 12 float py = py_start;
1004
1005
2/2
✓ Branch 0 taken 468 times.
✓ Branch 1 taken 12 times.
480 for (int i = 0;i < steps;i++) {
1006 468 float px_before = px;
1007 468 float py_before = py;
1008
1009 468 px = px * ca - py * sa;
1010 468 py = py * ca + px_before * sa;
1011
1012 468 line(img, x + (int)px_before, y + (int)py_before,
1013 468 x + (int)px, y + (int)py, thickness, dot1, dot2, color);
1014 }
1015
1016
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if (sector) {
1017 1 line(img, x + (int)px, y + (int)py,
1018 x, y,
1019 thickness, dot1, dot2, color);
1020 1 line(img, x, y,
1021 1 x + (int)px_start, y + (int)py_start,
1022 thickness, dot1, dot2, color);
1023 }
1024
1025
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if (segment) {
1026 1 line(img, x + (int)px, y + (int)py,
1027 1 x + (int)px_start, y + (int)py_start,
1028 thickness, dot1, dot2, color);
1029 }
1030 12 }
1031
1032 // thin arc helper function
1033 // handles a single pixel in the complete circle, checking if the pixel is part
1034 // of the arc.
1035 42976 static void handle_thin_arc_pixel(image_buffer_t *img, int x, int y,
1036 int c_x, int c_y, int cap0_x, int cap0_y, int cap1_x, int cap1_y, int min_y, int max_y, bool angle_is_closed, uint32_t color) {
1037
4/4
✓ Branch 0 taken 34386 times.
✓ Branch 1 taken 8590 times.
✓ Branch 2 taken 12736 times.
✓ Branch 3 taken 21650 times.
42976 if (y > max_y || y < min_y) {
1038 21326 return;
1039 }
1040
1041 21650 int line_is_past_0 = point_past_line(x, y, 0, 0, cap0_x, cap0_y);
1042 21650 int line_is_past_1 = -point_past_line(x, y, 0, 0, cap1_x, cap1_y);
1043
1044 21650 bool in_cap0_quadrant = points_same_quadrant(
1045 x, y, cap0_x, cap0_y);
1046 21650 bool in_cap1_quadrant = points_same_quadrant(
1047 x, y, cap1_x, cap1_y);
1048
1049
2/2
✓ Branch 0 taken 2798 times.
✓ Branch 1 taken 18852 times.
21650 if (angle_is_closed) {
1050
4/4
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 2208 times.
✓ Branch 2 taken 159 times.
✓ Branch 3 taken 431 times.
2798 if (line_is_past_0 == 1 && line_is_past_1 == 1) {
1051 159 return;
1052 }
1053 } else {
1054
4/4
✓ Branch 0 taken 14241 times.
✓ Branch 1 taken 4611 times.
✓ Branch 2 taken 9750 times.
✓ Branch 3 taken 4491 times.
18852 if (line_is_past_0 == 1 || line_is_past_1 == 1
1055
4/4
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 9631 times.
✓ Branch 2 taken 111 times.
✓ Branch 3 taken 8 times.
9750 || (line_is_past_0 == 0 && !in_cap0_quadrant)
1056
4/4
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 9633 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 104 times.
9742 || (line_is_past_1 == 0 && !in_cap1_quadrant)) {
1057 9115 return;
1058 }
1059 }
1060
1061 12376 putpixel(img, c_x + x, c_y + y, color);
1062 }
1063
1064 // single pixel wide arc
1065 166 static void thin_arc(image_buffer_t *img, int c_x, int c_y, int radius, float angle0, float angle1, bool sector, bool segment, uint32_t color) {
1066
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 156 times.
166 if (radius == 0) {
1067 10 return;
1068 }
1069
1070 156 angle0 *= (float)M_PI / 180.0f;
1071 156 angle1 *= (float)M_PI / 180.0f;
1072 156 norm_angle_0_2pi(&angle0);
1073 156 norm_angle_0_2pi(&angle1);
1074
1075
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 146 times.
156 if (angle0 == angle1) {
1076 10 return;
1077 }
1078
1079 bool angle_is_closed;
1080 // if the angle of the filled in part of the arc is greater than 180°
1081 // honestly unsure if it'd be better if this was called angle_is_open
1082
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 28 times.
146 if (angle1 - angle0 > 0.0) {
1083 118 angle_is_closed = fabsf(angle1 - angle0) > M_PI;
1084 } else {
1085 28 angle_is_closed = fabsf(angle1 - angle0) < M_PI;
1086 }
1087
1088 146 int cap0_x = (int)(cosf(angle0) * (float)(radius));
1089 146 int cap0_y = (int)(sinf(angle0) * (float)(radius));
1090
1091 146 int cap1_x = (int)(cosf(angle1) * (float)(radius));
1092 146 int cap1_y = (int)(sinf(angle1) * (float)(radius));
1093
1094 // Highest and lowest (y coord wise) drawn line of the base arc (excluding
1095 // the circular end caps). This range is *inclusive*!
1096 // Note that these might be slightly off due to inconsistent rounding between
1097 // my circle drawing algorithm and point rotation.
1098 146 int min_y = MIN(cap0_y, cap1_y);
1099 146 int max_y = MAX(cap0_y, cap1_y);
1100
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 28 times.
146 if (angle0 < angle1) {
1101
4/4
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 70 times.
118 if (angle0 < M_PI_2 && angle1 >= M_3PI_2) {
1102 4 min_y = -radius;
1103 4 max_y = radius;
1104
4/4
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 90 times.
114 } else if (angle0 < M_3PI_2 && angle1 > M_3PI_2) {
1105 23 min_y = -radius;
1106
4/4
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 10 times.
91 } else if (angle0 < M_PI_2 && angle1 > M_PI_2) {
1107 60 max_y = radius;
1108 }
1109 } else {
1110
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
28 if ((angle0 < M_3PI_2 && angle1 >= M_PI_2)
1111
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1 times.
28 || (angle0 < M_PI_2)
1112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 || (angle1 > M_3PI_2)) {
1113 1 min_y = -radius;
1114 1 max_y = radius;
1115
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
27 } else if (angle0 < M_3PI_2 && angle1 < M_PI_2) {
1116 min_y = -radius;
1117
2/4
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
27 } else if (angle0 > M_PI_2 && angle1 > M_PI_2) {
1118 max_y = radius;
1119 }
1120 }
1121
1122 146 int radius_dbl_sq = radius * radius * 4;
1123
1124 146 int last_x = 0;
1125
2/2
✓ Branch 0 taken 7639 times.
✓ Branch 1 taken 146 times.
7785 for (int y = radius - 1; y >= 0; y--) {
1126 7639 int y_dbl_offs = 2 * y + 1;
1127 7639 int y_dbl_offs_sq = y_dbl_offs * y_dbl_offs;
1128
1129
1/2
✓ Branch 0 taken 898574 times.
✗ Branch 1 not taken.
898574 for (int x = -radius; x <= 0; x++) {
1130 898574 int x_dbl_offs = 2 * x + 1;
1131
2/2
✓ Branch 0 taken 7639 times.
✓ Branch 1 taken 890935 times.
898574 if (x_dbl_offs * x_dbl_offs + y_dbl_offs_sq <= radius_dbl_sq) {
1132
2/2
✓ Branch 0 taken 6201 times.
✓ Branch 1 taken 1438 times.
7639 if (last_x - x < 2) {
1133 // This is horrible...
1134 6201 handle_thin_arc_pixel(img, x, y,
1135 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1136 6201 handle_thin_arc_pixel(img, -x - 1, y,
1137 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1138
1139 6201 handle_thin_arc_pixel(img, x, -y - 1,
1140 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1141 6201 handle_thin_arc_pixel(img, -x - 1, -y - 1,
1142 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1143 } else {
1144
2/2
✓ Branch 0 taken 4543 times.
✓ Branch 1 taken 1438 times.
5981 for (int x0 = x; x0 < last_x; x0++) {
1145 4543 handle_thin_arc_pixel(img, x0, y,
1146 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1147 4543 handle_thin_arc_pixel(img, -x0 - 1, y,
1148 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1149
1150 4543 handle_thin_arc_pixel(img, x0, -y - 1,
1151 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1152 4543 handle_thin_arc_pixel(img, -x0 - 1, -y - 1,
1153 c_x, c_y, cap0_x, cap0_y, cap1_x, cap1_y, min_y, max_y, angle_is_closed, color);
1154 }
1155 }
1156
1157 7639 last_x = x;
1158 7639 break;
1159 }
1160 }
1161 }
1162
1163
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 130 times.
146 if (sector) {
1164 16 line(img, c_x, c_y, c_x + cap0_x, c_y + cap0_y, 1, 0, 0, color);
1165 16 line(img, c_x, c_y, c_x + cap1_x, c_y + cap1_y, 1, 0, 0, color);
1166 }
1167
1168
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 108 times.
146 if (segment) {
1169 38 line(img, c_x + cap0_x, c_y + cap0_y, c_x + cap1_x, c_y + cap1_y, 1, 0, 0, color);
1170 }
1171 }
1172
1173 // arc helper function
1174 // handles a horizontal slice at the given outer arc point
1175 64540 static void handle_arc_slice(image_buffer_t *img, int outer_x, int outer_y, int c_x, int c_y, uint32_t color,
1176 int outer_x0, int outer_y0, int outer_x1, int outer_y1,
1177 int cap0_min_y, int cap0_max_y, int cap1_min_y, int cap1_max_y,
1178 int radius_outer, int radius_inner,
1179 int min_y, int max_y,
1180 float angle0, float angle1, bool angle_is_closed,
1181 bool filled, bool segment,
1182 int radius_inner_dbl_sq) {
1183 (void) radius_outer;
1184
4/4
✓ Branch 0 taken 56252 times.
✓ Branch 1 taken 8288 times.
✓ Branch 2 taken 20254 times.
✓ Branch 3 taken 35998 times.
64540 if (outer_y > max_y || outer_y < min_y) {
1185 28542 return;
1186 }
1187
1188 int line_is_past_0, line_is_past_1;
1189 35998 line_is_past_0 = point_past_line(outer_x, outer_y, 0, 0, outer_x0, outer_y0);
1190 35998 line_is_past_1 = -point_past_line(outer_x, outer_y, 0, 0, outer_x1, outer_y1);
1191
1192 35998 int outer_x_sign = sign(outer_x);
1193 35998 int outer_y_sign = sign(outer_y);
1194
1195 35998 int outer_x0_sign = sign(outer_x0);
1196 35998 int outer_y0_sign = sign(outer_y0);
1197
1198 35998 int outer_x1_sign = sign(outer_x1);
1199 35998 int outer_y1_sign = sign(outer_y1);
1200
1201 bool in_cap0, in_cap1, in_both_caps;
1202
4/4
✓ Branch 0 taken 22268 times.
✓ Branch 1 taken 13730 times.
✓ Branch 2 taken 1954 times.
✓ Branch 3 taken 20314 times.
35998 if (segment && filled) {
1203 3908 in_cap0 = outer_y <= MAX(outer_y0, outer_y1)
1204
4/4
✓ Branch 0 taken 1474 times.
✓ Branch 1 taken 480 times.
✓ Branch 2 taken 1298 times.
✓ Branch 3 taken 176 times.
1954 && outer_y >= MIN(outer_y0, outer_y1);
1205 1954 in_cap1 = false;
1206 }
1207
2/2
✓ Branch 0 taken 5526 times.
✓ Branch 1 taken 28518 times.
34044 else if (filled) {
1208 5526 in_cap0 = outer_y <= cap0_max_y
1209
2/2
✓ Branch 0 taken 828 times.
✓ Branch 1 taken 1746 times.
2574 && outer_x0_sign == outer_x_sign
1210
4/4
✓ Branch 0 taken 2574 times.
✓ Branch 1 taken 2952 times.
✓ Branch 2 taken 310 times.
✓ Branch 3 taken 518 times.
8100 && outer_y0_sign == outer_y_sign;
1211 5526 in_cap1 = outer_y <= cap1_max_y
1212
2/2
✓ Branch 0 taken 963 times.
✓ Branch 1 taken 1891 times.
2854 && outer_x1_sign == outer_x_sign
1213
4/4
✓ Branch 0 taken 2854 times.
✓ Branch 1 taken 2672 times.
✓ Branch 2 taken 670 times.
✓ Branch 3 taken 293 times.
8380 && outer_y1_sign == outer_y_sign;
1214 } else {
1215 28518 in_cap0 = outer_y >= cap0_min_y
1216
2/2
✓ Branch 0 taken 3376 times.
✓ Branch 1 taken 17574 times.
20950 && outer_y <= cap0_max_y
1217
4/4
✓ Branch 0 taken 20950 times.
✓ Branch 1 taken 7568 times.
✓ Branch 2 taken 1491 times.
✓ Branch 3 taken 1885 times.
49468 && outer_x_sign == outer_x0_sign;
1218 28518 in_cap1 = outer_y >= cap1_min_y
1219
2/2
✓ Branch 0 taken 3578 times.
✓ Branch 1 taken 10934 times.
14512 && outer_y <= cap1_max_y
1220
4/4
✓ Branch 0 taken 14512 times.
✓ Branch 1 taken 14006 times.
✓ Branch 2 taken 1586 times.
✓ Branch 3 taken 1992 times.
43030 && outer_x_sign == outer_x1_sign;
1221 }
1222
4/4
✓ Branch 0 taken 3099 times.
✓ Branch 1 taken 32899 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 3077 times.
35998 in_both_caps = in_cap0 && in_cap1;
1223
1224 35998 bool in_cap0_quadrant = points_same_quadrant(outer_x, outer_y, outer_x0, outer_y0);
1225 35998 bool in_cap1_quadrant = points_same_quadrant(outer_x, outer_y, outer_x1, outer_y1);
1226
1227 35998 bool caps_in_same_quadrant = points_same_quadrant(outer_x0, outer_y0, outer_x1, outer_y1);
1228
1229 // Check if slice is outside caps and drawn sections of the arc.
1230
4/4
✓ Branch 0 taken 32899 times.
✓ Branch 1 taken 3099 times.
✓ Branch 2 taken 30665 times.
✓ Branch 3 taken 2234 times.
35998 if (!in_cap0 && !in_cap1) {
1231
2/2
✓ Branch 0 taken 16214 times.
✓ Branch 1 taken 14451 times.
30665 if (angle_is_closed) {
1232
4/4
✓ Branch 0 taken 5879 times.
✓ Branch 1 taken 10335 times.
✓ Branch 2 taken 2486 times.
✓ Branch 3 taken 3393 times.
16214 if (line_is_past_0 == 1 && line_is_past_1 == 1
1233 // Failsafe for closed angles with a very small difference.
1234 // Otherwise a tiny section at the opposite side of the arc
1235 // might get skipped.
1236
4/6
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 2317 times.
✓ Branch 2 taken 169 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 169 times.
✗ Branch 5 not taken.
2486 && (!caps_in_same_quadrant || (in_cap0_quadrant && in_cap1_quadrant))) {
1237 2486 return;
1238 }
1239 } else {
1240
4/4
✓ Branch 0 taken 10850 times.
✓ Branch 1 taken 3601 times.
✓ Branch 2 taken 9989 times.
✓ Branch 3 taken 861 times.
14451 if (line_is_past_0 == 1 || line_is_past_1 == 1
1241
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9988 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
9989 || (line_is_past_0 == 0 && !in_cap0_quadrant)
1242
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9987 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
9988 || (line_is_past_1 == 0 && !in_cap1_quadrant)) {
1243 4463 return;
1244 }
1245 }
1246 }
1247
1248 // Find slice width if arc spanned the complete circle.
1249 int x, x1;
1250 29049 int width = 0;
1251 29049 int width1 = 0;
1252 29049 bool slice_is_split = false;
1253
1254 bool slice_filled;
1255
2/2
✓ Branch 0 taken 6229 times.
✓ Branch 1 taken 22820 times.
29049 if (filled) {
1256 6229 slice_filled = true;
1257 } else {
1258
2/2
✓ Branch 0 taken 8996 times.
✓ Branch 1 taken 13824 times.
22820 if (outer_y < 0) {
1259 8996 slice_filled = -outer_y > radius_inner;
1260 } else {
1261 13824 slice_filled = outer_y >= radius_inner;
1262 }
1263 }
1264
1265
2/2
✓ Branch 0 taken 11257 times.
✓ Branch 1 taken 17792 times.
29049 if (slice_filled) {
1266
2/2
✓ Branch 0 taken 5360 times.
✓ Branch 1 taken 5897 times.
11257 if (outer_x < 0) {
1267 5360 x = outer_x;
1268 5360 width = -x;
1269 } else {
1270 5897 x = 0;
1271 5897 width = outer_x + 1;
1272 }
1273 } else {
1274 17792 x = outer_x;
1275 17792 int cur_x = outer_x;
1276
2/2
✓ Branch 0 taken 10515 times.
✓ Branch 1 taken 7277 times.
17792 int delta = outer_x > 0 ? -1 : 1;
1277
1278 // TODO: this could probably be binary searched
1279 17792 int y_dbl_off = outer_y * 2 + 1;
1280 17792 int y_dbl_off_sq = y_dbl_off * y_dbl_off;
1281 239064 while (true) {
1282 256856 cur_x += delta;
1283 256856 int x_dbl_off = cur_x * 2 + 1;
1284
2/2
✓ Branch 0 taken 239064 times.
✓ Branch 1 taken 17792 times.
256856 if (x_dbl_off * x_dbl_off + y_dbl_off_sq <= radius_inner_dbl_sq
1285
1/2
✓ Branch 0 taken 239064 times.
✗ Branch 1 not taken.
239064 || abs(x) > 2000) { // failsafe
1286 break;
1287 }
1288 }
1289 17792 width = abs(cur_x - x);
1290
2/2
✓ Branch 0 taken 10515 times.
✓ Branch 1 taken 7277 times.
17792 if (outer_x > 0) {
1291 10515 x = cur_x + 1;
1292 }
1293 }
1294
1295 // Check which cap lines intersects this slice
1296
8/8
✓ Branch 0 taken 25950 times.
✓ Branch 1 taken 3099 times.
✓ Branch 2 taken 2234 times.
✓ Branch 3 taken 23716 times.
✓ Branch 4 taken 4048 times.
✓ Branch 5 taken 1285 times.
✓ Branch 6 taken 2750 times.
✓ Branch 7 taken 1298 times.
29049 if ((in_cap0 || in_cap1) && !(segment && filled)) {
1297 // the range from x_start to x_end is *inclusive*
1298 4035 int x_start = x;
1299 4035 int x_end = x_start + width - 1;
1300
1301 // when a point is "past" a line, it is on the wrong cleared side of it
1302 4035 int start_is_past0 = point_past_line(x_start, outer_y,
1303 0, 0,
1304 outer_x0, outer_y0);
1305 4035 int end_is_past0 = point_past_line(x_end, outer_y,
1306 0, 0,
1307 outer_x0, outer_y0);
1308
1309 4035 int start_is_past1 = -point_past_line(x_start, outer_y,
1310 0, 0,
1311 outer_x1, outer_y1);
1312 4035 int end_is_past1 = -point_past_line(x_end, outer_y,
1313 0, 0,
1314 outer_x1, outer_y1);
1315
1316 // TODO: look into this:
1317 // end_is_past0!=0 is always true.
1318 // end_is_part1!=0 is always true.
1319 4035 bool slice_overlaps0 = start_is_past0 != end_is_past0
1320
5/6
✓ Branch 0 taken 1655 times.
✓ Branch 1 taken 2380 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 1630 times.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
4035 && (start_is_past0 != 0 || end_is_past0 != 0);
1321 4035 bool slice_overlaps1 = start_is_past1 != end_is_past1
1322
5/6
✓ Branch 0 taken 1824 times.
✓ Branch 1 taken 2211 times.
✓ Branch 2 taken 83 times.
✓ Branch 3 taken 1741 times.
✓ Branch 4 taken 83 times.
✗ Branch 5 not taken.
4035 && (start_is_past1 != 0 || end_is_past1 != 0);
1323
1324
8/8
✓ Branch 0 taken 1801 times.
✓ Branch 1 taken 2234 times.
✓ Branch 2 taken 1779 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 768 times.
✓ Branch 5 taken 1011 times.
✓ Branch 6 taken 659 times.
✓ Branch 7 taken 109 times.
4035 if ((in_cap0 && !in_cap1 && start_is_past0 == 1 && end_is_past0 == 1)
1325
7/8
✓ Branch 0 taken 2234 times.
✓ Branch 1 taken 1692 times.
✓ Branch 2 taken 2234 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1435 times.
✓ Branch 5 taken 799 times.
✓ Branch 6 taken 1202 times.
✓ Branch 7 taken 233 times.
3926 || (!in_cap0 && in_cap1 && start_is_past1 == 1 && end_is_past1 == 1)
1326
5/6
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3671 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
3693 || (in_both_caps && !angle_is_closed && (
1327 (start_is_past0 == 1 && end_is_past0 == 1)
1328
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 || (start_is_past1 == 1 && end_is_past1 == 1)
1329 ))
1330
6/6
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3671 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 6 times.
3693 || (in_both_caps && angle_is_closed && (
1331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 (start_is_past0 == 1 && end_is_past0 == 1)
1332 && (start_is_past1 == 1 && end_is_past1 == 1)
1333 ))) {
1334 342 return;
1335 }
1336
1337 // The repetition in all these cases could probably be reduced...
1338
6/6
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3671 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 1 times.
3693 if ((in_both_caps && slice_overlaps0 && !slice_overlaps1)
1339
6/6
✓ Branch 0 taken 1691 times.
✓ Branch 1 taken 2001 times.
✓ Branch 2 taken 1670 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 1467 times.
✓ Branch 5 taken 203 times.
3692 || (in_cap0 && !in_cap1 && slice_overlaps0)) {
1340 // intersect with cap line 0
1341
4/4
✓ Branch 0 taken 681 times.
✓ Branch 1 taken 787 times.
✓ Branch 2 taken 666 times.
✓ Branch 3 taken 15 times.
1468 if (start_is_past0 != -1 && end_is_past0 != 1) {
1342
2/2
✓ Branch 0 taken 9536 times.
✓ Branch 1 taken 666 times.
10202 while (start_is_past0 == 1) {
1343 9536 x_start += 1;
1344 9536 start_is_past0 = point_past_line(x_start, outer_y,
1345 0, 0,
1346 outer_x0, outer_y0);
1347 }
1348 } else {
1349
2/2
✓ Branch 0 taken 9128 times.
✓ Branch 1 taken 802 times.
9930 while (end_is_past0 == 1) {
1350 9128 x_end -= 1;
1351 9128 end_is_past0 = point_past_line(x_end, outer_y,
1352 0, 0,
1353 outer_x0, outer_y0);
1354 }
1355 }
1356
5/6
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2204 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
2225 } else if ((in_both_caps && !slice_overlaps0 && slice_overlaps1)
1357
5/6
✓ Branch 0 taken 2001 times.
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 2001 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1609 times.
✓ Branch 5 taken 392 times.
2225 || (!in_cap0 && in_cap1 && slice_overlaps1)) {
1358 // intersect with cap line 1
1359
4/4
✓ Branch 0 taken 1250 times.
✓ Branch 1 taken 359 times.
✓ Branch 2 taken 1249 times.
✓ Branch 3 taken 1 times.
1609 if (start_is_past1 != -1 && end_is_past1 != 1) {
1360
2/2
✓ Branch 0 taken 14004 times.
✓ Branch 1 taken 1249 times.
15253 while (start_is_past1 == 1) {
1361 14004 x_start += 1;
1362 14004 start_is_past1 = -point_past_line(x_start, outer_y,
1363 0, 0,
1364 outer_x1, outer_y1);
1365 }
1366 } else {
1367
2/2
✓ Branch 0 taken 5076 times.
✓ Branch 1 taken 360 times.
5436 while (end_is_past1 == 1) {
1368 5076 x_end -= 1;
1369 5076 end_is_past1 = -point_past_line(x_end, outer_y,
1370 0, 0,
1371 outer_x1, outer_y1);
1372 }
1373 }
1374
5/6
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 595 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
616 } else if (in_both_caps && slice_overlaps0 && slice_overlaps1) {
1375 // intersect with both cap lines
1376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (angle0 < angle1) {
1377 if (angle0 < M_PI) {
1378 while (start_is_past1 == 1) {
1379 x_start += 1;
1380 start_is_past1 = -point_past_line(x_start, outer_y,
1381 0, 0,
1382 outer_x1, outer_y1);
1383 }
1384 while (end_is_past0 == 1) {
1385 x_end -= 1;
1386 end_is_past0 = point_past_line(x_end, outer_y,
1387 0, 0,
1388 outer_x0, outer_y0);
1389 }
1390 } else {
1391 while (start_is_past0 == 1) {
1392 x_start += 1;
1393 start_is_past0 = point_past_line(x_start, outer_y,
1394 0, 0,
1395 outer_x0, outer_y0);
1396 }
1397 while (end_is_past1 == 1) {
1398 x_end -= 1;
1399 end_is_past1 = -point_past_line(x_end, outer_y,
1400 0, 0,
1401 outer_x1, outer_y1);
1402 }
1403 }
1404 } else {
1405 // split the slice into two
1406
1407 14 slice_is_split = true;
1408
1409 14 int x_start1 = x_start;
1410 14 int x_end1 = x_end;
1411
1412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (angle0 < M_PI) {
1413 while (end_is_past0 == 1) {
1414 x_end -= 1;
1415 end_is_past0 = point_past_line(x_end, outer_y,
1416 0, 0,
1417 outer_x0, outer_y0);
1418 }
1419 while (start_is_past1 == 1) {
1420 x_start1 += 1;
1421 start_is_past1 = -point_past_line(x_start1, outer_y,
1422 0, 0,
1423 outer_x1, outer_y1);
1424 }
1425 } else {
1426
2/2
✓ Branch 0 taken 314 times.
✓ Branch 1 taken 14 times.
328 while (end_is_past1 == 1) {
1427 314 x_end1 -= 1;
1428 314 end_is_past1 = -point_past_line(x_end1, outer_y,
1429 0, 0,
1430 outer_x1, outer_y1);
1431 }
1432
2/2
✓ Branch 0 taken 362 times.
✓ Branch 1 taken 14 times.
376 while (start_is_past0 == 1) {
1433 362 x_start += 1;
1434 362 start_is_past0 = point_past_line(x_start, outer_y,
1435 0, 0,
1436 outer_x0, outer_y0);
1437 }
1438 }
1439
1440 14 x1 = x_start1;
1441 14 width1 = x_end1 + 1 - x_start1 ;
1442 }
1443 }
1444 3693 x = x_start;
1445 3693 width = x_end + 1 - x_start;
1446
4/6
✓ Branch 0 taken 1298 times.
✓ Branch 1 taken 23716 times.
✓ Branch 2 taken 1298 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1298 times.
✗ Branch 5 not taken.
25014 } else if (in_cap0 && segment && filled) {
1447 // the range from x_start to x_end is *inclusive*
1448 1298 int x_start = x;
1449 1298 int x_end = x_start + width - 1;
1450
1451 // when a point is "past" a line, it is on the wrong cleared side of it
1452 1298 int start_is_past = -point_past_line(x_start, outer_y,
1453 outer_x0, outer_y0, outer_x1, outer_y1);
1454 1298 int end_is_past = -point_past_line(x_end, outer_y,
1455 outer_x0, outer_y0, outer_x1, outer_y1);
1456
1457 1298 bool slice_overlaps = start_is_past != end_is_past
1458
5/6
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 688 times.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 528 times.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
1298 && (start_is_past != 0 || end_is_past != 0);
1459
1460
4/4
✓ Branch 0 taken 739 times.
✓ Branch 1 taken 559 times.
✓ Branch 2 taken 455 times.
✓ Branch 3 taken 284 times.
1298 if (start_is_past == 1 && end_is_past == 1) {
1461 455 return;
1462 }
1463
1464
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 233 times.
843 if (slice_overlaps) {
1465
4/4
✓ Branch 0 taken 366 times.
✓ Branch 1 taken 244 times.
✓ Branch 2 taken 285 times.
✓ Branch 3 taken 81 times.
610 if (start_is_past != -1 && end_is_past != 1) {
1466
2/2
✓ Branch 0 taken 5815 times.
✓ Branch 1 taken 285 times.
6100 while (start_is_past == 1) {
1467 5815 x_start += 1;
1468 5815 start_is_past = -point_past_line(x_start, outer_y,
1469 outer_x0, outer_y0, outer_x1, outer_y1);
1470 }
1471 } else {
1472
2/2
✓ Branch 0 taken 6497 times.
✓ Branch 1 taken 325 times.
6822 while (end_is_past == 1) {
1473 6497 x_end -= 1;
1474 6497 end_is_past = -point_past_line(x_end, outer_y,
1475 outer_x0, outer_y0, outer_x1, outer_y1);
1476 }
1477 }
1478 }
1479
1480 843 x = x_start;
1481 843 width = x_end + 1 - x_start;
1482 }
1483
1484 28252 h_line(img, c_x + x, c_y + outer_y, width, color);
1485
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28238 times.
28252 if (slice_is_split) {
1486 14 h_line(img, c_x + x1, c_y + outer_y, width1, color);
1487 }
1488 }
1489
1490 // TODO: Fix unwanted slice with angles 130 to 115 (I think, angles might be
1491 // slightly off).
1492 // TODO: Look into buggy rendering with angles around 180°-270°. This seems to
1493 // affect arcs, sectors, and segments likewise.
1494 545 static void arc(image_buffer_t *img, int c_x, int c_y, int radius, float angle0, float angle1,
1495 int thickness, bool rounded, bool filled, bool sector, bool segment, int dot1, int dot2, int resolution, uint32_t color) {
1496
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 533 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
545 if (dot1 > 0 && !filled) {
1497 12 thickness /= 2;
1498
1499 12 radius -= thickness;
1500
1501
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (thickness == 0) {
1502 12 thickness = 1;
1503 }
1504
1505 12 generic_arc(img, c_x, c_y, radius, angle0, angle1, thickness, dot1, dot2, resolution, sector, segment, color);
1506
1507 12 return;
1508 }
1509
1510
4/4
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 273 times.
✓ Branch 2 taken 166 times.
✓ Branch 3 taken 94 times.
533 if (thickness <= 1 && !filled) {
1511 166 thin_arc(img, c_x, c_y, radius, angle0, angle1, sector, segment, color);
1512
1513 166 return;
1514 }
1515
1516
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 360 times.
367 if (radius == 0) {
1517 7 return;
1518 }
1519
1520 360 angle0 *= (float)M_PI / 180.0f;
1521 360 angle1 *= (float)M_PI / 180.0f;
1522 360 norm_angle_0_2pi(&angle0); // theses are probably unecessary?
1523 360 norm_angle_0_2pi(&angle1); // but who knows with floating point imprecision...
1524
1525
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 354 times.
360 if (angle0 == angle1) {
1526 6 return;
1527 }
1528
1529 bool angle_is_closed;
1530 // if the angle of the filled in part of the arc is greater than 180°
1531
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 102 times.
354 if (angle1 - angle0 > 0.0) {
1532 252 angle_is_closed = fabsf(angle1 - angle0) > M_PI;
1533 } else {
1534 102 angle_is_closed = fabsf(angle1 - angle0) < M_PI;
1535 }
1536
1537 // angles smaller than 1 degree seem to cause issues (with a radius of 62)
1538 // this is kinda ugly though, and it will probably still break at larger
1539 // radii or something...
1540
4/4
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 233 times.
354 if (!angle_is_closed && fabsf(angle1 - angle0) < 0.0174532925) { // one degree in radians
1541
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 9 times.
23 if (rounded) {
1542 14 float rad_f = (float)radius - ((float)thickness / 2.0f);
1543
1544 14 float angle = (angle0 + angle1) / 2.0f;
1545
1546 14 int cap_center_x = (int)floorf(cosf(angle) * rad_f);
1547 14 int cap_center_y = (int)floorf(sinf(angle) * rad_f);
1548
1549 14 fill_circle(img, c_x + cap_center_x, c_y + cap_center_y, thickness / 2, color);
1550 }
1551 23 return;
1552 }
1553
1554
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 329 times.
331 if (thickness >= radius) {
1555 2 filled = true;
1556 }
1557
1558 int radius_outer, radius_inner;
1559
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 254 times.
331 if (filled) {
1560 77 radius_outer = radius;
1561 77 radius_inner = 0;
1562 } else {
1563 254 radius_outer = radius;
1564 254 radius_inner = radius - thickness;
1565 }
1566 331 int radius_outer_dbl_sq = radius_outer * radius_outer * 4;
1567 331 int radius_inner_dbl_sq = radius_inner * radius_inner * 4;
1568
1569 331 float angle0_cos = cosf(angle0);
1570 331 float angle0_sin = sinf(angle0);
1571 331 float angle1_cos = cosf(angle1);
1572 331 float angle1_sin = sinf(angle1);
1573
1574 331 int outer_x0 = (int)(angle0_cos * (float)radius_outer);
1575 331 int outer_y0 = (int)(angle0_sin * (float)radius_outer);
1576
1577 331 int outer_x1 = (int)(angle1_cos * (float)radius_outer);
1578 331 int outer_y1 = (int)(angle1_sin * (float)radius_outer);
1579
1580 int inner_y0;
1581 int inner_y1;
1582
1583
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 254 times.
331 if (filled) {
1584 77 inner_y0 = 0;
1585
1586 77 inner_y1 = 0;
1587 } else {
1588 254 inner_y0 = (int)(angle0_sin * (float)radius_inner);
1589
1590 254 inner_y1 = (int)(angle1_sin * (float)radius_inner);
1591 }
1592
1593 331 int cap0_min_y = MIN(inner_y0, outer_y0);
1594 331 int cap0_max_y = MAX(inner_y0, outer_y0);
1595
1596 331 int cap1_min_y = MIN(inner_y1, outer_y1);
1597 331 int cap1_max_y = MAX(inner_y1, outer_y1);
1598
1599 // Highest and lowest (y coord wise) drawn line of the base arc (excluding
1600 // the circular end caps). This range is *inclusive*!
1601 // Note that these might be slightly off due to inconsistent rounding between
1602 // Bresenhamn's algorithm and point rotation. (I don't think the point about
1603 // Bresenhamn is relevant as we don't use it anymore. Still wouldn't trust
1604 // them completely though...)
1605 331 int min_y = MIN(outer_y0, MIN(outer_y1, MIN(inner_y0, inner_y1)));
1606 331 int max_y = MAX(outer_y0, MAX(outer_y1, MAX(inner_y0, inner_y1)));
1607
2/2
✓ Branch 0 taken 229 times.
✓ Branch 1 taken 102 times.
331 if (angle0 < angle1) {
1608
4/4
✓ Branch 0 taken 179 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 162 times.
229 if (angle0 < M_PI_2 && angle1 >= M_3PI_2) {
1609 17 min_y = -radius_outer;
1610 17 max_y = radius_outer;
1611
3/4
✓ Branch 0 taken 212 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 172 times.
212 } else if (angle0 < M_3PI_2 && angle1 > M_3PI_2) {
1612 40 min_y = -radius_outer;
1613
4/4
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 153 times.
✓ Branch 3 taken 9 times.
172 } else if (angle0 < M_PI_2 && angle1 > M_PI_2) {
1614 153 max_y = radius_outer;
1615 }
1616 } else {
1617
4/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 9 times.
102 if ((angle0 < M_3PI_2 && angle1 >= M_PI_2)
1618
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 1 times.
93 || (angle0 < M_PI_2)
1619
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 91 times.
92 || (angle1 > M_3PI_2)) {
1620 11 min_y = -radius_outer;
1621 11 max_y = radius_outer;
1622
3/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 67 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
91 } else if (angle0 < M_3PI_2 && angle1 < M_PI_2) {
1623 24 min_y = -radius_outer;
1624
3/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 50 times.
67 } else if (angle0 > M_PI_2 && angle1 > M_PI_2) {
1625 17 max_y = radius_outer;
1626 }
1627 }
1628
1629
2/2
✓ Branch 0 taken 16135 times.
✓ Branch 1 taken 331 times.
16466 for (int y = 0; y < radius_outer; y++) {
1630 16135 int y_dbl_offs = 2 * (y + 1);
1631 16135 int y_dbl_offs_sq = y_dbl_offs * y_dbl_offs;
1632
1633
1/2
✓ Branch 0 taken 223585 times.
✗ Branch 1 not taken.
223585 for (int x = -radius_outer; x <= 0; x++) {
1634 223585 int x_dbl_offs = 2 * (x + 1);
1635
2/2
✓ Branch 0 taken 16135 times.
✓ Branch 1 taken 207450 times.
223585 if (x_dbl_offs * x_dbl_offs + y_dbl_offs_sq <= radius_outer_dbl_sq) {
1636 // This is horrible...
1637 16135 handle_arc_slice(img, x, y,
1638 c_x, c_y, color, outer_x0, outer_y0, outer_x1, outer_y1,
1639 cap0_min_y, cap0_max_y, cap1_min_y, cap1_max_y, radius_outer, radius_inner, min_y, max_y,
1640 angle0, angle1, angle_is_closed, filled, segment, radius_inner_dbl_sq);
1641 16135 handle_arc_slice(img, -x - 1, y,
1642 c_x, c_y, color, outer_x0, outer_y0, outer_x1, outer_y1,
1643 cap0_min_y, cap0_max_y, cap1_min_y, cap1_max_y, radius_outer, radius_inner, min_y, max_y,
1644 angle0, angle1, angle_is_closed, filled, segment, radius_inner_dbl_sq);
1645
1646 16135 handle_arc_slice(img, x, -y - 1,
1647 c_x, c_y, color, outer_x0, outer_y0, outer_x1, outer_y1,
1648 cap0_min_y, cap0_max_y, cap1_min_y, cap1_max_y, radius_outer, radius_inner, min_y, max_y,
1649 angle0, angle1, angle_is_closed, filled, segment, radius_inner_dbl_sq);
1650 16135 handle_arc_slice(img, -x - 1, -y - 1,
1651 c_x, c_y, color, outer_x0, outer_y0, outer_x1, outer_y1,
1652 cap0_min_y, cap0_max_y, cap1_min_y, cap1_max_y, radius_outer, radius_inner, min_y, max_y,
1653 angle0, angle1, angle_is_closed, filled, segment, radius_inner_dbl_sq);
1654
1655 16135 break;
1656 }
1657 }
1658 }
1659
1660 // draw rounded line corners
1661
8/8
✓ Branch 0 taken 285 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 237 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 234 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 34 times.
✓ Branch 7 taken 200 times.
331 if (rounded && !filled && !sector && !segment) {
1662 34 float rad_f = (float)radius - ((float)thickness / 2.0f);
1663
1664 34 int cap0_center_x = (int)floorf(angle0_cos * rad_f);
1665 34 int cap0_center_y = (int)floorf(angle0_sin * rad_f);
1666
1667 34 int cap1_center_x = (int)floorf(angle1_cos * rad_f);
1668 34 int cap1_center_y = (int)floorf(angle1_sin * rad_f);
1669
1670 34 thickness /= 2;
1671
1672 34 fill_circle(img, c_x + cap0_center_x, c_y + cap0_center_y, thickness, color);
1673 34 fill_circle(img, c_x + cap1_center_x, c_y + cap1_center_y, thickness, color);
1674 }
1675
1676 // draw sector arc cap to center lines
1677 // (sectors are always rounded)
1678
4/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 305 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 23 times.
331 if (sector && !filled) {
1679 3 float rad_f = (float)radius - ((float)thickness / 2.0f);
1680
1681 3 int cap0_center_x = (int)floorf(angle0_cos * rad_f);
1682 3 int cap0_center_y = (int)floorf(angle0_sin * rad_f);
1683
1684 3 int cap1_center_x = (int)floorf(angle1_cos * rad_f);
1685 3 int cap1_center_y = (int)floorf(angle1_sin * rad_f);
1686
1687 3 thickness /= 2;
1688
1689 3 line(img, c_x + cap0_center_x, c_y + cap0_center_y,
1690 c_x, c_y, thickness, 0, 0, color);
1691 3 line(img, c_x + cap1_center_x, c_y + cap1_center_y,
1692 c_x, c_y, thickness, 0, 0, color);
1693 }
1694
1695
4/4
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 200 times.
✓ Branch 3 taken 25 times.
331 if (segment && !filled) {
1696 200 float rad_f = (float)radius - ((float)thickness / 2.0f);
1697
1698 200 int cap0_center_x = (int)floorf(angle0_cos * rad_f);
1699 200 int cap0_center_y = (int)floorf(angle0_sin * rad_f);
1700
1701 200 int cap1_center_x = (int)floorf(angle1_cos * rad_f);
1702 200 int cap1_center_y = (int)floorf(angle1_sin * rad_f);
1703
1704 200 thickness /= 2;
1705
1706 200 line(img, c_x + cap0_center_x, c_y + cap0_center_y,
1707 c_x + cap1_center_x, c_y + cap1_center_y, thickness, 0, 0, color);
1708 }
1709 }
1710
1711 734 static void img_putc(image_buffer_t *img, int x, int y, uint32_t *colors, int num_colors,
1712 uint8_t *font_data, uint8_t ch, bool up, bool down) {
1713 734 uint8_t w = font_data[0];
1714 734 uint8_t h = font_data[1];
1715 734 uint8_t char_num = font_data[2];
1716 734 uint8_t bits_per_pixel = font_data[3];
1717
1718 734 int pixels_per_byte = (int)(8 / bits_per_pixel);
1719 734 int bytes_per_char = (int)((w * h) / pixels_per_byte);
1720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
734 if ((w * h) % pixels_per_byte != 0) {
1721 bytes_per_char += 1;
1722 }
1723
1724 // There are some expectations on ch that are not documented here.
1725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
734 if (char_num == 10) {
1726 ch = (uint8_t)(ch - '0');
1727 } else {
1728 734 ch = (uint8_t)(ch - ' ');
1729 }
1730
1731
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
734 if (ch >= char_num) {
1732 return;
1733 }
1734
1735
2/2
✓ Branch 0 taken 524 times.
✓ Branch 1 taken 210 times.
734 if (bits_per_pixel == 2) {
1736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 524 times.
524 if (num_colors < 4) {
1737 return;
1738 }
1739
1740
2/2
✓ Branch 0 taken 217984 times.
✓ Branch 1 taken 524 times.
218508 for (int i = 0; i < w * h; i++) {
1741 217984 uint8_t byte = font_data[4 + bytes_per_char * ch + (i / 4)];
1742 217984 uint8_t bit_pos = (uint8_t)(i % pixels_per_byte);
1743 217984 uint8_t pixel_value = (byte >> (bit_pos * 2)) & 0x03;
1744 217984 int x0 = i % w;
1745 217984 int y0 = i / w;
1746
2/2
✓ Branch 0 taken 2912 times.
✓ Branch 1 taken 215072 times.
217984 if (up) {
1747 2912 putpixel(img, x + y0, y - x0, colors[pixel_value]);
1748
2/2
✓ Branch 0 taken 4576 times.
✓ Branch 1 taken 210496 times.
215072 } else if (down) {
1749 4576 putpixel(img, x - y0, y + x0, colors[pixel_value]);
1750 } else {
1751 210496 putpixel(img, x + x0, y + y0, colors[pixel_value]);
1752 }
1753 }
1754 } else {
1755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
210 if (num_colors < 1) {
1756 return;
1757 }
1758
1759 210 int32_t fg = (int32_t)colors[0];
1760 210 int32_t bg = -1;
1761
1762
1/2
✓ Branch 0 taken 210 times.
✗ Branch 1 not taken.
210 if (num_colors > 1) {
1763 210 bg = (int32_t)colors[1];
1764 }
1765
1766
2/2
✓ Branch 0 taken 87360 times.
✓ Branch 1 taken 210 times.
87570 for (int i = 0; i < w * h; i++) {
1767 87360 uint8_t byte = font_data[4 + bytes_per_char * ch + (i / 8)];
1768 87360 uint8_t bit_pos = (uint8_t)(i % 8);
1769 87360 uint8_t bit = (uint8_t)(byte & (1 << bit_pos));
1770
3/4
✓ Branch 0 taken 74918 times.
✓ Branch 1 taken 12442 times.
✓ Branch 2 taken 74918 times.
✗ Branch 3 not taken.
87360 if (bit || bg >= 0) {
1771 87360 int x0 = i % w;
1772 87360 int y0 = i / w;
1773
1774
2/2
✓ Branch 0 taken 32448 times.
✓ Branch 1 taken 54912 times.
87360 if (up) {
1775
2/2
✓ Branch 0 taken 4433 times.
✓ Branch 1 taken 28015 times.
32448 putpixel(img, x + y0, y - x0, bit ? (uint32_t)fg : (uint32_t)bg);
1776
2/2
✓ Branch 0 taken 37856 times.
✓ Branch 1 taken 17056 times.
54912 } else if (down) {
1777
2/2
✓ Branch 0 taken 5566 times.
✓ Branch 1 taken 32290 times.
37856 putpixel(img, x - y0, y + x0, bit ? (uint32_t)fg : (uint32_t)bg);
1778 } else {
1779
2/2
✓ Branch 0 taken 2443 times.
✓ Branch 1 taken 14613 times.
17056 putpixel(img, x + x0, y + y0, bit ? (uint32_t)fg : (uint32_t)bg);
1780 }
1781 }
1782 }
1783 }
1784 }
1785
1786 4386100 static inline void copy_pixel(
1787 image_buffer_t *img_dest,
1788 image_buffer_t *img_src,
1789 int dest_x, int dest_y,
1790 int src_x, int src_y,
1791 int src_w, int src_h,
1792 int transparent_color,
1793 bool tile
1794 ) {
1795
2/2
✓ Branch 0 taken 923300 times.
✓ Branch 1 taken 3462800 times.
4386100 if (tile) {
1796 923300 src_x = src_x % src_w;
1797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 923300 times.
923300 if (src_x < 0) src_x = src_x + src_w;
1798 923300 src_y = src_y % src_h;
1799
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 923300 times.
923300 if (src_y < 0) src_y = src_y + src_h;
1800 }
1801
1802
8/8
✓ Branch 0 taken 2553092 times.
✓ Branch 1 taken 1833008 times.
✓ Branch 2 taken 1223499 times.
✓ Branch 3 taken 1329593 times.
✓ Branch 4 taken 1130161 times.
✓ Branch 5 taken 93338 times.
✓ Branch 6 taken 1013488 times.
✓ Branch 7 taken 116673 times.
4386100 if (src_x >= 0 && src_x < src_w && src_y >= 0 && src_y < src_h) {
1803 1013488 uint32_t p = getpixel(img_src, src_x, src_y);
1804
4/4
✓ Branch 0 taken 122331 times.
✓ Branch 1 taken 891157 times.
✓ Branch 2 taken 53950 times.
✓ Branch 3 taken 68381 times.
1013488 if (transparent_color == -1 || p != (uint32_t)transparent_color) {
1805 945107 putpixel(img_dest, dest_x, dest_y, p);
1806 }
1807 }
1808 4386100 }
1809
1810 // Copy pixels from source to destination with transformations
1811 42 void blit(
1812 image_buffer_t *img_dest, // Destination image buffer
1813 image_buffer_t *img_src, // Source image buffer
1814 int dest_offset_x, int dest_offset_y, // Where on dest to start writing pixels
1815 float rot_x, float rot_y, // Coordinate in src to rotate around
1816 float rot_angle, // Rotation angle in degrees
1817 float scale, // Scale factor
1818 int32_t transparent_color, // Color that will not be drawn -1 to disable
1819 bool tile, // Tile src to fill dest
1820 int clip_x, int clip_y, // Clip start in dest
1821 int clip_w, int clip_h // Clip width and height
1822 ) {
1823
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 41 times.
42 if (scale == 0.0) return;
1824 41 int src_w = img_src->width;
1825 41 int src_h = img_src->height;
1826
1827 41 int dest_x_start = clip_x;
1828 41 int dest_y_start = clip_y;
1829 41 int dest_x_end = clip_w;
1830 41 int dest_y_end = clip_h;
1831
1832
4/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 10 times.
41 if (rot_angle == 0.0 && scale == 1.0) {
1833
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 if (dest_offset_x > 0) dest_x_start += dest_offset_x;
1834
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 if (dest_offset_y > 0) dest_y_start += dest_offset_y;
1835
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 10 times.
21 if (!tile) {
1836
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if ((dest_x_end - dest_offset_x) > src_w) dest_x_end = src_w + dest_offset_x;
1837
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if ((dest_y_end - dest_offset_y) > src_h) dest_y_end = src_h + dest_offset_y;
1838 }
1839
1840
2/2
✓ Branch 0 taken 2415 times.
✓ Branch 1 taken 21 times.
2436 for (int dest_y = dest_y_start; dest_y < dest_y_end; dest_y++) {
1841
2/2
✓ Branch 0 taken 866100 times.
✓ Branch 1 taken 2415 times.
868515 for (int dest_x = dest_x_start; dest_x < dest_x_end; dest_x++) {
1842 866100 int src_x = dest_x - dest_offset_x;
1843 866100 int src_y = dest_y - dest_offset_y;
1844 866100 copy_pixel(img_dest, img_src, dest_x, dest_y, src_x, src_y, src_w, src_h, transparent_color, tile);
1845 }
1846 }
1847
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 } else if (rot_angle == 0.0) {
1848 10 rot_x *= scale;
1849 10 rot_y *= scale;
1850
1851 10 const int fp_scale = 1000;
1852
1853 10 int rot_x_x = (int)rot_x;
1854 10 int rot_y_i = (int)rot_y;
1855 10 int scale_i = (int)(scale * (float) fp_scale);
1856
1857
2/2
✓ Branch 0 taken 3500 times.
✓ Branch 1 taken 10 times.
3510 for (int dest_y = dest_y_start; dest_y < dest_y_end; dest_y++) {
1858
2/2
✓ Branch 0 taken 1720000 times.
✓ Branch 1 taken 3500 times.
1723500 for (int dest_x = dest_x_start; dest_x < dest_x_end; dest_x++) {
1859 1720000 int src_x = (dest_x - dest_offset_x - rot_x_x) * fp_scale;
1860 1720000 int src_y = (dest_y - dest_offset_y - rot_y_i) * fp_scale;
1861
1862 1720000 src_x += rot_x_x * fp_scale;
1863 1720000 src_y += rot_y_i * fp_scale;
1864
1865 1720000 src_x /= scale_i;
1866 1720000 src_y /= scale_i;
1867 1720000 copy_pixel(img_dest, img_src, dest_x, dest_y, src_x, src_y, src_w, src_h, transparent_color, tile);
1868 }
1869 }
1870 } else {
1871 10 float sin_rot_angle = sinf(-rot_angle * (float)M_PI / 180.0f);
1872 10 float cos_rot_angle = cosf(-rot_angle * (float)M_PI / 180.0f);
1873
1874 10 rot_x *= scale;
1875 10 rot_y *= scale;
1876
1877 10 const int fp_scale = 1000;
1878
1879 10 int sin_rot_angle_i = (int)(sin_rot_angle * (float)fp_scale);
1880 10 int cos_rot_angle_i = (int)(cos_rot_angle * (float)fp_scale);
1881 10 int rot_x_i = (int)rot_x;
1882 10 int rot_y_i = (int)rot_y;
1883 10 int scale_i = (int)(scale * (float) fp_scale);
1884
1885
2/2
✓ Branch 0 taken 3600 times.
✓ Branch 1 taken 10 times.
3610 for (int dest_y = dest_y_start; dest_y < dest_y_end; dest_y++) {
1886
2/2
✓ Branch 0 taken 1800000 times.
✓ Branch 1 taken 3600 times.
1803600 for (int dest_x = dest_x_start; dest_x < dest_x_end; dest_x++) {
1887 1800000 int src_x = (dest_x - dest_offset_x - rot_x_i) * cos_rot_angle_i + (dest_y - dest_offset_y - rot_y_i) * sin_rot_angle_i;
1888 1800000 int src_y = -(dest_x - dest_offset_x - rot_x_i) * sin_rot_angle_i + (dest_y - dest_offset_y - rot_y_i) * cos_rot_angle_i;
1889
1890 1800000 src_x += rot_x_i * fp_scale;
1891 1800000 src_y += rot_y_i * fp_scale;
1892
1893 1800000 src_x /= scale_i;
1894 1800000 src_y /= scale_i;
1895 1800000 copy_pixel(img_dest, img_src, dest_x, dest_y, src_x, src_y, src_w, src_h, transparent_color, tile);
1896 }
1897 }
1898 }
1899 }
1900
1901 // Extensions
1902
1903 #define ATTR_MAX_ARGS 4
1904 #define ARG_MAX_NUM 8
1905
1906 typedef struct {
1907 bool is_valid;
1908 uint16_t arg_num;
1909 lbm_value args[ATTR_MAX_ARGS];
1910 } attr_t;
1911
1912 typedef struct {
1913 bool is_valid;
1914 image_buffer_t img;
1915 lbm_value args[ARG_MAX_NUM];
1916 attr_t attr_thickness;
1917 attr_t attr_filled;
1918 attr_t attr_rounded;
1919 attr_t attr_dotted;
1920 attr_t attr_scale;
1921 attr_t attr_rotate;
1922 attr_t attr_resolution;
1923 attr_t attr_tile;
1924 attr_t attr_clip;
1925 } img_args_t;
1926
1927 1085 static img_args_t decode_args(lbm_value *args, lbm_uint argn, int num_expected) {
1928 img_args_t res;
1929 1085 memset(&res, 0, sizeof(res));
1930 1085 res.is_valid = false;
1931
1932 lbm_array_header_t *arr;
1933
3/4
✓ Branch 0 taken 1085 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1048 times.
✓ Branch 3 taken 37 times.
1085 if (argn >= 1 && (arr = get_image_buffer(args[0]))) {
1934 // at least one argument which is an image buffer.
1935 1048 res.img.width = image_buffer_width((uint8_t*)arr->data);
1936 1048 res.img.height = image_buffer_height((uint8_t*)arr->data);
1937 1048 res.img.fmt = image_buffer_format((uint8_t*)arr->data);
1938 1048 res.img.mem_base = (uint8_t*)arr->data;
1939 1048 res.img.data = image_buffer_data((uint8_t*)arr->data);
1940
1941
1942 1048 int num_dec = 0;
1943
2/2
✓ Branch 0 taken 5391 times.
✓ Branch 1 taken 1022 times.
6413 for (unsigned int i = 1;i < argn;i++) {
1944
4/4
✓ Branch 0 taken 668 times.
✓ Branch 1 taken 4723 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 648 times.
5391 if (!lbm_is_number(args[i]) && !lbm_is_cons(args[i])) {
1945 20 return res;
1946 }
1947
1948
2/2
✓ Branch 0 taken 4723 times.
✓ Branch 1 taken 648 times.
5371 if (lbm_is_number(args[i])) {
1949 4723 res.args[num_dec] = args[i];
1950 4723 num_dec++;
1951
1952
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4723 times.
4723 if (num_dec > ARG_MAX_NUM) {
1953 return res;
1954 }
1955 } else {
1956 648 lbm_value curr = args[i];
1957 648 int attr_ind = 0;
1958 648 attr_t *attr_now = 0;
1959
2/2
✓ Branch 0 taken 1156 times.
✓ Branch 1 taken 646 times.
1802 while (lbm_is_cons(curr)) {
1960 1156 lbm_value arg = lbm_car(curr);
1961
1962
2/2
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 508 times.
1156 if (attr_ind == 0) {
1963
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
648 if (!lbm_is_symbol(arg)) {
1964 return res;
1965 }
1966
1967
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 302 times.
648 if (lbm_dec_sym(arg) == symbol_thickness) {
1968 346 attr_now = &res.attr_thickness;
1969 346 attr_now->arg_num = 1;
1970
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 168 times.
302 } else if (lbm_dec_sym(arg) == symbol_filled) {
1971 134 attr_now = &res.attr_filled;
1972 134 attr_now->arg_num = 0;
1973
2/2
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 77 times.
168 } else if (lbm_dec_sym(arg) == symbol_rounded) {
1974 91 attr_now = &res.attr_rounded;
1975 91 attr_now->arg_num = 1;
1976
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 49 times.
77 } else if (lbm_dec_sym(arg) == symbol_dotted) {
1977 28 attr_now = &res.attr_dotted;
1978 28 attr_now->arg_num = 2;
1979
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 34 times.
49 } else if (lbm_dec_sym(arg) == symbol_scale) {
1980 15 attr_now = &res.attr_scale;
1981 15 attr_now->arg_num = 1;
1982
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 23 times.
34 } else if (lbm_dec_sym(arg) == symbol_rotate) {
1983 11 attr_now = &res.attr_rotate;
1984 11 attr_now->arg_num = 3;
1985
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 } else if (lbm_dec_sym(arg) == symbol_resolution) {
1986 attr_now = &res.attr_resolution;
1987 attr_now->arg_num = 1;
1988
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 } else if (lbm_dec_sym(arg) == symbol_tile) {
1989 13 attr_now = &res.attr_tile;
1990 13 attr_now->arg_num = 0;
1991
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 } else if (lbm_dec_sym(arg) == symbol_clip) {
1992 9 attr_now = &res.attr_clip;
1993 9 attr_now->arg_num = 4;
1994 }
1995 else {
1996 1 return res;
1997 }
1998 } else {
1999
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 507 times.
508 if (!lbm_is_number(arg)) {
2000 1 return res;
2001 }
2002
2003 507 attr_now->args[attr_ind - 1] = arg;
2004 }
2005
2006 1154 attr_ind++;
2007
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1154 times.
1154 if (attr_ind > (ATTR_MAX_ARGS + 1)) {
2008 return res;
2009 }
2010
2011 1154 curr = lbm_cdr(curr);
2012 }
2013
2014 // does this really compare the pointer addresses?
2015
4/4
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 555 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 26 times.
646 if (attr_now == &res.attr_rounded && attr_ind == 1) {
2016 65 attr_now->arg_num = 0; // the `rounded` attribute may be empty
2017 }
2018
2019
2020
2/2
✓ Branch 0 taken 642 times.
✓ Branch 1 taken 4 times.
646 if ((attr_ind - 1) == attr_now->arg_num) {
2021 642 attr_now->is_valid = true;
2022 } else {
2023 4 return res;
2024 }
2025 }
2026 }
2027
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1022 times.
1022 if (num_dec != num_expected) {
2028 return res;
2029 }
2030 // I think this should go here ???
2031 1022 res.is_valid = true;
2032 }
2033 1059 return res;
2034 }
2035
2036 26 static lbm_value ext_image_dims(lbm_value *args, lbm_uint argn) {
2037 26 img_args_t arg_dec = decode_args(args, argn, 0);
2038
2039
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 24 times.
26 if (!arg_dec.is_valid) {
2040 2 return ENC_SYM_TERROR;
2041 }
2042
2043 24 lbm_value dims = lbm_heap_allocate_list(2);
2044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if (lbm_is_symbol(dims)) {
2045 return dims;
2046 }
2047 24 lbm_value curr = dims;
2048 24 lbm_set_car(curr, lbm_enc_i(arg_dec.img.width));
2049 24 curr = lbm_cdr(curr);
2050 24 lbm_set_car(curr, lbm_enc_i(arg_dec.img.height));
2051 24 return dims;
2052 }
2053
2054 76 static lbm_value ext_image_buffer(lbm_value *args, lbm_uint argn) {
2055 76 lbm_value res = ENC_SYM_TERROR;
2056 76 bool args_ok = false;
2057 76 color_format_t fmt = indexed2;
2058 76 lbm_uint w = 0;
2059 76 lbm_uint h = 0;
2060
2061
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
77 if (argn == 4 &&
2062
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 lbm_is_defrag_mem(args[0]) &&
2063
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 lbm_is_symbol(args[1]) &&
2064
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 lbm_is_number(args[2]) &&
2065 1 lbm_is_number(args[3])) {
2066 1 fmt = sym_to_color_format(args[1]);
2067 1 w = lbm_dec_as_u32(args[2]);
2068 1 h = lbm_dec_as_u32(args[3]);
2069 1 args_ok = true;
2070
2/4
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
150 } else if (argn == 3 &&
2071
1/2
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
150 lbm_is_symbol(args[0]) &&
2072
1/2
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
150 lbm_is_number(args[1]) &&
2073 75 lbm_is_number(args[2])) {
2074 75 fmt = sym_to_color_format(args[0]);
2075 75 w = lbm_dec_as_u32(args[1]);
2076 75 h = lbm_dec_as_u32(args[2]);
2077 75 args_ok = true;
2078 }
2079
2080
9/12
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 72 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 71 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 71 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 71 times.
✗ Branch 11 not taken.
76 if (args_ok && fmt != format_not_supported && w > 0 && h > 0 && w < MAX_WIDTH && h < MAX_HEIGHT) {
2081
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 1 times.
71 if (argn == 3) {
2082 70 res = image_buffer_allocate(fmt, (uint16_t)w, (uint16_t)h);
2083 } else {
2084 1 res = image_buffer_allocate_dm((lbm_uint*)lbm_car(args[0]), fmt, (uint16_t)w, (uint16_t)h);
2085 }
2086 }
2087 76 return res;
2088 }
2089
2090
2091 20 static lbm_value ext_is_image_buffer(lbm_value *args, lbm_uint argn) {
2092 20 lbm_value res = ENC_SYM_TERROR;
2093
2094
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (argn == 1) {
2095 20 res = ENC_SYM_NIL;
2096 20 lbm_array_header_t *array = lbm_dec_array_r(args[0]);
2097
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
20 if (array) {
2098 19 uint8_t *data = (uint8_t*)array->data;
2099
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (image_buffer_is_valid(data, array->size)) {
2100 19 res = ENC_SYM_TRUE;;
2101 }
2102 }
2103 }
2104 20 return res;
2105 }
2106
2107 28 static lbm_value ext_color(lbm_value *args, lbm_uint argn) {
2108 28 lbm_value res = ENC_SYM_TERROR;
2109
2110
4/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 1 times.
56 if (argn >= 2 && argn <= 6 &&
2111
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
55 lbm_is_symbol(args[0]) &&
2112 27 lbm_is_number(args[1])) {
2113
2114 // Color1 and color2 are int in the struct and decoded as i32, why
2115 // where they stored in uint32_t?
2116 27 int32_t color1 = lbm_dec_as_i32(args[1]);
2117
2118 27 int32_t color2 = 0;
2119
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 13 times.
27 if (argn >= 3) {
2120
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (lbm_is_number(args[2])) {
2121 14 color2 = lbm_dec_as_i32(args[2]);
2122 } else {
2123 return ENC_SYM_TERROR;
2124 }
2125 }
2126
2127 27 int32_t param1 = 0;
2128
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 13 times.
27 if (argn >= 4) {
2129
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (lbm_is_number(args[3])) {
2130 14 param1 = lbm_dec_as_i32(args[3]);
2131 } else {
2132 return ENC_SYM_TERROR;
2133 }
2134 }
2135
2136 27 int32_t param2 = 0;
2137
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 13 times.
27 if (argn >= 5) {
2138
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (lbm_is_number(args[4])) {
2139 14 param2 = lbm_dec_as_i32(args[4]);
2140 } else {
2141 return ENC_SYM_TERROR;
2142 }
2143 }
2144
2145 27 bool mirrored = false;
2146
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 17 times.
27 if (argn >= 6) {
2147
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (lbm_is_symbol(args[5])) {
2148 10 lbm_uint sym = lbm_dec_sym(args[5]);
2149
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 if (sym == symbol_repeat) {
2150 7 mirrored = false;
2151
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 } else if (sym == symbol_mirrored) {
2152 3 mirrored = true;
2153 } else {
2154 return ENC_SYM_TERROR;
2155 }
2156 } else {
2157 return ENC_SYM_TERROR;
2158 }
2159 }
2160
2161 COLOR_TYPE t;
2162
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 15 times.
27 if (lbm_dec_sym(args[0]) == symbol_regular) {
2163 12 t = COLOR_REGULAR;
2164
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 } else if (lbm_dec_sym(args[0]) == symbol_gradient_x) {
2165 6 t = COLOR_GRADIENT_X;
2166
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 } else if (lbm_dec_sym(args[0]) == symbol_gradient_y) {
2167 3 t = COLOR_GRADIENT_Y;
2168
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 } else if (lbm_dec_sym(args[0]) == symbol_gradient_x_pre) {
2169 4 t = COLOR_PRE_X;
2170
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 } else if (lbm_dec_sym(args[0]) == symbol_gradient_y_pre) {
2171 1 t = COLOR_PRE_Y;
2172 } else {
2173 1 return ENC_SYM_TERROR;
2174 }
2175
2176 // Maybe check if param is in ranges first ?
2177 26 res = color_allocate(t, color1, color2, (uint16_t)param1, (uint16_t)param2, mirrored);
2178 }
2179
2180 27 return res;
2181 }
2182
2183 25 static lbm_value ext_color_set(lbm_value *args, lbm_uint argn) {
2184 color_t *color;
2185
3/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 3 times.
25 if (argn != 3 || !(color = get_color(args[0])) || // color assignment
2186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 !lbm_is_symbol(args[1])) {
2187 3 return ENC_SYM_TERROR;
2188 }
2189
2190 22 bool is_regular = color->type == COLOR_REGULAR;
2191
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
22 bool is_gradient = color->type == COLOR_GRADIENT_X || color->type == COLOR_GRADIENT_Y;
2192
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 bool is_pre = color->type == COLOR_PRE_X || color->type == COLOR_PRE_Y;
2193
2194 22 lbm_uint prop = lbm_dec_sym(args[1]);
2195
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 16 times.
22 if (prop == symbol_color_0) {
2196
5/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
6 if (!lbm_is_number(args[2]) || !(is_regular || is_gradient)) {
2197 2 return ENC_SYM_TERROR;
2198 }
2199 4 color->color1 = lbm_dec_as_i32(args[2]);
2200
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
16 } else if (prop == symbol_color_1) {
2201
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
3 if (!lbm_is_number(args[2]) || !is_gradient) {
2202 2 return ENC_SYM_TERROR;
2203 }
2204 1 color->color2 = lbm_dec_as_i32(args[2]);
2205
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 } else if (prop == symbol_width) {
2206
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
5 if (!lbm_is_number(args[2]) || !is_gradient) {
2207 3 return ENC_SYM_TERROR;
2208 }
2209 2 color->param1 = (uint16_t)lbm_dec_as_u32(args[2]);
2210
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 } else if (prop == symbol_offset) {
2211
4/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
3 if (!lbm_is_number(args[2]) || !(is_gradient || is_pre)) {
2212 1 return ENC_SYM_TERROR;
2213 }
2214 2 color->param2 = (uint16_t)lbm_dec_as_u32(args[2]);
2215
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 } else if (prop == symbol_repeat_type) {
2216
2/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if (!lbm_is_symbol(args[2]) || !(is_gradient || is_pre)) {
2217 return ENC_SYM_TERROR;
2218 }
2219 3 lbm_uint sym = lbm_dec_sym(args[2]);
2220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (sym == symbol_repeat) {
2221 color->mirrored = false;
2222
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 } else if (sym == symbol_mirrored) {
2223 2 color->mirrored = true;
2224 } else {
2225 1 return ENC_SYM_TERROR;
2226 }
2227 } else {
2228 2 return ENC_SYM_TERROR;
2229 }
2230
2231 11 return ENC_SYM_TRUE;
2232 }
2233
2234 23 static lbm_value ext_color_get(lbm_value *args, lbm_uint argn) {
2235 color_t *color;
2236
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 1 times.
23 if (argn != 2 || !(color = get_color(args[0])) || // color assignment
2237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 !lbm_is_symbol(args[1])) {
2238 1 return ENC_SYM_TERROR;
2239 }
2240
2241
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 8 times.
22 bool is_gradient = color->type == COLOR_GRADIENT_X || color->type == COLOR_GRADIENT_Y;
2242
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 bool is_pre = color->type == COLOR_PRE_X || color->type == COLOR_PRE_Y;
2243
2244 22 lbm_uint prop = lbm_dec_sym(args[1]);
2245
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
22 if (prop == symbol_color_0) {
2246 // always allowed
2247 9 return lbm_enc_u32((uint32_t)color->color1);
2248
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
13 } else if (prop == symbol_color_1) {
2249
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
4 if (!is_gradient && !is_pre) {
2250 1 return ENC_SYM_TERROR;
2251 }
2252 3 return lbm_enc_u32((uint32_t)color->color2);
2253
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 } else if (prop == symbol_width) {
2254
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
5 if (!is_gradient && !is_pre) {
2255 1 return ENC_SYM_TERROR;
2256 }
2257 4 return lbm_enc_i32((int32_t)color->param1);
2258
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 } else if (prop == symbol_offset) {
2259
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (!is_gradient && !is_pre) {
2260 return ENC_SYM_TERROR;
2261 }
2262 3 return lbm_enc_i32((int32_t)color->param2);
2263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (prop == symbol_repeat_type) {
2264 if (!is_gradient && !is_pre) {
2265 return ENC_SYM_TERROR;
2266 }
2267 return lbm_enc_sym(color->mirrored ? symbol_mirrored : symbol_repeat);
2268 } else {
2269 1 return ENC_SYM_TERROR;
2270 }
2271
2272 return ENC_SYM_TRUE;
2273 }
2274
2275 7 static lbm_value ext_color_setpre(lbm_value *args, lbm_uint argn) {
2276 color_t *color;
2277
3/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
7 if (argn != 3 || !(color = get_color(args[0])) ||
2278
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
6 !lbm_is_number(args[1]) || !lbm_is_number(args[2])) {
2279 3 return ENC_SYM_TERROR;
2280 }
2281
2282 4 uint32_t pos = lbm_dec_as_u32(args[1]);
2283 4 int new_color = lbm_dec_as_i32(args[2]);
2284
2285
3/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
4 if (color->precalc == 0 || pos >= COLOR_PRECALC_LEN) {
2286 1 return ENC_SYM_EERROR;
2287 }
2288
2289 3 color->precalc[pos] = (uint32_t)new_color;
2290
2291 3 return ENC_SYM_TRUE;
2292 }
2293
2294 14 static lbm_value ext_color_getpre(lbm_value *args, lbm_uint argn) {
2295 color_t *color;
2296
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 1 times.
14 if (argn != 2 || !(color = get_color(args[0])) ||
2297
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
13 !lbm_is_number(args[1])) {
2298 2 return ENC_SYM_TERROR;
2299 }
2300 12 uint32_t pos = lbm_dec_as_u32(args[1]);
2301
2302
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 11 times.
12 if (color->precalc == 0 || pos >= COLOR_PRECALC_LEN) {
2303 1 return ENC_SYM_EERROR;
2304 }
2305
2306 11 return lbm_enc_u32(color->precalc[pos]);
2307 }
2308
2309 57 static lbm_value ext_clear(lbm_value *args, lbm_uint argn) {
2310
2311 57 lbm_value res = ENC_SYM_TERROR;
2312 lbm_array_header_t *arr;
2313
5/6
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
✓ Branch 5 taken 3 times.
114 if ((argn == 1 || argn == 2) &&
2314
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 1 times.
111 (arr = get_image_buffer(args[0])) && // assignment
2315
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 (argn != 2 || lbm_is_number(args[1]))) { // ( argn == 2 -> lbm_is_number(args[1]))
2316 image_buffer_t img_buf;
2317 54 img_buf.width = image_buffer_width((uint8_t*)arr->data);
2318 54 img_buf.height = image_buffer_height((uint8_t*)arr->data);
2319 54 img_buf.fmt = image_buffer_format((uint8_t*)arr->data);
2320 54 img_buf.mem_base = (uint8_t*)arr->data;
2321 54 img_buf.data = image_buffer_data((uint8_t*)arr->data);
2322
2323 54 uint32_t color = 0;
2324
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 1 times.
54 if (argn == 2) {
2325 53 color = lbm_dec_as_u32(args[1]);
2326 }
2327
2328 54 image_buffer_clear(&img_buf, color);
2329 54 res = ENC_SYM_TRUE;
2330 }
2331 57 return res;
2332 }
2333
2334 113 static lbm_value ext_putpixel(lbm_value *args, lbm_uint argn) {
2335 113 img_args_t arg_dec = decode_args(args, argn, 3);
2336
2337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113 if (!arg_dec.is_valid) {
2338 return ENC_SYM_TERROR;
2339 }
2340
2341 113 putpixel(&arg_dec.img,
2342 lbm_dec_as_i32(arg_dec.args[0]),
2343 lbm_dec_as_i32(arg_dec.args[1]),
2344 lbm_dec_as_u32(arg_dec.args[2]));
2345 113 return ENC_SYM_TRUE;
2346 }
2347
2348 167 static lbm_value ext_getpixel(lbm_value *args, lbm_uint argn) {
2349 167 img_args_t arg_dec = decode_args(args, argn, 2);
2350
2351
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 143 times.
167 if (!arg_dec.is_valid) {
2352 24 return ENC_SYM_TERROR;
2353 }
2354
2355 143 uint32_t c = getpixel(&arg_dec.img,
2356 lbm_dec_as_i32(arg_dec.args[0]),
2357 lbm_dec_as_i32(arg_dec.args[1]));
2358 143 return lbm_enc_u32(c);
2359 }
2360
2361 // lisp args: img x1 y1 x2 y2 color opt-attr1 ... opt-attrN
2362 43 static lbm_value ext_line(lbm_value *args, lbm_uint argn) {
2363 43 img_args_t arg_dec = decode_args(args, argn, 5);
2364
2365
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 41 times.
43 if (!arg_dec.is_valid) {
2366 2 return ENC_SYM_TERROR;
2367 }
2368
2369 41 line(&arg_dec.img,
2370 lbm_dec_as_i32(arg_dec.args[0]),
2371 lbm_dec_as_i32(arg_dec.args[1]),
2372 lbm_dec_as_i32(arg_dec.args[2]),
2373 lbm_dec_as_i32(arg_dec.args[3]),
2374 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2375 lbm_dec_as_i32(arg_dec.attr_dotted.args[0]),
2376 lbm_dec_as_i32(arg_dec.attr_dotted.args[1]),
2377 lbm_dec_as_u32(arg_dec.args[4]));
2378
2379 41 return ENC_SYM_TRUE;
2380 }
2381
2382 // lisp args: img cx cy r color opt-attr1 ... opt-attrN
2383 83 static lbm_value ext_circle(lbm_value *args, lbm_uint argn) {
2384 83 img_args_t arg_dec = decode_args(args, argn, 4);
2385
2386
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 80 times.
83 if (!arg_dec.is_valid) {
2387 3 return ENC_SYM_TERROR;
2388 }
2389
2390
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 57 times.
80 if (arg_dec.attr_filled.is_valid) {
2391 23 fill_circle(&arg_dec.img,
2392 lbm_dec_as_i32(arg_dec.args[0]),
2393 lbm_dec_as_i32(arg_dec.args[1]),
2394 lbm_dec_as_i32(arg_dec.args[2]),
2395 lbm_dec_as_u32(arg_dec.args[3]));
2396
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 78 times.
80 } if (arg_dec.attr_dotted.is_valid) {
2397 4 arc(&arg_dec.img,
2398 lbm_dec_as_i32(arg_dec.args[0]),
2399 lbm_dec_as_i32(arg_dec.args[1]),
2400 lbm_dec_as_i32(arg_dec.args[2]),
2401 0, 359.9f,
2402 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2403 2 arg_dec.attr_rounded.is_valid, // currently does nothing as the line function doesn't support square ends.
2404 false,
2405 false, false,
2406 lbm_dec_as_i32(arg_dec.attr_dotted.args[0]),
2407 lbm_dec_as_i32(arg_dec.attr_dotted.args[1]),
2408 lbm_dec_as_i32(arg_dec.attr_resolution.args[0]),
2409 lbm_dec_as_u32(arg_dec.args[3]));
2410 } else {
2411 78 circle(&arg_dec.img,
2412 lbm_dec_as_i32(arg_dec.args[0]),
2413 lbm_dec_as_i32(arg_dec.args[1]),
2414 lbm_dec_as_i32(arg_dec.args[2]),
2415 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2416 lbm_dec_as_u32(arg_dec.args[3]));
2417 }
2418
2419 80 return ENC_SYM_TRUE;
2420 }
2421
2422 // lisp args: img cx cy r ang-s ang-e color opt-attr1 ... opt-attrN
2423 137 static lbm_value ext_arc(lbm_value *args, lbm_uint argn) {
2424 137 img_args_t arg_dec = decode_args(args, argn, 6);
2425
2426
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 128 times.
137 if (!arg_dec.is_valid) {
2427 9 return ENC_SYM_TERROR;
2428 }
2429
2430 256 arc(&arg_dec.img,
2431 lbm_dec_as_i32(arg_dec.args[0]),
2432 lbm_dec_as_i32(arg_dec.args[1]),
2433 lbm_dec_as_i32(arg_dec.args[2]),
2434 lbm_dec_as_float(arg_dec.args[3]),
2435 lbm_dec_as_float(arg_dec.args[4]),
2436 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2437 128 arg_dec.attr_rounded.is_valid,
2438 128 arg_dec.attr_filled.is_valid,
2439 false, false,
2440 lbm_dec_as_i32(arg_dec.attr_dotted.args[0]),
2441 lbm_dec_as_i32(arg_dec.attr_dotted.args[1]),
2442 lbm_dec_as_i32(arg_dec.attr_resolution.args[0]),
2443 lbm_dec_as_u32(arg_dec.args[5]));
2444
2445 128 return ENC_SYM_TRUE;
2446 }
2447
2448 // lisp args: img cx cy r ang-s ang-e color opt-attr1 ... opt-attrN
2449 51 static lbm_value ext_circle_sector(lbm_value *args, lbm_uint argn) {
2450 51 img_args_t arg_dec = decode_args(args, argn, 6);
2451
2452
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 50 times.
51 if (!arg_dec.is_valid) {
2453 1 return ENC_SYM_TERROR;
2454 }
2455
2456 100 arc(&arg_dec.img,
2457 lbm_dec_as_i32(arg_dec.args[0]),
2458 lbm_dec_as_i32(arg_dec.args[1]),
2459 lbm_dec_as_i32(arg_dec.args[2]),
2460 lbm_dec_as_float(arg_dec.args[3]),
2461 lbm_dec_as_float(arg_dec.args[4]),
2462 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2463 true,
2464 50 arg_dec.attr_filled.is_valid,
2465 true, false,
2466 lbm_dec_as_i32(arg_dec.attr_dotted.args[0]),
2467 lbm_dec_as_i32(arg_dec.attr_dotted.args[1]),
2468 lbm_dec_as_i32(arg_dec.attr_resolution.args[0]),
2469 lbm_dec_as_u32(arg_dec.args[5]));
2470
2471 50 return ENC_SYM_TRUE;
2472 }
2473
2474 // lisp args: img cx cy r ang-s ang-e color opt-attr1 ... opt-attrN
2475 284 static lbm_value ext_circle_segment(lbm_value *args, lbm_uint argn) {
2476 284 img_args_t arg_dec = decode_args(args, argn, 6);
2477
2478
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 273 times.
284 if (!arg_dec.is_valid) {
2479 11 return ENC_SYM_TERROR;
2480 }
2481
2482 546 arc(&arg_dec.img,
2483 lbm_dec_as_i32(arg_dec.args[0]),
2484 lbm_dec_as_i32(arg_dec.args[1]),
2485 lbm_dec_as_i32(arg_dec.args[2]),
2486 lbm_dec_as_float(arg_dec.args[3]),
2487 lbm_dec_as_float(arg_dec.args[4]),
2488 lbm_dec_as_i32(arg_dec.attr_thickness.args[0]),
2489 true,
2490 273 arg_dec.attr_filled.is_valid,
2491 false, true,
2492 lbm_dec_as_i32(arg_dec.attr_dotted.args[0]),
2493 lbm_dec_as_i32(arg_dec.attr_dotted.args[1]),
2494 lbm_dec_as_i32(arg_dec.attr_resolution.args[0]),
2495 lbm_dec_as_u32(arg_dec.args[5]));
2496
2497
2498 273 return ENC_SYM_TRUE;
2499 }
2500
2501 // lisp args: img x y width height color opt-attr1 ... opt-attrN
2502 108 static lbm_value ext_rectangle(lbm_value *args, lbm_uint argn) {
2503 108 img_args_t arg_dec = decode_args(args, argn, 5);
2504
2505
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 105 times.
108 if (!arg_dec.is_valid) {
2506 3 return ENC_SYM_TERROR;
2507 }
2508
2509 105 image_buffer_t *img = &arg_dec.img;
2510 105 int x = lbm_dec_as_i32(arg_dec.args[0]);
2511 105 int y = lbm_dec_as_i32(arg_dec.args[1]);
2512 105 int width = lbm_dec_as_i32(arg_dec.args[2]);
2513 105 int height = lbm_dec_as_i32(arg_dec.args[3]);
2514 105 int rad = lbm_dec_as_i32(arg_dec.attr_rounded.args[0]);
2515 105 int thickness = lbm_dec_as_i32(arg_dec.attr_thickness.args[0]);
2516 105 uint32_t color = lbm_dec_as_u32(arg_dec.args[4]);
2517 105 int dot1 = lbm_dec_as_i32(arg_dec.attr_dotted.args[0]);
2518 105 int dot2 = lbm_dec_as_i32(arg_dec.attr_dotted.args[1]);
2519 105 int resolution = lbm_dec_as_i32(arg_dec.attr_resolution.args[0]);
2520
2521
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 78 times.
105 if (arg_dec.attr_rounded.is_valid) {
2522
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 23 times.
27 if (arg_dec.attr_filled.is_valid) {
2523 4 rectangle(img, x + rad, y, width - 2 * rad, rad, 1, 1, 0, 0, color);
2524 4 rectangle(img, x + rad, y + height - rad, width - 2 * rad, rad, 1, 1, 0, 0, color);
2525 4 rectangle(img, x, y + rad, width, height - 2 * rad, 1, 1, 0, 0, color);
2526 4 fill_circle(img, x + rad, y + rad, rad, color);
2527 4 fill_circle(img, x + rad, y + height - rad, rad, color);
2528 4 fill_circle(img, x + width - rad, y + rad, rad, color);
2529 4 fill_circle(img, x + width - rad, y + height - rad, rad, color);
2530 } else {
2531 // Remember to change these to use the rounded attribute,
2532 // when/if line supports it!
2533
2534 23 int line_thickness = thickness / 2;
2535 23 thickness = line_thickness * 2; // round it to even for consistency.
2536
2537 // top
2538 23 line(img, x + rad, y + line_thickness, x + width - rad, y + line_thickness, line_thickness, dot1, dot2, color);
2539 // bottom
2540 23 line(img, x + rad, y + height - line_thickness, x + width - rad, y + height - line_thickness, line_thickness, dot1, dot2, color);
2541 // left
2542 23 line(img, x + line_thickness, y + rad, x + line_thickness, y + height - rad, line_thickness, dot1, dot2, color);
2543 // right
2544 23 line(img, x + width - line_thickness, y + rad, x + width - line_thickness, y + height - rad, line_thickness, dot1, dot2, color);
2545
2546 // upper left
2547 23 arc(img, x + rad, y + rad, rad, 180, 270, thickness, false, false, false, false, dot1, dot2, resolution, color);
2548 // upper right
2549 23 arc(img, x + width - rad, y + rad, rad, 270, 0, thickness, false, false, false, false, dot1, dot2, resolution, color);
2550 // bottom left
2551 23 arc(img, x + rad, y + height - rad, rad, 90, 180, thickness, false, false, false, false, dot1, dot2, resolution, color);
2552 // bottom right
2553 23 arc(img, x + width - rad, y + height - rad, rad, 0, 90, thickness, false, false, false, false, dot1, dot2, resolution, color);
2554 }
2555 } else {
2556 78 rectangle(img,
2557 x, y,
2558 width, height,
2559 78 arg_dec.attr_filled.is_valid,
2560 thickness,
2561 dot1, dot2,
2562 color);
2563 }
2564
2565 105 return ENC_SYM_TRUE;
2566 }
2567
2568 // lisp args: img x1 y1 x2 y2 x3 y3 color opt-attr1 ... opt-attrN
2569 22 static lbm_value ext_triangle(lbm_value *args, lbm_uint argn) {
2570 22 img_args_t arg_dec = decode_args(args, argn, 7);
2571
2572
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (!arg_dec.is_valid) {
2573 2 return ENC_SYM_TERROR;
2574 }
2575
2576 20 image_buffer_t *img = &arg_dec.img;
2577 20 int x0 = lbm_dec_as_i32(arg_dec.args[0]);
2578 20 int y0 = lbm_dec_as_i32(arg_dec.args[1]);
2579 20 int x1 = lbm_dec_as_i32(arg_dec.args[2]);
2580 20 int y1 = lbm_dec_as_i32(arg_dec.args[3]);
2581 20 int x2 = lbm_dec_as_i32(arg_dec.args[4]);
2582 20 int y2 = lbm_dec_as_i32(arg_dec.args[5]);
2583 20 int thickness = lbm_dec_as_i32(arg_dec.attr_thickness.args[0]);
2584 20 int dot1 = lbm_dec_as_i32(arg_dec.attr_dotted.args[0]);
2585 20 int dot2 = lbm_dec_as_i32(arg_dec.attr_dotted.args[1]);
2586 20 uint32_t color = lbm_dec_as_u32(arg_dec.args[6]);
2587
2588
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
20 if (arg_dec.attr_filled.is_valid) {
2589 2 fill_triangle(img, x0, y0, x1, y1, x2, y2, color);
2590 } else {
2591 18 line(img, x0, y0, x1, y1, thickness, dot1, dot2, color);
2592 18 line(img, x1, y1, x2, y2, thickness, dot1, dot2, color);
2593 18 line(img, x2, y2, x0, y0, thickness, dot1, dot2, color);
2594 }
2595
2596 20 return ENC_SYM_TRUE;
2597 }
2598
2599 // lisp args: img x y fg bg font str
2600 107 static lbm_value ext_text(lbm_value *args, lbm_uint argn) {
2601 107 bool up = false;
2602 107 bool down = false;
2603
2604
4/4
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 63 times.
107 if (argn >= 7 && lbm_is_symbol(args[argn - 1])) {
2605
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 15 times.
28 if (lbm_dec_sym(args[argn - 1]) == symbol_up) {
2606 13 up = true;
2607 13 argn--;
2608 }
2609
2610
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 15 times.
28 if (lbm_dec_sym(args[argn - 1]) == symbol_down) {
2611 13 down = true;
2612 13 argn--;
2613 }
2614 }
2615
2616
4/4
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 88 times.
107 if (argn != 6 && argn != 7) {
2617 3 return ENC_SYM_TERROR;
2618 }
2619
2620 104 int x = lbm_dec_as_i32(args[1]);
2621 104 int y = lbm_dec_as_i32(args[2]);
2622
2623 104 int32_t colors[4] = {-1, -1, -1, -1}; // how big? int vs int32
2624
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 16 times.
104 if (argn == 7) {
2625
2/4
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
88 if (!lbm_is_number(args[3]) || !lbm_is_number(args[4])) {
2626 return ENC_SYM_TERROR;
2627 }
2628 88 colors[0] = lbm_dec_as_i32(args[3]);
2629 88 colors[1] = lbm_dec_as_i32(args[4]);
2630 } else {
2631 16 lbm_value curr = args[3];
2632 16 int ind = 0;
2633
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 11 times.
58 while (lbm_is_cons(curr)) {
2634 47 lbm_value arg = lbm_car(curr);
2635
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if (lbm_is_number(arg)) {
2636 47 colors[ind++] = lbm_dec_as_i32(arg);
2637 } else {
2638 return ENC_SYM_TERROR;
2639 }
2640
2641
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 42 times.
47 if (ind == 4) {
2642 5 break;
2643 }
2644
2645 42 curr = lbm_cdr(curr);
2646 }
2647 }
2648
2649
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 100 times.
104 if (!array_is_image_buffer(args[0])) {
2650 4 return ENC_SYM_TERROR;
2651 }
2652 100 lbm_array_header_t *arr = (lbm_array_header_t *)lbm_car(args[0]);
2653 image_buffer_t img_buf;
2654 100 img_buf.width = image_buffer_width((uint8_t*)arr->data);
2655 100 img_buf.height = image_buffer_height((uint8_t*)arr->data);
2656 100 img_buf.fmt = image_buffer_format((uint8_t*)arr->data);
2657 100 img_buf.mem_base = (uint8_t*)arr->data;
2658 100 img_buf.data = image_buffer_data((uint8_t*)arr->data);
2659
2660 100 lbm_array_header_t *font = 0;
2661 // Allow both const and non-const fonts.
2662
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (lbm_type_of_functional(args[argn - 2]) == LBM_TYPE_ARRAY) {
2663 100 font = (lbm_array_header_t *)lbm_car(args[argn - 2]);
2664 }
2665
2666 100 char *txt = lbm_dec_str(args[argn - 1]);
2667
2668
5/6
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 97 times.
100 if (!font || !txt || font->size < (4 + 5 * 5 * 10)) {
2669 3 return ENC_SYM_TERROR;
2670 }
2671
2672 97 uint8_t *font_data = (uint8_t*)font->data;
2673 97 uint8_t w = font_data[0];
2674 97 uint8_t h = font_data[1];
2675
2676 97 int incx = 1;
2677 97 int incy = 0;
2678
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 84 times.
97 if (up) {
2679 13 incx = 0;
2680 13 incy = -1;
2681
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 71 times.
84 } else if (down) {
2682 13 incx = 0;
2683 13 incy = 1;
2684 }
2685
2686 97 int ind = 0;
2687
2/2
✓ Branch 0 taken 734 times.
✓ Branch 1 taken 97 times.
831 while (txt[ind] != 0) {
2688 2202 img_putc(&img_buf,
2689
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 547 times.
734 x + ind * ((up || down) ? h : w) * incx,
2690
4/4
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 547 times.
✓ Branch 2 taken 649 times.
✓ Branch 3 taken 85 times.
734 y + ind * ((up || down) ? w : h) * incy,
2691 (uint32_t *)colors,
2692 4,
2693 font_data,
2694
2/2
✓ Branch 0 taken 649 times.
✓ Branch 1 taken 85 times.
734 (uint8_t)txt[ind],
2695 up,
2696 down);
2697 734 ind++;
2698 }
2699
2700 97 return ENC_SYM_TRUE;
2701 }
2702
2703 51 static lbm_value ext_blit(lbm_value *args, lbm_uint argn) {
2704 51 img_args_t arg_dec = decode_args(args + 1, argn - 1, 3);
2705
2706 51 lbm_value res = ENC_SYM_TERROR;
2707 lbm_array_header_t *arr;
2708
4/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 3 times.
51 if (arg_dec.is_valid && (arr = get_image_buffer(args[0]))) { //assignment
2709 image_buffer_t dest_buf;
2710 42 dest_buf.width = image_buffer_width((uint8_t*)arr->data);
2711 42 dest_buf.height = image_buffer_height((uint8_t*)arr->data);
2712 42 dest_buf.fmt = image_buffer_format((uint8_t*)arr->data);
2713 42 dest_buf.mem_base = (uint8_t*)arr->data;
2714 42 dest_buf.data = image_buffer_data((uint8_t*)arr->data);
2715
2716 42 float scale = 1.0;
2717
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28 times.
42 if (arg_dec.attr_scale.is_valid) {
2718 14 scale = lbm_dec_as_float(arg_dec.attr_scale.args[0]);
2719 }
2720
2721 108 blit(
2722 &dest_buf,
2723 &arg_dec.img,
2724 lbm_dec_as_i32(arg_dec.args[0]),
2725 lbm_dec_as_i32(arg_dec.args[1]),
2726 lbm_dec_as_float(arg_dec.attr_rotate.args[0]),
2727 lbm_dec_as_float(arg_dec.attr_rotate.args[1]),
2728 lbm_dec_as_float(arg_dec.attr_rotate.args[2]),
2729 scale,
2730 lbm_dec_as_i32(arg_dec.args[2]),
2731 42 arg_dec.attr_tile.is_valid,
2732
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 33 times.
42 arg_dec.attr_clip.is_valid ? lbm_dec_as_i32(arg_dec.attr_clip.args[0]) : 0,
2733
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 33 times.
42 arg_dec.attr_clip.is_valid ? lbm_dec_as_i32(arg_dec.attr_clip.args[1]) : 0,
2734
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 33 times.
42 arg_dec.attr_clip.is_valid ? lbm_dec_as_i32(arg_dec.attr_clip.args[2]) : dest_buf.width,
2735
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 33 times.
42 arg_dec.attr_clip.is_valid ? lbm_dec_as_i32(arg_dec.attr_clip.args[3]) : dest_buf.height
2736 );
2737 42 res = ENC_SYM_TRUE;
2738 }
2739 51 return res;
2740 }
2741
2742 void display_dummy_reset(void) {
2743 return;
2744 }
2745
2746 void display_dummy_clear(uint32_t color) {
2747 (void) color;
2748 return;
2749 }
2750
2751 bool display_dummy_render_image(image_buffer_t *img, uint16_t x, uint16_t y, color_t *colors) {
2752 (void) img;
2753 (void) x;
2754 (void) y;
2755 (void) colors;
2756 return false;
2757 }
2758
2759 static bool(* volatile disp_render_image)(image_buffer_t *img, uint16_t x, uint16_t y, color_t *colors) = display_dummy_render_image;
2760 static void(* volatile disp_clear)(uint32_t color) = display_dummy_clear;
2761 static void(* volatile disp_reset)(void) = display_dummy_reset;
2762
2763 static char *msg_not_supported = "Command not supported or display driver not initialized";
2764
2765 1 static lbm_value ext_disp_reset(lbm_value *args, lbm_uint argn) {
2766 (void) args;
2767 (void) argn;
2768
2769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (disp_reset == NULL) {
2770 lbm_set_error_reason(msg_not_supported);
2771 return ENC_SYM_EERROR;
2772 }
2773
2774 1 disp_reset();
2775
2776 1 return ENC_SYM_TRUE;
2777 }
2778
2779 1 static lbm_value ext_disp_clear(lbm_value *args, lbm_uint argn) {
2780
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (disp_clear == NULL) {
2781 lbm_set_error_reason(msg_not_supported);
2782 return ENC_SYM_EERROR;
2783 }
2784
2785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (argn > 1) {
2786 return ENC_SYM_TERROR;
2787 }
2788
2789 1 uint32_t clear_color = 0;
2790
2791
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (argn == 1) {
2792 if (!lbm_is_number(args[0])) {
2793 return ENC_SYM_TERROR;
2794 }
2795
2796 clear_color = lbm_dec_as_u32(args[0]);
2797 }
2798
2799 1 disp_clear(clear_color);
2800
2801 1 return ENC_SYM_TRUE;
2802 }
2803
2804 50 static lbm_value ext_disp_render(lbm_value *args, lbm_uint argn) {
2805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (disp_render_image == NULL) {
2806 lbm_set_error_reason(msg_not_supported);
2807 return ENC_SYM_EERROR;
2808 }
2809
2810 50 lbm_value res = ENC_SYM_TERROR;
2811 lbm_array_header_t *arr;
2812
4/6
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 50 times.
✗ Branch 5 not taken.
100 if ((argn == 3 || argn == 4) &&
2813
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
100 (arr = get_image_buffer(args[0])) &&
2814
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
100 lbm_is_number(args[1]) &&
2815 50 lbm_is_number(args[2])) {
2816 image_buffer_t img_buf;
2817 50 img_buf.fmt = image_buffer_format((uint8_t*)arr->data);
2818 50 img_buf.width = image_buffer_width((uint8_t*)arr->data);
2819 50 img_buf.height = image_buffer_height((uint8_t*)arr->data);
2820 50 img_buf.mem_base = (uint8_t*)arr->data;
2821 50 img_buf.data = image_buffer_data((uint8_t*)arr->data);
2822
2823 color_t colors[16];
2824 50 memset(colors, 0, sizeof(color_t) * 16);
2825
2826
3/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
50 if (argn == 4 && lbm_is_list(args[3])) {
2827 45 int i = 0;
2828 45 lbm_value curr = args[3];
2829
3/4
✓ Branch 0 taken 298 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 298 times.
✗ Branch 3 not taken.
343 while (lbm_is_cons(curr) && i < 16) {
2830 298 lbm_value arg = lbm_car(curr);
2831 color_t *color;
2832
2/2
✓ Branch 0 taken 290 times.
✓ Branch 1 taken 8 times.
298 if (lbm_is_number(arg)) {
2833 290 colors[i].color1 = (int)lbm_dec_as_u32(arg);
2834
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 } else if ((color = get_color(arg))) { // color assignment
2835 8 colors[i] = *color;
2836 } else {
2837 return ENC_SYM_TERROR;
2838 }
2839
2840 298 curr = lbm_cdr(curr);
2841 298 i++;
2842 }
2843 }
2844
2845 // img_buf is a stack allocated image_buffer_t.
2846 50 bool render_res = disp_render_image(&img_buf, (uint16_t)lbm_dec_as_u32(args[1]), (uint16_t)lbm_dec_as_u32(args[2]), colors);
2847
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (!render_res) {
2848 lbm_set_error_reason("Could not render image. Check if the format and location is compatible with the display.");
2849 return ENC_SYM_EERROR;
2850 }
2851 50 res = ENC_SYM_TRUE;
2852 }
2853 50 return res;
2854 }
2855
2856 // Jpg decoder
2857
2858 typedef struct {
2859 uint8_t *data;
2860 int pos;
2861 int size;
2862 int ofs_x;
2863 int ofs_y;
2864 } jpg_bufdef;
2865
2866 105 size_t jpg_input_func (JDEC* jd, uint8_t* buff, size_t ndata) {
2867 105 jpg_bufdef *dev = (jpg_bufdef*)jd->device;
2868
2869
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 104 times.
105 if ((int)ndata > (dev->size - dev->pos)) {
2870 1 ndata = (size_t)(dev->size - dev->pos);
2871 }
2872
2873
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 3 times.
105 if (buff) {
2874 102 memcpy(buff, dev->data + dev->pos, ndata);
2875 }
2876 105 dev->pos += (int)ndata;
2877 105 return ndata;
2878 }
2879
2880 1530 int jpg_output_func ( /* 1:Ok, 0:Aborted */
2881 JDEC* jd, /* Decompression object */
2882 void* bitmap, /* Bitmap data to be output */
2883 JRECT* rect /* Rectangular region to output */
2884 ) {
2885 1530 jpg_bufdef *dev = (jpg_bufdef*)jd->device;
2886
2887 image_buffer_t img;
2888 1530 img.mem_base = (uint8_t*)bitmap;
2889 1530 img.data = (uint8_t*)bitmap;
2890 1530 img.width = (uint16_t)(rect->right - rect->left + 1);
2891 1530 img.height = (uint16_t)(rect->bottom - rect->top + 1);
2892 1530 img.fmt = rgb888;
2893
2894 1530 disp_render_image(&img, (uint16_t)(rect->left + dev->ofs_x), (uint16_t)(rect->top + dev->ofs_y), 0);
2895
2896 1530 return 1;
2897 }
2898
2899 1 static lbm_value ext_disp_render_jpg(lbm_value *args, lbm_uint argn) {
2900
2901 lbm_array_header_t *array;
2902 1 lbm_value res = ENC_SYM_TERROR;
2903
2904
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 if (argn == 3 &&
2905
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 (array = lbm_dec_array_r(args[0])) && //asignment
2906
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 lbm_is_number(args[1]) &&
2907 1 lbm_is_number(args[2])) {
2908
2909 JDEC jd;
2910 void *jdwork;
2911 // make a bit of room before the buffer.
2912 1 const size_t sz_work = 4096 + IMAGE_BUFFER_HEADER_SIZE;
2913
2914 1 jdwork = lbm_malloc(sz_work);
2915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!jdwork) {
2916 return ENC_SYM_MERROR;
2917 }
2918
2919
2920
2921 jpg_bufdef iodev;
2922 1 iodev.data = (uint8_t*)(array->data);
2923 1 iodev.size = (int)array->size;
2924 1 iodev.pos = 0;
2925 1 iodev.ofs_x = lbm_dec_as_i32(args[1]);
2926 1 iodev.ofs_y = lbm_dec_as_i32(args[2]);
2927 1 jd_prepare(&jd, jpg_input_func, jdwork, sz_work + IMAGE_BUFFER_HEADER_SIZE, &iodev);
2928 1 jd_decomp(&jd, jpg_output_func, 0);
2929 1 lbm_free(jdwork);
2930 1 res = ENC_SYM_TRUE;
2931 }
2932 1 return res;
2933 }
2934
2935 411 void lbm_display_extensions_init(void) {
2936 411 register_symbols();
2937
2938 411 disp_render_image = NULL;
2939 411 disp_clear = NULL;
2940 411 disp_reset = NULL;
2941
2942 411 lbm_add_extension("img-buffer", ext_image_buffer);
2943 411 lbm_add_extension("img-buffer?", ext_is_image_buffer);
2944 411 lbm_add_extension("img-color", ext_color);
2945 411 lbm_add_extension("img-color-set", ext_color_set);
2946 411 lbm_add_extension("img-color-get", ext_color_get);
2947 411 lbm_add_extension("img-color-setpre", ext_color_setpre);
2948 411 lbm_add_extension("img-color-getpre", ext_color_getpre);
2949 411 lbm_add_extension("img-dims", ext_image_dims);
2950 411 lbm_add_extension("img-setpix", ext_putpixel);
2951 411 lbm_add_extension("img-getpix", ext_getpixel);
2952 411 lbm_add_extension("img-line", ext_line);
2953 411 lbm_add_extension("img-text", ext_text);
2954 411 lbm_add_extension("img-clear", ext_clear);
2955 411 lbm_add_extension("img-circle", ext_circle);
2956 411 lbm_add_extension("img-arc", ext_arc);
2957 411 lbm_add_extension("img-circle-sector", ext_circle_sector);
2958 411 lbm_add_extension("img-circle-segment", ext_circle_segment);
2959 411 lbm_add_extension("img-rectangle", ext_rectangle);
2960 411 lbm_add_extension("img-triangle", ext_triangle);
2961 411 lbm_add_extension("img-blit", ext_blit);
2962
2963 411 lbm_add_extension("disp-reset", ext_disp_reset);
2964 411 lbm_add_extension("disp-clear", ext_disp_clear);
2965 411 lbm_add_extension("disp-render", ext_disp_render);
2966 411 lbm_add_extension("disp-render-jpg", ext_disp_render_jpg);
2967 411 }
2968
2969 223 void lbm_display_extensions_set_callbacks(
2970 bool(* volatile render_image)(image_buffer_t *img, uint16_t x, uint16_t y, color_t *colors),
2971 void(* volatile clear)(uint32_t color),
2972 void(* volatile reset)(void)
2973 ) {
2974
1/2
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
223 disp_render_image = render_image ? render_image : display_dummy_render_image;
2975
1/2
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
223 disp_clear = clear ? clear : display_dummy_clear;
2976
1/2
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
223 disp_reset = reset ? reset : display_dummy_reset;
2977 223 }
2978