-
Notifications
You must be signed in to change notification settings - Fork 0
/
Utilities.spin2
467 lines (401 loc) · 13.5 KB
/
Utilities.spin2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
{Utilities for Text Display of numbers and drawing}
CON
DEGREE = $80000000 / 180 '32bit degree unit
OBJ
color : "Colors"
graphics : "Graphics"
PUB null()
''+--------------------------------------------------------------------+
'' Math methods
''+--------------------------------------------------------------------+
PUB exponent(a,b):r
''Calculates a to the b power
{
Spin 2 apparently does not provide an exponent
operator. This is an implementation of the
exponent operation.
}
r := 1
if b == 0
return
else
repeat b
r := r * a
PUB scale(x,xmin,xmax,ymin,ymax):y | b, yr, xr
''Calculates y for a given x
{
This function implements the line equation y = m*x + b
for integer math.
}
yr := (ymax > ymin)?(ymax-ymin): -1 * (ymin-ymax)
xr := (xmax > xmin)?(xmax-xmin): -1 * (xmin-xmax)
b := (ymax - (yr * xmax)/xr) 'm = yr/xr, but have to multiply first by xmax to prevent 0 when yr < xr (integer math)
y := x*yr/xr + b
PUB pol_to_xy(l,angle):x,y
''Calculates xy for l,angle
{
This function returns the coordinates
of a point with polar coordinates l,angle
}
angle := angle * DEGREE
ORG
QROTATE l, angle
GETQX x
GETQY y
END
PUB rotate_point(xi,yi,a):x,y
'' Does the same as ROTXY
a := a*DEGREE
ORG
SETQ yi
QROTATE xi, a
GETQX x
GETQY y
END
PUB rotate_image_rotxy(orig, rotated, angle)| xr, yr, x, y, xa, ya
''Rotate 16x16 orig image into rotated (top-left is 0,0)
repeat x from 0 to 15
repeat y from 0 to 15
xr, yr := ROTXY(x-8, y-8, angle*DEGREE) 'fastest
'xr, yr := rotate_point(x-8, y-8, angle) 'slightly slower
'Constrain to avoid drawing defects
xr := -8 #> xr <# 7
yr := -8 #> yr <# 7
WORD[rotated][yr+8].[xr+8] := WORD[orig][y].[x]
''+--------------------------------------------------------------------+
'' Debugging Methods
''+--------------------------------------------------------------------+
PUB debug_icon(ptr)|y
'' Utility to debug contents of 16x16 image array
repeat y from 0 to 15
debug(ubin_word(WORD[ptr][y]))
PUB debug_string(s)|k, i, n
'' Utility to debug zero terminated strings
debug("Debugging string: ", ZSTR(s))
debug("characters:")
i := 0
repeat
k := BYTE[s][i++]
debug(uhex_byte(k))
if k == 0
quit
n := i-1
debug("total characters: ", udec(n))
''+--------------------------------------------------------------------+
'' Text Methods
''+--------------------------------------------------------------------+
PUB get_characters(buffer):size
return STRSIZE(buffer)
{PUB get_characters(buffer):characters|val
''Get number of characters in buffer
'debug(ZSTR(buffer))
repeat
val := BYTE[buffer + characters]
if val == 0
quit
else
characters++
}
PUB get_non_space_characters(buffer):characters|val
''Get number of characters in buffer
{
Checks buffer for characters until a terminating 0 is found
}
repeat
val := BYTE[buffer + characters]
if (val == 0) OR (val == $20)
quit
else
characters++
PUB get_text_width(buffer, font_size, compression):w|n,c
n := 0
repeat
c := BYTE[buffer + n]
if c == 0
'debug(udec(n),udec(font_size), udec(compression), udec(w))
w := (n > 0 AND font_size == 2) ? (w-4) : w 'Adjustment for 8x16 font
quit
else
w := w + (font_size*8 - compression)
if (font_size == 2) AND ((c == ($6C)) OR (c == ($69)) OR (c == ($3A)) OR (c == ($2E))) 'i, l, . , :
w := w - 2
n++
PUB get_text_height(font_size):h
h := font_size*8
PUB get_digits(no):digits | x
''Get the number of digits in no
{
Calculates how many digits are in the integer number
passed.
}
if no
x := no
repeat while x <> 0
x := x / 10
digits++
PUB set_value(val,buffer)|n,digits,exp_val,char_val,prev_digits,extra,c
''Fill buffer with digits of val
{
Takes an integer val and convert it to
characters representing its digits in buffer
When val has less digits than the previous
value, it inserts white_spaces in place of the
extra digits so draw_text can fill over those
characters with background color.
For this feature to work, Text_Display must be
configured to enable buffering (enable_buffering(true))
otherwise the white space character won't be drawn.
}
c := 0
extra := 0
digits := get_digits(val)
prev_digits := get_characters(buffer)
if Byte[buffer] == $20
prev_digits--
if val == 0
BYTE[buffer] := $30 '0 character
digits := 1
else
'When new number is less than previous number...
'fill with spaces to cover missing digits
if prev_digits > digits
extra := prev_digits - digits
repeat c from 0 to extra-1
BYTE[buffer + c] := $20 'Insert spaces
repeat n from digits to 1
exp_val := exponent(10, n-1)
char_val := val / exp_val
val := val - char_val*exp_val
BYTE[buffer + (extra) + (digits-n)] := char_val + $30 'number character
BYTE[buffer+digits+extra] := 0 'string terminating 0
PUB set_value_ns(val,buffer)|n,digits,exp_val,char_val
''Fill buffer with digits of val no-spaces
{
Takes an integer val and convert it to
characters representing its digits in buffer
}
digits := get_digits(val)
'debug("set_value_ns(", sdec(val),") ",sdec(digits) )
if val == 0
BYTE[buffer] := $30 '0 character
digits := 1
else
'No need to fill spaces
repeat n from digits to 1
exp_val := exponent(10, n-1)
char_val := val / exp_val
val := val - char_val*exp_val
BYTE[buffer + (digits-n)] := char_val + $30 'number character
BYTE[buffer+digits] := 0 'string terminating 0
PUB get_value(buffer):value|digits, char, d
''Returns value of buffer
{
Calculates the integer value of numerical characters
in buffer
}
digits := get_characters(buffer)
repeat d from 0 to digits-1
char := BYTE[buffer+d] & $FF
if (char >= $30) AND (char <= $39)
value := (char - $30)*exponent(10,(digits-d-1)) + value
PUB join_strings(string1_ptr, string2_ptr, result_ptr, max)|c1,c2,c,k
'' Join zero terminated strings into new string
'First string
repeat c1 from 0 to max-1
k := Byte[string1_ptr + c1]
if k == 0
c := c1
quit
Byte[result_ptr + c1] := k
'debug(uhex_byte(k))
'Second string
repeat c2 from 0 to (max-c-1)
k := Byte[string2_ptr + c2]
if k == 0 OR (c+c2) >= (max-1)
c := c + c2
quit
Byte[result_ptr + c + c2] := k
'debug(uhex_byte(k))
'End character (0)
Byte[result_ptr + c] := 0
PUB clear_string(addr, max)|i
'' Clears a zero terminated string (all values == 0)
repeat i from 0 to max
BYTE[addr][i] := 0
''+--------------------------------------------------------------------+
'' Drawing Methods
''+--------------------------------------------------------------------+
PUB draw_border(xt,yt,xb,yb,c,w,invert)| i, l_color, d_color
'' Draws a border (Used by widgets)
{
ptr - pointer to drawing_method array of method pointers
xt,yt,xb,yb - coordinates of rectangle
c - color
w - border width
invert - flag to invert the border colors (to simulate sunken borders)
}
if w > 0 AND xb > xt AND yb > yt
if invert
l_color := color.darken(c,4)
d_color := color.lighten(c,12)
else
l_color := color.lighten(c,12)
d_color := color.darken(c,4)
repeat i from 0 to w-1
'Left side
graphics.draw_line(xt+i,yt+i,xt+i,yb-i,l_color)
'Top side
graphics.draw_line(xt+i,yt+i,xb-i,yt+i,l_color)
'Right side
graphics.draw_line(xb-i,yt+i,xb-i,yb-i,d_color)
'Bottom side
graphics.draw_line(xt+i,yb-i,xb-i,yb-i,d_color)
PUB draw_text(x,y,buffer,previous_buffer,font_size,compression,bg_color,fg_color)| chars, n, w, h, k, k_old, xs, xe, execution_time
'' Draws a zero terminated string on the display
{
Draws buffer text on screen at x,y position.
If an address is passed through previous_buffer, then
it will only draw the characters that differ
from the previous_buffer.
---A special case is
the white space character ($20) which will fill
the area occupied by the white space with the
background color.---
ptr - pointer to drawing_method array of method pointers
x,y - coordinates of top left corner of text
buffer - address of text buffer (contents[])
previous_buffer - address of prev_contents[]
font_size - scaling factor for 8x8 font
compression - factor to reduce distance between characters in pixels
bg_color - background color
fg_color - foreground color
}
graphics.select_font_size(1 #> font_size <# 2)
chars := get_characters(buffer)
'debug("Utilities draw_text() ", sdec(chars))
'Early exit if no chars in buffer
if chars <= 0
return
w := font_size * 8
h := font_size * 8
'Set colors
graphics.set_background_color(bg_color)
graphics.set_foreground_color(fg_color)
'debug("Utilities draw_text() ", sdec(previous_buffer))
if previous_buffer <> 0
'Draw characters that are different from previous value
repeat n from 0 to chars-1
k := BYTE[buffer+n]
k_old := BYTE[previous_buffer + n]
xs := x+(w-compression)*(n)
xe := xs + w
if (font_size == 2) AND ((k == ($6C)) OR (k == ($69)) OR (k == ($3A)) OR (k == ($3B)) OR (k == ($2C)) OR (k == ($2E)) ) 'i, l, . , :
xs := xs - 2
xe := xe - 2
if k <> k_old 'when new character differs from previous character in buffer
graphics.set_window(xs, y, xe, y+h)
graphics.draw_char(k)
Byte[previous_buffer+n] := k
x := x - 4
else
if k <> k_old 'when new character differs from previous character in buffer
graphics.set_window(xs, y, xe, y+h)
graphics.draw_char(k)
Byte[previous_buffer+n] := k
else
'Draw all characters
repeat n from 0 to chars-1
k := BYTE[buffer+n]
xs := x+(w-compression)*(n)
xe := xs + w
if (font_size == 2) AND ((k == ($6C)) OR (k == ($69)) OR (k == ($3A)) OR (k == ($3B)) OR (k == ($2C)) OR (k == ($2E)) ) 'i, l, . , :
xs := xs - 2
xe := xe - 2
graphics.set_window(xs, y, xe, y+h)
graphics.draw_char(k)
x := x - 4
else
graphics.set_window(xs, y, xe, y+h)
graphics.draw_char(k)
PUB send_image(image_pointer, xpos, ypos, width, height, transparent_color)
if image_pointer
graphics.send_image(image_pointer,xpos,ypos,width,height)
PUB draw_image(image_pointer, xpos, ypos, width, height, transparent_color)| pixel, x, y
'' Draws image stored in FILE at xpos, ypos
{
image_pointer - address of image in hub memory
transparent_color - color to ignore when painting pixels
width - image width
height - image height
xpos - top-left coordinate of image
ypos - top-left coordinate of image
ptr - drawing_methods pointer
}
'Requires image in a DAT section using FILE that has been converted to 5-6-5 BGR
'format. I used a sketch in Processing to resize and convert any image into this
'format.
' DAT
' img FILE "image_file.p2"
if image_pointer == 0
return
repeat y from 0 to height-1
repeat x from 0 to width-1
pixel := WORD[image_pointer + (2*x + 2*y*width)]
if pixel <> transparent_color
graphics.draw_pixel(xpos+x,ypos+y,pixel)
PUB draw_image_area(image_pointer, xpos, ypos, width, height, transparent_color, axt, ayt, axb, ayb) | area_pointer, ah, aw, x, y, pixel
'' Draws partial image area defined by axt, ayt, axb, ayb
{
image_pointer - address of image
xpos - top-left coordinate of image
ypos - top-left coordinate of image
width - image width
height - image height
ptr - drawing_methods_pointer
axt - area top x (absolute)
ayt - area top y
axb - area bottom x
ayb - area bottom y
}
area_pointer := image_pointer + 2*(axt - xpos) + 2*(ayt - ypos)*width
ah := ayb - ayt
aw := axb - axt
repeat y from 0 to ah-1
repeat x from 0 to aw-1
pixel := WORD[area_pointer + 2*x + 2*y*width]
if pixel <> transparent_color
graphics.draw_pixel(axt+x, ayt+y, pixel)
PUB draw_darken_image(image_pointer, xpos, ypos, width, height, transparent_color, darken_factor) | pixel, x, y
'' Draws a darkened version of an image using darken_factor
if image_pointer == 0
return
repeat y from 0 to height-1
repeat x from 0 to width-1
pixel := WORD[image_pointer + (2*x + 2*y*width)]
if pixel <> transparent_color
pixel := color.darken(pixel, darken_factor)
graphics.draw_pixel(xpos+x,ypos+y,pixel)
''+--------------------------------------------------------------------+
'' Other Methods
''+--------------------------------------------------------------------+
PUB red_to_green(val, min, max):c | mid, r, g
'' Utility to create a color based on a value in the Red-Green range
{
Returns a color from red to green based on the
value passed. Red represents the min parameter
and Green reprents the max parameter.
To invert the colors invert the passed parameters.
g - Green component will be proportional to val from min
to mid, which is the midpoint between min and max.
Green component in RGB 5-6-5 is 6 bits wide, so the max value
for green is $3F, or 63
r - Red component will be inversely proportional to val
from mid to max.
R component in RGB 5-6-5 is 5 bits wide, so the max value
for green is $1F, or 31
}
mid := min + (max - min)/2
g := 0 #> scale(val, min, mid, 0, $3F) <# $3F
r := 0 #> scale(val, mid, max, $1F, 0) <# $1F
c := g<<5 + r
'debug("red to green ",udec(val),udec(min), udec(max), Uhex_word(c))