summaryrefslogtreecommitdiff
path: root/apps/plugins/lib
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2004-05-19 08:18:25 +0000
committerJens Arnold <amiconn@rockbox.org>2004-05-19 08:18:25 +0000
commita05c41377469f52fc8c76494c1e9d8d5a2094f9a (patch)
tree96e5189563588ad66253913999c80b2ac5591f87 /apps/plugins/lib
parentf4b52b82eaa29ddddf52474cdc995af24080f933 (diff)
downloadrockbox-a05c41377469f52fc8c76494c1e9d8d5a2094f9a.tar.gz
rockbox-a05c41377469f52fc8c76494c1e9d8d5a2094f9a.zip
First real library version of the grayscale framework
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4641 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/lib')
-rw-r--r--apps/plugins/lib/gray.c1538
-rw-r--r--apps/plugins/lib/gray.h80
2 files changed, 1618 insertions, 0 deletions
diff --git a/apps/plugins/lib/gray.c b/apps/plugins/lib/gray.c
new file mode 100644
index 0000000000..6435f49007
--- /dev/null
+++ b/apps/plugins/lib/gray.c
@@ -0,0 +1,1538 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Grayscale framework
11*
12* Copyright (C) 2004 Jens Arnold
13*
14* All files in this archive are subject to the GNU General Public License.
15* See the file COPYING in the source tree root for full license agreement.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#ifndef SIMULATOR /* not for simulator by now */
23#include "plugin.h"
24
25#ifdef HAVE_LCD_BITMAP /* and also not for the Player */
26#include "gray.h"
27
28/* This is a generic framework to use grayscale display within rockbox
29 * plugins. It obviously does not work for the player.
30 */
31
32/**** internal core functions and definitions ****/
33
34/* You do not want to touch these if you don't know exactly what you're
35 * doing. */
36
37#define GRAY_RUNNING 0x0001 /* grayscale overlay is running */
38#define GRAY_DEFERRED_UPDATE 0x0002 /* lcd_update() requested */
39
40/* unsigned 16 bit multiplication (a single instruction on the SH) */
41#define MULU16(a, b) (((unsigned short) (a)) * ((unsigned short) (b)))
42
43typedef struct
44{
45 int x;
46 int by; /* 8-pixel units */
47 int width;
48 int height;
49 int bheight; /* 8-pixel units */
50 int plane_size;
51 int depth; /* number_of_bitplanes = (number_of_grayscales - 1) */
52 int cur_plane; /* for the timer isr */
53 unsigned long randmask; /* mask for random value in graypixel() */
54 unsigned long flags; /* various flags, see #defines */
55 unsigned char *data; /* pointer to start of bitplane data */
56 unsigned long *bitpattern; /* pointer to start of pattern table */
57} tGraybuf;
58
59static struct plugin_api *rb = NULL; /* global api struct pointer */
60static tGraybuf *graybuf = NULL;
61static short gray_random_buffer;
62
63/* timer interrupt handler: display next bitplane */
64static void gray_timer_isr(void)
65{
66 rb->lcd_blit(graybuf->data + MULU16(graybuf->plane_size, graybuf->cur_plane),
67 graybuf->x, graybuf->by, graybuf->width, graybuf->bheight,
68 graybuf->width);
69
70 if (++graybuf->cur_plane >= graybuf->depth)
71 graybuf->cur_plane = 0;
72
73 if (graybuf->flags & GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */
74 {
75 int x1 = MAX(graybuf->x, 0);
76 int x2 = MIN(graybuf->x + graybuf->width, LCD_WIDTH);
77 int y1 = MAX(graybuf->by << 3, 0);
78 int y2 = MIN((graybuf->by + graybuf->bheight) << 3, LCD_HEIGHT);
79
80 if (y1 > 0) /* refresh part above overlay, full width */
81 rb->lcd_update_rect(0, 0, LCD_WIDTH, y1);
82
83 if (y2 < LCD_HEIGHT) /* refresh part below overlay, full width */
84 rb->lcd_update_rect(0, y2, LCD_WIDTH, LCD_HEIGHT - y2);
85
86 if (x1 > 0) /* refresh part to the left of overlay */
87 rb->lcd_update_rect(0, y1, x1, y2 - y1);
88
89 if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */
90 rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1);
91
92 graybuf->flags &= ~GRAY_DEFERRED_UPDATE; /* clear request */
93 }
94}
95
96/* Set a pixel to a specific bit pattern
97 * This is the fundamental graphics primitive, asm optimized */
98static void graypixel(int x, int y, unsigned long pattern)
99{
100 register long address, mask, random;
101
102 /* Some (pseudo-)random function must be used here to shift the bit
103 * pattern randomly, otherwise you would get flicker and/or moire.
104 * Since rand() is relatively slow, I've implemented a simple, but very
105 * fast pseudo-random generator based on linear congruency in assembler.
106 * It delivers 16 pseudo-random bits in each iteration. */
107
108 /* simple but fast pseudo-random generator */
109 asm(
110 "mov.w @%1,%0 \n" /* load last value */
111 "mov #75,r1 \n"
112 "mulu %0,r1 \n" /* multiply by 75 */
113 "sts macl,%0 \n" /* get result */
114 "add #74,%0 \n" /* add another 74 */
115 "mov.w %0,@%1 \n" /* store new value */
116 /* Since the lower bits are not very random: */
117 "shlr8 %0 \n" /* get bits 8..15 (need max. 5) */
118 "and %2,%0 \n" /* mask out unneeded bits */
119 : /* outputs */
120 /* %0 */ "=&r"(random)
121 : /* inputs */
122 /* %1 */ "r"(&gray_random_buffer),
123 /* %2 */ "r"(graybuf->randmask)
124 : /* clobbers */
125 "r1","macl"
126 );
127
128 /* precalculate mask and byte address in first bitplane */
129 asm(
130 "mov %3,%0 \n" /* take y as base for address offset */
131 "shlr2 %0 \n" /* shift right by 3 (= divide by 8) */
132 "shlr %0 \n"
133 "mulu %0,%2 \n" /* multiply with width */
134 "and #7,%3 \n" /* get lower 3 bits of y */
135 "sts macl,%0 \n" /* get mulu result */
136 "add %4,%0 \n" /* add base + x to get final address */
137
138 "mov %3,%1 \n" /* move lower 3 bits of y out of r0 */
139 "mova .pp_table,%3 \n" /* get address of mask table in r0 */
140 "bra .pp_end \n" /* skip the table */
141 "mov.b @(%3,%1),%1 \n" /* get entry from mask table */
142
143 ".align 2 \n"
144 ".pp_table: \n" /* mask table */
145 ".byte 0x01 \n"
146 ".byte 0x02 \n"
147 ".byte 0x04 \n"
148 ".byte 0x08 \n"
149 ".byte 0x10 \n"
150 ".byte 0x20 \n"
151 ".byte 0x40 \n"
152 ".byte 0x80 \n"
153
154 ".pp_end: \n"
155 : /* outputs */
156 /* %0 */ "=&r"(address),
157 /* %1 */ "=&r"(mask)
158 : /* inputs */
159 /* %2 */ "r"(graybuf->width),
160 /* %3 = r0 */ "z"(y),
161 /* %4 */ "r"(graybuf->data + x)
162 : /* clobbers */
163 "macl"
164 );
165
166 /* the hard part: set bits in all bitplanes according to pattern */
167 asm(
168 "cmp/hs %1,%5 \n" /* random >= depth ? */
169 "bf .p_ntrim \n"
170 "sub %1,%5 \n" /* yes: random -= depth */
171 /* it's sufficient to do this once, since the mask guarantees
172 * random < 2 * depth */
173 ".p_ntrim: \n"
174
175 /* calculate some addresses */
176 "mulu %4,%1 \n" /* end address offset */
177 "not %3,r1 \n" /* get inverse mask (for "and") */
178 "sts macl,%1 \n" /* result of mulu */
179 "mulu %4,%5 \n" /* address offset of <random>'th plane */
180 "add %2,%1 \n" /* end offset -> end address */
181 "sts macl,%5 \n" /* result of mulu */
182 "add %2,%5 \n" /* address of <random>'th plane */
183 "bra .p_start1 \n"
184 "mov %5,r2 \n" /* copy address */
185
186 /* first loop: set bits from <random>'th bitplane to last */
187 ".p_loop1: \n"
188 "mov.b @r2,r3 \n" /* get data byte */
189 "shlr %0 \n" /* shift bit mask, sets t bit */
190 "and r1,r3 \n" /* reset bit (-> "white") */
191 "bf .p_white1 \n" /* t=0? -> "white" bit */
192 "or %3,r3 \n" /* set bit ("black" bit) */
193 ".p_white1: \n"
194 "mov.b r3,@r2 \n" /* store data byte */
195 "add %4,r2 \n" /* advance address to next bitplane */
196 ".p_start1: \n"
197 "cmp/hi r2,%1 \n" /* address < end address ? */
198 "bt .p_loop1 \n"
199
200 "bra .p_start2 \n"
201 "nop \n"
202
203 /* second loop: set bits from first to <random-1>'th bitplane
204 * Bit setting works the other way round here to equalize average
205 * execution times for bright and dark pixels */
206 ".p_loop2: \n"
207 "mov.b @%2,r3 \n" /* get data byte */
208 "shlr %0 \n" /* shift bit mask, sets t bit */
209 "or %3,r3 \n" /* set bit (-> "black") */
210 "bt .p_black2 \n" /* t=1? -> "black" bit */
211 "and r1,r3 \n" /* reset bit ("white" bit) */
212 ".p_black2: \n"
213 "mov.b r3,@%2 \n" /* store data byte */
214 "add %4,%2 \n" /* advance address to next bitplane */
215 ".p_start2: \n"
216 "cmp/hi %2,%5 \n" /* address < <random>'th address ? */
217 "bt .p_loop2 \n"
218 : /* outputs */
219 : /* inputs */
220 /* %0 */ "r"(pattern),
221 /* %1 */ "r"(graybuf->depth),
222 /* %2 */ "r"(address),
223 /* %3 */ "r"(mask),
224 /* %4 */ "r"(graybuf->plane_size),
225 /* %5 */ "r"(random)
226 : /* clobbers */
227 "r1", "r2", "r3", "macl"
228 );
229}
230
231/* Set 8 pixels to specific gray values at once, asm optimized
232 * This greatly enhances performance of gray_fillrect() and gray_drawgraymap()
233 * for larger rectangles and graymaps */
234static void grayblock(int x, int by, unsigned char* src, int stride)
235{
236 /* precalculate the bit patterns with random shifts (same RNG as graypixel,
237 * see there for an explanation) for all 8 pixels and put them on the
238 * stack (!) */
239 asm(
240 "mova .gb_reload,r0 \n" /* set default loopback address */
241 "tst %1,%1 \n" /* stride == 0 ? */
242 "bf .gb_needreload \n" /* no: keep that address */
243 "mova .gb_reuse,r0 \n" /* yes: set shortcut (no reload) */
244 ".gb_needreload: \n"
245 "mov r0,r2 \n" /* loopback address to r2 */
246 "mov #7,r3 \n" /* loop count in r3: 8 pixels */
247
248 ".align 2 \n" /** load pattern for pixel **/
249 ".gb_reload: \n"
250 "mov.b @%0,r0 \n" /* load src byte */
251 "extu.b r0,r0 \n" /* extend unsigned */
252 "mulu %2,r0 \n" /* macl = byte * depth; */
253 "add %1,%0 \n" /* src += stride; */
254 "sts macl,r4 \n" /* r4 = macl; */
255 "add r4,r0 \n" /* byte += r4; */
256 "shlr8 r0 \n" /* byte >>= 8; */
257 "shll2 r0 \n"
258 "mov.l @(r0,%3),r4 \n" /* r4 = bitpattern[byte]; */
259
260 ".align 2 \n" /** RNG **/
261 ".gb_reuse: \n"
262 "mov.w @%4,r1 \n" /* load last value */
263 "mov #75,r0 \n"
264 "mulu r0,r1 \n" /* multiply by 75 */
265 "sts macl,r1 \n"
266 "add #74,r1 \n" /* add another 74 */
267 "mov.w r1,@%4 \n" /* store new value */
268 /* Since the lower bits are not very random: */
269 "shlr8 r1 \n" /* get bits 8..15 (need max. 5) */
270 "and %5,r1 \n" /* mask out unneeded bits */
271
272 "cmp/hs %2,r1 \n" /* random >= depth ? */
273 "bf .gb_ntrim \n"
274 "sub %2,r1 \n" /* yes: random -= depth; */
275 ".gb_ntrim: \n"
276
277 "mov.l .ashlsi3,r0 \n" /** rotate pattern **/
278 "jsr @r0 \n" /* shift r4 left by r1 */
279 "mov r1,r5 \n"
280
281 "mov %2,r5 \n"
282 "sub r1,r5 \n" /* r5 = depth - r1 */
283 "mov.l .lshrsi3,r1 \n"
284 "jsr @r1 \n" /* shift r4 right by r5 */
285 "mov r0,r1 \n" /* last result stored in r1 */
286
287 "or r1,r0 \n" /* rotated_pattern = r0 | r1 */
288 "mov.l r0,@-r15 \n" /* push pattern */
289
290 "cmp/pl r3 \n" /* loop count > 0? */
291 "bf .gb_patdone \n" /* no: done */
292
293 "jmp @r2 \n" /* yes: loop */
294 "add #-1,r3 \n" /* decrease loop count */
295
296 ".align 2 \n"
297 ".ashlsi3: \n" /* C library routine: */
298 ".long ___ashlsi3 \n" /* shift r4 left by r5, return in r0 */
299 ".lshrsi3: \n" /* C library routine: */
300 ".long ___lshrsi3 \n" /* shift r4 right by r5, return in r0 */
301 /* both routines preserve r4, destroy r5 and take ~16 cycles */
302
303 ".gb_patdone: \n"
304 : /* outputs */
305 : /* inputs */
306 /* %0 */ "r"(src),
307 /* %1 */ "r"(stride),
308 /* %2 */ "r"(graybuf->depth),
309 /* %3 */ "r"(graybuf->bitpattern),
310 /* %4 */ "r"(&gray_random_buffer),
311 /* %5 */ "r"(graybuf->randmask)
312 : /* clobbers */
313 "r0", "r1", "r2", "r3", "r4", "r5", "macl"
314 );
315
316 /* calculate start address in first bitplane and end address */
317 register unsigned char *address = graybuf->data + x
318 + MULU16(graybuf->width, by);
319 register unsigned char *end_addr = address
320 + MULU16(graybuf->depth, graybuf->plane_size);
321
322 /* set the bits for all 8 pixels in all bytes according to the
323 * precalculated patterns on the stack */
324 asm (
325 "mov.l @r15+,r1 \n" /* pop all 8 patterns */
326 "mov.l @r15+,r2 \n"
327 "mov.l @r15+,r3 \n"
328 "mov.l @r15+,r4 \n"
329 "mov.l @r15+,r5 \n"
330 "mov.l @r15+,r6 \n"
331 "mov.l @r15+,r7 \n"
332 "mov.l @r15+,r8 \n"
333
334 ".gb_loop: \n" /* loop for all bitplanes */
335 "shlr r1 \n" /* rotate lsb of pattern 1 to t bit */
336 "rotcl r0 \n" /* rotate t bit into r0 */
337 "shlr r2 \n"
338 "rotcl r0 \n"
339 "shlr r3 \n"
340 "rotcl r0 \n"
341 "shlr r4 \n"
342 "rotcl r0 \n"
343 "shlr r5 \n"
344 "rotcl r0 \n"
345 "shlr r6 \n"
346 "rotcl r0 \n"
347 "shlr r7 \n"
348 "rotcl r0 \n"
349 "shlr r8 \n"
350 "rotcl r0 \n"
351 "mov.b r0,@%0 \n" /* store byte to bitplane */
352 "add %2,%0 \n" /* advance to next bitplane */
353 "cmp/hi %0,%1 \n" /* last bitplane done? */
354 "bt .gb_loop \n" /* no: loop */
355 : /* outputs */
356 : /* inputs */
357 /* %0 */ "r"(address),
358 /* %1 */ "r"(end_addr),
359 /* %2 */ "r"(graybuf->plane_size)
360 : /* clobbers */
361 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8"
362 );
363}
364
365/* Invert the bits for 1-8 pixels within the buffer */
366static void grayinvertmasked(int x, int by, unsigned char mask)
367{
368 asm(
369 "mulu %4,%5 \n" /* width * by (offset of row) */
370 "mov #0,r1 \n" /* current_plane = 0 */
371 "sts macl,r2 \n" /* get mulu result */
372 "add r2,%1 \n" /* -> address in 1st bitplane */
373
374 ".i_loop: \n"
375 "mov.b @%1,r2 \n" /* get data byte */
376 "add #1,r1 \n" /* current_plane++; */
377 "xor %2,r2 \n" /* invert bits */
378 "mov.b r2,@%1 \n" /* store data byte */
379 "add %3,%1 \n" /* advance address to next bitplane */
380 "cmp/hi r1,%0 \n" /* current_plane < depth ? */
381 "bt .i_loop \n"
382 : /* outputs */
383 : /* inputs */
384 /* %0 */ "r"(graybuf->depth),
385 /* %1 */ "r"(graybuf->data + x),
386 /* %2 */ "r"(mask),
387 /* %3 */ "r"(graybuf->plane_size),
388 /* %4 */ "r"(graybuf->width),
389 /* %5 */ "r"(by)
390 : /* clobbers */
391 "r1", "r2", "macl"
392 );
393}
394
395/*** public core functions ***/
396
397/* Initialize the framework */
398void gray_init(struct plugin_api* newrb)
399{
400 rb = newrb;
401}
402
403/* Prepare the grayscale display buffer
404 *
405 * arguments:
406 * gbuf = pointer to the memory area to use (e.g. plugin buffer)
407 * gbuf_size = max usable size of the buffer
408 * width = width in pixels (1..112)
409 * bheight = height in 8-pixel units (1..8)
410 * depth = desired number of shades - 1 (1..32)
411 *
412 * result:
413 * = depth if there was enough memory
414 * < depth if there wasn't enough memory. The number of displayable
415 * shades is smaller than desired, but it still works
416 * = 0 if there wasn't even enough memory for 1 bitplane (black & white)
417 *
418 * You can request any depth from 1 to 32, not just powers of 2. The routine
419 * performs "graceful degradation" if the memory is not sufficient for the
420 * desired depth. As long as there is at least enough memory for 1 bitplane,
421 * it creates as many bitplanes as fit into memory, although 1 bitplane will
422 * only deliver black & white display.
423 *
424 * The total memory needed can be calculated as follows:
425 * total_mem =
426 * sizeof(tGraymap) (= 48 bytes currently)
427 * + sizeof(long) (= 4 bytes)
428 * + (width * bheight + sizeof(long)) * depth
429 * + 0..3 (longword alignment of grayscale display buffer)
430 */
431int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
432 int bheight, int depth)
433{
434 int possible_depth, plane_size;
435 int i, j;
436
437 if (rb == NULL || (unsigned) width > LCD_WIDTH
438 || (unsigned) bheight > (LCD_HEIGHT/8)
439 || depth < 1)
440 return 0;
441
442 while ((unsigned long)gbuf & 3) /* the buffer has to be long aligned */
443 {
444 gbuf++;
445 gbuf_size--;
446 }
447
448 plane_size = width * bheight;
449 possible_depth = (gbuf_size - sizeof(tGraybuf) - sizeof(unsigned long))
450 / (plane_size + sizeof(unsigned long));
451
452 if (possible_depth < 1)
453 return 0;
454
455 depth = MIN(depth, 32);
456 depth = MIN(depth, possible_depth);
457
458 graybuf = (tGraybuf *) gbuf; /* global pointer to buffer structure */
459
460 graybuf->x = 0;
461 graybuf->by = 0;
462 graybuf->width = width;
463 graybuf->height = bheight << 3;
464 graybuf->bheight = bheight;
465 graybuf->plane_size = plane_size;
466 graybuf->depth = depth;
467 graybuf->cur_plane = 0;
468 graybuf->flags = 0;
469 graybuf->data = gbuf + sizeof(tGraybuf);
470 graybuf->bitpattern = (unsigned long *) (graybuf->data
471 + depth * plane_size);
472
473 i = depth;
474 j = 8;
475 while (i != 0)
476 {
477 i >>= 1;
478 j--;
479 }
480 graybuf->randmask = 0xFF >> j;
481
482 /* initial state is all white */
483 rb->memset(graybuf->data, 0, depth * plane_size);
484
485 /* Precalculate the bit patterns for all possible pixel values */
486 for (i = 0; i <= depth; i++)
487 {
488 unsigned long pattern = 0;
489 int value = 0;
490
491 for (j = 0; j < depth; j++)
492 {
493 pattern <<= 1;
494 value += i;
495
496 if (value >= depth)
497 value -= depth; /* "white" bit */
498 else
499 pattern |= 1; /* "black" bit */
500 }
501 /* now the lower <depth> bits contain the pattern */
502
503 graybuf->bitpattern[i] = pattern;
504 }
505
506 return depth;
507}
508
509/* Release the grayscale display buffer
510 *
511 * Switches the grayscale overlay off at first if it is still running,
512 * then sets the pointer to NULL.
513 * DO CALL either this function or at least gray_show_display(false)
514 * before you exit, otherwise nasty things may happen.
515 */
516void gray_release_buffer(void)
517{
518 gray_show_display(false);
519 graybuf = NULL;
520}
521
522/* Set position of the top left corner of the grayscale overlay
523 *
524 * arguments:
525 * x = left margin in pixels
526 * by = top margin in 8-pixel units
527 *
528 * You may set this in a way that the overlay spills across the right or
529 * bottom display border. In this case it will simply be clipped by the
530 * LCD controller. You can even set negative values, this will clip at the
531 * left or top border. I did not test it, but the limits may be +127 / -128
532 *
533 * If you use this while the grayscale overlay is running, the now-freed area
534 * will be restored.
535 */
536void gray_position_display(int x, int by)
537{
538 if (graybuf == NULL)
539 return;
540
541 graybuf->x = x;
542 graybuf->by = by;
543
544 if (graybuf->flags & GRAY_RUNNING)
545 graybuf->flags |= GRAY_DEFERRED_UPDATE;
546}
547
548/* Switch the grayscale overlay on or off
549 *
550 * arguments:
551 * enable = true: the grayscale overlay is switched on if initialized
552 * = false: the grayscale overlay is switched off and the regular lcd
553 * content is restored
554 *
555 * DO NOT call lcd_update() or any other api function that directly accesses
556 * the lcd while the grayscale overlay is running! If you need to do
557 * lcd_update() to update something outside the grayscale overlay area, use
558 * gray_deferred_update() instead.
559 *
560 * Other functions to avoid are:
561 * lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(),
562 * lcd_set_invert_display(), lcd_set_flip(), lcd_roll()
563 *
564 * The grayscale display consumes ~50 % CPU power (for a full screen overlay,
565 * less if the overlay is smaller) when switched on. You can switch the overlay
566 * on and off as many times as you want.
567 */
568void gray_show_display(bool enable)
569{
570 if (graybuf == NULL)
571 return;
572
573 if (enable)
574 {
575 graybuf->flags |= GRAY_RUNNING;
576 rb->plugin_register_timer(FREQ / 67, 1, gray_timer_isr);
577 }
578 else
579 {
580 rb->plugin_unregister_timer();
581 graybuf->flags &= ~GRAY_RUNNING;
582 rb->lcd_update(); /* restore whatever there was before */
583 }
584}
585
586/*** public optional functions ***/
587
588/* Here are the various graphics primitives. Cut out functions you do not
589 * need in order to keep plugin code size down.
590 */
591
592/* Clear the grayscale display (sets all pixels to white)
593 */
594void gray_clear_display(void)
595{
596 if (graybuf == NULL)
597 return;
598
599 rb->memset(graybuf->data, 0, MULU16(graybuf->depth, graybuf->plane_size));
600}
601
602/* Set the grayscale display to all black
603 */
604void gray_black_display(void)
605{
606 if (graybuf == NULL)
607 return;
608
609 rb->memset(graybuf->data, 0xFF, MULU16(graybuf->depth, graybuf->plane_size));
610}
611
612/* Do an lcd_update() to show changes done by rb->lcd_xxx() functions (in areas
613 * of the screen not covered by the grayscale overlay). If the grayscale
614 * overlay is running, the update will be done in the next call of the
615 * interrupt routine, otherwise it will be performed right away. See also
616 * comment for the gray_show_display() function.
617 */
618void gray_deferred_update(void)
619{
620 if (graybuf != NULL && (graybuf->flags & GRAY_RUNNING))
621 graybuf->flags |= GRAY_DEFERRED_UPDATE;
622 else
623 rb->lcd_update();
624}
625
626/* Scroll the whole grayscale buffer left by <count> pixels
627 *
628 * black_border determines if the pixels scrolled in at the right are black
629 * or white
630 *
631 * Scrolling left/right by an even pixel count is almost twice as fast as
632 * scrolling by an odd pixel count.
633 */
634void gray_scroll_left(int count, bool black_border)
635{
636 int by, d;
637 unsigned char *ptr;
638 unsigned char filler;
639
640 if (graybuf == NULL || (unsigned) count >= (unsigned) graybuf->width)
641 return;
642
643 if (black_border)
644 filler = 0xFF;
645 else
646 filler = 0;
647
648 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
649 for (by = 0; by < graybuf->bheight; by++)
650 {
651 ptr = graybuf->data + MULU16(graybuf->width, by);
652 for (d = 0; d < graybuf->depth; d++)
653 {
654 if (count & 1) /* odd count: scroll byte-wise */
655 asm volatile (
656 ".sl_loop1: \n"
657 "mov.b @%0+,r1 \n"
658 "mov.b r1,@(%2,%0) \n"
659 "cmp/hi %0,%1 \n"
660 "bt .sl_loop1 \n"
661 : /* outputs */
662 : /* inputs */
663 /* %0 */ "r"(ptr + count),
664 /* %1 */ "r"(ptr + graybuf->width),
665 /* %2 */ "z"(-count - 1)
666 : /* clobbers */
667 "r1"
668 );
669 else /* even count: scroll word-wise */
670 asm volatile (
671 ".sl_loop2: \n"
672 "mov.w @%0+,r1 \n"
673 "mov.w r1,@(%2,%0) \n"
674 "cmp/hi %0,%1 \n"
675 "bt .sl_loop2 \n"
676 : /* outputs */
677 : /* inputs */
678 /* %0 */ "r"(ptr + count),
679 /* %1 */ "r"(ptr + graybuf->width),
680 /* %2 */ "z"(-count - 2)
681 : /* clobbers */
682 "r1"
683 );
684
685 rb->memset(ptr + graybuf->width - count, filler, count);
686 ptr += graybuf->plane_size;
687 }
688 }
689}
690
691/* Scroll the whole grayscale buffer right by <count> pixels
692 *
693 * black_border determines if the pixels scrolled in at the left are black
694 * or white
695 *
696 * Scrolling left/right by an even pixel count is almost twice as fast as
697 * scrolling by an odd pixel count.
698 */
699void gray_scroll_right(int count, bool black_border)
700{
701 int by, d;
702 unsigned char *ptr;
703 unsigned char filler;
704
705 if (graybuf == NULL || (unsigned) count >= (unsigned) graybuf->width)
706 return;
707
708 if (black_border)
709 filler = 0xFF;
710 else
711 filler = 0;
712
713 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
714 for (by = 0; by < graybuf->bheight; by++)
715 {
716 ptr = graybuf->data + MULU16(graybuf->width, by);
717 for (d = 0; d < graybuf->depth; d++)
718 {
719 if (count & 1) /* odd count: scroll byte-wise */
720 asm volatile (
721 ".sr_loop1: \n"
722 "mov.b @(%2,%0),r1 \n"
723 "mov.b r1,@-%0 \n"
724 "cmp/hi %1,%0 \n"
725 "bt .sr_loop1 \n"
726 : /* outputs */
727 : /* inputs */
728 /* %0 */ "r"(ptr + graybuf->width),
729 /* %1 */ "r"(ptr + count),
730 /* %2 */ "z"(-count - 1)
731 : /* clobbers */
732 "r1"
733 );
734 else /* even count: scroll word-wise */
735 asm volatile (
736 ".sr_loop2: \n"
737 "mov.w @(%2,%0),r1 \n"
738 "mov.w r1,@-%0 \n"
739 "cmp/hi %1,%0 \n"
740 "bt .sr_loop2 \n"
741 : /* outputs */
742 : /* inputs */
743 /* %0 */ "r"(ptr + graybuf->width),
744 /* %1 */ "r"(ptr + count),
745 /* %2 */ "z"(-count - 2)
746 : /* clobbers */
747 "r1"
748 );
749
750 rb->memset(ptr, filler, count);
751 ptr += graybuf->plane_size;
752 }
753 }
754}
755
756/* Scroll the whole grayscale buffer up by 8 pixels
757 *
758 * black_border determines if the pixels scrolled in at the bottom are black
759 * or white
760 *
761 * Scrolling up/down by 8 pixels is very fast.
762 */
763void gray_scroll_up8(bool black_border)
764{
765 int by, d;
766 unsigned char *ptr;
767 unsigned char filler;
768
769 if (graybuf == NULL)
770 return;
771
772 if (black_border)
773 filler = 0xFF;
774 else
775 filler = 0;
776
777 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
778 for (by = 1; by < graybuf->bheight; by++)
779 {
780 ptr = graybuf->data + MULU16(graybuf->width, by);
781 for (d = 0; d < graybuf->depth; d++)
782 {
783 rb->memcpy(ptr - graybuf->width, ptr, graybuf->width);
784 ptr += graybuf->plane_size;
785 }
786 }
787 /* fill last row */
788 ptr = graybuf->data + graybuf->plane_size - graybuf->width;
789 for (d = 0; d < graybuf->depth; d++)
790 {
791 rb->memset(ptr, filler, graybuf->width);
792 ptr += graybuf->plane_size;
793 }
794}
795
796/* Scroll the whole grayscale buffer down by 8 pixels
797 *
798 * black_border determines if the pixels scrolled in at the top are black
799 * or white
800 *
801 * Scrolling up/down by 8 pixels is very fast.
802 */
803void gray_scroll_down8(bool black_border)
804{
805 int by, d;
806 unsigned char *ptr;
807 unsigned char filler;
808
809 if (graybuf == NULL)
810 return;
811
812 if (black_border)
813 filler = 0xFF;
814 else
815 filler = 0;
816
817 /* Scroll row by row to minimize flicker (byte rows = 8 pixels each) */
818 for (by = graybuf->bheight - 1; by > 0; by--)
819 {
820 ptr = graybuf->data + MULU16(graybuf->width, by);
821 for (d = 0; d < graybuf->depth; d++)
822 {
823 rb->memcpy(ptr, ptr - graybuf->width, graybuf->width);
824 ptr += graybuf->plane_size;
825 }
826 }
827 /* fill first row */
828 ptr = graybuf->data;
829 for (d = 0; d < graybuf->depth; d++)
830 {
831 rb->memset(ptr, filler, graybuf->width);
832 ptr += graybuf->plane_size;
833 }
834}
835
836/* Scroll the whole grayscale buffer up by <count> pixels (<= 7)
837 *
838 * black_border determines if the pixels scrolled in at the bottom are black
839 * or white
840 *
841 * Scrolling up/down pixel-wise is significantly slower than scrolling
842 * left/right or scrolling up/down byte-wise because it involves bit
843 * shifting. That's why it is asm optimized.
844 */
845void gray_scroll_up(int count, bool black_border)
846{
847 unsigned long filler;
848
849 if (graybuf == NULL || (unsigned) count > 7)
850 return;
851
852 if (black_border)
853 filler = 0xFF;
854 else
855 filler = 0;
856
857 /* scroll column by column to minimize flicker */
858 asm(
859 "mov #0,r6 \n" /* x = 0 */
860 "mova .su_shifttbl,r0 \n" /* calculate jump destination for */
861 "mov.b @(r0,%6),%6 \n" /* shift amount from table */
862 "bra .su_cloop \n" /* skip table */
863 "add r0,%6 \n"
864
865 ".align 2 \n"
866 ".su_shifttbl: \n" /* shift jump offset table */
867 ".byte .su_shift0 - .su_shifttbl \n"
868 ".byte .su_shift1 - .su_shifttbl \n"
869 ".byte .su_shift2 - .su_shifttbl \n"
870 ".byte .su_shift3 - .su_shifttbl \n"
871 ".byte .su_shift4 - .su_shifttbl \n"
872 ".byte .su_shift5 - .su_shifttbl \n"
873 ".byte .su_shift6 - .su_shifttbl \n"
874 ".byte .su_shift7 - .su_shifttbl \n"
875
876 ".su_cloop: \n" /* repeat for every column */
877 "mov %1,r2 \n" /* get start address */
878 "mov #0,r3 \n" /* current_plane = 0 */
879
880 ".su_oloop: \n" /* repeat for every bitplane */
881 "mov r2,r4 \n" /* get start address */
882 "mov #0,r5 \n" /* current_row = 0 */
883 "mov %5,r1 \n" /* get filler bits */
884
885 ".su_iloop: \n" /* repeat for all rows */
886 "sub %2,r4 \n" /* address -= width */
887 "mov.b @r4,r0 \n" /* get data byte */
888 "shll8 r1 \n" /* old data to 2nd byte */
889 "extu.b r0,r0 \n" /* extend unsigned */
890 "or r1,r0 \n" /* combine old data */
891 "jmp @%6 \n" /* jump into shift "path" */
892 "extu.b r0,r1 \n" /* store data for next round */
893
894 ".su_shift6: \n" /* shift right by 0..7 bits */
895 "shlr2 r0 \n"
896 ".su_shift4: \n"
897 "shlr2 r0 \n"
898 ".su_shift2: \n"
899 "bra .su_shift0 \n"
900 "shlr2 r0 \n"
901 ".su_shift7: \n"
902 "shlr2 r0 \n"
903 ".su_shift5: \n"
904 "shlr2 r0 \n"
905 ".su_shift3: \n"
906 "shlr2 r0 \n"
907 ".su_shift1: \n"
908 "shlr r0 \n"
909 ".su_shift0: \n"
910
911 "mov.b r0,@r4 \n" /* store data */
912 "add #1,r5 \n" /* current_row++ */
913 "cmp/hi r5,%3 \n" /* current_row < bheight ? */
914 "bt .su_iloop \n"
915
916 "add %4,r2 \n" /* start_address += plane_size */
917 "add #1,r3 \n" /* current_plane++ */
918 "cmp/hi r3,%0 \n" /* current_plane < depth ? */
919 "bt .su_oloop \n"
920
921 "add #1,%1 \n" /* start_address++ */
922 "add #1,r6 \n" /* x++ */
923 "cmp/hi r6,%2 \n" /* x < width ? */
924 "bt .su_cloop \n"
925 : /* outputs */
926 : /* inputs */
927 /* %0 */ "r"(graybuf->depth),
928 /* %1 */ "r"(graybuf->data + graybuf->plane_size),
929 /* %2 */ "r"(graybuf->width),
930 /* %3 */ "r"(graybuf->bheight),
931 /* %4 */ "r"(graybuf->plane_size),
932 /* %5 */ "r"(filler),
933 /* %6 */ "r"(count)
934 : /* clobbers */
935 "r0", "r1", "r2", "r3", "r4", "r5", "r6"
936 );
937}
938
939/* Scroll the whole grayscale buffer down by <count> pixels (<= 7)
940 *
941 * black_border determines if the pixels scrolled in at the top are black
942 * or white
943 *
944 * Scrolling up/down pixel-wise is significantly slower than scrolling
945 * left/right or scrolling up/down byte-wise because it involves bit
946 * shifting. That's why it is asm optimized.
947 */
948void gray_scroll_down(int count, bool black_border)
949{
950 unsigned long filler;
951
952 if (graybuf == NULL || (unsigned) count > 7)
953 return;
954
955 if (black_border)
956 filler = 0xFF << count; /* calculate filler bits */
957 else
958 filler = 0;
959
960 /* scroll column by column to minimize flicker */
961 asm(
962 "mov #0,r6 \n" /* x = 0 */
963 "mova .sd_shifttbl,r0 \n" /* calculate jump destination for */
964 "mov.b @(r0,%6),%6 \n" /* shift amount from table */
965 "bra .sd_cloop \n" /* skip table */
966 "add r0,%6 \n"
967
968 ".align 2 \n"
969 ".sd_shifttbl: \n" /* shift jump offset table */
970 ".byte .sd_shift0 - .sd_shifttbl \n"
971 ".byte .sd_shift1 - .sd_shifttbl \n"
972 ".byte .sd_shift2 - .sd_shifttbl \n"
973 ".byte .sd_shift3 - .sd_shifttbl \n"
974 ".byte .sd_shift4 - .sd_shifttbl \n"
975 ".byte .sd_shift5 - .sd_shifttbl \n"
976 ".byte .sd_shift6 - .sd_shifttbl \n"
977 ".byte .sd_shift7 - .sd_shifttbl \n"
978
979 ".sd_cloop: \n" /* repeat for every column */
980 "mov %1,r2 \n" /* get start address */
981 "mov #0,r3 \n" /* current_plane = 0 */
982
983 ".sd_oloop: \n" /* repeat for every bitplane */
984 "mov r2,r4 \n" /* get start address */
985 "mov #0,r5 \n" /* current_row = 0 */
986 "mov %5,r1 \n" /* get filler bits */
987
988 ".sd_iloop: \n" /* repeat for all rows */
989 "shlr8 r1 \n" /* shift right to get residue */
990 "mov.b @r4,r0 \n" /* get data byte */
991 "jmp @%6 \n" /* jump into shift "path" */
992 "extu.b r0,r0 \n" /* extend unsigned */
993
994 ".sd_shift6: \n" /* shift left by 0..7 bits */
995 "shll2 r0 \n"
996 ".sd_shift4: \n"
997 "shll2 r0 \n"
998 ".sd_shift2: \n"
999 "bra .sd_shift0 \n"
1000 "shll2 r0 \n"
1001 ".sd_shift7: \n"
1002 "shll2 r0 \n"
1003 ".sd_shift5: \n"
1004 "shll2 r0 \n"
1005 ".sd_shift3: \n"
1006 "shll2 r0 \n"
1007 ".sd_shift1: \n"
1008 "shll r0 \n"
1009 ".sd_shift0: \n"
1010
1011 "or r0,r1 \n" /* combine with last residue */
1012 "mov.b r1,@r4 \n" /* store data */
1013 "add %2,r4 \n" /* address += width */
1014 "add #1,r5 \n" /* current_row++ */
1015 "cmp/hi r5,%3 \n" /* current_row < bheight ? */
1016 "bt .sd_iloop \n"
1017
1018 "add %4,r2 \n" /* start_address += plane_size */
1019 "add #1,r3 \n" /* current_plane++ */
1020 "cmp/hi r3,%0 \n" /* current_plane < depth ? */
1021 "bt .sd_oloop \n"
1022
1023 "add #1,%1 \n" /* start_address++ */
1024 "add #1,r6 \n" /* x++ */
1025 "cmp/hi r6,%2 \n" /* x < width ? */
1026 "bt .sd_cloop \n"
1027 : /* outputs */
1028 : /* inputs */
1029 /* %0 */ "r"(graybuf->depth),
1030 /* %1 */ "r"(graybuf->data),
1031 /* %2 */ "r"(graybuf->width),
1032 /* %3 */ "r"(graybuf->bheight),
1033 /* %4 */ "r"(graybuf->plane_size),
1034 /* %5 */ "r"(filler),
1035 /* %6 */ "r"(count)
1036 : /* clobbers */
1037 "r0", "r1", "r2", "r3", "r4", "r5", "r6"
1038 );
1039}
1040
1041/* Set a pixel to a specific gray value
1042 *
1043 * brightness is 0..255 (black to white) regardless of real bit depth
1044 */
1045void gray_drawpixel(int x, int y, int brightness)
1046{
1047 if (graybuf == NULL
1048 || (unsigned) x >= (unsigned) graybuf->width
1049 || (unsigned) y >= (unsigned) graybuf->height
1050 || (unsigned) brightness > 255)
1051 return;
1052
1053 graypixel(x, y, graybuf->bitpattern[MULU16(brightness,
1054 graybuf->depth + 1) >> 8]);
1055}
1056
1057/* Invert a pixel
1058 *
1059 * The bit pattern for that pixel in the buffer is inverted, so white becomes
1060 * black, light gray becomes dark gray etc.
1061 */
1062void gray_invertpixel(int x, int y)
1063{
1064 if (graybuf == NULL
1065 || (unsigned) x >= (unsigned) graybuf->width
1066 || (unsigned) y >= (unsigned) graybuf->height)
1067 return;
1068
1069 grayinvertmasked(x, (y >> 3), 1 << (y & 7));
1070}
1071
1072/* Draw a line from (x1, y1) to (x2, y2) with a specific gray value
1073 *
1074 * brightness is 0..255 (black to white) regardless of real bit depth
1075 */
1076void gray_drawline(int x1, int y1, int x2, int y2, int brightness)
1077{
1078 int numpixels;
1079 int i;
1080 int deltax, deltay;
1081 int d, dinc1, dinc2;
1082 int x, xinc1, xinc2;
1083 int y, yinc1, yinc2;
1084 unsigned long pattern;
1085
1086 if (graybuf == NULL
1087 || (unsigned) x1 >= (unsigned) graybuf->width
1088 || (unsigned) y1 >= (unsigned) graybuf->height
1089 || (unsigned) x2 >= (unsigned) graybuf->width
1090 || (unsigned) y2 >= (unsigned) graybuf->height
1091 || (unsigned) brightness > 255)
1092 return;
1093
1094 pattern = graybuf->bitpattern[MULU16(brightness, graybuf->depth + 1) >> 8];
1095
1096 deltax = abs(x2 - x1);
1097 deltay = abs(y2 - y1);
1098 xinc2 = 1;
1099 yinc2 = 1;
1100
1101 if (deltax >= deltay)
1102 {
1103 numpixels = deltax;
1104 d = 2 * deltay - deltax;
1105 dinc1 = deltay * 2;
1106 dinc2 = (deltay - deltax) * 2;
1107 xinc1 = 1;
1108 yinc1 = 0;
1109 }
1110 else
1111 {
1112 numpixels = deltay;
1113 d = 2 * deltax - deltay;
1114 dinc1 = deltax * 2;
1115 dinc2 = (deltax - deltay) * 2;
1116 xinc1 = 0;
1117 yinc1 = 1;
1118 }
1119 numpixels++; /* include endpoints */
1120
1121 if (x1 > x2)
1122 {
1123 xinc1 = -xinc1;
1124 xinc2 = -xinc2;
1125 }
1126
1127 if (y1 > y2)
1128 {
1129 yinc1 = -yinc1;
1130 yinc2 = -yinc2;
1131 }
1132
1133 x = x1;
1134 y = y1;
1135
1136 for (i=0; i<numpixels; i++)
1137 {
1138 graypixel(x, y, pattern);
1139
1140 if (d < 0)
1141 {
1142 d += dinc1;
1143 x += xinc1;
1144 y += yinc1;
1145 }
1146 else
1147 {
1148 d += dinc2;
1149 x += xinc2;
1150 y += yinc2;
1151 }
1152 }
1153}
1154
1155/* Invert a line from (x1, y1) to (x2, y2)
1156 *
1157 * The bit patterns for the pixels of the line are inverted, so white becomes
1158 * black, light gray becomes dark gray etc.
1159 */
1160void gray_invertline(int x1, int y1, int x2, int y2)
1161{
1162 int numpixels;
1163 int i;
1164 int deltax, deltay;
1165 int d, dinc1, dinc2;
1166 int x, xinc1, xinc2;
1167 int y, yinc1, yinc2;
1168
1169 if (graybuf == NULL
1170 || (unsigned) x1 >= (unsigned) graybuf->width
1171 || (unsigned) y1 >= (unsigned) graybuf->height
1172 || (unsigned) x2 >= (unsigned) graybuf->width
1173 || (unsigned) y2 >= (unsigned) graybuf->height)
1174 return;
1175
1176 deltax = abs(x2 - x1);
1177 deltay = abs(y2 - y1);
1178 xinc2 = 1;
1179 yinc2 = 1;
1180
1181 if (deltax >= deltay)
1182 {
1183 numpixels = deltax;
1184 d = 2 * deltay - deltax;
1185 dinc1 = deltay * 2;
1186 dinc2 = (deltay - deltax) * 2;
1187 xinc1 = 1;
1188 yinc1 = 0;
1189 }
1190 else
1191 {
1192 numpixels = deltay;
1193 d = 2 * deltax - deltay;
1194 dinc1 = deltax * 2;
1195 dinc2 = (deltax - deltay) * 2;
1196 xinc1 = 0;
1197 yinc1 = 1;
1198 }
1199 numpixels++; /* include endpoints */
1200
1201 if (x1 > x2)
1202 {
1203 xinc1 = -xinc1;
1204 xinc2 = -xinc2;
1205 }
1206
1207 if (y1 > y2)
1208 {
1209 yinc1 = -yinc1;
1210 yinc2 = -yinc2;
1211 }
1212
1213 x = x1;
1214 y = y1;
1215
1216 for (i=0; i<numpixels; i++)
1217 {
1218 grayinvertmasked(x, (y >> 3), 1 << (y & 7));
1219
1220 if (d < 0)
1221 {
1222 d += dinc1;
1223 x += xinc1;
1224 y += yinc1;
1225 }
1226 else
1227 {
1228 d += dinc2;
1229 x += xinc2;
1230 y += yinc2;
1231 }
1232 }
1233}
1234
1235/* Draw a (hollow) rectangle with a specific gray value,
1236 * corners are (x1, y1) and (x2, y2)
1237 *
1238 * brightness is 0..255 (black to white) regardless of real bit depth
1239 */
1240void gray_drawrect(int x1, int y1, int x2, int y2, int brightness)
1241{
1242 int x, y;
1243 unsigned long pattern;
1244 unsigned char srcpixel;
1245
1246 if (graybuf == NULL
1247 || (unsigned) x1 >= (unsigned) graybuf->width
1248 || (unsigned) y1 >= (unsigned) graybuf->height
1249 || (unsigned) x2 >= (unsigned) graybuf->width
1250 || (unsigned) y2 >= (unsigned) graybuf->height
1251 || (unsigned) brightness > 255)
1252 return;
1253
1254 if (y1 > y2)
1255 {
1256 y = y1;
1257 y1 = y2;
1258 y2 = y;
1259 }
1260 if (x1 > x2)
1261 {
1262 x = x1;
1263 x1 = x2;
1264 x2 = x;
1265 }
1266
1267 pattern = graybuf->bitpattern[MULU16(brightness, graybuf->depth + 1) >> 8];
1268 srcpixel = brightness;
1269
1270 for (x = x1 + 1; x < x2; x++)
1271 {
1272 graypixel(x, y1, pattern);
1273 graypixel(x, y2, pattern);
1274 }
1275 for (y = y1; y <= y2; )
1276 {
1277 if (!(y & 7) && (y2 - y >= 7))
1278 /* current row byte aligned in fb & at least 8 rows left */
1279 {
1280 /* shortcut: draw all 8 rows at once: 2..3 times faster */
1281 grayblock(x1, y >> 3, &srcpixel, 0);
1282 grayblock(x2, y >> 3, &srcpixel, 0);
1283 y += 8;
1284 }
1285 else
1286 {
1287 graypixel(x1, y, pattern);
1288 graypixel(x2, y, pattern);
1289 y++;
1290 }
1291 }
1292}
1293
1294/* Fill a rectangle with a specific gray value
1295 * corners are (x1, y1) and (x2, y2)
1296 *
1297 * brightness is 0..255 (black to white) regardless of real bit depth
1298 */
1299void gray_fillrect(int x1, int y1, int x2, int y2, int brightness)
1300{
1301 int x, y;
1302 unsigned long pattern;
1303 unsigned char srcpixel;
1304
1305 if (graybuf == NULL
1306 || (unsigned) x1 >= (unsigned) graybuf->width
1307 || (unsigned) y1 >= (unsigned) graybuf->height
1308 || (unsigned) x2 >= (unsigned) graybuf->width
1309 || (unsigned) y2 >= (unsigned) graybuf->height
1310 || (unsigned) brightness > 255)
1311 return;
1312
1313 if (y1 > y2)
1314 {
1315 y = y1;
1316 y1 = y2;
1317 y2 = y;
1318 }
1319 if (x1 > x2)
1320 {
1321 x = x1;
1322 x1 = x2;
1323 x2 = x;
1324 }
1325
1326 pattern = graybuf->bitpattern[MULU16(brightness, graybuf->depth + 1) >> 8];
1327 srcpixel = brightness;
1328
1329 for (y = y1; y <= y2; )
1330 {
1331 if (!(y & 7) && (y2 - y >= 7))
1332 /* current row byte aligned in fb & at least 8 rows left */
1333 {
1334 for (x = x1; x <= x2; x++)
1335 {
1336 /* shortcut: draw all 8 rows at once: 2..3 times faster */
1337 grayblock(x, y >> 3, &srcpixel, 0);
1338 }
1339 y += 8;
1340 }
1341 else
1342 {
1343 for (x = x1; x <= x2; x++)
1344 {
1345 graypixel(x, y, pattern);
1346 }
1347 y++;
1348 }
1349 }
1350}
1351
1352/* Invert a (solid) rectangle, corners are (x1, y1) and (x2, y2)
1353 *
1354 * The bit patterns for all pixels of the rectangle are inverted, so white
1355 * becomes black, light gray becomes dark gray etc. This is the fastest of
1356 * all gray_xxxrect() functions! Perfectly suited for cursors.
1357 */
1358void gray_invertrect(int x1, int y1, int x2, int y2)
1359{
1360 int x, yb, yb1, yb2;
1361 unsigned char mask;
1362
1363 if (graybuf == NULL
1364 || (unsigned) x1 >= (unsigned) graybuf->width
1365 || (unsigned) y1 >= (unsigned) graybuf->height
1366 || (unsigned) x2 >= (unsigned) graybuf->width
1367 || (unsigned) y2 >= (unsigned) graybuf->height)
1368 return;
1369
1370 if (y1 > y2)
1371 {
1372 yb = y1;
1373 y1 = y2;
1374 y2 = yb;
1375 }
1376 if (x1 > x2)
1377 {
1378 x = x1;
1379 x1 = x2;
1380 x2 = x;
1381 }
1382
1383 yb1 = y1 >> 3;
1384 yb2 = y2 >> 3;
1385
1386 if (yb1 == yb2)
1387 {
1388 mask = 0xFF << (y1 & 7);
1389 mask &= 0xFF >> (7 - (y2 & 7));
1390
1391 for (x = x1; x <= x2; x++)
1392 grayinvertmasked(x, yb1, mask);
1393 }
1394 else
1395 {
1396 mask = 0xFF << (y1 & 7);
1397
1398 for (x = x1; x <= x2; x++)
1399 grayinvertmasked(x, yb1, mask);
1400
1401 for (yb = yb1 + 1; yb < yb2; yb++)
1402 {
1403 for (x = x1; x <= x2; x++)
1404 grayinvertmasked(x, yb, 0xFF);
1405 }
1406
1407 mask = 0xFF >> (7 - (y2 & 7));
1408
1409 for (x = x1; x <= x2; x++)
1410 grayinvertmasked(x, yb2, mask);
1411 }
1412}
1413
1414/* Copy a grayscale bitmap into the display
1415 *
1416 * A grayscale bitmap contains one byte for every pixel that defines the
1417 * brightness of the pixel (0..255). Bytes are read in row-major order.
1418 * The <stride> parameter is useful if you want to show only a part of a
1419 * bitmap. It should always be set to the "row length" of the bitmap, so
1420 * for displaying the whole bitmap, nx == stride.
1421 */
1422void gray_drawgraymap(unsigned char *src, int x, int y, int nx, int ny,
1423 int stride)
1424{
1425 int xi, yi;
1426 unsigned char *row;
1427
1428 if (graybuf == NULL
1429 || (unsigned) x >= (unsigned) graybuf->width
1430 || (unsigned) y >= (unsigned) graybuf->height)
1431 return;
1432
1433 if ((y + ny) >= graybuf->height) /* clip bottom */
1434 ny = graybuf->height - y;
1435
1436 if ((x + nx) >= graybuf->width) /* clip right */
1437 nx = graybuf->width - x;
1438
1439 for (yi = y; yi < y + ny; )
1440 {
1441 row = src;
1442
1443 if (!(yi & 7) && (y + ny - yi > 7))
1444 /* current row byte aligned in fb & at least 8 rows left */
1445 {
1446 for (xi = x; xi < x + nx; xi++)
1447 {
1448 /* shortcut: draw all 8 rows at once: 2..3 times faster */
1449 grayblock(xi, yi >> 3, row++, stride);
1450 }
1451 yi += 8;
1452 src += stride << 3;
1453 }
1454 else
1455 {
1456 for (xi = x; xi < x + nx; xi++)
1457 {
1458 graypixel(xi, yi, graybuf->bitpattern[MULU16(*row++,
1459 graybuf->depth + 1) >> 8]);
1460 }
1461 yi++;
1462 src += stride;
1463 }
1464 }
1465}
1466
1467/* Display a bitmap with specific foreground and background gray values
1468 *
1469 * This (now) uses the same bitmap format as the core b&w graphics routines,
1470 * so you can use bmp2rb to generate bitmaps for use with this function as
1471 * well.
1472 *
1473 * A bitmap contains one bit for every pixel that defines if that pixel is
1474 * foreground (1) or background (0). Bits within a byte are arranged
1475 * vertically, LSB at top.
1476 * The bytes are stored in row-major order, with byte 0 being top left,
1477 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
1478 * 0..7, the second row defines pixel row 8..15 etc.
1479 *
1480 * The <stride> parameter is useful if you want to show only a part of a
1481 * bitmap. It should always be set to the "row length" of the bitmap.
1482 *
1483 * If draw_bg is false, only foreground pixels are drawn, so the background
1484 * is transparent. In this case bg_brightness is ignored.
1485 */
1486void gray_drawbitmap(unsigned char *src, int x, int y, int nx, int ny,
1487 int stride, bool draw_bg, int fg_brightness,
1488 int bg_brightness)
1489{
1490 int xi, dy;
1491 int bits = 0; /* Have to initialize to prevent warning */
1492 unsigned long fg_pattern, bg_pattern;
1493 unsigned char *col;
1494
1495 if (graybuf == NULL
1496 || (unsigned) x >= (unsigned) graybuf->width
1497 || (unsigned) y >= (unsigned) graybuf->height
1498 || (unsigned) fg_brightness > 255
1499 || (unsigned) bg_brightness > 255)
1500 return;
1501
1502 if ((y + ny) >= graybuf->height) /* clip bottom */
1503 ny = graybuf->height - y;
1504
1505 if ((x + nx) >= graybuf->width) /* clip right */
1506 nx = graybuf->width - x;
1507
1508 fg_pattern = graybuf->bitpattern[MULU16(fg_brightness,
1509 graybuf->depth + 1) >> 8];
1510
1511 bg_pattern = graybuf->bitpattern[MULU16(bg_brightness,
1512 graybuf->depth + 1) >> 8];
1513
1514 for (xi = x; xi < x + nx; xi++)
1515 {
1516 col = src++;
1517 for (dy = 0; dy < ny; dy++)
1518 {
1519 if (!(dy & 7)) /* get next 8 bits */
1520 {
1521 bits = (int)(*col);
1522 col += stride;
1523 }
1524
1525 if (bits & 0x01)
1526 graypixel(xi, y + dy, fg_pattern);
1527 else
1528 if (draw_bg)
1529 graypixel(xi, y + dy, bg_pattern);
1530
1531 bits >>= 1;
1532 }
1533 }
1534}
1535
1536#endif // #ifdef HAVE_LCD_BITMAP
1537#endif // #ifndef SIMULATOR
1538
diff --git a/apps/plugins/lib/gray.h b/apps/plugins/lib/gray.h
new file mode 100644
index 0000000000..64bc4dd8a5
--- /dev/null
+++ b/apps/plugins/lib/gray.h
@@ -0,0 +1,80 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Grayscale framework
11*
12* Copyright (C) 2004 Jens Arnold
13*
14* All files in this archive are subject to the GNU General Public License.
15* See the file COPYING in the source tree root for full license agreement.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#ifndef __GRAY_H__
23#define __GRAY_H__
24
25#ifndef SIMULATOR /* not for simulator by now */
26#include "plugin.h"
27
28#ifdef HAVE_LCD_BITMAP /* and also not for the Player */
29
30/* This is a generic framework to use grayscale display within rockbox
31 * plugins. It obviously does not work for the player.
32 */
33
34/* every framework needs such a function, and it has to be called as
35 * the very first one */
36void gray_init(struct plugin_api* newrb);
37
38/* general functions */
39int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
40 int bheight, int depth);
41void gray_release_buffer(void);
42void gray_position_display(int x, int by);
43void gray_show_display(bool enable);
44
45/* functions affecting the whole display */
46void gray_clear_display(void);
47void gray_black_display(void);
48void gray_deferred_update(void);
49
50/* scrolling functions */
51void gray_scroll_left(int count, bool black_border);
52void gray_scroll_right(int count, bool black_border);
53void gray_scroll_up8(bool black_border);
54void gray_scroll_down8(bool black_border);
55void gray_scroll_up(int count, bool black_border);
56void gray_scroll_down(int count, bool black_border);
57
58/* pixel functions */
59void gray_drawpixel(int x, int y, int brightness);
60void gray_invertpixel(int x, int y);
61
62/* line functions */
63void gray_drawline(int x1, int y1, int x2, int y2, int brightness);
64void gray_invertline(int x1, int y1, int x2, int y2);
65
66/* rectangle functions */
67void gray_drawrect(int x1, int y1, int x2, int y2, int brightness);
68void gray_fillrect(int x1, int y1, int x2, int y2, int brightness);
69void gray_invertrect(int x1, int y1, int x2, int y2);
70
71/* bitmap functions */
72void gray_drawgraymap(unsigned char *src, int x, int y, int nx, int ny,
73 int stride);
74void gray_drawbitmap(unsigned char *src, int x, int y, int nx, int ny,
75 int stride, bool draw_bg, int fg_brightness,
76 int bg_brightness);
77
78#endif /* HAVE_LCD_BITMAP */
79#endif /* SIMULATOR */
80#endif /* __GRAY_H__ */