Add support to buildzip.pl for Lua scripts
[kugel-rb.git] / firmware / drivers / lcd-16bit.c
blob6d6a3b2104ec9c537588cbe2a4f9c9a6f1beac2b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Dave Chapman
12 * Rockbox driver for 16-bit colour LCDs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "config.h"
25 #include "cpu.h"
26 #include "lcd.h"
27 #include "kernel.h"
28 #include "thread.h"
29 #include <string.h>
30 #include <stdlib.h>
31 #include "memory.h"
32 #include "file.h"
33 #include "debug.h"
34 #include "system.h"
35 #include "font.h"
36 #include "rbunicode.h"
37 #include "bidi.h"
38 #include "scroll_engine.h"
40 enum fill_opt {
41 OPT_NONE = 0,
42 OPT_SET,
43 OPT_COPY
46 /*** globals ***/
47 fb_data lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH]
48 IRAM_LCDFRAMEBUFFER CACHEALIGN_AT_LEAST_ATTR(16);
51 static fb_data* lcd_backdrop = NULL;
52 static long lcd_backdrop_offset IDATA_ATTR = 0;
54 static struct viewport default_vp =
56 .x = 0,
57 .y = 0,
58 .width = LCD_WIDTH,
59 .height = LCD_HEIGHT,
60 .font = FONT_SYSFIXED,
61 .drawmode = DRMODE_SOLID,
62 .fg_pattern = LCD_DEFAULT_FG,
63 .bg_pattern = LCD_DEFAULT_BG,
64 .lss_pattern = LCD_DEFAULT_BG,
65 .lse_pattern = LCD_DEFAULT_BG,
66 .lst_pattern = LCD_DEFAULT_BG,
69 static struct viewport* current_vp IDATA_ATTR = &default_vp;
71 /* LCD init */
72 void lcd_init(void)
74 lcd_clear_display();
76 /* Call device specific init */
77 lcd_init_device();
78 scroll_init();
80 /*** Viewports ***/
82 void lcd_set_viewport(struct viewport* vp)
84 if (vp == NULL)
85 current_vp = &default_vp;
86 else
87 current_vp = vp;
90 void lcd_update_viewport(void)
92 lcd_update_rect(current_vp->x, current_vp->y,
93 current_vp->width, current_vp->height);
96 void lcd_update_viewport_rect(int x, int y, int width, int height)
98 lcd_update_rect(current_vp->x + x, current_vp->y + y, width, height);
101 /*** parameter handling ***/
103 void lcd_set_drawmode(int mode)
105 current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
108 int lcd_get_drawmode(void)
110 return current_vp->drawmode;
113 void lcd_set_foreground(unsigned color)
115 current_vp->fg_pattern = color;
118 unsigned lcd_get_foreground(void)
120 return current_vp->fg_pattern;
123 void lcd_set_background(unsigned color)
125 current_vp->bg_pattern = color;
128 unsigned lcd_get_background(void)
130 return current_vp->bg_pattern;
133 void lcd_set_selector_start(unsigned color)
135 current_vp->lss_pattern = color;
138 void lcd_set_selector_end(unsigned color)
140 current_vp->lse_pattern = color;
143 void lcd_set_selector_text(unsigned color)
145 current_vp->lst_pattern = color;
148 void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
150 lcd_set_drawmode(mode);
151 current_vp->fg_pattern = fg_color;
152 current_vp->bg_pattern = bg_color;
155 int lcd_getwidth(void)
157 return current_vp->width;
160 int lcd_getheight(void)
162 return current_vp->height;
165 void lcd_setfont(int newfont)
167 current_vp->font = newfont;
170 int lcd_getfont(void)
172 return current_vp->font;
175 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
177 return font_getstringsize(str, w, h, current_vp->font);
180 /*** low-level drawing functions ***/
182 #define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)])
184 static void ICODE_ATTR setpixel(fb_data *address)
186 *address = current_vp->fg_pattern;
189 static void ICODE_ATTR clearpixel(fb_data *address)
191 *address = current_vp->bg_pattern;
194 static void ICODE_ATTR clearimgpixel(fb_data *address)
196 *address = *(fb_data *)((long)address + lcd_backdrop_offset);
199 static void ICODE_ATTR flippixel(fb_data *address)
201 *address = ~(*address);
204 static void ICODE_ATTR nopixel(fb_data *address)
206 (void)address;
209 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_bgcolor[8] = {
210 flippixel, nopixel, setpixel, setpixel,
211 nopixel, clearpixel, nopixel, clearpixel
214 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_backdrop[8] = {
215 flippixel, nopixel, setpixel, setpixel,
216 nopixel, clearimgpixel, nopixel, clearimgpixel
219 lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
221 void lcd_set_backdrop(fb_data* backdrop)
223 lcd_backdrop = backdrop;
224 if (backdrop)
226 lcd_backdrop_offset = (long)backdrop - (long)&lcd_framebuffer[0][0];
227 lcd_fastpixelfuncs = lcd_fastpixelfuncs_backdrop;
229 else
231 lcd_backdrop_offset = 0;
232 lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
236 fb_data* lcd_get_backdrop(void)
238 return lcd_backdrop;
241 /*** drawing functions ***/
243 /* Clear the current viewport */
244 void lcd_clear_viewport(void)
246 fb_data *dst, *dst_end;
248 dst = LCDADDR(current_vp->x, current_vp->y);
249 dst_end = dst + current_vp->height * LCD_WIDTH;
251 if (current_vp->drawmode & DRMODE_INVERSEVID)
255 memset16(dst, current_vp->fg_pattern, current_vp->width);
256 dst += LCD_WIDTH;
258 while (dst < dst_end);
260 else
262 if (!lcd_backdrop)
266 memset16(dst, current_vp->bg_pattern, current_vp->width);
267 dst += LCD_WIDTH;
269 while (dst < dst_end);
271 else
275 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
276 current_vp->width * sizeof(fb_data));
277 dst += LCD_WIDTH;
279 while (dst < dst_end);
283 if (current_vp == &default_vp)
285 lcd_scroll_info.lines = 0;
287 else
289 lcd_scroll_stop(current_vp);
293 /* Clear the whole display */
294 void lcd_clear_display(void)
296 struct viewport* old_vp = current_vp;
298 current_vp = &default_vp;
300 lcd_clear_viewport();
302 current_vp = old_vp;
305 /* Set a single pixel */
306 void lcd_drawpixel(int x, int y)
308 if (((unsigned)x < (unsigned)current_vp->width) &&
309 ((unsigned)y < (unsigned)current_vp->height))
310 lcd_fastpixelfuncs[current_vp->drawmode](LCDADDR(current_vp->x+x, current_vp->y+y));
313 /* Draw a line */
314 void lcd_drawline(int x1, int y1, int x2, int y2)
316 int numpixels;
317 int i;
318 int deltax, deltay;
319 int d, dinc1, dinc2;
320 int x, xinc1, xinc2;
321 int y, yinc1, yinc2;
322 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[current_vp->drawmode];
324 deltay = abs(y2 - y1);
325 if (deltay == 0)
327 DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n");
328 lcd_hline(x1, x2, y1);
329 return;
331 deltax = abs(x2 - x1);
332 if (deltax == 0)
334 DEBUGF("lcd_drawline() called for vertical line - optimisation.\n");
335 lcd_vline(x1, y1, y2);
336 return;
338 xinc2 = 1;
339 yinc2 = 1;
341 if (deltax >= deltay)
343 numpixels = deltax;
344 d = 2 * deltay - deltax;
345 dinc1 = deltay * 2;
346 dinc2 = (deltay - deltax) * 2;
347 xinc1 = 1;
348 yinc1 = 0;
350 else
352 numpixels = deltay;
353 d = 2 * deltax - deltay;
354 dinc1 = deltax * 2;
355 dinc2 = (deltax - deltay) * 2;
356 xinc1 = 0;
357 yinc1 = 1;
359 numpixels++; /* include endpoints */
361 if (x1 > x2)
363 xinc1 = -xinc1;
364 xinc2 = -xinc2;
367 if (y1 > y2)
369 yinc1 = -yinc1;
370 yinc2 = -yinc2;
373 x = x1;
374 y = y1;
376 for (i = 0; i < numpixels; i++)
378 if (((unsigned)x < (unsigned)current_vp->width) && ((unsigned)y < (unsigned)current_vp->height))
379 pfunc(LCDADDR(x + current_vp->x, y + current_vp->y));
381 if (d < 0)
383 d += dinc1;
384 x += xinc1;
385 y += yinc1;
387 else
389 d += dinc2;
390 x += xinc2;
391 y += yinc2;
396 /* Draw a horizontal line (optimised) */
397 void lcd_hline(int x1, int x2, int y)
399 int x, width;
400 unsigned bits = 0;
401 enum fill_opt fillopt = OPT_NONE;
402 fb_data *dst, *dst_end;
404 /* direction flip */
405 if (x2 < x1)
407 x = x1;
408 x1 = x2;
409 x2 = x;
412 /* nothing to draw? */
413 if (((unsigned)y >= (unsigned)current_vp->height) ||
414 (x1 >= current_vp->width) ||
415 (x2 < 0))
416 return;
418 /* drawmode and optimisation */
419 if (current_vp->drawmode & DRMODE_INVERSEVID)
421 if (current_vp->drawmode & DRMODE_BG)
423 if (!lcd_backdrop)
425 fillopt = OPT_SET;
426 bits = current_vp->bg_pattern;
428 else
429 fillopt = OPT_COPY;
432 else
434 if (current_vp->drawmode & DRMODE_FG)
436 fillopt = OPT_SET;
437 bits = current_vp->fg_pattern;
440 if (fillopt == OPT_NONE && current_vp->drawmode != DRMODE_COMPLEMENT)
441 return;
443 /* clipping */
444 if (x1 < 0)
445 x1 = 0;
446 if (x2 >= current_vp->width)
447 x2 = current_vp->width-1;
449 width = x2 - x1 + 1;
451 /* Adjust x1 and y to viewport */
452 x1 += current_vp->x;
453 y += current_vp->y;
455 dst = LCDADDR(x1, y);
457 switch (fillopt)
459 case OPT_SET:
460 memset16(dst, bits, width);
461 break;
463 case OPT_COPY:
464 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
465 width * sizeof(fb_data));
466 break;
468 case OPT_NONE: /* DRMODE_COMPLEMENT */
469 dst_end = dst + width;
471 *dst = ~(*dst);
472 while (++dst < dst_end);
473 break;
477 /* Draw a vertical line (optimised) */
478 void lcd_vline(int x, int y1, int y2)
480 int y;
481 fb_data *dst, *dst_end;
482 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[current_vp->drawmode];
484 /* direction flip */
485 if (y2 < y1)
487 y = y1;
488 y1 = y2;
489 y2 = y;
492 /* nothing to draw? */
493 if (((unsigned)x >= (unsigned)current_vp->width) ||
494 (y1 >= current_vp->height) ||
495 (y2 < 0))
496 return;
498 /* clipping */
499 if (y1 < 0)
500 y1 = 0;
501 if (y2 >= current_vp->height)
502 y2 = current_vp->height-1;
504 dst = LCDADDR(x + current_vp->x, y1 + current_vp->y);
505 dst_end = dst + (y2 - y1) * LCD_WIDTH;
509 pfunc(dst);
510 dst += LCD_WIDTH;
512 while (dst <= dst_end);
515 /* Draw a rectangular box */
516 void lcd_drawrect(int x, int y, int width, int height)
518 if ((width <= 0) || (height <= 0))
519 return;
521 int x2 = x + width - 1;
522 int y2 = y + height - 1;
524 lcd_vline(x, y, y2);
525 lcd_vline(x2, y, y2);
526 lcd_hline(x, x2, y);
527 lcd_hline(x, x2, y2);
530 /* Fill a rectangular area */
531 void lcd_fillrect(int x, int y, int width, int height)
533 unsigned bits = 0;
534 enum fill_opt fillopt = OPT_NONE;
535 fb_data *dst, *dst_end;
537 /* nothing to draw? */
538 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
539 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
540 return;
542 /* drawmode and optimisation */
543 if (current_vp->drawmode & DRMODE_INVERSEVID)
545 if (current_vp->drawmode & DRMODE_BG)
547 if (!lcd_backdrop)
549 fillopt = OPT_SET;
550 bits = current_vp->bg_pattern;
552 else
553 fillopt = OPT_COPY;
556 else
558 if (current_vp->drawmode & DRMODE_FG)
560 fillopt = OPT_SET;
561 bits = current_vp->fg_pattern;
564 if (fillopt == OPT_NONE && current_vp->drawmode != DRMODE_COMPLEMENT)
565 return;
567 /* clipping */
568 if (x < 0)
570 width += x;
571 x = 0;
573 if (y < 0)
575 height += y;
576 y = 0;
578 if (x + width > current_vp->width)
579 width = current_vp->width - x;
580 if (y + height > current_vp->height)
581 height = current_vp->height - y;
583 dst = LCDADDR(current_vp->x + x, current_vp->y + y);
584 dst_end = dst + height * LCD_WIDTH;
588 fb_data *dst_row, *row_end;
590 switch (fillopt)
592 case OPT_SET:
593 memset16(dst, bits, width);
594 break;
596 case OPT_COPY:
597 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
598 width * sizeof(fb_data));
599 break;
601 case OPT_NONE: /* DRMODE_COMPLEMENT */
602 dst_row = dst;
603 row_end = dst_row + width;
605 *dst_row = ~(*dst_row);
606 while (++dst_row < row_end);
607 break;
609 dst += LCD_WIDTH;
611 while (dst < dst_end);
614 /* About Rockbox' internal monochrome bitmap format:
616 * A bitmap contains one bit for every pixel that defines if that pixel is
617 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
618 * at top.
619 * The bytes are stored in row-major order, with byte 0 being top left,
620 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
621 * 0..7, the second row defines pixel row 8..15 etc.
623 * This is the mono bitmap format used on all other targets so far; the
624 * pixel packing doesn't really matter on a 8bit+ target. */
626 /* Draw a partial monochrome bitmap */
628 void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
629 int src_y, int stride, int x, int y,
630 int width, int height)
632 const unsigned char *src_end;
633 fb_data *dst, *dst_end;
634 unsigned dmask = 0x100; /* bit 8 == sentinel */
635 int drmode = current_vp->drawmode;
637 /* nothing to draw? */
638 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
639 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
640 return;
642 /* clipping */
643 if (x < 0)
645 width += x;
646 src_x -= x;
647 x = 0;
649 if (y < 0)
651 height += y;
652 src_y -= y;
653 y = 0;
655 if (x + width > current_vp->width)
656 width = current_vp->width - x;
657 if (y + height > current_vp->height)
658 height = current_vp->height - y;
660 src += stride * (src_y >> 3) + src_x; /* move starting point */
661 src_y &= 7;
662 src_end = src + width;
663 dst = LCDADDR(current_vp->x + x, current_vp->y + y);
664 dst_end = dst + height * LCD_WIDTH;
666 if (drmode & DRMODE_INVERSEVID)
668 dmask = 0x1ff; /* bit 8 == sentinel */
669 drmode &= DRMODE_SOLID; /* mask out inversevid */
674 const unsigned char *src_col = src++;
675 unsigned data = (*src_col ^ dmask) >> src_y;
676 fb_data *dst_col = dst++;
677 int fg, bg;
678 long bo;
680 #define UPDATE_SRC do { \
681 data >>= 1; \
682 if (data == 0x001) { \
683 src_col += stride; \
684 data = *src_col ^ dmask; \
686 } while (0)
688 switch (drmode)
690 case DRMODE_COMPLEMENT:
693 if (data & 0x01)
694 *dst_col = ~(*dst_col);
696 dst_col += LCD_WIDTH;
697 UPDATE_SRC;
699 while (dst_col < dst_end);
700 break;
702 case DRMODE_BG:
703 if (lcd_backdrop)
705 bo = lcd_backdrop_offset;
708 if (!(data & 0x01))
709 *dst_col = *(fb_data *)((long)dst_col + bo);
711 dst_col += LCD_WIDTH;
712 UPDATE_SRC;
714 while (dst_col < dst_end);
716 else
718 bg = current_vp->bg_pattern;
721 if (!(data & 0x01))
722 *dst_col = bg;
724 dst_col += LCD_WIDTH;
725 UPDATE_SRC;
727 while (dst_col < dst_end);
729 break;
731 case DRMODE_FG:
732 fg = current_vp->fg_pattern;
735 if (data & 0x01)
736 *dst_col = fg;
738 dst_col += LCD_WIDTH;
739 UPDATE_SRC;
741 while (dst_col < dst_end);
742 break;
744 case DRMODE_SOLID:
745 fg = current_vp->fg_pattern;
746 if (lcd_backdrop)
748 bo = lcd_backdrop_offset;
751 *dst_col = (data & 0x01) ? fg
752 : *(fb_data *)((long)dst_col + bo);
753 dst_col += LCD_WIDTH;
754 UPDATE_SRC;
756 while (dst_col < dst_end);
758 else
760 bg = current_vp->bg_pattern;
763 *dst_col = (data & 0x01) ? fg : bg;
764 dst_col += LCD_WIDTH;
765 UPDATE_SRC;
767 while (dst_col < dst_end);
769 break;
772 while (src < src_end);
774 /* Draw a full monochrome bitmap */
775 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
777 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
780 /* Draw a partial native bitmap */
781 void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
782 int stride, int x, int y, int width,
783 int height)
785 fb_data *dst;
787 if (x + width > current_vp->width)
788 width = current_vp->width - x; /* Clip right */
790 if (x < 0) /* Clip left */
792 width += x;
793 src_x -= x;
794 x = 0;
797 if (width <= 0)
798 return; /* nothing left to do */
800 if (y + height > current_vp->height)
801 height = current_vp->height - y; /* Clip bottom */
803 if (y < 0) /* Clip top */
805 height += y;
806 src_y -= y;
807 y = 0;
810 if (height <= 0)
811 return; /* nothing left to do */
813 src += stride * src_y + src_x; /* move starting point */
814 dst = LCDADDR(current_vp->x + x, current_vp->y + y);
818 memcpy(dst, src, width * sizeof(fb_data));
819 src += stride;
820 dst += LCD_WIDTH;
822 while (--height > 0);
825 /* Draw a full native bitmap */
826 void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
828 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
831 /* Draw a partial native bitmap with transparency and foreground colors */
832 void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
833 int src_y, int stride, int x,
834 int y, int width, int height)
836 fb_data *dst;
837 unsigned fg = current_vp->fg_pattern;
839 if (x + width > current_vp->width)
840 width = current_vp->width - x; /* Clip right */
842 if (x < 0) /* Clip left */
844 width += x;
845 src_x -= x;
846 x = 0;
849 if (width <= 0)
850 return; /* nothing left to do */
852 if (y + height > current_vp->height)
853 height = current_vp->height - y; /* Clip bottom */
855 if (y < 0) /* Clip top */
857 height += y;
858 src_y -= y;
859 y = 0;
862 if (height <= 0)
863 return; /* nothing left to do */
865 src += stride * src_y + src_x; /* move starting point */
866 dst = LCDADDR(current_vp->x + x, current_vp->y + y);
868 #ifdef CPU_ARM
870 int w, px;
871 asm volatile (
872 ".rowstart: \n"
873 "mov %[w], %[width] \n" /* Load width for inner loop */
874 ".nextpixel: \n"
875 "ldrh %[px], [%[s]], #2 \n" /* Load src pixel */
876 "add %[d], %[d], #2 \n" /* Uncoditionally increment dst */
877 /* done here for better pipelining */
878 "cmp %[px], %[fgcolor] \n" /* Compare to foreground color */
879 "streqh %[fgpat], [%[d], #-2] \n" /* Store foregroud if match */
880 "cmpne %[px], %[transcolor] \n" /* Compare to transparent color */
881 "strneh %[px], [%[d], #-2] \n" /* Store dst if not transparent */
882 "subs %[w], %[w], #1 \n" /* Width counter has run down? */
883 "bgt .nextpixel \n" /* More in this row? */
884 "add %[s], %[s], %[sstp], lsl #1 \n" /* Skip over to start of next line */
885 "add %[d], %[d], %[dstp], lsl #1 \n"
886 "subs %[h], %[h], #1 \n" /* Height counter has run down? */
887 "bgt .rowstart \n" /* More rows? */
888 : [w]"=&r"(w), [h]"+&r"(height), [px]"=&r"(px),
889 [s]"+&r"(src), [d]"+&r"(dst)
890 : [width]"r"(width),
891 [sstp]"r"(stride - width),
892 [dstp]"r"(LCD_WIDTH - width),
893 [transcolor]"r"(TRANSPARENT_COLOR),
894 [fgcolor]"r"(REPLACEWITHFG_COLOR),
895 [fgpat]"r"(fg)
898 #else /* optimized C version */
901 const fb_data *src_row = src;
902 fb_data *dst_row = dst;
903 fb_data *row_end = dst_row + width;
906 unsigned data = *src_row++;
907 if (data != TRANSPARENT_COLOR)
909 if (data == REPLACEWITHFG_COLOR)
910 data = fg;
911 *dst_row = data;
914 while (++dst_row < row_end);
915 src += stride;
916 dst += LCD_WIDTH;
918 while (--height > 0);
919 #endif
922 /* Draw a full native bitmap with transparent and foreground colors */
923 void lcd_bitmap_transparent(const fb_data *src, int x, int y,
924 int width, int height)
926 lcd_bitmap_transparent_part(src, 0, 0, width, x, y, width, height);
929 #include "lcd-bitmap-common.c"