GCC Code Coverage Report


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