Simplify my previous commit and taking the chance to mention that it should have...
[Rockbox.git] / firmware / drivers / lcd-2bit-horz.c
blob70d6b439cef332edbf9b9fd46e61d9a0ef12d32a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 * All files in this archive are subject to the GNU General Public License.
17 * See the file COPYING in the source tree root for full license agreement.
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"
24 #include "cpu.h"
25 #include "lcd.h"
26 #include "kernel.h"
27 #include "thread.h"
28 #include <string.h>
29 #include <stdlib.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 /*** globals ***/
40 unsigned char lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH] IBSS_ATTR;
42 static const unsigned char pixmask[4] ICONST_ATTR = {
43 0xC0, 0x30, 0x0C, 0x03
46 static fb_data* lcd_backdrop = NULL;
47 static long lcd_backdrop_offset IDATA_ATTR = 0;
49 static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
50 static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
51 static int drawmode = DRMODE_SOLID;
52 static int xmargin = 0;
53 static int ymargin = 0;
54 static int curfont = FONT_SYSFIXED;
56 /* LCD init */
57 void lcd_init(void)
59 lcd_clear_display();
60 /* Call device specific init */
61 lcd_init_device();
62 scroll_init();
65 /*** parameter handling ***/
67 void lcd_set_drawmode(int mode)
69 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
72 int lcd_get_drawmode(void)
74 return drawmode;
77 void lcd_set_foreground(unsigned brightness)
79 fg_pattern = 0x55 * (~brightness & 3);
82 unsigned lcd_get_foreground(void)
84 return ~fg_pattern & 3;
87 void lcd_set_background(unsigned brightness)
89 bg_pattern = 0x55 * (~brightness & 3);
92 unsigned lcd_get_background(void)
94 return ~bg_pattern & 3;
97 void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
99 lcd_set_drawmode(mode);
100 lcd_set_foreground(fg_brightness);
101 lcd_set_background(bg_brightness);
104 void lcd_setmargins(int x, int y)
106 xmargin = x;
107 ymargin = y;
110 int lcd_getxmargin(void)
112 return xmargin;
115 int lcd_getymargin(void)
117 return ymargin;
120 void lcd_setfont(int newfont)
122 curfont = newfont;
125 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
127 return font_getstringsize(str, w, h, curfont);
130 /*** low-level drawing functions ***/
132 static void setpixel(int x, int y)
134 unsigned mask = pixmask[x & 3];
135 fb_data *address = &lcd_framebuffer[y][x>>2];
136 unsigned data = *address;
138 *address = data ^ ((data ^ fg_pattern) & mask);
141 static void clearpixel(int x, int y)
143 unsigned mask = pixmask[x & 3];
144 fb_data *address = &lcd_framebuffer[y][x>>2];
145 unsigned data = *address;
147 *address = data ^ ((data ^ bg_pattern) & mask);
150 static void clearimgpixel(int x, int y)
152 unsigned mask = pixmask[x & 3];
153 fb_data *address = &lcd_framebuffer[y][x>>2];
154 unsigned data = *address;
156 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask);
159 static void flippixel(int x, int y)
161 unsigned mask = pixmask[x & 3];
162 fb_data *address = &lcd_framebuffer[y][x>>2];
164 *address ^= mask;
167 static void nopixel(int x, int y)
169 (void)x;
170 (void)y;
173 lcd_pixelfunc_type* const lcd_pixelfuncs_bgcolor[8] = {
174 flippixel, nopixel, setpixel, setpixel,
175 nopixel, clearpixel, nopixel, clearpixel
178 lcd_pixelfunc_type* const lcd_pixelfuncs_backdrop[8] = {
179 flippixel, nopixel, setpixel, setpixel,
180 nopixel, clearimgpixel, nopixel, clearimgpixel
183 lcd_pixelfunc_type* const * lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
186 /* 'mask' and 'bits' contain 2 bits per pixel */
187 static void flipblock(fb_data *address, unsigned mask, unsigned bits)
188 ICODE_ATTR;
189 static void flipblock(fb_data *address, unsigned mask, unsigned bits)
191 *address ^= bits & mask;
194 static void bgblock(fb_data *address, unsigned mask, unsigned bits)
195 ICODE_ATTR;
196 static void bgblock(fb_data *address, unsigned mask, unsigned bits)
198 unsigned data = *address;
200 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
203 static void bgimgblock(fb_data *address, unsigned mask, unsigned bits)
204 ICODE_ATTR;
205 static void bgimgblock(fb_data *address, unsigned mask, unsigned bits)
207 unsigned data = *address;
209 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & ~bits);
212 static void fgblock(fb_data *address, unsigned mask, unsigned bits)
213 ICODE_ATTR;
214 static void fgblock(fb_data *address, unsigned mask, unsigned bits)
216 unsigned data = *address;
218 *address = data ^ ((data ^ fg_pattern) & mask & bits);
221 static void solidblock(fb_data *address, unsigned mask, unsigned bits)
222 ICODE_ATTR;
223 static void solidblock(fb_data *address, unsigned mask, unsigned bits)
225 unsigned data = *address;
226 unsigned bgp = bg_pattern;
228 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
229 *address = data ^ ((data ^ bits) & mask);
232 static void solidimgblock(fb_data *address, unsigned mask, unsigned bits)
233 ICODE_ATTR;
234 static void solidimgblock(fb_data *address, unsigned mask, unsigned bits)
236 unsigned data = *address;
237 unsigned bgp = *(address + lcd_backdrop_offset);
239 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
240 *address = data ^ ((data ^ bits) & mask);
243 static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
244 ICODE_ATTR;
245 static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
247 *address ^= ~bits & mask;
250 static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
251 ICODE_ATTR;
252 static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
254 unsigned data = *address;
256 *address = data ^ ((data ^ bg_pattern) & mask & bits);
259 static void bgimginvblock(fb_data *address, unsigned mask, unsigned bits)
260 ICODE_ATTR;
261 static void bgimginvblock(fb_data *address, unsigned mask, unsigned bits)
263 unsigned data = *address;
265 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & bits);
268 static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
269 ICODE_ATTR;
270 static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
272 unsigned data = *address;
274 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
277 static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
278 ICODE_ATTR;
279 static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
281 unsigned data = *address;
282 unsigned fgp = fg_pattern;
284 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
285 *address = data ^ ((data ^ bits) & mask);
288 static void solidimginvblock(fb_data *address, unsigned mask, unsigned bits)
289 ICODE_ATTR;
290 static void solidimginvblock(fb_data *address, unsigned mask, unsigned bits)
292 unsigned data = *address;
293 unsigned fgp = fg_pattern;
295 bits = fgp ^ ((fgp ^ *(address + lcd_backdrop_offset)) & bits);
296 *address = data ^ ((data ^ bits) & mask);
299 lcd_blockfunc_type* const lcd_blockfuncs_bgcolor[8] = {
300 flipblock, bgblock, fgblock, solidblock,
301 flipinvblock, bginvblock, fginvblock, solidinvblock
304 lcd_blockfunc_type* const lcd_blockfuncs_backdrop[8] = {
305 flipblock, bgimgblock, fgblock, solidimgblock,
306 flipinvblock, bgimginvblock, fginvblock, solidimginvblock
309 lcd_blockfunc_type* const * lcd_blockfuncs = lcd_blockfuncs_bgcolor;
312 void lcd_set_backdrop(fb_data* backdrop)
314 lcd_backdrop = backdrop;
315 if (backdrop)
317 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
318 lcd_pixelfuncs = lcd_pixelfuncs_backdrop;
319 lcd_blockfuncs = lcd_blockfuncs_backdrop;
321 else
323 lcd_backdrop_offset = 0;
324 lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
325 lcd_blockfuncs = lcd_blockfuncs_bgcolor;
329 fb_data* lcd_get_backdrop(void)
331 return lcd_backdrop;
335 static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
337 unsigned data = *address;
339 bits ^= data;
340 *address = data ^ (bits & mask);
343 /*** drawing functions ***/
345 /* Clear the whole display */
346 void lcd_clear_display(void)
348 if (drawmode & DRMODE_INVERSEVID)
350 memset(lcd_framebuffer, fg_pattern, sizeof lcd_framebuffer);
352 else
354 if (lcd_backdrop)
355 memcpy(lcd_framebuffer, lcd_backdrop, sizeof lcd_framebuffer);
356 else
357 memset(lcd_framebuffer, bg_pattern, sizeof lcd_framebuffer);
360 lcd_scroll_info.lines = 0;
363 /* Set a single pixel */
364 void lcd_drawpixel(int x, int y)
366 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
367 lcd_pixelfuncs[drawmode](x, y);
370 /* Draw a line */
371 void lcd_drawline(int x1, int y1, int x2, int y2)
373 int numpixels;
374 int i;
375 int deltax, deltay;
376 int d, dinc1, dinc2;
377 int x, xinc1, xinc2;
378 int y, yinc1, yinc2;
379 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
381 deltax = abs(x2 - x1);
382 deltay = abs(y2 - y1);
383 xinc2 = 1;
384 yinc2 = 1;
386 if (deltax >= deltay)
388 numpixels = deltax;
389 d = 2 * deltay - deltax;
390 dinc1 = deltay * 2;
391 dinc2 = (deltay - deltax) * 2;
392 xinc1 = 1;
393 yinc1 = 0;
395 else
397 numpixels = deltay;
398 d = 2 * deltax - deltay;
399 dinc1 = deltax * 2;
400 dinc2 = (deltax - deltay) * 2;
401 xinc1 = 0;
402 yinc1 = 1;
404 numpixels++; /* include endpoints */
406 if (x1 > x2)
408 xinc1 = -xinc1;
409 xinc2 = -xinc2;
412 if (y1 > y2)
414 yinc1 = -yinc1;
415 yinc2 = -yinc2;
418 x = x1;
419 y = y1;
421 for (i = 0; i < numpixels; i++)
423 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
424 pfunc(x, y);
426 if (d < 0)
428 d += dinc1;
429 x += xinc1;
430 y += yinc1;
432 else
434 d += dinc2;
435 x += xinc2;
436 y += yinc2;
441 /* Draw a horizontal line (optimised) */
442 void lcd_hline(int x1, int x2, int y)
444 int nx;
445 unsigned char *dst;
446 unsigned mask, mask_right;
447 lcd_blockfunc_type *bfunc;
449 /* direction flip */
450 if (x2 < x1)
452 nx = x1;
453 x1 = x2;
454 x2 = nx;
457 /* nothing to draw? */
458 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
459 return;
461 /* clipping */
462 if (x1 < 0)
463 x1 = 0;
464 if (x2 >= LCD_WIDTH)
465 x2 = LCD_WIDTH-1;
467 bfunc = lcd_blockfuncs[drawmode];
468 dst = &lcd_framebuffer[y][x1>>2];
469 nx = x2 - (x1 & ~3);
470 mask = 0xFFu >> (2 * (x1 & 3));
471 mask_right = 0xFFu << (2 * (~nx & 3));
473 for (; nx >= 4; nx -= 4)
475 bfunc(dst++, mask, 0xFFu);
476 mask = 0xFFu;
478 mask &= mask_right;
479 bfunc(dst, mask, 0xFFu);
482 /* Draw a vertical line (optimised) */
483 void lcd_vline(int x, int y1, int y2)
485 int y;
486 unsigned char *dst, *dst_end;
487 unsigned mask;
488 lcd_blockfunc_type *bfunc;
490 /* direction flip */
491 if (y2 < y1)
493 y = y1;
494 y1 = y2;
495 y2 = y;
498 /* nothing to draw? */
499 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
500 return;
502 /* clipping */
503 if (y1 < 0)
504 y1 = 0;
505 if (y2 >= LCD_HEIGHT)
506 y2 = LCD_HEIGHT-1;
508 bfunc = lcd_blockfuncs[drawmode];
509 dst = &lcd_framebuffer[y1][x>>2];
510 mask = pixmask[x & 3];
512 dst_end = dst + (y2 - y1) * LCD_FBWIDTH;
515 bfunc(dst, mask, 0xFFu);
516 dst += LCD_FBWIDTH;
518 while (dst <= dst_end);
521 /* Draw a rectangular box */
522 void lcd_drawrect(int x, int y, int width, int height)
524 if ((width <= 0) || (height <= 0))
525 return;
527 int x2 = x + width - 1;
528 int y2 = y + height - 1;
530 lcd_vline(x, y, y2);
531 lcd_vline(x2, y, y2);
532 lcd_hline(x, x2, y);
533 lcd_hline(x, x2, y2);
536 /* Fill a rectangular area */
537 void lcd_fillrect(int x, int y, int width, int height)
539 int nx;
540 unsigned char *dst, *dst_end;
541 unsigned mask, mask_right;
542 lcd_blockfunc_type *bfunc;
544 /* nothing to draw? */
545 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
546 || (x + width <= 0) || (y + height <= 0))
547 return;
549 /* clipping */
550 if (x < 0)
552 width += x;
553 x = 0;
555 if (y < 0)
557 height += y;
558 y = 0;
560 if (x + width > LCD_WIDTH)
561 width = LCD_WIDTH - x;
562 if (y + height > LCD_HEIGHT)
563 height = LCD_HEIGHT - y;
565 bfunc = lcd_blockfuncs[drawmode];
566 dst = &lcd_framebuffer[y][x>>2];
567 nx = width - 1 + (x & 3);
568 mask = 0xFFu >> (2 * (x & 3));
569 mask_right = 0xFFu << (2 * (~nx & 3));
571 for (; nx >= 4; nx -= 4)
573 unsigned char *dst_col = dst;
575 dst_end = dst_col + height * LCD_FBWIDTH;
578 bfunc(dst_col, mask, 0xFFu);
579 dst_col += LCD_FBWIDTH;
581 while (dst_col < dst_end);
583 dst++;
584 mask = 0xFFu;
586 mask &= mask_right;
588 dst_end = dst + height * LCD_FBWIDTH;
591 bfunc(dst, mask, 0xFFu);
592 dst += LCD_FBWIDTH;
594 while (dst < dst_end);
597 /* About Rockbox' internal monochrome bitmap format:
599 * A bitmap contains one bit for every pixel that defines if that pixel is
600 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
601 * at top.
602 * The bytes are stored in row-major order, with byte 0 being top left,
603 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
604 * 0..7, the second row defines pixel row 8..15 etc. */
606 /* Draw a partial monochrome bitmap */
607 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
608 int stride, int x, int y, int width, int height)
609 ICODE_ATTR;
610 void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
611 int stride, int x, int y, int width, int height)
613 int ny, nx, ymax;
614 const unsigned char * src_end;
615 lcd_pixelfunc_type* fgfunc;
616 lcd_pixelfunc_type* bgfunc;
618 /* nothing to draw? */
619 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
620 || (x + width <= 0) || (y + height <= 0))
621 return;
623 /* clipping */
624 if (x < 0)
626 width += x;
627 src_x -= x;
628 x = 0;
630 if (y < 0)
632 height += y;
633 src_y -= y;
634 y = 0;
636 if (x + width > LCD_WIDTH)
637 width = LCD_WIDTH - x;
638 if (y + height > LCD_HEIGHT)
639 height = LCD_HEIGHT - y;
641 src += stride * (src_y >> 3) + src_x; /* move starting point */
642 src_y &= 7;
643 src_end = src + width;
645 fgfunc = lcd_pixelfuncs[drawmode];
646 bgfunc = lcd_pixelfuncs[drawmode ^ DRMODE_INVERSEVID];
647 nx = x;
650 const unsigned char *src_col = src++;
651 unsigned data = *src_col >> src_y;
652 int numbits = 8 - ((int)src_y);
654 ymax = y + height;
655 ny = y;
658 if (data & 0x01)
659 fgfunc(nx,ny);
660 else
661 bgfunc(nx,ny);
663 ny++;
665 data >>= 1;
666 if (--numbits == 0)
668 src_col += stride;
669 data = *src_col;
670 numbits = 8;
673 while (ny < ymax);
674 nx++;
676 while (src < src_end);
679 /* Draw a full monochrome bitmap */
680 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
682 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
685 /* About Rockbox' internal native bitmap format:
687 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
688 * 10 = dark grey, 11 = black. Bits within a byte are arranged horizontally,
689 * MSB at the left.
690 * The bytes are stored in row-major order, with byte 0 being top left,
691 * byte 1 2nd from left etc. Each row of bytes defines one pixel row.
693 * This is the same as the internal lcd hw format. */
695 /* Draw a partial native bitmap */
696 void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
697 int stride, int x, int y, int width, int height)
698 ICODE_ATTR;
699 void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
700 int stride, int x, int y, int width, int height)
702 int shift, nx;
703 unsigned char *dst, *dst_end;
704 unsigned mask, mask_right;
706 /* nothing to draw? */
707 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
708 || (x + width <= 0) || (y + height <= 0))
709 return;
711 /* clipping */
712 if (x < 0)
714 width += x;
715 src_x -= x;
716 x = 0;
718 if (y < 0)
720 height += y;
721 src_y -= y;
722 y = 0;
724 if (x + width > LCD_WIDTH)
725 width = LCD_WIDTH - x;
726 if (y + height > LCD_HEIGHT)
727 height = LCD_HEIGHT - y;
729 stride = (stride + 3) >> 2; /* convert to no. of bytes */
731 src += stride * src_y + (src_x >> 2); /* move starting point */
732 src_x &= 3;
733 x -= src_x;
734 dst = &lcd_framebuffer[y][x>>2];
735 shift = x & 3;
736 nx = width - 1 + shift + src_x;
738 mask = 0xFF00u >> (2 * (shift + src_x));
739 mask_right = 0xFFu << (2 * (~nx & 3));
741 shift *= 2;
742 dst_end = dst + height * LCD_FBWIDTH;
745 const unsigned char *src_row = src;
746 unsigned char *dst_row = dst;
747 unsigned mask_row = mask >> 8;
748 unsigned data = 0;
750 for (x = nx; x >= 4; x -= 4)
752 data = (data << 8) | *src_row++;
754 if (mask_row & 0xFF)
756 setblock(dst_row, mask_row, data >> shift);
757 mask_row = 0xFF;
759 else
760 mask_row = mask;
762 dst_row++;
764 data = (data << 8) | *src_row;
765 setblock(dst_row, mask_row & mask_right, data >> shift);
767 src += stride;
768 dst += LCD_FBWIDTH;
770 while (dst < dst_end);
773 /* Draw a full native bitmap */
774 void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
776 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
779 /* put a string at a given pixel position, skipping first ofs pixel columns */
780 static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
782 unsigned short ch;
783 unsigned short *ucs;
784 struct font* pf = font_get(curfont);
786 ucs = bidi_l2v(str, 1);
788 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
790 int width;
791 const unsigned char *bits;
793 /* get proportional width and glyph bits */
794 width = font_get_width(pf,ch);
796 if (ofs > width)
798 ofs -= width;
799 continue;
802 bits = font_get_bits(pf, ch);
804 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
805 pf->height);
807 x += width - ofs;
808 ofs = 0;
812 /* put a string at a given pixel position */
813 void lcd_putsxy(int x, int y, const unsigned char *str)
815 lcd_putsxyofs(x, y, 0, str);
818 /*** line oriented text output ***/
820 /* put a string at a given char position */
821 void lcd_puts(int x, int y, const unsigned char *str)
823 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
826 void lcd_puts_style(int x, int y, const unsigned char *str, int style)
828 lcd_puts_style_offset(x, y, str, style, 0);
831 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
833 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
836 /* put a string at a given char position, style, and pixel position,
837 * skipping first offset pixel columns */
838 void lcd_puts_style_offset(int x, int y, const unsigned char *str,
839 int style, int offset)
841 int xpos,ypos,w,h,xrect;
842 int lastmode = drawmode;
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((char *)str);
852 ypos = ymargin + y*h;
853 drawmode = (style & STYLE_INVERT) ?
854 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
855 lcd_putsxyofs(xpos, ypos, offset, str);
856 drawmode ^= DRMODE_INVERSEVID;
857 xrect = xpos + MAX(w - offset, 0);
858 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
859 drawmode = lastmode;
862 /*** scrolling ***/
863 void lcd_puts_scroll(int x, int y, const unsigned char *string)
865 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
868 void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
870 lcd_puts_scroll_style_offset(x, y, string, style, 0);
873 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
875 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
878 void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
879 int style, int offset)
881 struct scrollinfo* s;
882 int w, h;
884 if(y>=LCD_SCROLLABLE_LINES) return;
886 s = &lcd_scroll_info.scroll[y];
888 s->start_tick = current_tick + lcd_scroll_info.delay;
889 s->invert = false;
890 if (style & STYLE_INVERT) {
891 s->invert = true;
892 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
894 else
895 lcd_puts_offset(x,y,string,offset);
897 lcd_getstringsize(string, &w, &h);
899 if (LCD_WIDTH - x * 8 - xmargin < w) {
900 /* prepare scroll line */
901 char *end;
903 memset(s->line, 0, sizeof s->line);
904 strcpy(s->line, (char *)string);
906 /* get width */
907 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
909 /* scroll bidirectional or forward only depending on the string
910 width */
911 if ( lcd_scroll_info.bidir_limit ) {
912 s->bidir = s->width < (LCD_WIDTH - xmargin) *
913 (100 + lcd_scroll_info.bidir_limit) / 100;
915 else
916 s->bidir = false;
918 if (!s->bidir) { /* add spaces if scrolling in the round */
919 strcat(s->line, " ");
920 /* get new width incl. spaces */
921 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
924 end = strchr(s->line, '\0');
925 strncpy(end, (char *)string, LCD_WIDTH/2);
927 s->len = utf8length((char *)string);
928 s->offset = offset;
929 s->startx = xmargin + x * s->width / s->len;;
930 s->backward = false;
931 lcd_scroll_info.lines |= (1<<y);
933 else
934 /* force a bit switch-off since it doesn't scroll */
935 lcd_scroll_info.lines &= ~(1<<y);
938 void lcd_scroll_fn(void)
940 struct font* pf;
941 struct scrollinfo* s;
942 int index;
943 int xpos, ypos;
944 int lastmode;
946 for ( index = 0; index < LCD_SCROLLABLE_LINES; index++ ) {
947 /* really scroll? */
948 if ((lcd_scroll_info.lines & (1 << index)) == 0)
949 continue;
951 s = &lcd_scroll_info.scroll[index];
953 /* check pause */
954 if (TIME_BEFORE(current_tick, s->start_tick))
955 continue;
957 if (s->backward)
958 s->offset -= lcd_scroll_info.step;
959 else
960 s->offset += lcd_scroll_info.step;
962 pf = font_get(curfont);
963 xpos = s->startx;
964 ypos = ymargin + index * pf->height;
966 if (s->bidir) { /* scroll bidirectional */
967 if (s->offset <= 0) {
968 /* at beginning of line */
969 s->offset = 0;
970 s->backward = false;
971 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
973 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
974 /* at end of line */
975 s->offset = s->width - (LCD_WIDTH - xpos);
976 s->backward = true;
977 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
980 else {
981 /* scroll forward the whole time */
982 if (s->offset >= s->width)
983 s->offset %= s->width;
986 lastmode = drawmode;
987 drawmode = s->invert ?
988 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
989 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
990 drawmode = lastmode;
991 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);