summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd-2bit-horz.c
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-02-05 16:01:50 +0000
committerDave Chapman <dave@dchapman.com>2006-02-05 16:01:50 +0000
commite8048cefed2715dcecb82ae49549ed619cf3104b (patch)
tree53b3bc65888735de5a1492b4b6a9c4e794d0c01d /firmware/drivers/lcd-2bit-horz.c
parentc6c584e4c1fcf46d058e23dc96c8dca8886466a8 (diff)
downloadrockbox-e8048cefed2715dcecb82ae49549ed619cf3104b.tar.gz
rockbox-e8048cefed2715dcecb82ae49549ed619cf3104b.zip
Initial version of iPod greyscale LCD driver from Seven Le Mesle. This is currently written to use vertically-packed mono and native bitmap formats.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8580 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/lcd-2bit-horz.c')
-rw-r--r--firmware/drivers/lcd-2bit-horz.c813
1 files changed, 813 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-2bit-horz.c b/firmware/drivers/lcd-2bit-horz.c
new file mode 100644
index 0000000000..01f5d93cf3
--- /dev/null
+++ b/firmware/drivers/lcd-2bit-horz.c
@@ -0,0 +1,813 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * LCD driver for horizontally-packed 2bpp greyscale display
11 *
12 * Based on code from the rockbox lcd's driver
13 *
14 * Copyright (c) 2006 Seven Le Mesle (sevlm@free.fr)
15 *
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.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
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
37#define SCROLLABLE_LINES 26
38
39/*** globals ***/
40
41unsigned char lcd_framebuffer[LCD_HEIGHT][LCD_WIDTH/4] IBSS_ATTR;
42
43static const unsigned char dibits[16] ICONST_ATTR = {
44 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
45 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
46};
47
48static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
49static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
50static int drawmode = DRMODE_SOLID;
51static int xmargin = 0;
52static int ymargin = 0;
53static int curfont = FONT_SYSFIXED;
54
55/* scrolling */
56static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
57static void scroll_thread(void);
58static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
59static const char scroll_name[] = "scroll";
60static int scroll_ticks = 12; /* # of ticks between updates*/
61static int scroll_delay = HZ/2; /* ticks delay before start */
62static int scroll_step = 6; /* pixels per scroll step */
63static int bidir_limit = 50; /* percent */
64static struct scrollinfo scroll[SCROLLABLE_LINES];
65
66static const char scroll_tick_table[16] = {
67 /* Hz values:
68 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
69 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
70};
71
72
73static unsigned char notmask[4] = { 0xfc, 0xf3, 0xcf, 0x3f };
74
75/* LCD init */
76void lcd_init(void)
77{
78 lcd_clear_display();
79 /* Call device specific init */
80 lcd_init_device();
81 create_thread(scroll_thread, scroll_stack,
82 sizeof(scroll_stack), scroll_name);
83}
84
85/*** parameter handling ***/
86
87void lcd_set_drawmode(int mode)
88{
89 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
90}
91
92int lcd_get_drawmode(void)
93{
94 return drawmode;
95}
96
97void lcd_set_foreground(unsigned brightness)
98{
99 fg_pattern = 0x55 * (~brightness & 3);
100}
101
102unsigned lcd_get_foreground(void)
103{
104 return ~fg_pattern & 3;
105}
106
107void lcd_set_background(unsigned brightness)
108{
109 bg_pattern = 0x55 * (~brightness & 3);
110}
111
112unsigned lcd_get_background(void)
113{
114 return ~bg_pattern & 3;
115}
116
117void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
118{
119 lcd_set_drawmode(mode);
120 lcd_set_foreground(fg_brightness);
121 lcd_set_background(bg_brightness);
122}
123
124void lcd_setmargins(int x, int y)
125{
126 xmargin = x;
127 ymargin = y;
128}
129
130int lcd_getxmargin(void)
131{
132 return xmargin;
133}
134
135int lcd_getymargin(void)
136{
137 return ymargin;
138}
139
140void lcd_setfont(int newfont)
141{
142 curfont = newfont;
143}
144
145int lcd_getstringsize(const unsigned char *str, int *w, int *h)
146{
147 return font_getstringsize(str, w, h, curfont);
148}
149
150/*** low-level drawing functions ***/
151
152static void setpixel_value(int x, int y, unsigned val)
153{
154 unsigned char *data = &lcd_framebuffer[y][x>>2];
155
156 *data = (*data & notmask[x&3]) | (val << ((x&3)<<1));
157}
158
159static void setpixel(int x, int y)
160{
161 unsigned char *data = &lcd_framebuffer[y][x>>2];
162
163 *data = (*data & notmask[x&3]) | (fg_pattern << ((x&3)<<1));
164}
165
166static void clearpixel(int x, int y)
167{
168 unsigned char *data = &lcd_framebuffer[y][x>>2];
169*data = (*data & notmask[x&3]) | (bg_pattern << ((x&3)<<1));
170}
171
172static void flippixel(int x, int y)
173{
174 lcd_framebuffer[y][x>>2] ^= 3 << (2 * (x & 3));
175}
176
177static void nopixel(int x, int y)
178{
179 (void)x;
180 (void)y;
181}
182
183lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
184 flippixel, nopixel, setpixel, setpixel,
185 nopixel, clearpixel, nopixel, clearpixel
186};
187
188
189/*** drawing functions ***/
190
191/* Clear the whole display */
192void lcd_clear_display(void)
193{
194 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? fg_pattern : bg_pattern;
195
196 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
197 scrolling_lines = 0;
198}
199
200/* Set a single pixel */
201void lcd_drawpixel(int x, int y)
202{
203 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
204 lcd_pixelfuncs[drawmode](x, y);
205}
206
207/* Draw a line */
208void lcd_drawline(int x1, int y1, int x2, int y2)
209{
210 int numpixels;
211 int i;
212 int deltax, deltay;
213 int d, dinc1, dinc2;
214 int x, xinc1, xinc2;
215 int y, yinc1, yinc2;
216 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
217
218 deltax = abs(x2 - x1);
219 deltay = abs(y2 - y1);
220 xinc2 = 1;
221 yinc2 = 1;
222
223 if (deltax >= deltay)
224 {
225 numpixels = deltax;
226 d = 2 * deltay - deltax;
227 dinc1 = deltay * 2;
228 dinc2 = (deltay - deltax) * 2;
229 xinc1 = 1;
230 yinc1 = 0;
231 }
232 else
233 {
234 numpixels = deltay;
235 d = 2 * deltax - deltay;
236 dinc1 = deltax * 2;
237 dinc2 = (deltax - deltay) * 2;
238 xinc1 = 0;
239 yinc1 = 1;
240 }
241 numpixels++; /* include endpoints */
242
243 if (x1 > x2)
244 {
245 xinc1 = -xinc1;
246 xinc2 = -xinc2;
247 }
248
249 if (y1 > y2)
250 {
251 yinc1 = -yinc1;
252 yinc2 = -yinc2;
253 }
254
255 x = x1;
256 y = y1;
257
258 for (i = 0; i < numpixels; i++)
259 {
260 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
261 pfunc(x, y);
262
263 if (d < 0)
264 {
265 d += dinc1;
266 x += xinc1;
267 y += yinc1;
268 }
269 else
270 {
271 d += dinc2;
272 x += xinc2;
273 y += yinc2;
274 }
275 }
276}
277
278/* Draw a horizontal line (optimised) */
279void lcd_hline(int x1, int x2, int y)
280{
281 int x;
282
283 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
284
285 /* direction flip */
286 if (x2 < x1)
287 {
288 x = x1;
289 x1 = x2;
290 x2 = x;
291 }
292
293 /* nothing to draw? */
294 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
295 return;
296
297 /* clipping */
298 if (x1 < 0)
299 x1 = 0;
300 if (x2 >= LCD_WIDTH)
301 x2 = LCD_WIDTH-1;
302
303 while(x1 <= x2) {
304 pfunc(x1,y);
305 ++x1;
306 }
307}
308
309/* Draw a vertical line (optimised) */
310void lcd_vline(int x, int y1, int y2)
311{
312 int ny;
313
314 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
315
316 /* direction flip */
317 if (y2 < y1)
318 {
319 ny = y1;
320 y1 = y2;
321 y2 = ny;
322 }
323
324 /* nothing to draw? */
325 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
326 return;
327
328 /* clipping */
329 if (y1 < 0)
330 y1 = 0;
331 if (y2 >= LCD_HEIGHT)
332 y2 = LCD_HEIGHT-1;
333
334
335 while(y1++ <= y2) {
336 pfunc(x, y1);
337 }
338}
339
340/* Draw a rectangular box */
341void lcd_drawrect(int x, int y, int width, int height)
342{
343 if ((width <= 0) || (height <= 0))
344 return;
345
346 int x2 = x + width - 1;
347 int y2 = y + height - 1;
348
349 lcd_vline(x, y, y2);
350 lcd_vline(x2, y, y2);
351 lcd_hline(x, x2, y);
352 lcd_hline(x, x2, y2);
353}
354
355/* Fill a rectangular area */
356void lcd_fillrect(int x, int y, int width, int height)
357{
358 int ny;
359
360
361 /* nothing to draw? */
362 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
363 || (x + width <= 0) || (y + height <= 0))
364 return;
365
366 /* clipping */
367 if (x < 0)
368 {
369 width += x;
370 x = 0;
371 }
372 if (y < 0)
373 {
374 height += y;
375 y = 0;
376 }
377 if (x + width > LCD_WIDTH)
378 width = LCD_WIDTH - x;
379 if (y + height > LCD_HEIGHT)
380 height = LCD_HEIGHT - y;
381
382 ny = y;
383 while (ny <= height)
384 {
385 lcd_hline (x, width, ny);
386 ny++;
387 }
388}
389
390/* About Rockbox' internal monochrome bitmap format:
391 *
392 * A bitmap contains one bit for every pixel that defines if that pixel is
393 * black (1) or white (0). Bits within a byte are arranged horizontally, LSB
394 * at top.
395 * The bytes are stored in row-major order, with byte 0 being top left,
396 * byte 1 2nd from left etc. The first row of bytes defines pixel row
397 * 0, the second row defines pixel row 1 etc.
398 *
399 * This is similar to the internal lcd hw format. */
400
401/* Draw a partial monochrome bitmap */
402void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
403 int stride, int x, int y, int width, int height)
404 ICODE_ATTR;
405void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
406 int stride, int x, int y, int width, int height)
407{
408 int ny, nx, ymax;
409 const unsigned char * src_end;
410
411 /* nothing to draw? */
412 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
413 || (x + width <= 0) || (y + height <= 0))
414 return;
415
416 /* clipping */
417 if (x < 0)
418 {
419 width += x;
420 src_x -= x;
421 x = 0;
422 }
423 if (y < 0)
424 {
425 height += y;
426 src_y -= y;
427 y = 0;
428 }
429 if (x + width > LCD_WIDTH)
430 width = LCD_WIDTH - x;
431 if (y + height > LCD_HEIGHT)
432 height = LCD_HEIGHT - y;
433
434 src += stride * (src_y >> 3) + src_x; /* move starting point */
435 src_y &= 7;
436 src_end = src + width;
437
438 nx = x;
439 do
440 {
441 const unsigned char *src_col = src++;
442 unsigned data = *src_col >> src_y;
443 int numbits = 8 - ((int)src_y);
444
445 ymax = y + height;
446 ny = y;
447 do
448 {
449 if (data & 0x01)
450 setpixel (nx,ny);
451 else
452 clearpixel (nx,ny);
453
454 ny++;
455
456 data >>= 1;
457 if (--numbits == 0)
458 {
459 src_col += stride;
460 data = *src_col;
461 numbits = 8;
462 }
463 }
464 while (ny < ymax);
465 nx++;
466 }
467 while (src < src_end);
468}
469
470/* Draw a full monochrome bitmap */
471void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
472{
473 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
474}
475
476/* About Rockbox' internal native bitmap format:
477 *
478 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
479 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
480 * at top.
481 * The bytes are stored in row-major order, with byte 0 being top left,
482 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
483 * 0..3, the second row defines pixel row 4..7 etc.
484 *
485 * This is the same as the internal lcd hw format. */
486
487/* Draw a partial native bitmap */
488void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
489 int stride, int x, int y, int width, int height)
490 ICODE_ATTR;
491void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
492 int stride, int x, int y, int width, int height)
493{
494 int ny, nx, ymax;
495 const unsigned char * src_end;
496
497 /* nothing to draw? */
498 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
499 || (x + width <= 0) || (y + height <= 0))
500 return;
501
502 /* clipping */
503 if (x < 0)
504 {
505 width += x;
506 src_x -= x;
507 x = 0;
508 }
509 if (y < 0)
510 {
511 height += y;
512 src_y -= y;
513 y = 0;
514 }
515 if (x + width > LCD_WIDTH)
516 width = LCD_WIDTH - x;
517 if (y + height > LCD_HEIGHT)
518 height = LCD_HEIGHT - y;
519
520 src += stride * (src_y >> 3) + src_x; /* move starting point */
521 src_y &= 7;
522 src_end = src + width;
523
524 nx = x;
525 do
526 {
527 const unsigned char *src_col = src++;
528 unsigned data = *src_col >> src_y;
529 int numbits = 8 - src_y;
530
531 ymax = y + height;
532 ny = y;
533 do
534 {
535 if (data & 0x03)
536 setpixel_value (nx,ny, 0xFF);
537 else
538 if (data & 0x01)
539 setpixel_value (nx,ny, 0x3F);
540 else
541 if (data & 0x02)
542 setpixel_value (nx,ny, 0xCF);
543 else
544 setpixel_value (nx,ny, 0x00);
545
546 ny++;
547
548 data >>= 2;
549 if (--numbits == 0)
550 {
551 src_col += stride;
552 data = *src_col;
553 numbits = 4;
554 }
555 }
556 while (ny < ymax);
557 nx++;
558 }
559 while (src < src_end);
560}
561
562/* Draw a full native bitmap */
563void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
564{
565 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
566}
567
568/* put a string at a given pixel position, skipping first ofs pixel columns */
569static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
570{
571 unsigned short ch;
572 unsigned short *ucs;
573 struct font* pf = font_get(curfont);
574
575 ucs = bidi_l2v(str, 1);
576
577 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
578 {
579 int width;
580 const unsigned char *bits;
581
582 /* check input range */
583 if (ch < pf->firstchar || ch >= pf->firstchar+pf->size)
584 ch = pf->defaultchar;
585 ch -= pf->firstchar;
586
587 /* get proportional width and glyph bits */
588 width = font_get_width(pf,ch);
589
590 if (ofs > width)
591 {
592 ofs -= width;
593 continue;
594 }
595
596 bits = font_get_bits(pf, ch);
597
598 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
599
600 x += width - ofs;
601 ofs = 0;
602 }
603}
604
605/* put a string at a given pixel position */
606void lcd_putsxy(int x, int y, const unsigned char *str)
607{
608 lcd_putsxyofs(x, y, 0, str);
609}
610
611/*** line oriented text output ***/
612
613void lcd_puts_style(int x, int y, const unsigned char *str, int style)
614{
615 int xpos,ypos,w,h;
616 int lastmode = drawmode;
617
618 /* make sure scrolling is turned off on the line we are updating */
619 scrolling_lines &= ~(1 << y);
620
621 if(!str || !str[0])
622 return;
623
624 lcd_getstringsize(str, &w, &h);
625 xpos = xmargin + x*w / utf8length((char *)str);
626 ypos = ymargin + y*h;
627 lcd_putsxy(xpos, ypos, str);
628 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
629 lcd_fillrect(xpos + w, ypos, LCD_WIDTH - (xpos + w), h);
630 if (style & STYLE_INVERT)
631 {
632 drawmode = DRMODE_COMPLEMENT;
633 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, h);
634 }
635 drawmode = lastmode;
636}
637
638/* put a string at a given char position */
639void lcd_puts(int x, int y, const unsigned char *str)
640{
641 lcd_puts_style(x, y, str, STYLE_DEFAULT);
642}
643
644/*** scrolling ***/
645
646/* Reverse the invert setting of the scrolling line (if any) at given char
647 position. Setting will go into affect next time line scrolls. */
648void lcd_invertscroll(int x, int y)
649{
650 struct scrollinfo* s;
651
652 (void)x;
653
654 s = &scroll[y];
655 s->invert = !s->invert;
656}
657
658void lcd_stop_scroll(void)
659{
660 scrolling_lines=0;
661}
662
663void lcd_scroll_speed(int speed)
664{
665 scroll_ticks = scroll_tick_table[speed];
666}
667
668void lcd_scroll_step(int step)
669{
670 scroll_step = step;
671}
672
673void lcd_scroll_delay(int ms)
674{
675 scroll_delay = ms / (HZ / 10);
676}
677
678void lcd_bidir_scroll(int percent)
679{
680 bidir_limit = percent;
681}
682
683void lcd_puts_scroll(int x, int y, const unsigned char *string)
684{
685 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
686}
687
688void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
689{
690 struct scrollinfo* s;
691 int w, h;
692
693 s = &scroll[y];
694
695 s->start_tick = current_tick + scroll_delay;
696 s->invert = false;
697 if (style & STYLE_INVERT) {
698 s->invert = true;
699 lcd_puts_style(x,y,string,STYLE_INVERT);
700 }
701 else
702 lcd_puts(x,y,string);
703
704 lcd_getstringsize(string, &w, &h);
705
706 if (LCD_WIDTH - x * 8 - xmargin < w) {
707 /* prepare scroll line */
708 char *end;
709
710 memset(s->line, 0, sizeof s->line);
711 strcpy(s->line, (char *)string);
712
713 /* get width */
714 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
715
716 /* scroll bidirectional or forward only depending on the string
717 width */
718 if ( bidir_limit ) {
719 s->bidir = s->width < (LCD_WIDTH - xmargin) *
720 (100 + bidir_limit) / 100;
721 }
722 else
723 s->bidir = false;
724
725 if (!s->bidir) { /* add spaces if scrolling in the round */
726 strcat(s->line, " ");
727 /* get new width incl. spaces */
728 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
729 }
730
731 end = strchr(s->line, '\0');
732 strncpy(end, (char *)string, LCD_WIDTH/2);
733
734 s->len = utf8length((char *)string);
735 s->offset = 0;
736 s->startx = x;
737 s->backward = false;
738 scrolling_lines |= (1<<y);
739 }
740 else
741 /* force a bit switch-off since it doesn't scroll */
742 scrolling_lines &= ~(1<<y);
743}
744
745static void scroll_thread(void)
746{
747 struct font* pf;
748 struct scrollinfo* s;
749 int index;
750 int xpos, ypos;
751 int lastmode;
752
753 /* initialize scroll struct array */
754 scrolling_lines = 0;
755
756 while ( 1 ) {
757 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
758 /* really scroll? */
759 if ( !(scrolling_lines&(1<<index)) )
760 continue;
761
762 s = &scroll[index];
763
764 /* check pause */
765 if (TIME_BEFORE(current_tick, s->start_tick))
766 continue;
767
768 if (s->backward)
769 s->offset -= scroll_step;
770 else
771 s->offset += scroll_step;
772
773 pf = font_get(curfont);
774 xpos = xmargin + s->startx * s->width / s->len;
775 ypos = ymargin + index * pf->height;
776
777 if (s->bidir) { /* scroll bidirectional */
778 if (s->offset <= 0) {
779 /* at beginning of line */
780 s->offset = 0;
781 s->backward = false;
782 s->start_tick = current_tick + scroll_delay * 2;
783 }
784 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
785 /* at end of line */
786 s->offset = s->width - (LCD_WIDTH - xpos);
787 s->backward = true;
788 s->start_tick = current_tick + scroll_delay * 2;
789 }
790 }
791 else {
792 /* scroll forward the whole time */
793 if (s->offset >= s->width)
794 s->offset %= s->width;
795 }
796
797 lastmode = drawmode;
798 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
799 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
800 drawmode = DRMODE_SOLID;
801 lcd_putsxyofs(xpos, ypos, s->offset, (unsigned char *)s->line);
802 if (s->invert)
803 {
804 drawmode = DRMODE_COMPLEMENT;
805 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
806 }
807 drawmode = lastmode;
808 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
809 }
810
811 sleep(scroll_ticks);
812 }
813}