blob: d12ebbc9752f8b3811028e1a3d094b62e4429576 [file] [log] [blame]
Michael Sevakisf64f5892006-11-10 00:02:28 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Linus Nielsen Feltzing
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Michael Sevakisf64f5892006-11-10 00:02:28 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22
23#include "system.h"
24#include "cpu.h"
25#include "kernel.h"
26#include "lcd.h"
27#include "thread.h"
28#include <string.h>
29#include <stdlib.h>
30#include "file.h"
31#include "debug.h"
32#include "font.h"
33#include "rbunicode.h"
34#include "bidi.h"
Michael Sevakis58fc2792007-07-28 08:12:05 +000035#include "scroll_engine.h"
Michael Sevakisf64f5892006-11-10 00:02:28 +000036
37/*** globals ***/
38
Jens Arnold51223e52007-02-20 19:31:34 +000039fb_data lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH] IBSS_ATTR;
Michael Sevakisf64f5892006-11-10 00:02:28 +000040
41const unsigned char lcd_dibits[16] ICONST_ATTR = {
42 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
43 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
44};
45
46static const unsigned char pixmask[4] ICONST_ATTR = {
47 0x03, 0x0C, 0x30, 0xC0
48};
49
Jens Arnold02978732006-11-13 00:45:21 +000050static fb_data* lcd_backdrop = NULL;
51static long lcd_backdrop_offset IDATA_ATTR = 0;
52
Dave Chapman945c8a22008-01-07 20:34:11 +000053static struct viewport default_vp =
54{
55 .x = 0,
56 .y = 0,
57 .width = LCD_WIDTH,
58 .height = LCD_HEIGHT,
59 .font = FONT_SYSFIXED,
60 .drawmode = DRMODE_SOLID,
Dave Chapman945c8a22008-01-07 20:34:11 +000061 .fg_pattern = LCD_DEFAULT_FG,
62 .bg_pattern = LCD_DEFAULT_BG
63};
64
65static struct viewport* current_vp IBSS_ATTR;
66static unsigned fg_pattern IBSS_ATTR;
67static unsigned bg_pattern IBSS_ATTR;
Michael Sevakisf64f5892006-11-10 00:02:28 +000068
Michael Sevakisf64f5892006-11-10 00:02:28 +000069/* LCD init */
70void lcd_init(void)
71{
Dave Chapman945c8a22008-01-07 20:34:11 +000072 /* Initialise the viewport */
73 lcd_set_viewport(NULL);
74
Michael Sevakisf64f5892006-11-10 00:02:28 +000075 lcd_clear_display();
76 /* Call device specific init */
77 lcd_init_device();
Michael Sevakis58fc2792007-07-28 08:12:05 +000078 scroll_init();
Michael Sevakisf64f5892006-11-10 00:02:28 +000079}
80
Dave Chapman945c8a22008-01-07 20:34:11 +000081/*** Viewports ***/
82
83void lcd_set_viewport(struct viewport* vp)
84{
85 if (vp == NULL)
86 current_vp = &default_vp;
87 else
88 current_vp = vp;
89
90 fg_pattern = 0x55 * (~current_vp->fg_pattern & 3);
91 bg_pattern = 0x55 * (~current_vp->bg_pattern & 3);
92}
93
94void lcd_update_viewport(void)
95{
96 lcd_update_rect(current_vp->x, current_vp->y,
97 current_vp->width, current_vp->height);
98}
99
100void lcd_update_viewport_rect(int x, int y, int width, int height)
101{
102 lcd_update_rect(current_vp->x + x, current_vp->y + y, width, height);
103}
104
105
Michael Sevakisf64f5892006-11-10 00:02:28 +0000106/*** parameter handling ***/
107
108void lcd_set_drawmode(int mode)
109{
Dave Chapman945c8a22008-01-07 20:34:11 +0000110 current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
Michael Sevakisf64f5892006-11-10 00:02:28 +0000111}
112
113int lcd_get_drawmode(void)
114{
Dave Chapman945c8a22008-01-07 20:34:11 +0000115 return current_vp->drawmode;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000116}
117
118void lcd_set_foreground(unsigned brightness)
119{
Dave Chapman945c8a22008-01-07 20:34:11 +0000120 current_vp->fg_pattern = brightness;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000121 fg_pattern = 0x55 * (~brightness & 3);
122}
123
124unsigned lcd_get_foreground(void)
125{
Dave Chapman945c8a22008-01-07 20:34:11 +0000126 return current_vp->fg_pattern;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000127}
128
129void lcd_set_background(unsigned brightness)
130{
Dave Chapmanf59a3272008-01-09 02:09:19 +0000131 current_vp->bg_pattern = brightness;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000132 bg_pattern = 0x55 * (~brightness & 3);
133}
134
135unsigned lcd_get_background(void)
136{
Dave Chapman945c8a22008-01-07 20:34:11 +0000137 return current_vp->bg_pattern;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000138}
139
140void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
141{
142 lcd_set_drawmode(mode);
143 lcd_set_foreground(fg_brightness);
144 lcd_set_background(bg_brightness);
145}
146
Dave Chapman945c8a22008-01-07 20:34:11 +0000147int lcd_getwidth(void)
148{
149 return current_vp->width;
150}
151
152int lcd_getheight(void)
153{
154 return current_vp->height;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000155}
156
157void lcd_setfont(int newfont)
158{
Dave Chapman945c8a22008-01-07 20:34:11 +0000159 current_vp->font = newfont;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000160}
161
Dave Chapman3646c312008-01-08 01:22:14 +0000162int lcd_getfont(void)
163{
164 return current_vp->font;
165}
166
Michael Sevakisf64f5892006-11-10 00:02:28 +0000167int lcd_getstringsize(const unsigned char *str, int *w, int *h)
168{
Dave Chapman945c8a22008-01-07 20:34:11 +0000169 return font_getstringsize(str, w, h, current_vp->font);
Michael Sevakisf64f5892006-11-10 00:02:28 +0000170}
171
172/*** low-level drawing functions ***/
173
174static void setpixel(int x, int y)
175{
176 unsigned mask = pixmask[y & 3];
177 fb_data *address = &lcd_framebuffer[y>>2][x];
178 unsigned data = *address;
179
180 *address = data ^ ((data ^ fg_pattern) & mask);
181}
182
183static void clearpixel(int x, int y)
184{
185 unsigned mask = pixmask[y & 3];
186 fb_data *address = &lcd_framebuffer[y>>2][x];
187 unsigned data = *address;
188
189 *address = data ^ ((data ^ bg_pattern) & mask);
190}
191
Jens Arnold02978732006-11-13 00:45:21 +0000192static void clearimgpixel(int x, int y)
193{
194 unsigned mask = pixmask[y & 3];
195 fb_data *address = &lcd_framebuffer[y>>2][x];
196 unsigned data = *address;
197
198 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask);
199}
200
Michael Sevakisf64f5892006-11-10 00:02:28 +0000201static void flippixel(int x, int y)
202{
203 unsigned mask = pixmask[y & 3];
204 fb_data *address = &lcd_framebuffer[y>>2][x];
205
206 *address ^= mask;
207}
208
209static void nopixel(int x, int y)
210{
211 (void)x;
212 (void)y;
213}
214
Jens Arnold02978732006-11-13 00:45:21 +0000215lcd_pixelfunc_type* const lcd_pixelfuncs_bgcolor[8] = {
Michael Sevakisf64f5892006-11-10 00:02:28 +0000216 flippixel, nopixel, setpixel, setpixel,
217 nopixel, clearpixel, nopixel, clearpixel
218};
219
Jens Arnold02978732006-11-13 00:45:21 +0000220lcd_pixelfunc_type* const lcd_pixelfuncs_backdrop[8] = {
221 flippixel, nopixel, setpixel, setpixel,
222 nopixel, clearimgpixel, nopixel, clearimgpixel
223};
224
225
226lcd_pixelfunc_type* const * lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
227
Michael Sevakisf64f5892006-11-10 00:02:28 +0000228/* 'mask' and 'bits' contain 2 bits per pixel */
Jens Arnold00ac8092008-04-12 07:53:33 +0000229static void ICODE_ATTR flipblock(fb_data *address, unsigned mask,
230 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000231{
232 *address ^= bits & mask;
233}
234
Jens Arnold00ac8092008-04-12 07:53:33 +0000235static void ICODE_ATTR bgblock(fb_data *address, unsigned mask,
236 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000237{
238 unsigned data = *address;
239
240 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
241}
242
Jens Arnold00ac8092008-04-12 07:53:33 +0000243static void ICODE_ATTR bgimgblock(fb_data *address, unsigned mask,
244 unsigned bits)
Jens Arnold02978732006-11-13 00:45:21 +0000245{
246 unsigned data = *address;
247
248 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & ~bits);
249}
250
Jens Arnold00ac8092008-04-12 07:53:33 +0000251static void ICODE_ATTR fgblock(fb_data *address, unsigned mask,
252 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000253{
254 unsigned data = *address;
255
256 *address = data ^ ((data ^ fg_pattern) & mask & bits);
257}
258
Jens Arnold00ac8092008-04-12 07:53:33 +0000259static void ICODE_ATTR solidblock(fb_data *address, unsigned mask,
260 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000261{
262 unsigned data = *address;
263 unsigned bgp = bg_pattern;
264
265 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
266 *address = data ^ ((data ^ bits) & mask);
267}
268
Jens Arnold00ac8092008-04-12 07:53:33 +0000269static void ICODE_ATTR solidimgblock(fb_data *address, unsigned mask,
270 unsigned bits)
Jens Arnold02978732006-11-13 00:45:21 +0000271{
272 unsigned data = *address;
273 unsigned bgp = *(address + lcd_backdrop_offset);
274
275 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
276 *address = data ^ ((data ^ bits) & mask);
277}
278
Jens Arnold00ac8092008-04-12 07:53:33 +0000279static void ICODE_ATTR flipinvblock(fb_data *address, unsigned mask,
280 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000281{
282 *address ^= ~bits & mask;
283}
284
Jens Arnold00ac8092008-04-12 07:53:33 +0000285static void ICODE_ATTR bginvblock(fb_data *address, unsigned mask,
286 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000287{
288 unsigned data = *address;
289
290 *address = data ^ ((data ^ bg_pattern) & mask & bits);
291}
292
Jens Arnold00ac8092008-04-12 07:53:33 +0000293static void ICODE_ATTR bgimginvblock(fb_data *address, unsigned mask,
294 unsigned bits)
Jens Arnold02978732006-11-13 00:45:21 +0000295{
296 unsigned data = *address;
297
298 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & bits);
299}
300
Jens Arnold00ac8092008-04-12 07:53:33 +0000301static void ICODE_ATTR fginvblock(fb_data *address, unsigned mask,
302 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000303{
304 unsigned data = *address;
305
306 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
307}
308
Jens Arnold00ac8092008-04-12 07:53:33 +0000309static void ICODE_ATTR solidinvblock(fb_data *address, unsigned mask,
310 unsigned bits)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000311{
312 unsigned data = *address;
313 unsigned fgp = fg_pattern;
314
315 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
316 *address = data ^ ((data ^ bits) & mask);
317}
318
Jens Arnold00ac8092008-04-12 07:53:33 +0000319static void ICODE_ATTR solidimginvblock(fb_data *address, unsigned mask,
320 unsigned bits)
Jens Arnold02978732006-11-13 00:45:21 +0000321{
322 unsigned data = *address;
323 unsigned fgp = fg_pattern;
324
325 bits = fgp ^ ((fgp ^ *(address + lcd_backdrop_offset)) & bits);
326 *address = data ^ ((data ^ bits) & mask);
327}
328
329lcd_blockfunc_type* const lcd_blockfuncs_bgcolor[8] = {
Michael Sevakisf64f5892006-11-10 00:02:28 +0000330 flipblock, bgblock, fgblock, solidblock,
331 flipinvblock, bginvblock, fginvblock, solidinvblock
332};
333
Jens Arnold02978732006-11-13 00:45:21 +0000334lcd_blockfunc_type* const lcd_blockfuncs_backdrop[8] = {
335 flipblock, bgimgblock, fgblock, solidimgblock,
336 flipinvblock, bgimginvblock, fginvblock, solidimginvblock
337};
338
339lcd_blockfunc_type* const * lcd_blockfuncs = lcd_blockfuncs_bgcolor;
340
341
342void lcd_set_backdrop(fb_data* backdrop)
343{
344 lcd_backdrop = backdrop;
345 if (backdrop)
346 {
347 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
348 lcd_pixelfuncs = lcd_pixelfuncs_backdrop;
349 lcd_blockfuncs = lcd_blockfuncs_backdrop;
350 }
351 else
352 {
353 lcd_backdrop_offset = 0;
354 lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
355 lcd_blockfuncs = lcd_blockfuncs_bgcolor;
356 }
357}
358
359fb_data* lcd_get_backdrop(void)
360{
361 return lcd_backdrop;
362}
363
364
Michael Sevakisf64f5892006-11-10 00:02:28 +0000365static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
366{
367 unsigned data = *address;
368
369 bits ^= data;
370 *address = data ^ (bits & mask);
371}
372
373/*** drawing functions ***/
374
375/* Clear the whole display */
376void lcd_clear_display(void)
377{
Dave Chapman945c8a22008-01-07 20:34:11 +0000378 if (current_vp->drawmode & DRMODE_INVERSEVID)
Jens Arnold02978732006-11-13 00:45:21 +0000379 {
380 memset(lcd_framebuffer, fg_pattern, sizeof lcd_framebuffer);
381 }
382 else
383 {
384 if (lcd_backdrop)
385 memcpy(lcd_framebuffer, lcd_backdrop, sizeof lcd_framebuffer);
386 else
387 memset(lcd_framebuffer, bg_pattern, sizeof lcd_framebuffer);
388 }
Michael Sevakis58fc2792007-07-28 08:12:05 +0000389
390 lcd_scroll_info.lines = 0;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000391}
392
Dave Chapman945c8a22008-01-07 20:34:11 +0000393/* Clear the current viewport */
394void lcd_clear_viewport(void)
395{
396 int lastmode;
397
398 if (current_vp == &default_vp)
399 {
400 lcd_clear_display();
401 }
402 else
403 {
404 lastmode = current_vp->drawmode;
405
406 /* Invert the INVERSEVID bit and set basic mode to SOLID */
407 current_vp->drawmode = (~lastmode & DRMODE_INVERSEVID) |
408 DRMODE_SOLID;
409
410 lcd_fillrect(0, 0, current_vp->width, current_vp->height);
411
412 current_vp->drawmode = lastmode;
413
414 lcd_scroll_stop(current_vp);
415 }
416}
417
Michael Sevakisf64f5892006-11-10 00:02:28 +0000418/* Set a single pixel */
419void lcd_drawpixel(int x, int y)
420{
Dave Chapman945c8a22008-01-07 20:34:11 +0000421 if (((unsigned)x < (unsigned)current_vp->width) &&
422 ((unsigned)y < (unsigned)current_vp->height))
423 lcd_pixelfuncs[current_vp->drawmode](current_vp->x + x, current_vp->y + y);
Michael Sevakisf64f5892006-11-10 00:02:28 +0000424}
425
426/* Draw a line */
427void lcd_drawline(int x1, int y1, int x2, int y2)
428{
429 int numpixels;
430 int i;
431 int deltax, deltay;
432 int d, dinc1, dinc2;
433 int x, xinc1, xinc2;
434 int y, yinc1, yinc2;
Dave Chapman945c8a22008-01-07 20:34:11 +0000435 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[current_vp->drawmode];
Michael Sevakisf64f5892006-11-10 00:02:28 +0000436
437 deltax = abs(x2 - x1);
Jens Arnold00ac8092008-04-12 07:53:33 +0000438 if (deltax == 0)
439 {
440 DEBUGF("lcd_drawline() called for vertical line - optimisation.\n");
441 lcd_vline(x1, y1, y2);
442 return;
443 }
Michael Sevakisf64f5892006-11-10 00:02:28 +0000444 deltay = abs(y2 - y1);
Jens Arnold00ac8092008-04-12 07:53:33 +0000445 if (deltay == 0)
446 {
447 DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n");
448 lcd_hline(x1, x2, y1);
449 return;
450 }
Michael Sevakisf64f5892006-11-10 00:02:28 +0000451 xinc2 = 1;
452 yinc2 = 1;
453
454 if (deltax >= deltay)
455 {
456 numpixels = deltax;
457 d = 2 * deltay - deltax;
458 dinc1 = deltay * 2;
459 dinc2 = (deltay - deltax) * 2;
460 xinc1 = 1;
461 yinc1 = 0;
462 }
463 else
464 {
465 numpixels = deltay;
466 d = 2 * deltax - deltay;
467 dinc1 = deltax * 2;
468 dinc2 = (deltax - deltay) * 2;
469 xinc1 = 0;
470 yinc1 = 1;
471 }
472 numpixels++; /* include endpoints */
473
474 if (x1 > x2)
475 {
476 xinc1 = -xinc1;
477 xinc2 = -xinc2;
478 }
479
480 if (y1 > y2)
481 {
482 yinc1 = -yinc1;
483 yinc2 = -yinc2;
484 }
485
486 x = x1;
487 y = y1;
488
489 for (i = 0; i < numpixels; i++)
490 {
Dave Chapman945c8a22008-01-07 20:34:11 +0000491 if (((unsigned)x < (unsigned)current_vp->width) &&
492 ((unsigned)y < (unsigned)current_vp->height))
493 pfunc(current_vp->x + x, current_vp->y + y);
Michael Sevakisf64f5892006-11-10 00:02:28 +0000494
495 if (d < 0)
496 {
497 d += dinc1;
498 x += xinc1;
499 y += yinc1;
500 }
501 else
502 {
503 d += dinc2;
504 x += xinc2;
505 y += yinc2;
506 }
507 }
508}
509
510/* Draw a horizontal line (optimised) */
511void lcd_hline(int x1, int x2, int y)
512{
513 int x;
Dave Chapman945c8a22008-01-07 20:34:11 +0000514 int width;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000515 fb_data *dst, *dst_end;
516 unsigned mask;
517 lcd_blockfunc_type *bfunc;
518
519 /* direction flip */
520 if (x2 < x1)
521 {
522 x = x1;
523 x1 = x2;
524 x2 = x;
525 }
526
527 /* nothing to draw? */
Dave Chapman945c8a22008-01-07 20:34:11 +0000528 if (((unsigned)y >= (unsigned)current_vp->height) || (x1 >= current_vp->width)
529 || (x2 < 0))
Michael Sevakisf64f5892006-11-10 00:02:28 +0000530 return;
531
532 /* clipping */
533 if (x1 < 0)
534 x1 = 0;
Dave Chapman945c8a22008-01-07 20:34:11 +0000535 if (x2 >= current_vp->width)
536 x2 = current_vp->width-1;
537
538 width = x2 - x1 + 1;
539
540 /* adjust x1 and y to viewport */
541 x1 += current_vp->x;
542 y += current_vp->y;
543
544 bfunc = lcd_blockfuncs[current_vp->drawmode];
Michael Sevakisf64f5892006-11-10 00:02:28 +0000545 dst = &lcd_framebuffer[y>>2][x1];
546 mask = pixmask[y & 3];
547
Dave Chapman945c8a22008-01-07 20:34:11 +0000548 dst_end = dst + width;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000549 do
550 bfunc(dst++, mask, 0xFFu);
Dave Chapman945c8a22008-01-07 20:34:11 +0000551 while (dst < dst_end);
Michael Sevakisf64f5892006-11-10 00:02:28 +0000552}
553
554/* Draw a vertical line (optimised) */
555void lcd_vline(int x, int y1, int y2)
556{
557 int ny;
558 fb_data *dst;
559 unsigned mask, mask_bottom;
560 lcd_blockfunc_type *bfunc;
561
562 /* direction flip */
563 if (y2 < y1)
564 {
565 ny = y1;
566 y1 = y2;
567 y2 = ny;
568 }
569
570 /* nothing to draw? */
Dave Chapman945c8a22008-01-07 20:34:11 +0000571 if (((unsigned)x >= (unsigned)current_vp->width) || (y1 >= current_vp->height)
572 || (y2 < 0))
Michael Sevakisf64f5892006-11-10 00:02:28 +0000573 return;
574
575 /* clipping */
576 if (y1 < 0)
577 y1 = 0;
Dave Chapman945c8a22008-01-07 20:34:11 +0000578 if (y2 >= current_vp->height)
579 y2 = current_vp->height-1;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000580
Dave Chapman945c8a22008-01-07 20:34:11 +0000581 /* adjust for viewport */
582 y1 += current_vp->y;
583 y2 += current_vp->y;
584 x += current_vp->x;
585
586 bfunc = lcd_blockfuncs[current_vp->drawmode];
Michael Sevakisf64f5892006-11-10 00:02:28 +0000587 dst = &lcd_framebuffer[y1>>2][x];
588 ny = y2 - (y1 & ~3);
589 mask = 0xFFu << (2 * (y1 & 3));
590 mask_bottom = 0xFFu >> (2 * (~ny & 3));
591
592 for (; ny >= 4; ny -= 4)
593 {
594 bfunc(dst, mask, 0xFFu);
595 dst += LCD_WIDTH;
596 mask = 0xFFu;
597 }
598 mask &= mask_bottom;
599 bfunc(dst, mask, 0xFFu);
600}
601
602/* Draw a rectangular box */
603void lcd_drawrect(int x, int y, int width, int height)
604{
605 if ((width <= 0) || (height <= 0))
606 return;
607
608 int x2 = x + width - 1;
609 int y2 = y + height - 1;
610
611 lcd_vline(x, y, y2);
612 lcd_vline(x2, y, y2);
613 lcd_hline(x, x2, y);
614 lcd_hline(x, x2, y2);
615}
616
617/* Fill a rectangular area */
618void lcd_fillrect(int x, int y, int width, int height)
619{
620 int ny;
621 fb_data *dst, *dst_end;
622 unsigned mask, mask_bottom;
623 unsigned bits = 0;
624 lcd_blockfunc_type *bfunc;
625 bool fillopt = false;
626
627 /* nothing to draw? */
Dave Chapman945c8a22008-01-07 20:34:11 +0000628 if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
629 || (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
Michael Sevakisf64f5892006-11-10 00:02:28 +0000630 return;
631
632 /* clipping */
633 if (x < 0)
634 {
635 width += x;
636 x = 0;
637 }
638 if (y < 0)
639 {
640 height += y;
641 y = 0;
642 }
Dave Chapman945c8a22008-01-07 20:34:11 +0000643 if (x + width > current_vp->width)
644 width = current_vp->width - x;
645 if (y + height > current_vp->height)
646 height = current_vp->height - y;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000647
Dave Chapman945c8a22008-01-07 20:34:11 +0000648 /* adjust for viewport */
649 x += current_vp->x;
650 y += current_vp->y;
651
652 if (current_vp->drawmode & DRMODE_INVERSEVID)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000653 {
Dave Chapman945c8a22008-01-07 20:34:11 +0000654 if ((current_vp->drawmode & DRMODE_BG) && !lcd_backdrop)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000655 {
656 fillopt = true;
657 bits = bg_pattern;
658 }
659 }
660 else
661 {
Dave Chapman945c8a22008-01-07 20:34:11 +0000662 if (current_vp->drawmode & DRMODE_FG)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000663 {
664 fillopt = true;
665 bits = fg_pattern;
666 }
667 }
Dave Chapman945c8a22008-01-07 20:34:11 +0000668 bfunc = lcd_blockfuncs[current_vp->drawmode];
Michael Sevakisf64f5892006-11-10 00:02:28 +0000669 dst = &lcd_framebuffer[y>>2][x];
670 ny = height - 1 + (y & 3);
671 mask = 0xFFu << (2 * (y & 3));
672 mask_bottom = 0xFFu >> (2 * (~ny & 3));
673
674 for (; ny >= 4; ny -= 4)
675 {
676 if (fillopt && (mask == 0xFFu))
677 memset(dst, bits, width);
678 else
679 {
680 fb_data *dst_row = dst;
681
682 dst_end = dst_row + width;
683 do
684 bfunc(dst_row++, mask, 0xFFu);
685 while (dst_row < dst_end);
686 }
687
688 dst += LCD_WIDTH;
689 mask = 0xFFu;
690 }
691 mask &= mask_bottom;
692
693 if (fillopt && (mask == 0xFFu))
694 memset(dst, bits, width);
695 else
696 {
697 dst_end = dst + width;
698 do
699 bfunc(dst++, mask, 0xFFu);
700 while (dst < dst_end);
701 }
702}
703
704/* About Rockbox' internal monochrome bitmap format:
705 *
706 * A bitmap contains one bit for every pixel that defines if that pixel is
707 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
708 * at top.
709 * The bytes are stored in row-major order, with byte 0 being top left,
710 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
711 * 0..7, the second row defines pixel row 8..15 etc.
712 *
713 * This is similar to the internal lcd hw format. */
714
715/* Draw a partial monochrome bitmap */
Jens Arnold00ac8092008-04-12 07:53:33 +0000716void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
717 int src_y, int stride, int x, int y,
718 int width, int height)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000719{
720 int shift, ny;
721 fb_data *dst, *dst_end;
722 unsigned mask, mask_bottom;
723 lcd_blockfunc_type *bfunc;
724
725 /* nothing to draw? */
Dave Chapman945c8a22008-01-07 20:34:11 +0000726 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
727 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
Michael Sevakisf64f5892006-11-10 00:02:28 +0000728 return;
729
730 /* clipping */
731 if (x < 0)
732 {
733 width += x;
734 src_x -= x;
735 x = 0;
736 }
737 if (y < 0)
738 {
739 height += y;
740 src_y -= y;
741 y = 0;
742 }
Dave Chapman945c8a22008-01-07 20:34:11 +0000743 if (x + width > current_vp->width)
744 width = current_vp->width - x;
745 if (y + height > current_vp->height)
746 height = current_vp->height - y;
747
748 /* adjust for viewport */
749 x += current_vp->x;
750 y += current_vp->y;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000751
752 src += stride * (src_y >> 3) + src_x; /* move starting point */
753 src_y &= 7;
754 y -= src_y;
755 dst = &lcd_framebuffer[y>>2][x];
756 shift = y & 3;
757 ny = height - 1 + shift + src_y;
758
Dave Chapman945c8a22008-01-07 20:34:11 +0000759 bfunc = lcd_blockfuncs[current_vp->drawmode];
Michael Sevakisf64f5892006-11-10 00:02:28 +0000760 mask = 0xFFu << (shift + src_y);
761 mask_bottom = 0xFFu >> (~ny & 7);
762
763 if (shift == 0)
764 {
765 unsigned dmask1, dmask2, data;
766
767 for (; ny >= 8; ny -= 8)
768 {
769 const unsigned char *src_row = src;
770 fb_data *dst_row = dst + LCD_WIDTH;
771
772 dmask1 = lcd_dibits[mask&0x0F];
773 dmask2 = lcd_dibits[(mask>>4)&0x0F];
774 dst_end = dst_row + width;
775
776 if (dmask1 != 0)
777 {
778 do
779 {
780 data = *src_row++;
781 bfunc(dst_row - LCD_WIDTH, dmask1, lcd_dibits[data&0x0F]);
782 bfunc(dst_row++, dmask2, lcd_dibits[(data>>4)&0x0F]);
783 }
784 while (dst_row < dst_end);
785 }
786 else
787 {
788 do
789 bfunc(dst_row++, dmask2, lcd_dibits[((*src_row++)>>4)&0x0F]);
790 while (dst_row < dst_end);
791 }
792 src += stride;
793 dst += 2*LCD_WIDTH;
794 mask = 0xFFu;
795 }
796 mask &= mask_bottom;
797 dmask1 = lcd_dibits[mask&0x0F];
798 dmask2 = lcd_dibits[(mask>>4)&0x0F];
799 dst_end = dst + width;
800
801 if (dmask1 != 0)
802 {
803 if (dmask2 != 0)
804 {
805 do
806 {
807 data = *src++;
808 bfunc(dst, dmask1, lcd_dibits[data&0x0F]);
809 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[(data>>4)&0x0F]);
810 }
811 while (dst < dst_end);
812 }
813 else
814 {
815 do
816 bfunc(dst++, dmask1, lcd_dibits[(*src++)&0x0F]);
817 while (dst < dst_end);
818 }
819 }
820 else
821 {
822 do
823 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[((*src++)>>4)&0x0F]);
824 while (dst < dst_end);
825 }
826 }
827 else
828 {
829 dst_end = dst + width;
830 do
831 {
832 const unsigned char *src_col = src++;
833 fb_data *dst_col = dst++;
834 unsigned mask_col = mask;
835 unsigned data = 0;
836
837 for (y = ny; y >= 8; y -= 8)
838 {
839 data |= *src_col << shift;
840
841 if (mask_col & 0xFFu)
842 {
843 if (mask_col & 0x0F)
844 bfunc(dst_col, lcd_dibits[mask_col&0x0F], lcd_dibits[data&0x0F]);
845 bfunc(dst_col + LCD_WIDTH, lcd_dibits[(mask_col>>4)&0x0F],
846 lcd_dibits[(data>>4)&0x0F]);
847 mask_col = 0xFFu;
848 }
849 else
850 mask_col >>= 8;
851
852 src_col += stride;
853 dst_col += 2*LCD_WIDTH;
854 data >>= 8;
855 }
856 data |= *src_col << shift;
857 mask_bottom &= mask_col;
858 if (mask_bottom & 0x0F)
859 bfunc(dst_col, lcd_dibits[mask_bottom&0x0F], lcd_dibits[data&0x0F]);
860 if (mask_bottom & 0xF0)
861 bfunc(dst_col + LCD_WIDTH, lcd_dibits[(mask_bottom&0xF0)>>4],
862 lcd_dibits[(data>>4)&0x0F]);
863 }
864 while (dst < dst_end);
865 }
866}
867
868/* Draw a full monochrome bitmap */
869void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
870{
871 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
872}
873
874/* About Rockbox' internal native bitmap format:
875 *
876 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
877 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
878 * at top.
879 * The bytes are stored in row-major order, with byte 0 being top left,
880 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
881 * 0..3, the second row defines pixel row 4..7 etc.
882 *
883 * This is the same as the internal lcd hw format. */
884
885/* Draw a partial native bitmap */
Jens Arnold00ac8092008-04-12 07:53:33 +0000886void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
887 int stride, int x, int y, int width,
888 int height)
Michael Sevakisf64f5892006-11-10 00:02:28 +0000889{
890 int shift, ny;
891 fb_data *dst, *dst_end;
892 unsigned mask, mask_bottom;
893
894 /* nothing to draw? */
Dave Chapman945c8a22008-01-07 20:34:11 +0000895 if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
896 || (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
Michael Sevakisf64f5892006-11-10 00:02:28 +0000897 return;
898
899 /* clipping */
900 if (x < 0)
901 {
902 width += x;
903 src_x -= x;
904 x = 0;
905 }
906 if (y < 0)
907 {
908 height += y;
909 src_y -= y;
910 y = 0;
911 }
Dave Chapman945c8a22008-01-07 20:34:11 +0000912 if (x + width > current_vp->width)
913 width = current_vp->width - x;
914 if (y + height > current_vp->height)
915 height = current_vp->height - y;
916
917 /* adjust for viewport */
918 x += current_vp->x;
919 y += current_vp->y;
Michael Sevakisf64f5892006-11-10 00:02:28 +0000920
921 src += stride * (src_y >> 2) + src_x; /* move starting point */
922 src_y &= 3;
923 y -= src_y;
924 dst = &lcd_framebuffer[y>>2][x];
925 shift = y & 3;
926 ny = height - 1 + shift + src_y;
927
928 mask = 0xFFu << (2 * (shift + src_y));
929 mask_bottom = 0xFFu >> (2 * (~ny & 3));
930
931 if (shift == 0)
932 {
933 for (; ny >= 4; ny -= 4)
934 {
935 if (mask == 0xFFu)
936 memcpy(dst, src, width);
937 else
938 {
939 const fb_data *src_row = src;
940 fb_data *dst_row = dst;
941
942 dst_end = dst_row + width;
943 do
944 setblock(dst_row++, mask, *src_row++);
945 while (dst_row < dst_end);
946 }
947 src += stride;
948 dst += LCD_WIDTH;
949 mask = 0xFFu;
950 }
951 mask &= mask_bottom;
952
953 if (mask == 0xFFu)
954 memcpy(dst, src, width);
955 else
956 {
957 dst_end = dst + width;
958 do
959 setblock(dst++, mask, *src++);
960 while (dst < dst_end);
961 }
962 }
963 else
964 {
965 shift *= 2;
966 dst_end = dst + width;
967 do
968 {
969 const fb_data *src_col = src++;
970 fb_data *dst_col = dst++;
971 unsigned mask_col = mask;
972 unsigned data = 0;
973
974 for (y = ny; y >= 4; y -= 4)
975 {
976 data |= *src_col << shift;
977
978 if (mask_col & 0xFFu)
979 {
980 setblock(dst_col, mask_col, data);
981 mask_col = 0xFFu;
982 }
983 else
984 mask_col >>= 8;
985
986 src_col += stride;
987 dst_col += LCD_WIDTH;
988 data >>= 8;
989 }
990 data |= *src_col << shift;
991 setblock(dst_col, mask_col & mask_bottom, data);
992 }
993 while (dst < dst_end);
994 }
995}
996
997/* Draw a full native bitmap */
998void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
999{
1000 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
1001}
1002
1003/* put a string at a given pixel position, skipping first ofs pixel columns */
1004static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
1005{
1006 unsigned short ch;
1007 unsigned short *ucs;
Dave Chapman945c8a22008-01-07 20:34:11 +00001008 struct font* pf = font_get(current_vp->font);
Michael Sevakisf64f5892006-11-10 00:02:28 +00001009
1010 ucs = bidi_l2v(str, 1);
1011
Dave Chapman945c8a22008-01-07 20:34:11 +00001012 while ((ch = *ucs++) != 0 && x < current_vp->width)
Michael Sevakisf64f5892006-11-10 00:02:28 +00001013 {
1014 int width;
1015 const unsigned char *bits;
1016
1017 /* get proportional width and glyph bits */
1018 width = font_get_width(pf,ch);
1019
1020 if (ofs > width)
1021 {
1022 ofs -= width;
1023 continue;
1024 }
1025
1026 bits = font_get_bits(pf, ch);
1027
1028 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
1029 pf->height);
1030
1031 x += width - ofs;
1032 ofs = 0;
1033 }
1034}
1035
1036/* put a string at a given pixel position */
1037void lcd_putsxy(int x, int y, const unsigned char *str)
1038{
1039 lcd_putsxyofs(x, y, 0, str);
1040}
1041
1042/*** line oriented text output ***/
1043
1044/* put a string at a given char position */
1045void lcd_puts(int x, int y, const unsigned char *str)
1046{
1047 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
1048}
1049
1050void lcd_puts_style(int x, int y, const unsigned char *str, int style)
1051{
1052 lcd_puts_style_offset(x, y, str, style, 0);
1053}
1054
1055void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
1056{
1057 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
1058}
1059
1060/* put a string at a given char position, style, and pixel position,
1061 * skipping first offset pixel columns */
1062void lcd_puts_style_offset(int x, int y, const unsigned char *str,
1063 int style, int offset)
1064{
1065 int xpos,ypos,w,h,xrect;
Dave Chapman945c8a22008-01-07 20:34:11 +00001066 int lastmode = current_vp->drawmode;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001067
1068 /* make sure scrolling is turned off on the line we are updating */
Dave Chapman945c8a22008-01-07 20:34:11 +00001069 lcd_scroll_stop_line(current_vp, y);
Michael Sevakisf64f5892006-11-10 00:02:28 +00001070
1071 if(!str || !str[0])
1072 return;
1073
1074 lcd_getstringsize(str, &w, &h);
Jonathan Gordonbdbdb972008-06-23 13:20:35 +00001075 xpos = x*w / utf8length((char *)str);
1076 ypos = y*h;
Dave Chapman945c8a22008-01-07 20:34:11 +00001077 current_vp->drawmode = (style & STYLE_INVERT) ?
1078 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001079 lcd_putsxyofs(xpos, ypos, offset, str);
Dave Chapman945c8a22008-01-07 20:34:11 +00001080 current_vp->drawmode ^= DRMODE_INVERSEVID;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001081 xrect = xpos + MAX(w - offset, 0);
Dave Chapman945c8a22008-01-07 20:34:11 +00001082 lcd_fillrect(xrect, ypos, current_vp->width - xrect, h);
1083 current_vp->drawmode = lastmode;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001084}
1085
1086/*** scrolling ***/
1087
Michael Sevakisf64f5892006-11-10 00:02:28 +00001088void lcd_puts_scroll(int x, int y, const unsigned char *string)
1089{
1090 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
1091}
1092
1093void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
1094{
1095 lcd_puts_scroll_style_offset(x, y, string, style, 0);
1096}
1097
1098void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
1099{
1100 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
1101}
1102
1103void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
1104 int style, int offset)
1105{
1106 struct scrollinfo* s;
1107 int w, h;
1108
Dave Chapman945c8a22008-01-07 20:34:11 +00001109 if ((unsigned)y >= (unsigned)current_vp->height)
1110 return;
Robert Kukla6dbcceb2007-04-01 17:28:46 +00001111
Dave Chapman945c8a22008-01-07 20:34:11 +00001112 /* remove any previously scrolling line at the same location */
1113 lcd_scroll_stop_line(current_vp, y);
1114
1115 if (lcd_scroll_info.lines >= LCD_SCROLLABLE_LINES) return;
1116
1117 s = &lcd_scroll_info.scroll[lcd_scroll_info.lines];
Michael Sevakisf64f5892006-11-10 00:02:28 +00001118
Michael Sevakis58fc2792007-07-28 08:12:05 +00001119 s->start_tick = current_tick + lcd_scroll_info.delay;
Antoine Cellerier2445f662007-09-30 21:40:22 +00001120 s->style = style;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001121 if (style & STYLE_INVERT) {
Michael Sevakisf64f5892006-11-10 00:02:28 +00001122 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
1123 }
1124 else
1125 lcd_puts_offset(x,y,string,offset);
1126
1127 lcd_getstringsize(string, &w, &h);
1128
Jonathan Gordonbdbdb972008-06-23 13:20:35 +00001129 if (current_vp->width - x * 8< w) {
Michael Sevakisf64f5892006-11-10 00:02:28 +00001130 /* prepare scroll line */
1131 char *end;
1132
1133 memset(s->line, 0, sizeof s->line);
1134 strcpy(s->line, (char *)string);
1135
1136 /* get width */
1137 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1138
1139 /* scroll bidirectional or forward only depending on the string
1140 width */
Michael Sevakis58fc2792007-07-28 08:12:05 +00001141 if ( lcd_scroll_info.bidir_limit ) {
Jonathan Gordonbdbdb972008-06-23 13:20:35 +00001142 s->bidir = s->width < (current_vp->width) *
Michael Sevakis58fc2792007-07-28 08:12:05 +00001143 (100 + lcd_scroll_info.bidir_limit) / 100;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001144 }
1145 else
1146 s->bidir = false;
1147
1148 if (!s->bidir) { /* add spaces if scrolling in the round */
1149 strcat(s->line, " ");
1150 /* get new width incl. spaces */
1151 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1152 }
1153
1154 end = strchr(s->line, '\0');
Dave Chapman945c8a22008-01-07 20:34:11 +00001155 strncpy(end, (char *)string, current_vp->width/2);
Michael Sevakisf64f5892006-11-10 00:02:28 +00001156
Dave Chapman945c8a22008-01-07 20:34:11 +00001157 s->vp = current_vp;
1158 s->y = y;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001159 s->len = utf8length((char *)string);
1160 s->offset = offset;
Jonathan Gordonbdbdb972008-06-23 13:20:35 +00001161 s->startx = x * s->width / s->len;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001162 s->backward = false;
Dave Chapman945c8a22008-01-07 20:34:11 +00001163
1164 lcd_scroll_info.lines++;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001165 }
Michael Sevakisf64f5892006-11-10 00:02:28 +00001166}
1167
Michael Sevakis58fc2792007-07-28 08:12:05 +00001168void lcd_scroll_fn(void)
Michael Sevakisf64f5892006-11-10 00:02:28 +00001169{
1170 struct font* pf;
1171 struct scrollinfo* s;
1172 int index;
1173 int xpos, ypos;
1174 int lastmode;
Dave Chapman945c8a22008-01-07 20:34:11 +00001175 struct viewport* old_vp = current_vp;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001176
Dave Chapman945c8a22008-01-07 20:34:11 +00001177 for ( index = 0; index < lcd_scroll_info.lines; index++ ) {
Michael Sevakis58fc2792007-07-28 08:12:05 +00001178 s = &lcd_scroll_info.scroll[index];
Michael Sevakisf64f5892006-11-10 00:02:28 +00001179
Michael Sevakis58fc2792007-07-28 08:12:05 +00001180 /* check pause */
1181 if (TIME_BEFORE(current_tick, s->start_tick))
1182 continue;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001183
Dave Chapman945c8a22008-01-07 20:34:11 +00001184 lcd_set_viewport(s->vp);
1185
Michael Sevakis58fc2792007-07-28 08:12:05 +00001186 if (s->backward)
1187 s->offset -= lcd_scroll_info.step;
1188 else
1189 s->offset += lcd_scroll_info.step;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001190
Dave Chapman945c8a22008-01-07 20:34:11 +00001191 pf = font_get(current_vp->font);
Michael Sevakis58fc2792007-07-28 08:12:05 +00001192 xpos = s->startx;
Jonathan Gordonbdbdb972008-06-23 13:20:35 +00001193 ypos = s->y * pf->height;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001194
Michael Sevakis58fc2792007-07-28 08:12:05 +00001195 if (s->bidir) { /* scroll bidirectional */
1196 if (s->offset <= 0) {
1197 /* at beginning of line */
1198 s->offset = 0;
1199 s->backward = false;
1200 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001201 }
Dave Chapman945c8a22008-01-07 20:34:11 +00001202 if (s->offset >= s->width - (current_vp->width - xpos)) {
Michael Sevakis58fc2792007-07-28 08:12:05 +00001203 /* at end of line */
Dave Chapman945c8a22008-01-07 20:34:11 +00001204 s->offset = s->width - (current_vp->width - xpos);
Michael Sevakis58fc2792007-07-28 08:12:05 +00001205 s->backward = true;
1206 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001207 }
Michael Sevakis58fc2792007-07-28 08:12:05 +00001208 }
1209 else {
1210 /* scroll forward the whole time */
1211 if (s->offset >= s->width)
1212 s->offset %= s->width;
Michael Sevakisf64f5892006-11-10 00:02:28 +00001213 }
1214
Dave Chapman945c8a22008-01-07 20:34:11 +00001215 lastmode = current_vp->drawmode;
1216 current_vp->drawmode = (s->style&STYLE_INVERT) ?
1217 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
Michael Sevakis58fc2792007-07-28 08:12:05 +00001218 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
Dave Chapman945c8a22008-01-07 20:34:11 +00001219 current_vp->drawmode = lastmode;
1220 lcd_update_viewport_rect(xpos, ypos,
1221 current_vp->width - xpos, pf->height);
Michael Sevakisf64f5892006-11-10 00:02:28 +00001222 }
Dave Chapman945c8a22008-01-07 20:34:11 +00001223
1224 lcd_set_viewport(old_vp);
Michael Sevakisf64f5892006-11-10 00:02:28 +00001225}