Prevent file descriptor leakage or overuse
[Rockbox.git] / firmware / drivers / lcd-16bit.c
blob2c36ab993a7360e28426afd30a360de33cbf9be8
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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
23 #include "cpu.h"
24 #include "lcd.h"
25 #include "kernel.h"
26 #include "thread.h"
27 #include <string.h>
28 #include <stdlib.h>
29 #include "memory.h"
30 #include "file.h"
31 #include "debug.h"
32 #include "system.h"
33 #include "font.h"
34 #include "rbunicode.h"
35 #include "bidi.h"
36 #include "scroll_engine.h"
38 enum fill_opt {
39 OPT_NONE = 0,
40 OPT_SET,
41 OPT_COPY
44 /*** globals ***/
45 fb_data lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH] IRAM_LCDFRAMEBUFFER __attribute__ ((aligned (16)));
48 static fb_data* lcd_backdrop = NULL;
49 static long lcd_backdrop_offset IDATA_ATTR = 0;
51 #if !defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR)
52 static unsigned fg_pattern IDATA_ATTR = LCD_DEFAULT_FG;
53 static unsigned bg_pattern IDATA_ATTR = LCD_DEFAULT_BG;
54 static unsigned lss_pattern IDATA_ATTR = LCD_DEFAULT_LS;
55 static unsigned lse_pattern IDATA_ATTR = LCD_DEFAULT_BG;
56 static unsigned lst_pattern IDATA_ATTR = LCD_DEFAULT_FG;
57 #else
58 unsigned fg_pattern IDATA_ATTR = LCD_DEFAULT_FG;
59 unsigned bg_pattern IDATA_ATTR = LCD_DEFAULT_BG;
60 unsigned lss_pattern IDATA_ATTR = LCD_DEFAULT_LS;
61 unsigned lse_pattern IDATA_ATTR = LCD_DEFAULT_BG;
62 unsigned lst_pattern IDATA_ATTR = LCD_DEFAULT_FG;
63 #endif
65 static int drawmode = DRMODE_SOLID;
66 static int xmargin = 0;
67 static int ymargin = 0;
68 static int curfont = FONT_SYSFIXED;
70 /* LCD init */
71 void lcd_init(void)
73 lcd_clear_display();
75 /* Call device specific init */
76 lcd_init_device();
77 scroll_init();
80 /*** parameter handling ***/
82 void lcd_set_drawmode(int mode)
84 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
87 int lcd_get_drawmode(void)
89 return drawmode;
92 void lcd_set_foreground(unsigned color)
94 fg_pattern = color;
97 unsigned lcd_get_foreground(void)
99 return fg_pattern;
102 void lcd_set_background(unsigned color)
104 bg_pattern = color;
107 unsigned lcd_get_background(void)
109 return bg_pattern;
112 void lcd_set_selector_start(unsigned color)
114 lss_pattern = color;
117 void lcd_set_selector_end(unsigned color)
119 lse_pattern = color;
122 void lcd_set_selector_text(unsigned color)
124 lst_pattern = color;
127 void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
129 lcd_set_drawmode(mode);
130 fg_pattern = fg_color;
131 bg_pattern = bg_color;
134 void lcd_setmargins(int x, int y)
136 xmargin = x;
137 ymargin = y;
140 int lcd_getxmargin(void)
142 return xmargin;
145 int lcd_getymargin(void)
147 return ymargin;
150 void lcd_setfont(int newfont)
152 curfont = newfont;
155 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
157 return font_getstringsize(str, w, h, curfont);
160 /*** low-level drawing functions ***/
162 #define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)])
164 static void setpixel(fb_data *address) ICODE_ATTR;
165 static void setpixel(fb_data *address)
167 *address = fg_pattern;
170 static void clearpixel(fb_data *address) ICODE_ATTR;
171 static void clearpixel(fb_data *address)
173 *address = bg_pattern;
176 static void clearimgpixel(fb_data *address) ICODE_ATTR;
177 static void clearimgpixel(fb_data *address)
179 *address = *(fb_data *)((long)address + lcd_backdrop_offset);
182 static void flippixel(fb_data *address) ICODE_ATTR;
183 static void flippixel(fb_data *address)
185 *address = ~(*address);
188 static void nopixel(fb_data *address) ICODE_ATTR;
189 static void nopixel(fb_data *address)
191 (void)address;
194 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_bgcolor[8] = {
195 flippixel, nopixel, setpixel, setpixel,
196 nopixel, clearpixel, nopixel, clearpixel
199 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_backdrop[8] = {
200 flippixel, nopixel, setpixel, setpixel,
201 nopixel, clearimgpixel, nopixel, clearimgpixel
204 lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
206 void lcd_set_backdrop(fb_data* backdrop)
208 lcd_backdrop = backdrop;
209 if (backdrop)
211 lcd_backdrop_offset = (long)backdrop - (long)&lcd_framebuffer[0][0];
212 lcd_fastpixelfuncs = lcd_fastpixelfuncs_backdrop;
214 else
216 lcd_backdrop_offset = 0;
217 lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
221 fb_data* lcd_get_backdrop(void)
223 return lcd_backdrop;
226 /*** drawing functions ***/
228 /* Clear the whole display */
229 void lcd_clear_display(void)
231 fb_data *dst = LCDADDR(0, 0);
233 if (drawmode & DRMODE_INVERSEVID)
235 memset16(dst, fg_pattern, LCD_WIDTH*LCD_HEIGHT);
237 else
239 if (!lcd_backdrop)
240 memset16(dst, bg_pattern, LCD_WIDTH*LCD_HEIGHT);
241 else
242 memcpy(dst, lcd_backdrop, sizeof(lcd_framebuffer));
245 lcd_scroll_info.lines = 0;
248 /* Set a single pixel */
249 void lcd_drawpixel(int x, int y)
251 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
252 lcd_fastpixelfuncs[drawmode](LCDADDR(x, y));
255 /* Draw a line */
256 void lcd_drawline(int x1, int y1, int x2, int y2)
258 int numpixels;
259 int i;
260 int deltax, deltay;
261 int d, dinc1, dinc2;
262 int x, xinc1, xinc2;
263 int y, yinc1, yinc2;
264 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
266 deltax = abs(x2 - x1);
267 deltay = abs(y2 - y1);
268 xinc2 = 1;
269 yinc2 = 1;
271 if (deltax >= deltay)
273 numpixels = deltax;
274 d = 2 * deltay - deltax;
275 dinc1 = deltay * 2;
276 dinc2 = (deltay - deltax) * 2;
277 xinc1 = 1;
278 yinc1 = 0;
280 else
282 numpixels = deltay;
283 d = 2 * deltax - deltay;
284 dinc1 = deltax * 2;
285 dinc2 = (deltax - deltay) * 2;
286 xinc1 = 0;
287 yinc1 = 1;
289 numpixels++; /* include endpoints */
291 if (x1 > x2)
293 xinc1 = -xinc1;
294 xinc2 = -xinc2;
297 if (y1 > y2)
299 yinc1 = -yinc1;
300 yinc2 = -yinc2;
303 x = x1;
304 y = y1;
306 for (i = 0; i < numpixels; i++)
308 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
309 pfunc(LCDADDR(x, y));
311 if (d < 0)
313 d += dinc1;
314 x += xinc1;
315 y += yinc1;
317 else
319 d += dinc2;
320 x += xinc2;
321 y += yinc2;
326 /* Draw a horizontal line (optimised) */
327 void lcd_hline(int x1, int x2, int y)
329 int x, width;
330 unsigned bits = 0;
331 enum fill_opt fillopt = OPT_NONE;
332 fb_data *dst, *dst_end;
333 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
335 /* direction flip */
336 if (x2 < x1)
338 x = x1;
339 x1 = x2;
340 x2 = x;
343 /* nothing to draw? */
344 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
345 return;
347 /* clipping */
348 if (x1 < 0)
349 x1 = 0;
350 if (x2 >= LCD_WIDTH)
351 x2 = LCD_WIDTH-1;
353 if (drawmode & DRMODE_INVERSEVID)
355 if (drawmode & DRMODE_BG)
357 if (!lcd_backdrop)
359 fillopt = OPT_SET;
360 bits = bg_pattern;
362 else
363 fillopt = OPT_COPY;
366 else
368 if (drawmode & DRMODE_FG)
370 fillopt = OPT_SET;
371 bits = fg_pattern;
374 dst = LCDADDR(x1, y);
375 width = x2 - x1 + 1;
377 switch (fillopt)
379 case OPT_SET:
380 memset16(dst, bits, width);
381 break;
383 case OPT_COPY:
384 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
385 width * sizeof(fb_data));
386 break;
388 case OPT_NONE:
389 dst_end = dst + width;
391 pfunc(dst++);
392 while (dst < dst_end);
393 break;
397 /* Draw a vertical line (optimised) */
398 void lcd_vline(int x, int y1, int y2)
400 int y;
401 fb_data *dst, *dst_end;
402 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
404 /* direction flip */
405 if (y2 < y1)
407 y = y1;
408 y1 = y2;
409 y2 = y;
412 /* nothing to draw? */
413 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
414 return;
416 /* clipping */
417 if (y1 < 0)
418 y1 = 0;
419 if (y2 >= LCD_HEIGHT)
420 y2 = LCD_HEIGHT-1;
422 dst = LCDADDR(x, y1);
423 dst_end = dst + (y2 - y1) * LCD_WIDTH;
427 pfunc(dst);
428 dst += LCD_WIDTH;
430 while (dst <= dst_end);
433 /* Draw a rectangular box */
434 void lcd_drawrect(int x, int y, int width, int height)
436 if ((width <= 0) || (height <= 0))
437 return;
439 int x2 = x + width - 1;
440 int y2 = y + height - 1;
442 lcd_vline(x, y, y2);
443 lcd_vline(x2, y, y2);
444 lcd_hline(x, x2, y);
445 lcd_hline(x, x2, y2);
448 /* Fill a rectangular area */
449 void lcd_fillrect(int x, int y, int width, int height)
451 unsigned bits = 0;
452 enum fill_opt fillopt = OPT_NONE;
453 fb_data *dst, *dst_end;
454 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[drawmode];
456 /* nothing to draw? */
457 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
458 || (x + width <= 0) || (y + height <= 0))
459 return;
461 /* clipping */
462 if (x < 0)
464 width += x;
465 x = 0;
467 if (y < 0)
469 height += y;
470 y = 0;
472 if (x + width > LCD_WIDTH)
473 width = LCD_WIDTH - x;
474 if (y + height > LCD_HEIGHT)
475 height = LCD_HEIGHT - y;
477 if (drawmode & DRMODE_INVERSEVID)
479 if (drawmode & DRMODE_BG)
481 if (!lcd_backdrop)
483 fillopt = OPT_SET;
484 bits = bg_pattern;
486 else
487 fillopt = OPT_COPY;
490 else
492 if (drawmode & DRMODE_FG)
494 fillopt = OPT_SET;
495 bits = fg_pattern;
498 dst = LCDADDR(x, y);
499 dst_end = dst + height * LCD_WIDTH;
503 fb_data *dst_row, *row_end;
505 switch (fillopt)
507 case OPT_SET:
508 memset16(dst, bits, width);
509 break;
511 case OPT_COPY:
512 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
513 width * sizeof(fb_data));
514 break;
516 case OPT_NONE:
517 dst_row = dst;
518 row_end = dst_row + width;
520 pfunc(dst_row++);
521 while (dst_row < row_end);
522 break;
524 dst += LCD_WIDTH;
526 while (dst < dst_end);
529 /* Fill a rectangle with a gradient */
530 void lcd_gradient_rect(int x1, int x2, int y, int h)
532 if (h == 0) return;
534 int h_r = RGB_UNPACK_RED(lss_pattern) << 16;
535 int h_b = RGB_UNPACK_BLUE(lss_pattern) << 16;
536 int h_g = RGB_UNPACK_GREEN(lss_pattern) << 16;
537 int rstep = (h_r - ((signed)RGB_UNPACK_RED(lse_pattern) << 16)) / h;
538 int gstep = (h_g - ((signed)RGB_UNPACK_GREEN(lse_pattern) << 16)) / h;
539 int bstep = (h_b - ((signed)RGB_UNPACK_BLUE(lse_pattern) << 16)) / h;
540 int count;
542 fg_pattern = lss_pattern;
543 for(count = 0; count < h; count++) {
544 lcd_hline(x1, x2, y + count);
545 h_r -= rstep;
546 h_g -= gstep;
547 h_b -= bstep;
548 fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
552 /* About Rockbox' internal monochrome bitmap format:
554 * A bitmap contains one bit for every pixel that defines if that pixel is
555 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
556 * at top.
557 * The bytes are stored in row-major order, with byte 0 being top left,
558 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
559 * 0..7, the second row defines pixel row 8..15 etc.
561 * This is the mono bitmap format used on all other targets so far; the
562 * pixel packing doesn't really matter on a 8bit+ target. */
564 /* Draw a partial monochrome bitmap */
566 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
567 int stride, int x, int y, int width, int height)
568 ICODE_ATTR;
569 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
570 int stride, int x, int y, int width, int height)
572 const unsigned char *src_end;
573 bool has_backdrop;
574 fb_data *dst, *dst_end, *backdrop;
575 lcd_fastpixelfunc_type *fgfunc, *bgfunc;
577 /* nothing to draw? */
578 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
579 || (x + width <= 0) || (y + height <= 0))
580 return;
582 /* clipping */
583 if (x < 0)
585 width += x;
586 src_x -= x;
587 x = 0;
589 if (y < 0)
591 height += y;
592 src_y -= y;
593 y = 0;
595 if (x + width > LCD_WIDTH)
596 width = LCD_WIDTH - x;
597 if (y + height > LCD_HEIGHT)
598 height = LCD_HEIGHT - y;
600 src += stride * (src_y >> 3) + src_x; /* move starting point */
601 src_y &= 7;
602 src_end = src + width;
604 dst = LCDADDR(x, y);
605 has_backdrop = (lcd_backdrop != NULL);
606 backdrop = lcd_backdrop + y * LCD_WIDTH + x;
607 fgfunc = lcd_fastpixelfuncs[drawmode];
608 bgfunc = lcd_fastpixelfuncs[drawmode ^ DRMODE_INVERSEVID];
611 const unsigned char *src_col = src++;
612 unsigned data = *src_col >> src_y;
613 fb_data *dst_col = dst++;
614 int numbits = 8 - src_y;
615 fb_data *backdrop_col = backdrop++;
616 dst_end = dst_col + height * LCD_WIDTH;
619 switch (drawmode)
621 case DRMODE_SOLID:
622 if (data & 0x01)
623 *dst_col = fg_pattern;
624 else
625 *dst_col = has_backdrop ? *backdrop_col : bg_pattern;
626 break;
627 case DRMODE_FG:
628 if (data & 0x01)
629 *dst_col = fg_pattern;
630 break;
631 case (DRMODE_SOLID|DRMODE_INVERSEVID):
632 if (data & 0x01)
633 *dst_col = has_backdrop ? *backdrop_col : bg_pattern;
634 else
635 *dst_col = fg_pattern;
636 break;
637 default:
638 if (data & 0x01)
639 fgfunc(dst_col);
640 else
641 bgfunc(dst_col);
642 break;
644 dst_col += LCD_WIDTH;
645 backdrop_col += LCD_WIDTH;
646 data >>= 1;
647 if (--numbits == 0)
649 src_col += stride;
650 data = *src_col;
651 numbits = 8;
654 while (dst_col < dst_end);
656 while (src < src_end);
658 /* Draw a full monochrome bitmap */
659 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
661 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
664 /* Draw a partial native bitmap */
665 void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
666 int stride, int x, int y, int width, int height)
667 ICODE_ATTR;
668 void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
669 int stride, int x, int y, int width, int height)
671 fb_data *dst, *dst_end;
673 /* nothing to draw? */
674 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
675 || (x + width <= 0) || (y + height <= 0))
676 return;
678 /* clipping */
679 if (x < 0)
681 width += x;
682 src_x -= x;
683 x = 0;
685 if (y < 0)
687 height += y;
688 src_y -= y;
689 y = 0;
691 if (x + width > LCD_WIDTH)
692 width = LCD_WIDTH - x;
693 if (y + height > LCD_HEIGHT)
694 height = LCD_HEIGHT - y;
696 src += stride * src_y + src_x; /* move starting point */
697 dst = LCDADDR(x, y);
698 dst_end = dst + height * LCD_WIDTH;
702 memcpy(dst, src, width * sizeof(fb_data));
703 src += stride;
704 dst += LCD_WIDTH;
706 while (dst < dst_end);
709 /* Draw a full native bitmap */
710 void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
712 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
715 #if !defined(TOSHIBA_GIGABEAT_F) && !defined(TOSHIBA_GIGABEAT_S) \
716 || defined(SIMULATOR)
717 /* Draw a partial native bitmap */
718 void lcd_bitmap_transparent_part(const fb_data *src, int src_x, int src_y,
719 int stride, int x, int y, int width,
720 int height) ICODE_ATTR;
721 void lcd_bitmap_transparent_part(const fb_data *src, int src_x, int src_y,
722 int stride, int x, int y, int width,
723 int height)
725 fb_data *dst, *dst_end;
727 /* nothing to draw? */
728 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
729 || (x + width <= 0) || (y + height <= 0))
730 return;
732 /* clipping */
733 if (x < 0)
735 width += x;
736 src_x -= x;
737 x = 0;
739 if (y < 0)
741 height += y;
742 src_y -= y;
743 y = 0;
745 if (x + width > LCD_WIDTH)
746 width = LCD_WIDTH - x;
747 if (y + height > LCD_HEIGHT)
748 height = LCD_HEIGHT - y;
750 src += stride * src_y + src_x; /* move starting point */
751 dst = LCDADDR(x, y);
752 dst_end = dst + height * LCD_WIDTH;
756 int i;
757 for(i = 0;i < width;i++)
759 if (src[i] == REPLACEWITHFG_COLOR)
760 dst[i] = fg_pattern;
761 else if(src[i] != TRANSPARENT_COLOR)
762 dst[i] = src[i];
764 src += stride;
765 dst += LCD_WIDTH;
767 while (dst < dst_end);
769 #endif /* !defined(TOSHIBA_GIGABEAT_F) || defined(SIMULATOR) */
771 /* Draw a full native bitmap with a transparent color */
772 void lcd_bitmap_transparent(const fb_data *src, int x, int y,
773 int width, int height)
775 lcd_bitmap_transparent_part(src, 0, 0, width, x, y, width, height);
778 /* put a string at a given pixel position, skipping first ofs pixel columns */
779 static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
781 unsigned short ch;
782 unsigned short *ucs;
783 struct font* pf = font_get(curfont);
785 ucs = bidi_l2v(str, 1);
787 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
789 int width;
790 const unsigned char *bits;
792 /* get proportional width and glyph bits */
793 width = font_get_width(pf,ch);
795 if (ofs > width)
797 ofs -= width;
798 continue;
801 bits = font_get_bits(pf, ch);
803 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
805 x += width - ofs;
806 ofs = 0;
810 /* put a string at a given pixel position */
811 void lcd_putsxy(int x, int y, const unsigned char *str)
813 lcd_putsxyofs(x, y, 0, str);
816 /*** line oriented text output ***/
818 /* put a string at a given char position */
819 void lcd_puts(int x, int y, const unsigned char *str)
821 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
824 void lcd_puts_style(int x, int y, const unsigned char *str, int style)
826 lcd_puts_style_offset(x, y, str, style, 0);
829 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
831 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
834 /* put a string at a given char position, style, and pixel position,
835 * skipping first offset pixel columns */
836 void lcd_puts_style_offset(int x, int y, const unsigned char *str, int style,
837 int offset)
839 int xpos,ypos,w,h,xrect;
840 int lastmode = drawmode;
841 int oldfgcolor = fg_pattern;
842 int oldbgcolor = bg_pattern;
844 /* make sure scrolling is turned off on the line we are updating */
845 lcd_scroll_info.lines &= ~(1 << y);
847 if(!str || !str[0])
848 return;
850 lcd_getstringsize(str, &w, &h);
851 xpos = xmargin + x*w / utf8length(str);
852 ypos = ymargin + y*h;
853 drawmode = (style & STYLE_INVERT) ?
854 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
855 if (style & STYLE_COLORED) {
856 if (drawmode == DRMODE_SOLID)
857 fg_pattern = style & STYLE_COLOR_MASK;
858 else
859 bg_pattern = style & STYLE_COLOR_MASK;
861 drawmode ^= DRMODE_INVERSEVID;
862 xrect = xpos + MAX(w - offset, 0);
864 if (style & STYLE_GRADIENT) {
865 drawmode = DRMODE_FG;
866 lcd_gradient_rect(xpos, LCD_WIDTH, ypos, h*(style & STYLE_COLOR_MASK));
867 fg_pattern = lst_pattern;
869 else if (style & STYLE_COLORBAR) {
870 drawmode = DRMODE_FG;
871 fg_pattern = lss_pattern;
872 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, h);
873 fg_pattern = lst_pattern;
875 else {
876 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
877 drawmode = (style & STYLE_INVERT) ?
878 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
880 lcd_putsxyofs(xpos, ypos, offset, str);
881 drawmode = lastmode;
882 fg_pattern = oldfgcolor;
883 bg_pattern = oldbgcolor;
886 /*** scrolling ***/
887 void lcd_puts_scroll(int x, int y, const unsigned char *string)
889 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
892 void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
894 lcd_puts_scroll_style_offset(x, y, string, style, 0);
897 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
899 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
902 void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
903 int style, int offset)
905 struct scrollinfo* s;
906 int w, h;
908 if(y>=LCD_SCROLLABLE_LINES) return;
910 s = &lcd_scroll_info.scroll[y];
912 s->start_tick = current_tick + lcd_scroll_info.delay;
913 s->style = style;
914 lcd_puts_style_offset(x,y,string,style,offset);
916 lcd_getstringsize(string, &w, &h);
918 if (LCD_WIDTH - x * 8 - xmargin < w) {
919 /* prepare scroll line */
920 char *end;
922 memset(s->line, 0, sizeof s->line);
923 strcpy(s->line, string);
925 /* get width */
926 s->width = lcd_getstringsize(s->line, &w, &h);
928 /* scroll bidirectional or forward only depending on the string
929 width */
930 if ( lcd_scroll_info.bidir_limit ) {
931 s->bidir = s->width < (LCD_WIDTH - xmargin) *
932 (100 + lcd_scroll_info.bidir_limit) / 100;
934 else
935 s->bidir = false;
937 if (!s->bidir) { /* add spaces if scrolling in the round */
938 strcat(s->line, " ");
939 /* get new width incl. spaces */
940 s->width = lcd_getstringsize(s->line, &w, &h);
943 end = strchr(s->line, '\0');
944 strncpy(end, string, LCD_WIDTH/2);
946 s->len = utf8length(string);
947 s->offset = offset;
948 s->startx = xmargin + x * s->width / s->len;
949 s->backward = false;
950 lcd_scroll_info.lines |= (1<<y);
952 else
953 /* force a bit switch-off since it doesn't scroll */
954 lcd_scroll_info.lines &= ~(1<<y);
957 void lcd_scroll_fn(void)
959 struct font* pf;
960 struct scrollinfo* s;
961 int index;
962 int xpos, ypos;
963 int lastmode;
964 unsigned old_fgcolor = fg_pattern;
965 unsigned old_bgcolor = bg_pattern;
967 for ( index = 0; index < LCD_SCROLLABLE_LINES; index++ ) {
968 /* really scroll? */
969 if ((lcd_scroll_info.lines & (1 << index)) == 0)
970 continue;
972 s = &lcd_scroll_info.scroll[index];
974 /* check pause */
975 if (TIME_BEFORE(current_tick, s->start_tick))
976 continue;
978 if (s->style&STYLE_COLORED) {
979 if (s->style&STYLE_MODE_MASK) {
980 fg_pattern = old_fgcolor;
981 bg_pattern = s->style&STYLE_COLOR_MASK;
983 else {
984 fg_pattern = s->style&STYLE_COLOR_MASK;
985 bg_pattern = old_bgcolor;
989 if (s->backward)
990 s->offset -= lcd_scroll_info.step;
991 else
992 s->offset += lcd_scroll_info.step;
994 pf = font_get(curfont);
995 xpos = s->startx;
996 ypos = ymargin + index * pf->height;
998 if (s->bidir) { /* scroll bidirectional */
999 if (s->offset <= 0) {
1000 /* at beginning of line */
1001 s->offset = 0;
1002 s->backward = false;
1003 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
1005 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
1006 /* at end of line */
1007 s->offset = s->width - (LCD_WIDTH - xpos);
1008 s->backward = true;
1009 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
1012 else {
1013 /* scroll forward the whole time */
1014 if (s->offset >= s->width)
1015 s->offset %= s->width;
1018 lastmode = drawmode;
1019 switch (s->style&STYLE_MODE_MASK) {
1020 case STYLE_INVERT:
1021 drawmode = DRMODE_SOLID|DRMODE_INVERSEVID;
1022 break;
1023 case STYLE_COLORBAR:
1024 /* Solid colour line selector */
1025 drawmode = DRMODE_FG;
1026 fg_pattern = lss_pattern;
1027 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1028 fg_pattern = lst_pattern;
1029 break;
1030 case STYLE_GRADIENT:
1031 /* Gradient line selector */
1032 drawmode = DRMODE_FG;
1033 lcd_gradient_rect(xpos, LCD_WIDTH, ypos, (signed)pf->height);
1034 fg_pattern = lst_pattern;
1035 break;
1036 default:
1037 drawmode = DRMODE_SOLID;
1038 break;
1040 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
1041 drawmode = lastmode;
1042 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1045 fg_pattern = old_fgcolor;
1046 bg_pattern = old_bgcolor;