1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Alan Korr
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
30 #include "rbunicode.h"
32 #include "scroll_engine.h"
36 unsigned char lcd_framebuffer
[LCD_FBHEIGHT
][LCD_FBWIDTH
];
38 static int drawmode
= DRMODE_SOLID
;
39 static int xmargin
= 0;
40 static int ymargin
= 0;
41 static int curfont
= FONT_SYSFIXED
;
47 /* Call device specific init */
52 /*** parameter handling ***/
54 void lcd_set_drawmode(int mode
)
56 drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
59 int lcd_get_drawmode(void)
64 void lcd_setmargins(int x
, int y
)
70 int lcd_getxmargin(void)
75 int lcd_getymargin(void)
80 void lcd_setfont(int newfont
)
85 int lcd_getstringsize(const unsigned char *str
, int *w
, int *h
)
87 return font_getstringsize(str
, w
, h
, curfont
);
90 /*** low-level drawing functions ***/
92 static void setpixel(int x
, int y
)
94 lcd_framebuffer
[y
>>3][x
] |= 1 << (y
& 7);
97 static void clearpixel(int x
, int y
)
99 lcd_framebuffer
[y
>>3][x
] &= ~(1 << (y
& 7));
102 static void flippixel(int x
, int y
)
104 lcd_framebuffer
[y
>>3][x
] ^= 1 << (y
& 7);
107 static void nopixel(int x
, int y
)
113 lcd_pixelfunc_type
* const lcd_pixelfuncs
[8] = {
114 flippixel
, nopixel
, setpixel
, setpixel
,
115 nopixel
, clearpixel
, nopixel
, clearpixel
118 static void flipblock(unsigned char *address
, unsigned mask
, unsigned bits
)
120 static void flipblock(unsigned char *address
, unsigned mask
, unsigned bits
)
122 *address
^= bits
& mask
;
125 static void bgblock(unsigned char *address
, unsigned mask
, unsigned bits
)
127 static void bgblock(unsigned char *address
, unsigned mask
, unsigned bits
)
129 *address
&= bits
| ~mask
;
132 static void fgblock(unsigned char *address
, unsigned mask
, unsigned bits
)
134 static void fgblock(unsigned char *address
, unsigned mask
, unsigned bits
)
136 *address
|= bits
& mask
;
139 static void solidblock(unsigned char *address
, unsigned mask
, unsigned bits
)
141 static void solidblock(unsigned char *address
, unsigned mask
, unsigned bits
)
143 unsigned data
= *(char*)address
;
146 *address
= data
^ (bits
& mask
);
149 static void flipinvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
151 static void flipinvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
153 *address
^= ~bits
& mask
;
156 static void bginvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
158 static void bginvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
160 *address
&= ~(bits
& mask
);
163 static void fginvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
165 static void fginvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
167 *address
|= ~bits
& mask
;
170 static void solidinvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
172 static void solidinvblock(unsigned char *address
, unsigned mask
, unsigned bits
)
174 unsigned data
= *(char *)address
;
177 *address
= data
^ (bits
& mask
);
180 lcd_blockfunc_type
* const lcd_blockfuncs
[8] = {
181 flipblock
, bgblock
, fgblock
, solidblock
,
182 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
185 /*** drawing functions ***/
187 /* Clear the whole display */
188 void lcd_clear_display(void)
190 unsigned bits
= (drawmode
& DRMODE_INVERSEVID
) ? 0xFFu
: 0;
192 memset(lcd_framebuffer
, bits
, sizeof lcd_framebuffer
);
193 lcd_scroll_info
.lines
= 0;
196 /* Set a single pixel */
197 void lcd_drawpixel(int x
, int y
)
199 if (((unsigned)x
< LCD_WIDTH
) && ((unsigned)y
< LCD_HEIGHT
))
200 lcd_pixelfuncs
[drawmode
](x
, y
);
204 void lcd_drawline(int x1
, int y1
, int x2
, int y2
)
212 lcd_pixelfunc_type
*pfunc
= lcd_pixelfuncs
[drawmode
];
214 deltax
= abs(x2
- x1
);
215 deltay
= abs(y2
- y1
);
219 if (deltax
>= deltay
)
222 d
= 2 * deltay
- deltax
;
224 dinc2
= (deltay
- deltax
) * 2;
231 d
= 2 * deltax
- deltay
;
233 dinc2
= (deltax
- deltay
) * 2;
237 numpixels
++; /* include endpoints */
254 for (i
= 0; i
< numpixels
; i
++)
256 if (((unsigned)x
< LCD_WIDTH
) && ((unsigned)y
< LCD_HEIGHT
))
274 /* Draw a horizontal line (optimised) */
275 void lcd_hline(int x1
, int x2
, int y
)
278 unsigned char *dst
, *dst_end
;
280 lcd_blockfunc_type
*bfunc
;
290 /* nothing to draw? */
291 if (((unsigned)y
>= LCD_HEIGHT
) || (x1
>= LCD_WIDTH
) || (x2
< 0))
300 bfunc
= lcd_blockfuncs
[drawmode
];
301 dst
= &lcd_framebuffer
[y
>>3][x1
];
304 dst_end
= dst
+ x2
- x1
;
306 bfunc(dst
++, mask
, 0xFFu
);
307 while (dst
<= dst_end
);
310 /* Draw a vertical line (optimised) */
311 void lcd_vline(int x
, int y1
, int y2
)
315 unsigned mask
, mask_bottom
;
316 lcd_blockfunc_type
*bfunc
;
326 /* nothing to draw? */
327 if (((unsigned)x
>= LCD_WIDTH
) || (y1
>= LCD_HEIGHT
) || (y2
< 0))
333 if (y2
>= LCD_HEIGHT
)
336 bfunc
= lcd_blockfuncs
[drawmode
];
337 dst
= &lcd_framebuffer
[y1
>>3][x
];
339 mask
= 0xFFu
<< (y1
& 7);
340 mask_bottom
= 0xFFu
>> (~ny
& 7);
342 for (; ny
>= 8; ny
-= 8)
344 bfunc(dst
, mask
, 0xFFu
);
349 bfunc(dst
, mask
, 0xFFu
);
352 /* Draw a rectangular box */
353 void lcd_drawrect(int x
, int y
, int width
, int height
)
355 if ((width
<= 0) || (height
<= 0))
358 int x2
= x
+ width
- 1;
359 int y2
= y
+ height
- 1;
362 lcd_vline(x2
, y
, y2
);
364 lcd_hline(x
, x2
, y2
);
367 /* Fill a rectangular area */
368 void lcd_fillrect(int x
, int y
, int width
, int height
)
371 unsigned char *dst
, *dst_end
;
372 unsigned mask
, mask_bottom
;
374 lcd_blockfunc_type
*bfunc
;
375 bool fillopt
= false;
377 /* nothing to draw? */
378 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_WIDTH
) || (y
>= LCD_HEIGHT
)
379 || (x
+ width
<= 0) || (y
+ height
<= 0))
393 if (x
+ width
> LCD_WIDTH
)
394 width
= LCD_WIDTH
- x
;
395 if (y
+ height
> LCD_HEIGHT
)
396 height
= LCD_HEIGHT
- y
;
398 if (drawmode
& DRMODE_INVERSEVID
)
400 if (drawmode
& DRMODE_BG
)
407 if (drawmode
& DRMODE_FG
)
413 bfunc
= lcd_blockfuncs
[drawmode
];
414 dst
= &lcd_framebuffer
[y
>>3][x
];
415 ny
= height
- 1 + (y
& 7);
416 mask
= 0xFFu
<< (y
& 7);
417 mask_bottom
= 0xFFu
>> (~ny
& 7);
419 for (; ny
>= 8; ny
-= 8)
421 if (fillopt
&& (mask
== 0xFFu
))
422 memset(dst
, bits
, width
);
425 unsigned char *dst_row
= dst
;
427 dst_end
= dst_row
+ width
;
429 bfunc(dst_row
++, mask
, 0xFFu
);
430 while (dst_row
< dst_end
);
438 if (fillopt
&& (mask
== 0xFFu
))
439 memset(dst
, bits
, width
);
442 dst_end
= dst
+ width
;
444 bfunc(dst
++, mask
, 0xFFu
);
445 while (dst
< dst_end
);
449 /* About Rockbox' internal bitmap format:
451 * A bitmap contains one bit for every pixel that defines if that pixel is
452 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
454 * The bytes are stored in row-major order, with byte 0 being top left,
455 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
456 * 0..7, the second row defines pixel row 8..15 etc.
458 * This is the same as the internal lcd hw format. */
460 /* Draw a partial bitmap */
461 void lcd_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
462 int stride
, int x
, int y
, int width
, int height
)
464 void lcd_bitmap_part(const unsigned char *src
, int src_x
, int src_y
,
465 int stride
, int x
, int y
, int width
, int height
)
468 unsigned char *dst
, *dst_end
;
469 unsigned mask
, mask_bottom
;
470 lcd_blockfunc_type
*bfunc
;
472 /* nothing to draw? */
473 if ((width
<= 0) || (height
<= 0) || (x
>= LCD_WIDTH
) || (y
>= LCD_HEIGHT
)
474 || (x
+ width
<= 0) || (y
+ height
<= 0))
490 if (x
+ width
> LCD_WIDTH
)
491 width
= LCD_WIDTH
- x
;
492 if (y
+ height
> LCD_HEIGHT
)
493 height
= LCD_HEIGHT
- y
;
495 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
498 dst
= &lcd_framebuffer
[y
>>3][x
];
500 ny
= height
- 1 + shift
+ src_y
;
502 bfunc
= lcd_blockfuncs
[drawmode
];
503 mask
= 0xFFu
<< (shift
+ src_y
);
504 mask_bottom
= 0xFFu
>> (~ny
& 7);
508 bool copyopt
= (drawmode
== DRMODE_SOLID
);
510 for (; ny
>= 8; ny
-= 8)
512 if (copyopt
&& (mask
== 0xFFu
))
513 memcpy(dst
, src
, width
);
516 const unsigned char *src_row
= src
;
517 unsigned char *dst_row
= dst
;
519 dst_end
= dst_row
+ width
;
521 bfunc(dst_row
++, mask
, *src_row
++);
522 while (dst_row
< dst_end
);
531 if (copyopt
&& (mask
== 0xFFu
))
532 memcpy(dst
, src
, width
);
535 dst_end
= dst
+ width
;
537 bfunc(dst
++, mask
, *src
++);
538 while (dst
< dst_end
);
543 dst_end
= dst
+ width
;
546 const unsigned char *src_col
= src
++;
547 unsigned char *dst_col
= dst
++;
548 unsigned mask_col
= mask
;
551 for (y
= ny
; y
>= 8; y
-= 8)
553 data
|= *src_col
<< shift
;
555 if (mask_col
& 0xFFu
)
557 bfunc(dst_col
, mask_col
, data
);
564 dst_col
+= LCD_WIDTH
;
567 data
|= *src_col
<< shift
;
568 bfunc(dst_col
, mask_col
& mask_bottom
, data
);
570 while (dst
< dst_end
);
574 /* Draw a full bitmap */
575 void lcd_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
577 lcd_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
580 /* put a string at a given pixel position, skipping first ofs pixel columns */
581 static void lcd_putsxyofs(int x
, int y
, int ofs
, const unsigned char *str
)
585 struct font
* pf
= font_get(curfont
);
587 ucs
= bidi_l2v(str
, 1);
589 while ((ch
= *ucs
++) != 0 && x
< LCD_WIDTH
)
592 const unsigned char *bits
;
594 /* get proportional width and glyph bits */
595 width
= font_get_width(pf
,ch
);
603 bits
= font_get_bits(pf
, ch
);
605 lcd_mono_bitmap_part(bits
, ofs
, 0, width
, x
, y
, width
- ofs
,
612 /* put a string at a given pixel position */
613 void lcd_putsxy(int x
, int y
, const unsigned char *str
)
615 lcd_putsxyofs(x
, y
, 0, str
);
618 /*** Line oriented text output ***/
620 /* put a string at a given char position */
621 void lcd_puts(int x
, int y
, const unsigned char *str
)
623 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, 0);
626 void lcd_puts_style(int x
, int y
, const unsigned char *str
, int style
)
628 lcd_puts_style_offset(x
, y
, str
, style
, 0);
631 void lcd_puts_offset(int x
, int y
, const unsigned char *str
, int offset
)
633 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, offset
);
636 /* put a string at a given char position, style, and pixel position,
637 * skipping first offset pixel columns */
638 void lcd_puts_style_offset(int x
, int y
, const unsigned char *str
,
639 int style
, int offset
)
641 int xpos
,ypos
,w
,h
,xrect
;
642 int lastmode
= drawmode
;
644 /* make sure scrolling is turned off on the line we are updating */
645 lcd_scroll_info
.lines
&= ~(1 << y
);
650 lcd_getstringsize(str
, &w
, &h
);
651 xpos
= xmargin
+ x
*w
/ utf8length(str
);
652 ypos
= ymargin
+ y
*h
;
653 drawmode
= (style
& STYLE_INVERT
) ?
654 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
655 lcd_putsxyofs(xpos
, ypos
, offset
, str
);
656 drawmode
^= DRMODE_INVERSEVID
;
657 xrect
= xpos
+ MAX(w
- offset
, 0);
658 lcd_fillrect(xrect
, ypos
, LCD_WIDTH
- xrect
, h
);
663 void lcd_puts_scroll(int x
, int y
, const unsigned char *string
)
665 lcd_puts_scroll_style(x
, y
, string
, STYLE_DEFAULT
);
668 void lcd_puts_scroll_style(int x
, int y
, const unsigned char *string
, int style
)
670 lcd_puts_scroll_style_offset(x
, y
, string
, style
, 0);
673 void lcd_puts_scroll_offset(int x
, int y
, const unsigned char *string
,
676 lcd_puts_scroll_style_offset(x
, y
, string
, STYLE_DEFAULT
, offset
);
679 void lcd_puts_scroll_style_offset(int x
, int y
, const unsigned char *string
,
680 int style
, int offset
)
682 struct scrollinfo
* s
;
685 if(y
>=LCD_SCROLLABLE_LINES
) return;
687 s
= &lcd_scroll_info
.scroll
[y
];
689 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
;
691 if (style
& STYLE_INVERT
) {
692 lcd_puts_style_offset(x
,y
,string
,STYLE_INVERT
,offset
);
695 lcd_puts_offset(x
,y
,string
,offset
);
697 lcd_getstringsize(string
, &w
, &h
);
699 if (LCD_WIDTH
- x
* 8 - xmargin
< w
) {
700 /* prepare scroll line */
703 memset(s
->line
, 0, sizeof s
->line
);
704 strcpy(s
->line
, string
);
707 s
->width
= lcd_getstringsize(s
->line
, &w
, &h
);
709 /* scroll bidirectional or forward only depending on the string
711 if ( lcd_scroll_info
.bidir_limit
) {
712 s
->bidir
= s
->width
< (LCD_WIDTH
- xmargin
) *
713 (100 + lcd_scroll_info
.bidir_limit
) / 100;
718 if (!s
->bidir
) { /* add spaces if scrolling in the round */
719 strcat(s
->line
, " ");
720 /* get new width incl. spaces */
721 s
->width
= lcd_getstringsize(s
->line
, &w
, &h
);
724 end
= strchr(s
->line
, '\0');
725 strncpy(end
, string
, LCD_WIDTH
/2);
727 s
->len
= utf8length(string
);
729 s
->startx
= xmargin
+ x
* s
->width
/ s
->len
;;
731 lcd_scroll_info
.lines
|= (1<<y
);
734 /* force a bit switch-off since it doesn't scroll */
735 lcd_scroll_info
.lines
&= ~(1<<y
);
738 void lcd_scroll_fn(void)
741 struct scrollinfo
* s
;
746 for ( index
= 0; index
< LCD_SCROLLABLE_LINES
; index
++ ) {
748 if ((lcd_scroll_info
.lines
& (1 << index
)) == 0)
751 s
= &lcd_scroll_info
.scroll
[index
];
754 if (TIME_BEFORE(current_tick
, s
->start_tick
))
758 s
->offset
-= lcd_scroll_info
.step
;
760 s
->offset
+= lcd_scroll_info
.step
;
762 pf
= font_get(curfont
);
764 ypos
= ymargin
+ index
* pf
->height
;
766 if (s
->bidir
) { /* scroll bidirectional */
767 if (s
->offset
<= 0) {
768 /* at beginning of line */
771 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
773 if (s
->offset
>= s
->width
- (LCD_WIDTH
- xpos
)) {
775 s
->offset
= s
->width
- (LCD_WIDTH
- xpos
);
777 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
781 /* scroll forward the whole time */
782 if (s
->offset
>= s
->width
)
783 s
->offset
%= s
->width
;
787 drawmode
= (s
->style
&STYLE_INVERT
) ?
788 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
789 lcd_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
791 lcd_update_rect(xpos
, ypos
, LCD_WIDTH
- xpos
, pf
->height
);