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