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