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