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