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