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
] IBSS_ATTR
;
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
)
694 const unsigned char * src_end
;
695 lcd_pixelfunc_type
* fgfunc
;
696 lcd_pixelfunc_type
* bgfunc
;
698 /* nothing to draw? */
699 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) ||
700 (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
716 if (x
+ width
> current_vp
->width
)
717 width
= current_vp
->width
- x
;
718 if (y
+ height
> current_vp
->height
)
719 height
= current_vp
->height
- y
;
721 /* adjust for viewport */
725 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
727 src_end
= src
+ width
;
729 fgfunc
= lcd_pixelfuncs
[current_vp
->drawmode
];
730 bgfunc
= lcd_pixelfuncs
[current_vp
->drawmode
^ DRMODE_INVERSEVID
];
734 const unsigned char *src_col
= src
++;
735 unsigned data
= *src_col
>> src_y
;
736 int numbits
= 8 - ((int)src_y
);
760 while (src
< src_end
);
763 /* Draw a full monochrome bitmap */
764 void lcd_mono_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
766 lcd_mono_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
769 /* About Rockbox' internal native bitmap format:
771 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
772 * 10 = dark grey, 11 = black. Bits within a byte are arranged horizontally,
774 * The bytes are stored in row-major order, with byte 0 being top left,
775 * byte 1 2nd from left etc. Each row of bytes defines one pixel row.
777 * This is the same as the internal lcd hw format. */
779 /* Draw a partial native bitmap */
780 void ICODE_ATTR
lcd_bitmap_part(const unsigned char *src
, int src_x
,
781 int src_y
, int stride
, int x
, int y
,
782 int width
, int height
)
785 unsigned char *dst
, *dst_end
;
786 unsigned mask
, mask_right
;
788 /* nothing to draw? */
789 if ((width
<= 0) || (height
<= 0) || (x
>= current_vp
->width
) ||
790 (y
>= current_vp
->height
) || (x
+ width
<= 0) || (y
+ height
<= 0))
806 if (x
+ width
> current_vp
->width
)
807 width
= current_vp
->width
- x
;
808 if (y
+ height
> current_vp
->height
)
809 height
= current_vp
->height
- y
;
811 /* adjust for viewport */
815 stride
= (stride
+ 3) >> 2; /* convert to no. of bytes */
817 src
+= stride
* src_y
+ (src_x
>> 2); /* move starting point */
820 dst
= &lcd_framebuffer
[y
][x
>>2];
822 nx
= width
- 1 + shift
+ src_x
;
824 mask
= 0xFF00u
>> (2 * (shift
+ src_x
));
825 mask_right
= 0xFFu
<< (2 * (~nx
& 3));
828 dst_end
= dst
+ height
* LCD_FBWIDTH
;
831 const unsigned char *src_row
= src
;
832 unsigned char *dst_row
= dst
;
833 unsigned mask_row
= mask
>> 8;
836 for (x
= nx
; x
>= 4; x
-= 4)
838 data
= (data
<< 8) | *src_row
++;
842 setblock(dst_row
, mask_row
, data
>> shift
);
850 data
= (data
<< 8) | *src_row
;
851 setblock(dst_row
, mask_row
& mask_right
, data
>> shift
);
856 while (dst
< dst_end
);
859 /* Draw a full native bitmap */
860 void lcd_bitmap(const unsigned char *src
, int x
, int y
, int width
, int height
)
862 lcd_bitmap_part(src
, 0, 0, width
, x
, y
, width
, height
);
865 /* put a string at a given pixel position, skipping first ofs pixel columns */
866 static void lcd_putsxyofs(int x
, int y
, int ofs
, const unsigned char *str
)
870 struct font
* pf
= font_get(current_vp
->font
);
872 ucs
= bidi_l2v(str
, 1);
874 while ((ch
= *ucs
++) != 0 && x
< current_vp
->width
)
877 const unsigned char *bits
;
879 /* get proportional width and glyph bits */
880 width
= font_get_width(pf
,ch
);
888 bits
= font_get_bits(pf
, ch
);
890 lcd_mono_bitmap_part(bits
, ofs
, 0, width
, x
, y
, width
- ofs
,
898 /* put a string at a given pixel position */
899 void lcd_putsxy(int x
, int y
, const unsigned char *str
)
901 lcd_putsxyofs(x
, y
, 0, str
);
904 /*** line oriented text output ***/
906 /* put a string at a given char position */
907 void lcd_puts(int x
, int y
, const unsigned char *str
)
909 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, 0);
912 void lcd_puts_style(int x
, int y
, const unsigned char *str
, int style
)
914 lcd_puts_style_offset(x
, y
, str
, style
, 0);
917 void lcd_puts_offset(int x
, int y
, const unsigned char *str
, int offset
)
919 lcd_puts_style_offset(x
, y
, str
, STYLE_DEFAULT
, offset
);
922 /* put a string at a given char position, style, and pixel position,
923 * skipping first offset pixel columns */
924 void lcd_puts_style_offset(int x
, int y
, const unsigned char *str
,
925 int style
, int offset
)
927 int xpos
,ypos
,w
,h
,xrect
;
928 int lastmode
= current_vp
->drawmode
;
930 /* make sure scrolling is turned off on the line we are updating */
931 lcd_scroll_stop_line(current_vp
, y
);
936 lcd_getstringsize(str
, &w
, &h
);
937 xpos
= x
*w
/ utf8length((char *)str
);
939 current_vp
->drawmode
= (style
& STYLE_INVERT
) ?
940 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
941 lcd_putsxyofs(xpos
, ypos
, offset
, str
);
942 current_vp
->drawmode
^= DRMODE_INVERSEVID
;
943 xrect
= xpos
+ MAX(w
- offset
, 0);
944 lcd_fillrect(xrect
, ypos
, current_vp
->width
- xrect
, h
);
945 current_vp
->drawmode
= lastmode
;
949 void lcd_puts_scroll(int x
, int y
, const unsigned char *string
)
951 lcd_puts_scroll_style(x
, y
, string
, STYLE_DEFAULT
);
954 void lcd_puts_scroll_style(int x
, int y
, const unsigned char *string
, int style
)
956 lcd_puts_scroll_style_offset(x
, y
, string
, style
, 0);
959 void lcd_puts_scroll_offset(int x
, int y
, const unsigned char *string
, int offset
)
961 lcd_puts_scroll_style_offset(x
, y
, string
, STYLE_DEFAULT
, offset
);
964 void lcd_puts_scroll_style_offset(int x
, int y
, const unsigned char *string
,
965 int style
, int offset
)
967 struct scrollinfo
* s
;
970 if ((unsigned)y
>= (unsigned)current_vp
->height
)
973 /* remove any previously scrolling line at the same location */
974 lcd_scroll_stop_line(current_vp
, y
);
976 if (lcd_scroll_info
.lines
>= LCD_SCROLLABLE_LINES
) return;
978 s
= &lcd_scroll_info
.scroll
[lcd_scroll_info
.lines
];
980 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
;
982 if (style
& STYLE_INVERT
) {
983 lcd_puts_style_offset(x
,y
,string
,STYLE_INVERT
,offset
);
986 lcd_puts_offset(x
,y
,string
,offset
);
988 lcd_getstringsize(string
, &w
, &h
);
990 if (current_vp
->width
- x
* 8 < w
) {
991 /* prepare scroll line */
994 memset(s
->line
, 0, sizeof s
->line
);
995 strcpy(s
->line
, (char *)string
);
998 s
->width
= lcd_getstringsize((unsigned char *)s
->line
, &w
, &h
);
1000 /* scroll bidirectional or forward only depending on the string
1002 if ( lcd_scroll_info
.bidir_limit
) {
1003 s
->bidir
= s
->width
< (current_vp
->width
) *
1004 (100 + lcd_scroll_info
.bidir_limit
) / 100;
1009 if (!s
->bidir
) { /* add spaces if scrolling in the round */
1010 strcat(s
->line
, " ");
1011 /* get new width incl. spaces */
1012 s
->width
= lcd_getstringsize((unsigned char *)s
->line
, &w
, &h
);
1015 end
= strchr(s
->line
, '\0');
1016 strncpy(end
, (char *)string
, current_vp
->width
/2);
1020 s
->len
= utf8length((char *)string
);
1022 s
->startx
= x
* s
->width
/ s
->len
;
1023 s
->backward
= false;
1024 lcd_scroll_info
.lines
++;
1028 void lcd_scroll_fn(void)
1031 struct scrollinfo
* s
;
1035 struct viewport
* old_vp
= current_vp
;
1037 for ( index
= 0; index
< lcd_scroll_info
.lines
; index
++ ) {
1038 s
= &lcd_scroll_info
.scroll
[index
];
1041 if (TIME_BEFORE(current_tick
, s
->start_tick
))
1044 lcd_set_viewport(s
->vp
);
1047 s
->offset
-= lcd_scroll_info
.step
;
1049 s
->offset
+= lcd_scroll_info
.step
;
1051 pf
= font_get(current_vp
->font
);
1053 ypos
= s
->y
* pf
->height
;
1055 if (s
->bidir
) { /* scroll bidirectional */
1056 if (s
->offset
<= 0) {
1057 /* at beginning of line */
1059 s
->backward
= false;
1060 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
1062 if (s
->offset
>= s
->width
- (current_vp
->width
- xpos
)) {
1063 /* at end of line */
1064 s
->offset
= s
->width
- (current_vp
->width
- xpos
);
1066 s
->start_tick
= current_tick
+ lcd_scroll_info
.delay
* 2;
1070 /* scroll forward the whole time */
1071 if (s
->offset
>= s
->width
)
1072 s
->offset
%= s
->width
;
1075 lastmode
= current_vp
->drawmode
;
1076 current_vp
->drawmode
= (s
->style
&STYLE_INVERT
) ?
1077 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
1078 lcd_putsxyofs(xpos
, ypos
, s
->offset
, s
->line
);
1079 current_vp
->drawmode
= lastmode
;
1080 lcd_update_viewport_rect(xpos
, ypos
, current_vp
->width
- xpos
, pf
->height
);
1083 lcd_set_viewport(old_vp
);