1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Alan Korr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
33 #include "rbunicode.h"
35 #include "scroll_engine.h"
37 #ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */
38 #define LCDFN(fn) lcd_ ## fn
39 #define FBFN(fn) fb_ ## fn
40 #define LCDM(ma) LCD_ ## ma
41 #define LCDNAME "lcd_"
47 FBFN(data
) LCDFN(framebuffer
)[LCDM(FBHEIGHT
)][LCDM(FBWIDTH
)]
48 #if CONFIG_CPU != SH7034
53 static struct viewport default_vp
=
58 .height
= LCDM(HEIGHT
),
59 .font
= FONT_SYSFIXED
,
60 .drawmode
= DRMODE_SOLID
,
63 static struct viewport
* current_vp
= &default_vp
;
67 void LCDFN(set_viewport
)(struct viewport
* vp
)
70 current_vp
= &default_vp
;
75 void LCDFN(update_viewport
)(void)
77 LCDFN(update_rect
)(current_vp
->x
, current_vp
->y
,
78 current_vp
->width
, current_vp
->height
);
81 void LCDFN(update_viewport_rect
)(int x
, int y
, int width
, int height
)
83 LCDFN(update_rect
)(current_vp
->x
+ x
, current_vp
->y
+ y
, width
, height
);
87 void LCDFN(init
)(void)
89 LCDFN(clear_display
)();
99 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
100 static void (*lcd_activation_hook
)(void) = NULL
;
102 void lcd_activation_set_hook(void (*func
)(void))
104 lcd_activation_hook
= func
;
107 void lcd_activation_call_hook(void)
109 void (*func
)(void) = lcd_activation_hook
;
117 /*** parameter handling ***/
119 void LCDFN(set_drawmode
)(int mode
)
121 current_vp
->drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
124 int LCDFN(get_drawmode
)(void)
126 return current_vp
->drawmode
;
129 int LCDFN(getwidth
)(void)
131 return current_vp
->width
;
134 int LCDFN(getheight
)(void)
136 return current_vp
->height
;
139 void LCDFN(setfont
)(int newfont
)
141 current_vp
->font
= newfont
;
144 int LCDFN(getfont
)(void)
146 return current_vp
->font
;
149 int LCDFN(getstringsize
)(const unsigned char *str
, int *w
, int *h
)
151 return font_getstringsize(str
, w
, h
, current_vp
->font
);
154 /*** low-level drawing functions ***/
156 static void setpixel(int x
, int y
)
158 LCDFN(framebuffer
)[y
>>3][x
] |= BIT_N(y
& 7);
161 static void clearpixel(int x
, int y
)
163 LCDFN(framebuffer
)[y
>>3][x
] &= ~BIT_N(y
& 7);
166 static void flippixel(int x
, int y
)
168 LCDFN(framebuffer
)[y
>>3][x
] ^= BIT_N(y
& 7);
171 static void nopixel(int x
, int y
)
177 LCDFN(pixelfunc_type
)* const LCDFN(pixelfuncs
)[8] = {
178 flippixel
, nopixel
, setpixel
, setpixel
,
179 nopixel
, clearpixel
, nopixel
, clearpixel
182 static void ICODE_ATTR
flipblock(FBFN(data
) *address
, unsigned mask
,
185 *address
^= bits
& mask
;
188 static void ICODE_ATTR
bgblock(FBFN(data
) *address
, unsigned mask
,
191 *address
&= bits
| ~mask
;
194 static void ICODE_ATTR
fgblock(FBFN(data
) *address
, unsigned mask
,
197 *address
|= bits
& mask
;
200 static void ICODE_ATTR
solidblock(FBFN(data
) *address
, unsigned mask
,
203 unsigned data
= *(char*)address
;
206 *address
= data
^ (bits
& mask
);
209 static void ICODE_ATTR
flipinvblock(FBFN(data
) *address
, unsigned mask
,
212 *address
^= ~bits
& mask
;
215 static void ICODE_ATTR
bginvblock(FBFN(data
) *address
, unsigned mask
,
218 *address
&= ~(bits
& mask
);
221 static void ICODE_ATTR
fginvblock(FBFN(data
) *address
, unsigned mask
,
224 *address
|= ~bits
& mask
;
227 static void ICODE_ATTR
solidinvblock(FBFN(data
) *address
, unsigned mask
,
230 unsigned data
= *(char *)address
;
233 *address
= data
^ (bits
& mask
);
236 LCDFN(blockfunc_type
)* const LCDFN(blockfuncs
)[8] = {
237 flipblock
, bgblock
, fgblock
, solidblock
,
238 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
241 /*** drawing functions ***/
243 /* Clear the whole display */
244 void LCDFN(clear_display
)(void)
246 unsigned bits
= (current_vp
->drawmode
& DRMODE_INVERSEVID
) ? 0xFFu
: 0;
248 memset(LCDFN(framebuffer
), bits
, sizeof LCDFN(framebuffer
));
249 LCDFN(scroll_info
).lines
= 0;
252 /* Clear the current viewport */
253 void LCDFN(clear_viewport
)(void)
257 if (current_vp
== &default_vp
)
259 LCDFN(clear_display
)();
263 oldmode
= current_vp
->drawmode
;
265 /* Invert the INVERSEVID bit and set basic mode to SOLID */
266 current_vp
->drawmode
= (~current_vp
->drawmode
& DRMODE_INVERSEVID
) |
269 LCDFN(fillrect
)(0, 0, current_vp
->width
, current_vp
->height
);
271 current_vp
->drawmode
= oldmode
;
273 LCDFN(scroll_stop
)(current_vp
);
277 /* Set a single pixel */
278 void LCDFN(drawpixel
)(int x
, int y
)
280 if (((unsigned)x
< (unsigned)current_vp
->width
) &&
281 ((unsigned)y
< (unsigned)current_vp
->height
))
282 LCDFN(pixelfuncs
)[current_vp
->drawmode
](current_vp
->x
+ x
, current_vp
->y
+ y
);
286 void LCDFN(drawline
)(int x1
, int y1
, int x2
, int y2
)
294 LCDFN(pixelfunc_type
) *pfunc
= LCDFN(pixelfuncs
)[current_vp
->drawmode
];
296 deltax
= abs(x2
- x1
);
299 DEBUGF(LCDNAME
"drawline() called for vertical line - optimisation.\n");
300 LCDFN(vline
)(x1
, y1
, y2
);
303 deltay
= abs(y2
- y1
);
306 DEBUGF(LCDNAME
"drawline() called for horizontal line - optimisation.\n");
307 LCDFN(hline
)(x1
, x2
, y1
);
313 if (deltax
>= deltay
)
316 d
= 2 * deltay
- deltax
;
318 dinc2
= (deltay
- deltax
) * 2;
325 d
= 2 * deltax
- deltay
;
327 dinc2
= (deltax
- deltay
) * 2;
331 numpixels
++; /* include endpoints */
348 for (i
= 0; i
< numpixels
; i
++)
350 if (((unsigned)x
< (unsigned)current_vp
->width
)
351 && ((unsigned)y
< (unsigned)current_vp
->height
))
352 pfunc(current_vp
->x
+ x
, current_vp
->y
+ y
);
369 /* Draw a horizontal line (optimised) */
370 void LCDFN(hline
)(int x1
, int x2
, int y
)
373 unsigned char *dst
, *dst_end
;
375 LCDFN(blockfunc_type
) *bfunc
;
385 /* nothing to draw? */
386 if (((unsigned)y
>= (unsigned)current_vp
->height
) || (x1
>= current_vp
->width
)
393 if (x2
>= current_vp
->width
)
394 x2
= current_vp
->width
-1;
398 /* adjust to viewport */
402 bfunc
= LCDFN(blockfuncs
)[current_vp
->drawmode
];
403 dst
= &LCDFN(framebuffer
)[y
>>3][x1
];
406 dst_end
= dst
+ width
;
408 bfunc(dst
++, mask
, 0xFFu
);
409 while (dst
< dst_end
);
412 /* Draw a vertical line (optimised) */
413 void LCDFN(vline
)(int x
, int y1
, int y2
)
417 unsigned mask
, mask_bottom
;
418 LCDFN(blockfunc_type
) *bfunc
;
428 /* nothing to draw? */
429 if (((unsigned)x
>= (unsigned)current_vp
->width
) || (y1
>= current_vp
->height
)
436 if (y2
>= current_vp
->height
)
437 y2
= current_vp
->height
-1;
439 /* adjust for viewport */
444 bfunc
= LCDFN(blockfuncs
)[current_vp
->drawmode
];
445 dst
= &LCDFN(framebuffer
)[y1
>>3][x
];
447 mask
= 0xFFu
<< (y1
& 7);
448 mask_bottom
= 0xFFu
>> (~ny
& 7);
450 for (; ny
>= 8; ny
-= 8)
452 bfunc(dst
, mask
, 0xFFu
);
457 bfunc(dst
, mask
, 0xFFu
);
460 /* Draw a rectangular box */
461 void LCDFN(drawrect
)(int x
, int y
, int width
, int height
)
463 if ((width
<= 0) || (height
<= 0))
466 int x2
= x
+ width
- 1;
467 int y2
= y
+ height
- 1;
469 LCDFN(vline
)(x
, y
, y2
);
470 LCDFN(vline
)(x2
, y
, y2
);
471 LCDFN(hline
)(x
, x2
, y
);
472 LCDFN(hline
)(x
, x2
, y2
);
475 /* Fill a rectangular area */
476 void LCDFN(fillrect
)(int x
, int y
, int width
, int height
)
479 FBFN(data
) *dst
, *dst_end
;
480 unsigned mask
, mask_bottom
;
482 LCDFN(blockfunc_type
) *bfunc
;
483 bool fillopt
= false;
485 /* nothing to draw? */
486 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
)
487 || (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
501 if (x
+ width
> current_vp
->width
)
502 width
= current_vp
->width
- x
;
503 if (y
+ height
> current_vp
->height
)
504 height
= current_vp
->height
- y
;
506 /* adjust for viewport */
510 if (current_vp
->drawmode
& DRMODE_INVERSEVID
)
512 if (current_vp
->drawmode
& DRMODE_BG
)
519 if (current_vp
->drawmode
& DRMODE_FG
)
525 bfunc
= LCDFN(blockfuncs
)[current_vp
->drawmode
];
526 dst
= &LCDFN(framebuffer
)[y
>>3][x
];
527 ny
= height
- 1 + (y
& 7);
528 mask
= 0xFFu
<< (y
& 7);
529 mask_bottom
= 0xFFu
>> (~ny
& 7);
531 for (; ny
>= 8; ny
-= 8)
533 if (fillopt
&& (mask
== 0xFFu
))
534 memset(dst
, bits
, width
);
537 FBFN(data
) *dst_row
= dst
;
539 dst_end
= dst_row
+ width
;
541 bfunc(dst_row
++, mask
, 0xFFu
);
542 while (dst_row
< dst_end
);
550 if (fillopt
&& (mask
== 0xFFu
))
551 memset(dst
, bits
, width
);
554 dst_end
= dst
+ width
;
556 bfunc(dst
++, mask
, 0xFFu
);
557 while (dst
< dst_end
);
561 /* About Rockbox' internal bitmap format:
563 * A bitmap contains one bit for every pixel that defines if that pixel is
564 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
566 * The bytes are stored in row-major order, with byte 0 being top left,
567 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
568 * 0..7, the second row defines pixel row 8..15 etc.
570 * This is the same as the internal lcd hw format. */
572 /* Draw a partial bitmap */
573 void ICODE_ATTR
LCDFN(bitmap_part
)(const unsigned char *src
, int src_x
,
574 int src_y
, int stride
, int x
, int y
,
575 int width
, int height
)
578 FBFN(data
) *dst
, *dst_end
;
579 unsigned mask
, mask_bottom
;
580 LCDFN(blockfunc_type
) *bfunc
;
582 /* nothing to draw? */
583 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
)
584 || (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
600 if (x
+ width
> current_vp
->width
)
601 width
= current_vp
->width
- x
;
602 if (y
+ height
> current_vp
->height
)
603 height
= current_vp
->height
- y
;
605 /* adjust for viewport */
609 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
612 dst
= &LCDFN(framebuffer
)[y
>>3][x
];
614 ny
= height
- 1 + shift
+ src_y
;
616 bfunc
= LCDFN(blockfuncs
)[current_vp
->drawmode
];
617 mask
= 0xFFu
<< (shift
+ src_y
);
618 mask_bottom
= 0xFFu
>> (~ny
& 7);
622 bool copyopt
= (current_vp
->drawmode
== DRMODE_SOLID
);
624 for (; ny
>= 8; ny
-= 8)
626 if (copyopt
&& (mask
== 0xFFu
))
627 memcpy(dst
, src
, width
);
630 const unsigned char *src_row
= src
;
631 FBFN(data
) *dst_row
= dst
;
633 dst_end
= dst_row
+ width
;
635 bfunc(dst_row
++, mask
, *src_row
++);
636 while (dst_row
< dst_end
);
645 if (copyopt
&& (mask
== 0xFFu
))
646 memcpy(dst
, src
, width
);
649 dst_end
= dst
+ width
;
651 bfunc(dst
++, mask
, *src
++);
652 while (dst
< dst_end
);
657 dst_end
= dst
+ width
;
660 const unsigned char *src_col
= src
++;
661 FBFN(data
) *dst_col
= dst
++;
662 unsigned mask_col
= mask
;
665 for (y
= ny
; y
>= 8; y
-= 8)
667 data
|= *src_col
<< shift
;
669 if (mask_col
& 0xFFu
)
671 bfunc(dst_col
, mask_col
, data
);
678 dst_col
+= LCDM(WIDTH
);
681 data
|= *src_col
<< shift
;
682 bfunc(dst_col
, mask_col
& mask_bottom
, data
);
684 while (dst
< dst_end
);
688 /* Draw a full bitmap */
689 void LCDFN(bitmap
)(const unsigned char *src
, int x
, int y
, int width
,
692 LCDFN(bitmap_part
)(src
, 0, 0, width
, x
, y
, width
, height
);
695 /* put a string at a given pixel position, skipping first ofs pixel columns */
696 static void LCDFN(putsxyofs
)(int x
, int y
, int ofs
, const unsigned char *str
)
700 struct font
* pf
= font_get(current_vp
->font
);
702 ucs
= bidi_l2v(str
, 1);
704 while ((ch
= *ucs
++) != 0 && x
< current_vp
->width
)
707 const unsigned char *bits
;
709 /* get proportional width and glyph bits */
710 width
= font_get_width(pf
, ch
);
718 bits
= font_get_bits(pf
, ch
);
720 LCDFN(mono_bitmap_part
)(bits
, ofs
, 0, width
, x
, y
, width
- ofs
,
727 /* put a string at a given pixel position */
728 void LCDFN(putsxy
)(int x
, int y
, const unsigned char *str
)
730 LCDFN(putsxyofs
)(x
, y
, 0, str
);
733 /*** Line oriented text output ***/
735 /* put a string at a given char position */
736 void LCDFN(puts
)(int x
, int y
, const unsigned char *str
)
738 LCDFN(puts_style_offset
)(x
, y
, str
, STYLE_DEFAULT
, 0);
741 void LCDFN(puts_style
)(int x
, int y
, const unsigned char *str
, int style
)
743 LCDFN(puts_style_offset
)(x
, y
, str
, style
, 0);
746 void LCDFN(puts_offset
)(int x
, int y
, const unsigned char *str
, int offset
)
748 LCDFN(puts_style_offset
)(x
, y
, str
, STYLE_DEFAULT
, offset
);
751 /* put a string at a given char position, style, and pixel position,
752 * skipping first offset pixel columns */
753 void LCDFN(puts_style_offset
)(int x
, int y
, const unsigned char *str
,
754 int style
, int offset
)
756 int xpos
,ypos
,w
,h
,xrect
;
757 int lastmode
= current_vp
->drawmode
;
759 /* make sure scrolling is turned off on the line we are updating */
760 LCDFN(scroll_stop_line
)(current_vp
, y
);
765 LCDFN(getstringsize
)(str
, &w
, &h
);
766 xpos
= x
*w
/ utf8length(str
);
768 current_vp
->drawmode
= (style
& STYLE_INVERT
) ?
769 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
770 LCDFN(putsxyofs
)(xpos
, ypos
, offset
, str
);
771 current_vp
->drawmode
^= DRMODE_INVERSEVID
;
772 xrect
= xpos
+ MAX(w
- offset
, 0);
773 LCDFN(fillrect
)(xrect
, ypos
, current_vp
->width
- xrect
, h
);
774 current_vp
->drawmode
= lastmode
;
779 void LCDFN(puts_scroll
)(int x
, int y
, const unsigned char *string
)
781 LCDFN(puts_scroll_style
)(x
, y
, string
, STYLE_DEFAULT
);
784 void LCDFN(puts_scroll_style
)(int x
, int y
, const unsigned char *string
,
787 LCDFN(puts_scroll_style_offset
)(x
, y
, string
, style
, 0);
790 void LCDFN(puts_scroll_offset
)(int x
, int y
, const unsigned char *string
,
793 LCDFN(puts_scroll_style_offset
)(x
, y
, string
, STYLE_DEFAULT
, offset
);
796 void LCDFN(puts_scroll_style_offset
)(int x
, int y
, const unsigned char *string
,
797 int style
, int offset
)
799 struct scrollinfo
* s
;
802 if ((unsigned)y
>= (unsigned)current_vp
->height
)
805 /* remove any previously scrolling line at the same location */
806 LCDFN(scroll_stop_line
)(current_vp
, y
);
808 if (LCDFN(scroll_info
.lines
) >= LCDM(SCROLLABLE_LINES
)) return;
810 s
= &LCDFN(scroll_info
).scroll
[LCDFN(scroll_info
).lines
];
812 s
->start_tick
= current_tick
+ LCDFN(scroll_info
).delay
;
814 if (style
& STYLE_INVERT
) {
815 LCDFN(puts_style_offset
)(x
,y
,string
,STYLE_INVERT
,offset
);
818 LCDFN(puts_offset
)(x
,y
,string
,offset
);
820 LCDFN(getstringsize
)(string
, &w
, &h
);
822 if (current_vp
->width
- x
* 8 < w
) {
823 /* prepare scroll line */
826 memset(s
->line
, 0, sizeof s
->line
);
827 strcpy(s
->line
, string
);
830 s
->width
= LCDFN(getstringsize
)(s
->line
, &w
, &h
);
832 /* scroll bidirectional or forward only depending on the string
834 if ( LCDFN(scroll_info
).bidir_limit
) {
835 s
->bidir
= s
->width
< (current_vp
->width
) *
836 (100 + LCDFN(scroll_info
).bidir_limit
) / 100;
841 if (!s
->bidir
) { /* add spaces if scrolling in the round */
842 strcat(s
->line
, " ");
843 /* get new width incl. spaces */
844 s
->width
= LCDFN(getstringsize
)(s
->line
, &w
, &h
);
847 end
= strchr(s
->line
, '\0');
848 strncpy(end
, string
, current_vp
->width
/2);
852 s
->len
= utf8length(string
);
854 s
->startx
= x
* s
->width
/ s
->len
;
857 LCDFN(scroll_info
).lines
++;
861 void LCDFN(scroll_fn
)(void)
864 struct scrollinfo
* s
;
868 struct viewport
* old_vp
= current_vp
;
870 for ( index
= 0; index
< LCDFN(scroll_info
).lines
; index
++ ) {
871 s
= &LCDFN(scroll_info
).scroll
[index
];
874 if (TIME_BEFORE(current_tick
, s
->start_tick
))
877 LCDFN(set_viewport
)(s
->vp
);
880 s
->offset
-= LCDFN(scroll_info
).step
;
882 s
->offset
+= LCDFN(scroll_info
).step
;
884 pf
= font_get(current_vp
->font
);
886 ypos
= s
->y
* pf
->height
;
888 if (s
->bidir
) { /* scroll bidirectional */
889 if (s
->offset
<= 0) {
890 /* at beginning of line */
893 s
->start_tick
= current_tick
+ LCDFN(scroll_info
).delay
* 2;
895 if (s
->offset
>= s
->width
- (current_vp
->width
- xpos
)) {
897 s
->offset
= s
->width
- (current_vp
->width
- xpos
);
899 s
->start_tick
= current_tick
+ LCDFN(scroll_info
).delay
* 2;
903 /* scroll forward the whole time */
904 if (s
->offset
>= s
->width
)
905 s
->offset
%= s
->width
;
908 lastmode
= current_vp
->drawmode
;
909 current_vp
->drawmode
= (s
->style
&STYLE_INVERT
) ?
910 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
911 LCDFN(putsxyofs
)(xpos
, ypos
, s
->offset
, s
->line
);
912 current_vp
->drawmode
= lastmode
;
913 LCDFN(update_viewport_rect
)(xpos
, ypos
, current_vp
->width
- xpos
, pf
->height
);
916 LCDFN(set_viewport
)(old_vp
);