1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * LCD driver for horizontally-packed 2bpp greyscale display
12 * Based on code from the rockbox lcd's driver
14 * Copyright (c) 2006 Seven Le Mesle (sevlm@free.fr)
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
24 ****************************************************************************/
36 #include "rbunicode.h"
38 #include "scroll_engine.h"
42 unsigned char lcd_framebuffer
[LCD_FBHEIGHT
][LCD_FBWIDTH
] IRAM_LCDFRAMEBUFFER
;
44 static const unsigned char pixmask
[4] ICONST_ATTR
= {
45 0xC0, 0x30, 0x0C, 0x03
48 static fb_data
* lcd_backdrop
= NULL
;
49 static long lcd_backdrop_offset IDATA_ATTR
= 0;
51 static struct viewport default_vp
=
57 .font
= FONT_SYSFIXED
,
58 .drawmode
= DRMODE_SOLID
,
59 .fg_pattern
= LCD_DEFAULT_FG
,
60 .bg_pattern
= LCD_DEFAULT_BG
63 static struct viewport
* current_vp IBSS_ATTR
;
64 static unsigned fg_pattern IBSS_ATTR
;
65 static unsigned bg_pattern IBSS_ATTR
;
70 /* Initialise the viewport */
71 lcd_set_viewport(NULL
);
74 /* Call device specific init */
81 void lcd_set_viewport(struct viewport
* vp
)
84 current_vp
= &default_vp
;
88 fg_pattern
= 0x55 * (~current_vp
->fg_pattern
& 3);
89 bg_pattern
= 0x55 * (~current_vp
->bg_pattern
& 3);
92 void lcd_update_viewport(void)
94 lcd_update_rect(current_vp
->x
, current_vp
->y
,
95 current_vp
->width
, current_vp
->height
);
98 void lcd_update_viewport_rect(int x
, int y
, int width
, int height
)
100 lcd_update_rect(current_vp
->x
+ x
, current_vp
->y
+ y
, width
, height
);
103 /*** parameter handling ***/
105 void lcd_set_drawmode(int mode
)
107 current_vp
->drawmode
= mode
& (DRMODE_SOLID
|DRMODE_INVERSEVID
);
110 int lcd_get_drawmode(void)
112 return current_vp
->drawmode
;
115 void lcd_set_foreground(unsigned brightness
)
117 current_vp
->fg_pattern
= brightness
;
118 fg_pattern
= 0x55 * (~brightness
& 3);
121 unsigned lcd_get_foreground(void)
123 return current_vp
->fg_pattern
;
126 void lcd_set_background(unsigned brightness
)
128 current_vp
->bg_pattern
= brightness
;
129 bg_pattern
= 0x55 * (~brightness
& 3);
132 unsigned lcd_get_background(void)
134 return current_vp
->bg_pattern
;
137 void lcd_set_drawinfo(int mode
, unsigned fg_brightness
, unsigned bg_brightness
)
139 lcd_set_drawmode(mode
);
140 lcd_set_foreground(fg_brightness
);
141 lcd_set_background(bg_brightness
);
144 int lcd_getwidth(void)
146 return current_vp
->width
;
149 int lcd_getheight(void)
151 return current_vp
->height
;
154 void lcd_setfont(int newfont
)
156 current_vp
->font
= newfont
;
159 int lcd_getfont(void)
161 return current_vp
->font
;
164 int lcd_getstringsize(const unsigned char *str
, int *w
, int *h
)
166 return font_getstringsize(str
, w
, h
, current_vp
->font
);
169 /*** low-level drawing functions ***/
171 static void setpixel(int x
, int y
)
173 unsigned mask
= pixmask
[x
& 3];
174 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
175 unsigned data
= *address
;
177 *address
= data
^ ((data
^ fg_pattern
) & mask
);
180 static void clearpixel(int x
, int y
)
182 unsigned mask
= pixmask
[x
& 3];
183 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
184 unsigned data
= *address
;
186 *address
= data
^ ((data
^ bg_pattern
) & mask
);
189 static void clearimgpixel(int x
, int y
)
191 unsigned mask
= pixmask
[x
& 3];
192 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
193 unsigned data
= *address
;
195 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
);
198 static void flippixel(int x
, int y
)
200 unsigned mask
= pixmask
[x
& 3];
201 fb_data
*address
= &lcd_framebuffer
[y
][x
>>2];
206 static void nopixel(int x
, int y
)
212 lcd_pixelfunc_type
* const lcd_pixelfuncs_bgcolor
[8] = {
213 flippixel
, nopixel
, setpixel
, setpixel
,
214 nopixel
, clearpixel
, nopixel
, clearpixel
217 lcd_pixelfunc_type
* const lcd_pixelfuncs_backdrop
[8] = {
218 flippixel
, nopixel
, setpixel
, setpixel
,
219 nopixel
, clearimgpixel
, nopixel
, clearimgpixel
222 lcd_pixelfunc_type
* const * lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
225 /* 'mask' and 'bits' contain 2 bits per pixel */
226 static void ICODE_ATTR
flipblock(fb_data
*address
, unsigned mask
,
229 *address
^= bits
& mask
;
232 static void ICODE_ATTR
bgblock(fb_data
*address
, unsigned mask
,
235 unsigned data
= *address
;
237 *address
= data
^ ((data
^ bg_pattern
) & mask
& ~bits
);
240 static void ICODE_ATTR
bgimgblock(fb_data
*address
, unsigned mask
,
243 unsigned data
= *address
;
245 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& ~bits
);
248 static void ICODE_ATTR
fgblock(fb_data
*address
, unsigned mask
,
251 unsigned data
= *address
;
253 *address
= data
^ ((data
^ fg_pattern
) & mask
& bits
);
256 static void ICODE_ATTR
solidblock(fb_data
*address
, unsigned mask
,
259 unsigned data
= *address
;
260 unsigned bgp
= bg_pattern
;
262 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
263 *address
= data
^ ((data
^ bits
) & mask
);
266 static void ICODE_ATTR
solidimgblock(fb_data
*address
, unsigned mask
,
269 unsigned data
= *address
;
270 unsigned bgp
= *(address
+ lcd_backdrop_offset
);
272 bits
= bgp
^ ((bgp
^ fg_pattern
) & bits
);
273 *address
= data
^ ((data
^ bits
) & mask
);
276 static void ICODE_ATTR
flipinvblock(fb_data
*address
, unsigned mask
,
279 *address
^= ~bits
& mask
;
282 static void ICODE_ATTR
bginvblock(fb_data
*address
, unsigned mask
,
285 unsigned data
= *address
;
287 *address
= data
^ ((data
^ bg_pattern
) & mask
& bits
);
290 static void ICODE_ATTR
bgimginvblock(fb_data
*address
, unsigned mask
,
293 unsigned data
= *address
;
295 *address
= data
^ ((data
^ *(address
+ lcd_backdrop_offset
)) & mask
& bits
);
298 static void ICODE_ATTR
fginvblock(fb_data
*address
, unsigned mask
,
301 unsigned data
= *address
;
303 *address
= data
^ ((data
^ fg_pattern
) & mask
& ~bits
);
306 static void ICODE_ATTR
solidinvblock(fb_data
*address
, unsigned mask
,
309 unsigned data
= *address
;
310 unsigned fgp
= fg_pattern
;
312 bits
= fgp
^ ((fgp
^ bg_pattern
) & bits
);
313 *address
= data
^ ((data
^ bits
) & mask
);
316 static void ICODE_ATTR
solidimginvblock(fb_data
*address
, unsigned mask
,
319 unsigned data
= *address
;
320 unsigned fgp
= fg_pattern
;
322 bits
= fgp
^ ((fgp
^ *(address
+ lcd_backdrop_offset
)) & bits
);
323 *address
= data
^ ((data
^ bits
) & mask
);
326 lcd_blockfunc_type
* const lcd_blockfuncs_bgcolor
[8] = {
327 flipblock
, bgblock
, fgblock
, solidblock
,
328 flipinvblock
, bginvblock
, fginvblock
, solidinvblock
331 lcd_blockfunc_type
* const lcd_blockfuncs_backdrop
[8] = {
332 flipblock
, bgimgblock
, fgblock
, solidimgblock
,
333 flipinvblock
, bgimginvblock
, fginvblock
, solidimginvblock
336 lcd_blockfunc_type
* const * lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
339 void lcd_set_backdrop(fb_data
* backdrop
)
341 lcd_backdrop
= backdrop
;
344 lcd_backdrop_offset
= (long)backdrop
- (long)lcd_framebuffer
;
345 lcd_pixelfuncs
= lcd_pixelfuncs_backdrop
;
346 lcd_blockfuncs
= lcd_blockfuncs_backdrop
;
350 lcd_backdrop_offset
= 0;
351 lcd_pixelfuncs
= lcd_pixelfuncs_bgcolor
;
352 lcd_blockfuncs
= lcd_blockfuncs_bgcolor
;
356 fb_data
* lcd_get_backdrop(void)
362 static inline void setblock(fb_data
*address
, unsigned mask
, unsigned bits
)
364 unsigned data
= *address
;
367 *address
= data
^ (bits
& mask
);
370 /*** drawing functions ***/
372 /* Clear the whole display */
373 void lcd_clear_display(void)
375 if (current_vp
->drawmode
& DRMODE_INVERSEVID
)
377 memset(lcd_framebuffer
, fg_pattern
, sizeof lcd_framebuffer
);
382 memcpy(lcd_framebuffer
, lcd_backdrop
, sizeof lcd_framebuffer
);
384 memset(lcd_framebuffer
, bg_pattern
, sizeof lcd_framebuffer
);
387 lcd_scroll_info
.lines
= 0;
390 /* Clear the current viewport */
391 void lcd_clear_viewport(void)
395 if (current_vp
== &default_vp
)
401 lastmode
= current_vp
->drawmode
;
403 /* Invert the INVERSEVID bit and set basic mode to SOLID */
404 current_vp
->drawmode
= (~lastmode
& DRMODE_INVERSEVID
) |
407 lcd_fillrect(0, 0, current_vp
->width
, current_vp
->height
);
409 current_vp
->drawmode
= lastmode
;
411 lcd_scroll_stop(current_vp
);
415 /* Set a single pixel */
416 void lcd_drawpixel(int x
, int y
)
418 if (((unsigned)x
< (unsigned)current_vp
->width
) &&
419 ((unsigned)y
< (unsigned)current_vp
->height
))
420 lcd_pixelfuncs
[current_vp
->drawmode
](current_vp
->x
+ x
, current_vp
->y
+ y
);
424 void lcd_drawline(int x1
, int y1
, int x2
, int y2
)
432 lcd_pixelfunc_type
*pfunc
= lcd_pixelfuncs
[current_vp
->drawmode
];
434 deltay
= abs(y2
- y1
);
437 DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n");
438 lcd_hline(x1
, x2
, y1
);
441 deltax
= abs(x2
- x1
);
444 DEBUGF("lcd_drawline() called for vertical line - optimisation.\n");
445 lcd_vline(x1
, y1
, y2
);
451 if (deltax
>= deltay
)
454 d
= 2 * deltay
- deltax
;
456 dinc2
= (deltay
- deltax
) * 2;
463 d
= 2 * deltax
- deltay
;
465 dinc2
= (deltax
- deltay
) * 2;
469 numpixels
++; /* include endpoints */
486 for (i
= 0; i
< numpixels
; i
++)
488 if (((unsigned)x
< (unsigned)current_vp
->width
) &&
489 ((unsigned)y
< (unsigned)current_vp
->height
))
490 pfunc(current_vp
->x
+ x
, current_vp
->y
+ y
);
507 /* Draw a horizontal line (optimised) */
508 void lcd_hline(int x1
, int x2
, int y
)
512 unsigned mask
, mask_right
;
513 lcd_blockfunc_type
*bfunc
;
523 /* nothing to draw? */
524 if (((unsigned)y
>= (unsigned)current_vp
->height
) || (x1
>= current_vp
->width
)
531 if (x2
>= current_vp
->width
)
532 x2
= current_vp
->width
-1;
534 /* adjust to viewport */
539 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
540 dst
= &lcd_framebuffer
[y
][x1
>>2];
542 mask
= 0xFFu
>> (2 * (x1
& 3));
543 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
545 for (; nx
>= 4; nx
-= 4)
547 bfunc(dst
++, mask
, 0xFFu
);
551 bfunc(dst
, mask
, 0xFFu
);
554 /* Draw a vertical line (optimised) */
555 void lcd_vline(int x
, int y1
, int y2
)
558 unsigned char *dst
, *dst_end
;
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
][x
>>2];
588 mask
= pixmask
[x
& 3];
590 dst_end
= dst
+ (y2
- y1
) * LCD_FBWIDTH
;
593 bfunc(dst
, mask
, 0xFFu
);
596 while (dst
<= dst_end
);
599 /* Draw a rectangular box */
600 void lcd_drawrect(int x
, int y
, int width
, int height
)
602 if ((width
<= 0) || (height
<= 0))
605 int x2
= x
+ width
- 1;
606 int y2
= y
+ height
- 1;
609 lcd_vline(x2
, y
, y2
);
611 lcd_hline(x
, x2
, y2
);
614 /* Fill a rectangular area */
615 void lcd_fillrect(int x
, int y
, int width
, int height
)
618 unsigned char *dst
, *dst_end
;
619 unsigned mask
, mask_right
;
620 lcd_blockfunc_type
*bfunc
;
622 /* nothing to draw? */
623 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) || (y
>= current_vp
->height
)
624 || (x
+ width
<= 0) || (y
+ height
<= 0))
638 if (x
+ width
> current_vp
->width
)
639 width
= current_vp
->width
- x
;
640 if (y
+ height
> current_vp
->height
)
641 height
= current_vp
->height
- y
;
643 /* adjust for viewport */
647 bfunc
= lcd_blockfuncs
[current_vp
->drawmode
];
648 dst
= &lcd_framebuffer
[y
][x
>>2];
649 nx
= width
- 1 + (x
& 3);
650 mask
= 0xFFu
>> (2 * (x
& 3));
651 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
653 for (; nx
>= 4; nx
-= 4)
655 unsigned char *dst_col
= dst
;
657 dst_end
= dst_col
+ height
* LCD_FBWIDTH
;
660 bfunc(dst_col
, mask
, 0xFFu
);
661 dst_col
+= LCD_FBWIDTH
;
663 while (dst_col
< dst_end
);
670 dst_end
= dst
+ height
* LCD_FBWIDTH
;
673 bfunc(dst
, mask
, 0xFFu
);
676 while (dst
< dst_end
);
679 /* About Rockbox' internal monochrome bitmap format:
681 * A bitmap contains one bit for every pixel that defines if that pixel is
682 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
684 * The bytes are stored in row-major order, with byte 0 being top left,
685 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
686 * 0..7, the second row defines pixel row 8..15 etc. */
688 /* Draw a partial monochrome bitmap */
689 void ICODE_ATTR
lcd_mono_bitmap_part(const unsigned char *src
, int src_x
,
690 int src_y
, int stride
, int x
, int y
,
691 int width
, int height
)
693 const unsigned char *src_end
;
694 fb_data
*dst
, *dst_end
;
695 unsigned dmask
= 0x100; /* bit 8 == sentinel */
697 int drmode
= current_vp
->drawmode
;
699 /* nothing to draw? */
700 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) ||
701 (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
717 if (x
+ width
> current_vp
->width
)
718 width
= current_vp
->width
- x
;
719 if (y
+ height
> current_vp
->height
)
720 height
= current_vp
->height
- y
;
722 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
724 src_end
= src
+ width
;
725 x
+= current_vp
->x
; /* adjust for viewport */
726 dst
= &lcd_framebuffer
[current_vp
->y
+ y
][x
>> 2];
727 dst_end
= dst
+ height
* LCD_FBWIDTH
;
728 dst_mask
= pixmask
[x
& 3];
730 if (drmode
& DRMODE_INVERSEVID
)
732 dmask
= 0x1ff; /* bit 8 == sentinel */
733 drmode
&= DRMODE_SOLID
; /* mask out inversevid */
738 const unsigned char *src_col
= src
++;
739 unsigned data
= (*src_col
^ dmask
) >> src_y
;
740 fb_data
*dst_col
= dst
;
744 #define UPDATE_SRC do { \
746 if (data == 0x001) { \
748 data = *src_col ^ dmask; \
754 case DRMODE_COMPLEMENT
:
758 *dst_col
^= dst_mask
;
760 dst_col
+= LCD_FBWIDTH
;
763 while (dst_col
< dst_end
);
769 bo
= lcd_backdrop_offset
;
774 unsigned block
= *dst_col
;
776 ^ ((block
^ *(dst_col
+ bo
)) & dst_mask
);
778 dst_col
+= LCD_FBWIDTH
;
781 while (dst_col
< dst_end
);
790 unsigned block
= *dst_col
;
791 *dst_col
= block
^ ((block
^ bg
) & dst_mask
);
793 dst_col
+= LCD_FBWIDTH
;
796 while (dst_col
< dst_end
);
806 unsigned block
= *dst_col
;
807 *dst_col
= block
^ ((block
^ fg
) & dst_mask
);
809 dst_col
+= LCD_FBWIDTH
;
812 while (dst_col
< dst_end
);
819 bo
= lcd_backdrop_offset
;
822 unsigned block
= *dst_col
;
823 *dst_col
= block
^ ((block
^ ((data
& 0x01) ?
824 fg
: *(dst_col
+ bo
))) & dst_mask
);
826 dst_col
+= LCD_FBWIDTH
;
829 while (dst_col
< dst_end
);
836 unsigned block
= *dst_col
;
837 *dst_col
= block
^ ((block
^ ((data
& 0x01) ?
838 fg
: bg
)) & dst_mask
);
840 dst_col
+= LCD_FBWIDTH
;
843 while (dst_col
< dst_end
);
854 while (src
< src_end
);
857 /* Draw a full monochrome bitmap */
858 void lcd_mono_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
860 lcd_mono_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
863 /* About Rockbox' internal native bitmap format:
865 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
866 * 10 = dark grey, 11 = black. Bits within a byte are arranged horizontally,
868 * The bytes are stored in row-major order, with byte 0 being top left,
869 * byte 1 2nd from left etc. Each row of bytes defines one pixel row.
871 * This is the same as the internal lcd hw format. */
873 /* Draw a partial native bitmap */
874 void ICODE_ATTR
lcd_bitmap_part(const unsigned char *src
, int src_x
,
875 int src_y
, int stride
, int x
, int y
,
876 int width
, int height
)
879 unsigned char *dst
, *dst_end
;
880 unsigned mask
, mask_right
;
882 /* nothing to draw? */
883 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) ||
884 (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
900 if (x
+ width
> current_vp
->width
)
901 width
= current_vp
->width
- x
;
902 if (y
+ height
> current_vp
->height
)
903 height
= current_vp
->height
- y
;
905 /* adjust for viewport */
909 stride
= (stride
+ 3) >> 2; /* convert to no. of bytes */
911 src
+= stride
* src_y
+ (src_x
>> 2); /* move starting point */
914 dst
= &lcd_framebuffer
[y
][x
>>2];
916 nx
= width
- 1 + shift
+ src_x
;
918 mask
= 0xFF00u
>> (2 * (shift
+ src_x
));
919 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
922 dst_end
= dst
+ height
* LCD_FBWIDTH
;
925 const unsigned char *src_row
= src
;
926 unsigned char *dst_row
= dst
;
927 unsigned mask_row
= mask
>> 8;
930 for (x
= nx
; x
>= 4; x
-= 4)
932 data
= (data
<< 8) | *src_row
++;
936 setblock(dst_row
, mask_row
, data
>> shift
);
944 data
= (data
<< 8) | *src_row
;
945 setblock(dst_row
, mask_row
& mask_right
, data
>> shift
);
950 while (dst
< dst_end
);
953 /* Draw a full native bitmap */
954 void lcd_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
956 lcd_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
959 #include "lcd-bitmap-common.c"