1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2004 by Linus Nielsen Feltzing
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"
39 fb_data lcd_framebuffer
[LCD_FBHEIGHT
][LCD_FBWIDTH
] IRAM_LCDFRAMEBUFFER
;
41 const unsigned char lcd_dibits
[16] ICONST_ATTR
= {
42 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
43 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
46 static const unsigned char pixmask
[4] ICONST_ATTR
= {
47 0x03, 0x0C, 0x30, 0xC0
50 static fb_data
* lcd_backdrop
= NULL
;
51 static long lcd_backdrop_offset IDATA_ATTR
= 0;
53 static struct viewport default_vp
=
59 .font
= FONT_SYSFIXED
,
60 .drawmode
= DRMODE_SOLID
,
61 .fg_pattern
= LCD_DEFAULT_FG
,
62 .bg_pattern
= LCD_DEFAULT_BG
65 static struct viewport
* current_vp IBSS_ATTR
;
66 static unsigned fg_pattern IBSS_ATTR
;
67 static unsigned bg_pattern IBSS_ATTR
;
72 /* Initialise the viewport */
73 lcd_set_viewport(NULL
);
76 /* Call device specific init */
83 void lcd_set_viewport(struct viewport
* vp
)
86 current_vp
= &default_vp
;
90 fg_pattern
= 0x55 * (~current_vp
->fg_pattern
& 3);
91 bg_pattern
= 0x55 * (~current_vp
->bg_pattern
& 3);
94 void lcd_update_viewport(void)
96 lcd_update_rect(current_vp
->x
, current_vp
->y
,
97 current_vp
->width
, current_vp
->height
);
100 void lcd_update_viewport_rect(int x
, int y
, int width
, int height
)
102 lcd_update_rect(current_vp
->x
+ x
, current_vp
->y
+ y
, width
, height
);
106 /*** parameter handling ***/
108 void lcd_set_drawmode(int mode
)
110 current_vp
->drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
113 int lcd_get_drawmode(void)
115 return current_vp
->drawmode
;
118 void lcd_set_foreground(unsigned brightness
)
120 current_vp
->fg_pattern
= brightness
;
121 fg_pattern
= 0x55 * (~brightness
& 3);
124 unsigned lcd_get_foreground(void)
126 return current_vp
->fg_pattern
;
129 void lcd_set_background(unsigned brightness
)
131 current_vp
->bg_pattern
= brightness
;
132 bg_pattern
= 0x55 * (~brightness
& 3);
135 unsigned lcd_get_background(void)
137 return current_vp
->bg_pattern
;
140 void lcd_set_drawinfo(int mode
, unsigned fg_brightness
, unsigned bg_brightness
)
142 lcd_set_drawmode(mode
);
143 lcd_set_foreground(fg_brightness
);
144 lcd_set_background(bg_brightness
);
147 int lcd_getwidth(void)
149 return current_vp
->width
;
152 int lcd_getheight(void)
154 return current_vp
->height
;
157 void lcd_setfont(int newfont
)
159 current_vp
->font
= newfont
;
162 int lcd_getfont(void)
164 return current_vp
->font
;
167 int lcd_getstringsize(const unsigned char *str
, int *w
, int *h
)
169 return font_getstringsize(str
, w
, h
, current_vp
->font
);
172 /*** low-level drawing functions ***/
174 static void setpixel(int x
, int y
)
176 unsigned mask
= pixmask
[y
& 3];
177 fb_data
*address
= &lcd_framebuffer
[y
>>2][x
];
178 unsigned data
= *address
;
180 *address
= data
^ ((data
^ fg_pattern
) & mask
);
183 static void clearpixel(int x
, int y
)
185 unsigned mask
= pixmask
[y
& 3];
186 fb_data
*address
= &lcd_framebuffer
[y
>>2][x
];
187 unsigned data
= *address
;
189 *address
= data
^ ((data
^ bg_pattern
) & mask
);
192 static void clearimgpixel(int x
, int y
)
194 unsigned mask
= pixmask
[y
& 3];
195 fb_data
*address
= &lcd_framebuffer
[y
>>2][x
];
196 unsigned data
= *address
;
198 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
);
201 static void flippixel(int x
, int y
)
203 unsigned mask
= pixmask
[y
& 3];
204 fb_data
*address
= &lcd_framebuffer
[y
>>2][x
];
209 static void nopixel(int x
, int y
)
215 lcd_pixelfunc_type
* const lcd_pixelfuncs_bgcolor
[8] = {
216 flippixel
, nopixel
, setpixel
, setpixel
,
217 nopixel
, clearpixel
, nopixel
, clearpixel
220 lcd_pixelfunc_type
* const lcd_pixelfuncs_backdrop
[8] = {
221 flippixel
, nopixel
, setpixel
, setpixel
,
222 nopixel
, clearimgpixel
, nopixel
, clearimgpixel
226 lcd_pixelfunc_type
* const * lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
228 /* 'mask' and 'bits' contain 2 bits per pixel */
229 static void ICODE_ATTR
flipblock(fb_data
*address
, unsigned mask
,
232 *address
^= bits
& mask
;
235 static void ICODE_ATTR
bgblock(fb_data
*address
, unsigned mask
,
238 unsigned data
= *address
;
240 *address
= data
^ ((data
^ bg_pattern
) & mask
& ~bits
);
243 static void ICODE_ATTR
bgimgblock(fb_data
*address
, unsigned mask
,
246 unsigned data
= *address
;
248 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& ~bits
);
251 static void ICODE_ATTR
fgblock(fb_data
*address
, unsigned mask
,
254 unsigned data
= *address
;
256 *address
= data
^ ((data
^ fg_pattern
) & mask
& bits
);
259 static void ICODE_ATTR
solidblock(fb_data
*address
, unsigned mask
,
262 unsigned data
= *address
;
263 unsigned bgp
= bg_pattern
;
265 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
266 *address
= data
^ ((data
^ bits
) & mask
);
269 static void ICODE_ATTR
solidimgblock(fb_data
*address
, unsigned mask
,
272 unsigned data
= *address
;
273 unsigned bgp
= *(address
+ lcd_backdrop_offset
);
275 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
276 *address
= data
^ ((data
^ bits
) & mask
);
279 static void ICODE_ATTR
flipinvblock(fb_data
*address
, unsigned mask
,
282 *address
^= ~bits
& mask
;
285 static void ICODE_ATTR
bginvblock(fb_data
*address
, unsigned mask
,
288 unsigned data
= *address
;
290 *address
= data
^ ((data
^ bg_pattern
) & mask
& bits
);
293 static void ICODE_ATTR
bgimginvblock(fb_data
*address
, unsigned mask
,
296 unsigned data
= *address
;
298 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& bits
);
301 static void ICODE_ATTR
fginvblock(fb_data
*address
, unsigned mask
,
304 unsigned data
= *address
;
306 *address
= data
^ ((data
^ fg_pattern
) & mask
& ~bits
);
309 static void ICODE_ATTR
solidinvblock(fb_data
*address
, unsigned mask
,
312 unsigned data
= *address
;
313 unsigned fgp
= fg_pattern
;
315 bits
= fgp
^ ((fgp
^ bg_pattern
) & bits
);
316 *address
= data
^ ((data
^ bits
) & mask
);
319 static void ICODE_ATTR
solidimginvblock(fb_data
*address
, unsigned mask
,
322 unsigned data
= *address
;
323 unsigned fgp
= fg_pattern
;
325 bits
= fgp
^ ((fgp
^ *(address
+ lcd_backdrop_offset
)) & bits
);
326 *address
= data
^ ((data
^ bits
) & mask
);
329 lcd_blockfunc_type
* const lcd_blockfuncs_bgcolor
[8] = {
330 flipblock
, bgblock
, fgblock
, solidblock
,
331 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
334 lcd_blockfunc_type
* const lcd_blockfuncs_backdrop
[8] = {
335 flipblock
, bgimgblock
, fgblock
, solidimgblock
,
336 flipinvblock
, bgimginvblock
, fginvblock
, solidimginvblock
339 lcd_blockfunc_type
* const * lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
342 void lcd_set_backdrop(fb_data
* backdrop
)
344 lcd_backdrop
= backdrop
;
347 lcd_backdrop_offset
= (long)backdrop
- (long)lcd_framebuffer
;
348 lcd_pixelfuncs
= lcd_pixelfuncs_backdrop
;
349 lcd_blockfuncs
= lcd_blockfuncs_backdrop
;
353 lcd_backdrop_offset
= 0;
354 lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
355 lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
359 fb_data
* lcd_get_backdrop(void)
365 static inline void setblock(fb_data
*address
, unsigned mask
, unsigned bits
)
367 unsigned data
= *address
;
370 *address
= data
^ (bits
& mask
);
373 /*** drawing functions ***/
375 /* Clear the whole display */
376 void lcd_clear_display(void)
378 if (current_vp
->drawmode
& DRMODE_INVERSEVID
)
380 memset(lcd_framebuffer
, fg_pattern
, sizeof lcd_framebuffer
);
385 memcpy(lcd_framebuffer
, lcd_backdrop
, sizeof lcd_framebuffer
);
387 memset(lcd_framebuffer
, bg_pattern
, sizeof lcd_framebuffer
);
390 lcd_scroll_info
.lines
= 0;
393 /* Clear the current viewport */
394 void lcd_clear_viewport(void)
398 if (current_vp
== &default_vp
)
404 lastmode
= current_vp
->drawmode
;
406 /* Invert the INVERSEVID bit and set basic mode to SOLID */
407 current_vp
->drawmode
= (~lastmode
& DRMODE_INVERSEVID
) |
410 lcd_fillrect(0, 0, current_vp
->width
, current_vp
->height
);
412 current_vp
->drawmode
= lastmode
;
414 lcd_scroll_stop(current_vp
);
418 /* Set a single pixel */
419 void lcd_drawpixel(int x
, int y
)
421 if (((unsigned)x
< (unsigned)current_vp
->width
) &&
422 ((unsigned)y
< (unsigned)current_vp
->height
))
423 lcd_pixelfuncs
[current_vp
->drawmode
](current_vp
->x
+ x
, current_vp
->y
+ y
);
427 void lcd_drawline(int x1
, int y1
, int x2
, int y2
)
435 lcd_pixelfunc_type
*pfunc
= lcd_pixelfuncs
[current_vp
->drawmode
];
437 deltax
= abs(x2
- x1
);
440 DEBUGF("lcd_drawline() called for vertical line - optimisation.\n");
441 lcd_vline(x1
, y1
, y2
);
444 deltay
= abs(y2
- y1
);
447 DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n");
448 lcd_hline(x1
, x2
, y1
);
454 if (deltax
>= deltay
)
457 d
= 2 * deltay
- deltax
;
459 dinc2
= (deltay
- deltax
) * 2;
466 d
= 2 * deltax
- deltay
;
468 dinc2
= (deltax
- deltay
) * 2;
472 numpixels
++; /* include endpoints */
489 for (i
= 0; i
< numpixels
; i
++)
491 if (((unsigned)x
< (unsigned)current_vp
->width
) &&
492 ((unsigned)y
< (unsigned)current_vp
->height
))
493 pfunc(current_vp
->x
+ x
, current_vp
->y
+ y
);
510 /* Draw a horizontal line (optimised) */
511 void lcd_hline(int x1
, int x2
, int y
)
515 fb_data
*dst
, *dst_end
;
517 lcd_blockfunc_type
*bfunc
;
527 /* nothing to draw? */
528 if (((unsigned)y
>= (unsigned)current_vp
->height
) || (x1
>= current_vp
->width
)
535 if (x2
>= current_vp
->width
)
536 x2
= current_vp
->width
-1;
540 /* adjust x1 and y to viewport */
544 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
545 dst
= &lcd_framebuffer
[y
>>2][x1
];
546 mask
= pixmask
[y
& 3];
548 dst_end
= dst
+ width
;
550 bfunc(dst
++, mask
, 0xFFu
);
551 while (dst
< dst_end
);
554 /* Draw a vertical line (optimised) */
555 void lcd_vline(int x
, int y1
, int y2
)
559 unsigned mask
, mask_bottom
;
560 lcd_blockfunc_type
*bfunc
;
570 /* nothing to draw? */
571 if (((unsigned)x
>= (unsigned)current_vp
->width
) || (y1
>= current_vp
->height
)
578 if (y2
>= current_vp
->height
)
579 y2
= current_vp
->height
-1;
581 /* adjust for viewport */
586 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
587 dst
= &lcd_framebuffer
[y1
>>2][x
];
589 mask
= 0xFFu
<< (2 * (y1
& 3));
590 mask_bottom
= 0xFFu
>> (2 * (~ny
& 3));
592 for (; ny
>= 4; ny
-= 4)
594 bfunc(dst
, mask
, 0xFFu
);
599 bfunc(dst
, mask
, 0xFFu
);
602 /* Draw a rectangular box */
603 void lcd_drawrect(int x
, int y
, int width
, int height
)
605 if ((width
<= 0) || (height
<= 0))
608 int x2
= x
+ width
- 1;
609 int y2
= y
+ height
- 1;
612 lcd_vline(x2
, y
, y2
);
614 lcd_hline(x
, x2
, y2
);
617 /* Fill a rectangular area */
618 void lcd_fillrect(int x
, int y
, int width
, int height
)
621 fb_data
*dst
, *dst_end
;
622 unsigned mask
, mask_bottom
;
624 lcd_blockfunc_type
*bfunc
;
625 bool fillopt
= false;
627 /* nothing to draw? */
628 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
)
629 || (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
643 if (x
+ width
> current_vp
->width
)
644 width
= current_vp
->width
- x
;
645 if (y
+ height
> current_vp
->height
)
646 height
= current_vp
->height
- y
;
648 /* adjust for viewport */
652 if (current_vp
->drawmode
& DRMODE_INVERSEVID
)
654 if ((current_vp
->drawmode
& DRMODE_BG
) && !lcd_backdrop
)
662 if (current_vp
->drawmode
& DRMODE_FG
)
668 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
669 dst
= &lcd_framebuffer
[y
>>2][x
];
670 ny
= height
- 1 + (y
& 3);
671 mask
= 0xFFu
<< (2 * (y
& 3));
672 mask_bottom
= 0xFFu
>> (2 * (~ny
& 3));
674 for (; ny
>= 4; ny
-= 4)
676 if (fillopt
&& (mask
== 0xFFu
))
677 memset(dst
, bits
, width
);
680 fb_data
*dst_row
= dst
;
682 dst_end
= dst_row
+ width
;
684 bfunc(dst_row
++, mask
, 0xFFu
);
685 while (dst_row
< dst_end
);
693 if (fillopt
&& (mask
== 0xFFu
))
694 memset(dst
, bits
, width
);
697 dst_end
= dst
+ width
;
699 bfunc(dst
++, mask
, 0xFFu
);
700 while (dst
< dst_end
);
704 /* About Rockbox' internal monochrome bitmap format:
706 * A bitmap contains one bit for every pixel that defines if that pixel is
707 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
709 * The bytes are stored in row-major order, with byte 0 being top left,
710 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
711 * 0..7, the second row defines pixel row 8..15 etc.
713 * This is similar to the internal lcd hw format. */
715 /* Draw a partial monochrome bitmap */
716 void ICODE_ATTR
lcd_mono_bitmap_part(const unsigned char *src
, int src_x
,
717 int src_y
, int stride
, int x
, int y
,
718 int width
, int height
)
721 fb_data
*dst
, *dst_end
;
722 unsigned mask
, mask_bottom
;
723 lcd_blockfunc_type
*bfunc
;
725 /* nothing to draw? */
726 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) ||
727 (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
743 if (x
+ width
> current_vp
->width
)
744 width
= current_vp
->width
- x
;
745 if (y
+ height
> current_vp
->height
)
746 height
= current_vp
->height
- y
;
748 /* adjust for viewport */
752 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
755 dst
= &lcd_framebuffer
[y
>>2][x
];
757 ny
= height
- 1 + shift
+ src_y
;
758 mask
= 0xFFFFu
<< (2 * (shift
+ src_y
));
759 /* Overflowing bits aren't important. */
760 mask_bottom
= 0xFFFFu
>> (2 * (~ny
& 7));
762 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
766 unsigned dmask1
, dmask2
, data
;
768 dmask1
= mask
& 0xFFu
;
771 for (; ny
>= 8; ny
-= 8)
773 const unsigned char *src_row
= src
;
774 fb_data
*dst_row
= dst
+ LCD_WIDTH
;
776 dst_end
= dst_row
+ width
;
783 bfunc(dst_row
- LCD_WIDTH
, dmask1
, lcd_dibits
[data
&0x0F]);
784 bfunc(dst_row
++, dmask2
, lcd_dibits
[(data
>>4)&0x0F]);
786 while (dst_row
< dst_end
);
791 bfunc(dst_row
++, dmask2
, lcd_dibits
[((*src_row
++)>>4)&0x0F]);
792 while (dst_row
< dst_end
);
796 dmask1
= dmask2
= 0xFFu
;
798 dmask1
&= mask_bottom
;
799 /* & 0xFFu is unnecessary here - dmask1 can't exceed that*/
800 dmask2
&= (mask_bottom
>> 8);
801 dst_end
= dst
+ width
;
810 bfunc(dst
, dmask1
, lcd_dibits
[data
&0x0F]);
811 bfunc((dst
++) + LCD_WIDTH
, dmask2
, lcd_dibits
[(data
>>4)&0x0F]);
813 while (dst
< dst_end
);
818 bfunc(dst
++, dmask1
, lcd_dibits
[(*src
++)&0x0F]);
819 while (dst
< dst_end
);
825 bfunc((dst
++) + LCD_WIDTH
, dmask2
, lcd_dibits
[((*src
++)>>4)&0x0F]);
826 while (dst
< dst_end
);
831 dst_end
= dst
+ width
;
834 const unsigned char *src_col
= src
++;
835 fb_data
*dst_col
= dst
++;
836 unsigned mask_col
= mask
;
839 for (y
= ny
; y
>= 8; y
-= 8)
841 data
|= *src_col
<< shift
;
843 if (mask_col
& 0xFFFFu
)
845 if (mask_col
& 0xFFu
)
846 bfunc(dst_col
, mask_col
, lcd_dibits
[data
&0x0F]);
847 bfunc(dst_col
+ LCD_WIDTH
, mask_col
>> 8,
848 lcd_dibits
[(data
>>4)&0x0F]);
855 dst_col
+= 2*LCD_WIDTH
;
858 data
|= *src_col
<< shift
;
859 mask_col
&= mask_bottom
;
860 if (mask_col
& 0xFFu
)
861 bfunc(dst_col
, mask_col
, lcd_dibits
[data
&0x0F]);
862 if (mask_col
& 0xFF00u
)
863 bfunc(dst_col
+ LCD_WIDTH
, mask_col
>> 8,
864 lcd_dibits
[(data
>>4)&0x0F]);
866 while (dst
< dst_end
);
870 /* Draw a full monochrome bitmap */
871 void lcd_mono_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
873 lcd_mono_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
876 /* About Rockbox' internal native bitmap format:
878 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
879 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
881 * The bytes are stored in row-major order, with byte 0 being top left,
882 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
883 * 0..3, the second row defines pixel row 4..7 etc.
885 * This is the same as the internal lcd hw format. */
887 /* Draw a partial native bitmap */
888 void ICODE_ATTR
lcd_bitmap_part(const fb_data
*src
, int src_x
, int src_y
,
889 int stride
, int x
, int y
, int width
,
893 fb_data
*dst
, *dst_end
;
894 unsigned mask
, mask_bottom
;
896 /* nothing to draw? */
897 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
)
898 || (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
914 if (x
+ width
> current_vp
->width
)
915 width
= current_vp
->width
- x
;
916 if (y
+ height
> current_vp
->height
)
917 height
= current_vp
->height
- y
;
919 /* adjust for viewport */
923 src
+= stride
* (src_y
>> 2) + src_x
; /* move starting point */
926 dst
= &lcd_framebuffer
[y
>>2][x
];
928 ny
= height
- 1 + shift
+ src_y
;
930 mask
= 0xFFu
<< (2 * (shift
+ src_y
));
931 mask_bottom
= 0xFFu
>> (2 * (~ny
& 3));
935 for (; ny
>= 4; ny
-= 4)
938 memcpy(dst
, src
, width
);
941 const fb_data
*src_row
= src
;
942 fb_data
*dst_row
= dst
;
944 dst_end
= dst_row
+ width
;
946 setblock(dst_row
++, mask
, *src_row
++);
947 while (dst_row
< dst_end
);
956 memcpy(dst
, src
, width
);
959 dst_end
= dst
+ width
;
961 setblock(dst
++, mask
, *src
++);
962 while (dst
< dst_end
);
968 dst_end
= dst
+ width
;
971 const fb_data
*src_col
= src
++;
972 fb_data
*dst_col
= dst
++;
973 unsigned mask_col
= mask
;
976 for (y
= ny
; y
>= 4; y
-= 4)
978 data
|= *src_col
<< shift
;
980 if (mask_col
& 0xFFu
)
982 setblock(dst_col
, mask_col
, data
);
989 dst_col
+= LCD_WIDTH
;
992 data
|= *src_col
<< shift
;
993 setblock(dst_col
, mask_col
& mask_bottom
, data
);
995 while (dst
< dst_end
);
999 /* Draw a full native bitmap */
1000 void lcd_bitmap(const fb_data
*src
, int x
, int y
, int width
, int height
)
1002 lcd_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
1005 #include "lcd-bitmap-common.c"