summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2006-11-10 00:02:28 +0000
committerMichael Sevakis <jethead71@rockbox.org>2006-11-10 00:02:28 +0000
commitf64f589d2efeb2749314bb9f91523b4555ded2d5 (patch)
tree00144df027cefae876dff9c6e9f64421befa26c4
parent6170e8837b4cc8a6d33b1b3b095330b9984eaa9c (diff)
downloadrockbox-f64f589d2efeb2749314bb9f91523b4555ded2d5.tar.gz
rockbox-f64f589d2efeb2749314bb9f91523b4555ded2d5.zip
H100/120: Split lcd driver into pixel format and device specific parts. Adjustment of contrast range to just a little less than white to a little more than black. Really only had to reset minimum.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11487 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/SOURCES6
-rw-r--r--firmware/drivers/lcd-2bit-vert.c1093
-rw-r--r--firmware/export/config-h100.h8
-rw-r--r--firmware/export/config-h120.h8
-rw-r--r--firmware/target/coldfire/iriver/h100/lcd-h100.c1098
5 files changed, 1117 insertions, 1096 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 32de5a0c4e..49e5ace312 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -74,7 +74,9 @@ drivers/lcd-recorder.c
74#elif LCD_DEPTH == 2 74#elif LCD_DEPTH == 2
75#if LCD_PIXELFORMAT == HORIZONTAL_PACKING 75#if LCD_PIXELFORMAT == HORIZONTAL_PACKING
76drivers/lcd-2bit-horz.c 76drivers/lcd-2bit-horz.c
77#endif /* LCD_PIXELFORMAT == HORIZONTAL_PACKING */ 77#elif LCD_PIXELFORMAT == VERTICAL_PACKING
78drivers/lcd-2bit-vert.c
79#endif /* LCD_PIXELFORMAT */
78#elif LCD_DEPTH == 16 80#elif LCD_DEPTH == 16
79drivers/lcd-16bit.c 81drivers/lcd-16bit.c
80#endif /* LCD_DEPTH */ 82#endif /* LCD_DEPTH */
@@ -353,7 +355,6 @@ target/coldfire/iriver/h300/usb-h300.c
353#endif /* IRIVER_H300_SERIES */ 355#endif /* IRIVER_H300_SERIES */
354 356
355#ifdef IRIVER_H100_SERIES 357#ifdef IRIVER_H100_SERIES
356target/coldfire/iriver/h100/lcd-h100.c
357target/coldfire/iriver/h100/power-h100.c 358target/coldfire/iriver/h100/power-h100.c
358#ifndef SIMULATOR 359#ifndef SIMULATOR
359target/coldfire/iriver/ata-iriver.c 360target/coldfire/iriver/ata-iriver.c
@@ -362,6 +363,7 @@ target/coldfire/iriver/h100/adc-h100.c
362target/coldfire/iriver/h100/backlight-h100.c 363target/coldfire/iriver/h100/backlight-h100.c
363target/coldfire/iriver/h100/button-h100.c 364target/coldfire/iriver/h100/button-h100.c
364target/coldfire/iriver/h100/lcd-as-h100.S 365target/coldfire/iriver/h100/lcd-as-h100.S
366target/coldfire/iriver/h100/lcd-h100.c
365target/coldfire/iriver/h100/usb-h100.c 367target/coldfire/iriver/h100/usb-h100.c
366#endif /* SIMULATOR */ 368#endif /* SIMULATOR */
367#endif /* IRIVER_H100_SERIES */ 369#endif /* IRIVER_H100_SERIES */
diff --git a/firmware/drivers/lcd-2bit-vert.c b/firmware/drivers/lcd-2bit-vert.c
new file mode 100644
index 0000000000..a0598df318
--- /dev/null
+++ b/firmware/drivers/lcd-2bit-vert.c
@@ -0,0 +1,1093 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "config.h"
20
21#include "system.h"
22#include "cpu.h"
23#include "kernel.h"
24#include "lcd.h"
25#include "thread.h"
26#include <string.h>
27#include <stdlib.h>
28#include "file.h"
29#include "debug.h"
30#include "font.h"
31#include "rbunicode.h"
32#include "bidi.h"
33
34/*** definitions ***/
35
36#define SCROLLABLE_LINES 26
37
38/*** globals ***/
39
40fb_data lcd_framebuffer[LCD_HEIGHT/4][LCD_WIDTH] IBSS_ATTR;
41
42const unsigned char lcd_dibits[16] ICONST_ATTR = {
43 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
44 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
45};
46
47static const unsigned char pixmask[4] ICONST_ATTR = {
48 0x03, 0x0C, 0x30, 0xC0
49};
50
51static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
52static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
53static int drawmode = DRMODE_SOLID;
54static int xmargin = 0;
55static int ymargin = 0;
56static int curfont = FONT_SYSFIXED;
57
58/* scrolling */
59static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
60static void scroll_thread(void);
61static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
62static const char scroll_name[] = "scroll";
63static int scroll_ticks = 12; /* # of ticks between updates*/
64static int scroll_delay = HZ/2; /* ticks delay before start */
65static int scroll_step = 6; /* pixels per scroll step */
66static int bidir_limit = 50; /* percent */
67static struct scrollinfo scroll[SCROLLABLE_LINES];
68
69static const char scroll_tick_table[16] = {
70 /* Hz values:
71 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
72 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
73};
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 IF_PRIO(, PRIORITY_USER_INTERFACE));
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(int x, int y)
153{
154 unsigned mask = pixmask[y & 3];
155 fb_data *address = &lcd_framebuffer[y>>2][x];
156 unsigned data = *address;
157
158 *address = data ^ ((data ^ fg_pattern) & mask);
159}
160
161static void clearpixel(int x, int y)
162{
163 unsigned mask = pixmask[y & 3];
164 fb_data *address = &lcd_framebuffer[y>>2][x];
165 unsigned data = *address;
166
167 *address = data ^ ((data ^ bg_pattern) & mask);
168}
169
170static void flippixel(int x, int y)
171{
172 unsigned mask = pixmask[y & 3];
173 fb_data *address = &lcd_framebuffer[y>>2][x];
174
175 *address ^= mask;
176}
177
178static void nopixel(int x, int y)
179{
180 (void)x;
181 (void)y;
182}
183
184lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
185 flippixel, nopixel, setpixel, setpixel,
186 nopixel, clearpixel, nopixel, clearpixel
187};
188
189/* 'mask' and 'bits' contain 2 bits per pixel */
190static void flipblock(fb_data *address, unsigned mask, unsigned bits)
191 ICODE_ATTR;
192static void flipblock(fb_data *address, unsigned mask, unsigned bits)
193{
194 *address ^= bits & mask;
195}
196
197static void bgblock(fb_data *address, unsigned mask, unsigned bits)
198 ICODE_ATTR;
199static void bgblock(fb_data *address, unsigned mask, unsigned bits)
200{
201 unsigned data = *address;
202
203 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
204}
205
206static void fgblock(fb_data *address, unsigned mask, unsigned bits)
207 ICODE_ATTR;
208static void fgblock(fb_data *address, unsigned mask, unsigned bits)
209{
210 unsigned data = *address;
211
212 *address = data ^ ((data ^ fg_pattern) & mask & bits);
213}
214
215static void solidblock(fb_data *address, unsigned mask, unsigned bits)
216 ICODE_ATTR;
217static void solidblock(fb_data *address, unsigned mask, unsigned bits)
218{
219 unsigned data = *address;
220 unsigned bgp = bg_pattern;
221
222 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
223 *address = data ^ ((data ^ bits) & mask);
224}
225
226static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
227 ICODE_ATTR;
228static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
229{
230 *address ^= ~bits & mask;
231}
232
233static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
234 ICODE_ATTR;
235static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
236{
237 unsigned data = *address;
238
239 *address = data ^ ((data ^ bg_pattern) & mask & bits);
240}
241
242static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
243 ICODE_ATTR;
244static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
245{
246 unsigned data = *address;
247
248 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
249}
250
251static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
252 ICODE_ATTR;
253static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
254{
255 unsigned data = *address;
256 unsigned fgp = fg_pattern;
257
258 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
259 *address = data ^ ((data ^ bits) & mask);
260}
261
262lcd_blockfunc_type* const lcd_blockfuncs[8] = {
263 flipblock, bgblock, fgblock, solidblock,
264 flipinvblock, bginvblock, fginvblock, solidinvblock
265};
266
267static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
268{
269 unsigned data = *address;
270
271 bits ^= data;
272 *address = data ^ (bits & mask);
273}
274
275/*** drawing functions ***/
276
277/* Clear the whole display */
278void lcd_clear_display(void)
279{
280 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? fg_pattern : bg_pattern;
281
282 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
283 scrolling_lines = 0;
284}
285
286/* Set a single pixel */
287void lcd_drawpixel(int x, int y)
288{
289 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
290 lcd_pixelfuncs[drawmode](x, y);
291}
292
293/* Draw a line */
294void lcd_drawline(int x1, int y1, int x2, int y2)
295{
296 int numpixels;
297 int i;
298 int deltax, deltay;
299 int d, dinc1, dinc2;
300 int x, xinc1, xinc2;
301 int y, yinc1, yinc2;
302 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
303
304 deltax = abs(x2 - x1);
305 deltay = abs(y2 - y1);
306 xinc2 = 1;
307 yinc2 = 1;
308
309 if (deltax >= deltay)
310 {
311 numpixels = deltax;
312 d = 2 * deltay - deltax;
313 dinc1 = deltay * 2;
314 dinc2 = (deltay - deltax) * 2;
315 xinc1 = 1;
316 yinc1 = 0;
317 }
318 else
319 {
320 numpixels = deltay;
321 d = 2 * deltax - deltay;
322 dinc1 = deltax * 2;
323 dinc2 = (deltax - deltay) * 2;
324 xinc1 = 0;
325 yinc1 = 1;
326 }
327 numpixels++; /* include endpoints */
328
329 if (x1 > x2)
330 {
331 xinc1 = -xinc1;
332 xinc2 = -xinc2;
333 }
334
335 if (y1 > y2)
336 {
337 yinc1 = -yinc1;
338 yinc2 = -yinc2;
339 }
340
341 x = x1;
342 y = y1;
343
344 for (i = 0; i < numpixels; i++)
345 {
346 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
347 pfunc(x, y);
348
349 if (d < 0)
350 {
351 d += dinc1;
352 x += xinc1;
353 y += yinc1;
354 }
355 else
356 {
357 d += dinc2;
358 x += xinc2;
359 y += yinc2;
360 }
361 }
362}
363
364/* Draw a horizontal line (optimised) */
365void lcd_hline(int x1, int x2, int y)
366{
367 int x;
368 fb_data *dst, *dst_end;
369 unsigned mask;
370 lcd_blockfunc_type *bfunc;
371
372 /* direction flip */
373 if (x2 < x1)
374 {
375 x = x1;
376 x1 = x2;
377 x2 = x;
378 }
379
380 /* nothing to draw? */
381 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
382 return;
383
384 /* clipping */
385 if (x1 < 0)
386 x1 = 0;
387 if (x2 >= LCD_WIDTH)
388 x2 = LCD_WIDTH-1;
389
390 bfunc = lcd_blockfuncs[drawmode];
391 dst = &lcd_framebuffer[y>>2][x1];
392 mask = pixmask[y & 3];
393
394 dst_end = dst + x2 - x1;
395 do
396 bfunc(dst++, mask, 0xFFu);
397 while (dst <= dst_end);
398}
399
400/* Draw a vertical line (optimised) */
401void lcd_vline(int x, int y1, int y2)
402{
403 int ny;
404 fb_data *dst;
405 unsigned mask, mask_bottom;
406 lcd_blockfunc_type *bfunc;
407
408 /* direction flip */
409 if (y2 < y1)
410 {
411 ny = y1;
412 y1 = y2;
413 y2 = ny;
414 }
415
416 /* nothing to draw? */
417 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
418 return;
419
420 /* clipping */
421 if (y1 < 0)
422 y1 = 0;
423 if (y2 >= LCD_HEIGHT)
424 y2 = LCD_HEIGHT-1;
425
426 bfunc = lcd_blockfuncs[drawmode];
427 dst = &lcd_framebuffer[y1>>2][x];
428 ny = y2 - (y1 & ~3);
429 mask = 0xFFu << (2 * (y1 & 3));
430 mask_bottom = 0xFFu >> (2 * (~ny & 3));
431
432 for (; ny >= 4; ny -= 4)
433 {
434 bfunc(dst, mask, 0xFFu);
435 dst += LCD_WIDTH;
436 mask = 0xFFu;
437 }
438 mask &= mask_bottom;
439 bfunc(dst, mask, 0xFFu);
440}
441
442/* Draw a rectangular box */
443void lcd_drawrect(int x, int y, int width, int height)
444{
445 if ((width <= 0) || (height <= 0))
446 return;
447
448 int x2 = x + width - 1;
449 int y2 = y + height - 1;
450
451 lcd_vline(x, y, y2);
452 lcd_vline(x2, y, y2);
453 lcd_hline(x, x2, y);
454 lcd_hline(x, x2, y2);
455}
456
457/* Fill a rectangular area */
458void lcd_fillrect(int x, int y, int width, int height)
459{
460 int ny;
461 fb_data *dst, *dst_end;
462 unsigned mask, mask_bottom;
463 unsigned bits = 0;
464 lcd_blockfunc_type *bfunc;
465 bool fillopt = false;
466
467 /* nothing to draw? */
468 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
469 || (x + width <= 0) || (y + height <= 0))
470 return;
471
472 /* clipping */
473 if (x < 0)
474 {
475 width += x;
476 x = 0;
477 }
478 if (y < 0)
479 {
480 height += y;
481 y = 0;
482 }
483 if (x + width > LCD_WIDTH)
484 width = LCD_WIDTH - x;
485 if (y + height > LCD_HEIGHT)
486 height = LCD_HEIGHT - y;
487
488 if (drawmode & DRMODE_INVERSEVID)
489 {
490 if (drawmode & DRMODE_BG)
491 {
492 fillopt = true;
493 bits = bg_pattern;
494 }
495 }
496 else
497 {
498 if (drawmode & DRMODE_FG)
499 {
500 fillopt = true;
501 bits = fg_pattern;
502 }
503 }
504 bfunc = lcd_blockfuncs[drawmode];
505 dst = &lcd_framebuffer[y>>2][x];
506 ny = height - 1 + (y & 3);
507 mask = 0xFFu << (2 * (y & 3));
508 mask_bottom = 0xFFu >> (2 * (~ny & 3));
509
510 for (; ny >= 4; ny -= 4)
511 {
512 if (fillopt && (mask == 0xFFu))
513 memset(dst, bits, width);
514 else
515 {
516 fb_data *dst_row = dst;
517
518 dst_end = dst_row + width;
519 do
520 bfunc(dst_row++, mask, 0xFFu);
521 while (dst_row < dst_end);
522 }
523
524 dst += LCD_WIDTH;
525 mask = 0xFFu;
526 }
527 mask &= mask_bottom;
528
529 if (fillopt && (mask == 0xFFu))
530 memset(dst, bits, width);
531 else
532 {
533 dst_end = dst + width;
534 do
535 bfunc(dst++, mask, 0xFFu);
536 while (dst < dst_end);
537 }
538}
539
540/* About Rockbox' internal monochrome bitmap format:
541 *
542 * A bitmap contains one bit for every pixel that defines if that pixel is
543 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
544 * at top.
545 * The bytes are stored in row-major order, with byte 0 being top left,
546 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
547 * 0..7, the second row defines pixel row 8..15 etc.
548 *
549 * This is similar to the internal lcd hw format. */
550
551/* Draw a partial monochrome bitmap */
552void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
553 int stride, int x, int y, int width, int height)
554 ICODE_ATTR;
555void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
556 int stride, int x, int y, int width, int height)
557{
558 int shift, ny;
559 fb_data *dst, *dst_end;
560 unsigned mask, mask_bottom;
561 lcd_blockfunc_type *bfunc;
562
563 /* nothing to draw? */
564 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
565 || (x + width <= 0) || (y + height <= 0))
566 return;
567
568 /* clipping */
569 if (x < 0)
570 {
571 width += x;
572 src_x -= x;
573 x = 0;
574 }
575 if (y < 0)
576 {
577 height += y;
578 src_y -= y;
579 y = 0;
580 }
581 if (x + width > LCD_WIDTH)
582 width = LCD_WIDTH - x;
583 if (y + height > LCD_HEIGHT)
584 height = LCD_HEIGHT - y;
585
586 src += stride * (src_y >> 3) + src_x; /* move starting point */
587 src_y &= 7;
588 y -= src_y;
589 dst = &lcd_framebuffer[y>>2][x];
590 shift = y & 3;
591 ny = height - 1 + shift + src_y;
592
593 bfunc = lcd_blockfuncs[drawmode];
594 mask = 0xFFu << (shift + src_y);
595 mask_bottom = 0xFFu >> (~ny & 7);
596
597 if (shift == 0)
598 {
599 unsigned dmask1, dmask2, data;
600
601 for (; ny >= 8; ny -= 8)
602 {
603 const unsigned char *src_row = src;
604 fb_data *dst_row = dst + LCD_WIDTH;
605
606 dmask1 = lcd_dibits[mask&0x0F];
607 dmask2 = lcd_dibits[(mask>>4)&0x0F];
608 dst_end = dst_row + width;
609
610 if (dmask1 != 0)
611 {
612 do
613 {
614 data = *src_row++;
615 bfunc(dst_row - LCD_WIDTH, dmask1, lcd_dibits[data&0x0F]);
616 bfunc(dst_row++, dmask2, lcd_dibits[(data>>4)&0x0F]);
617 }
618 while (dst_row < dst_end);
619 }
620 else
621 {
622 do
623 bfunc(dst_row++, dmask2, lcd_dibits[((*src_row++)>>4)&0x0F]);
624 while (dst_row < dst_end);
625 }
626 src += stride;
627 dst += 2*LCD_WIDTH;
628 mask = 0xFFu;
629 }
630 mask &= mask_bottom;
631 dmask1 = lcd_dibits[mask&0x0F];
632 dmask2 = lcd_dibits[(mask>>4)&0x0F];
633 dst_end = dst + width;
634
635 if (dmask1 != 0)
636 {
637 if (dmask2 != 0)
638 {
639 do
640 {
641 data = *src++;
642 bfunc(dst, dmask1, lcd_dibits[data&0x0F]);
643 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[(data>>4)&0x0F]);
644 }
645 while (dst < dst_end);
646 }
647 else
648 {
649 do
650 bfunc(dst++, dmask1, lcd_dibits[(*src++)&0x0F]);
651 while (dst < dst_end);
652 }
653 }
654 else
655 {
656 do
657 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[((*src++)>>4)&0x0F]);
658 while (dst < dst_end);
659 }
660 }
661 else
662 {
663 dst_end = dst + width;
664 do
665 {
666 const unsigned char *src_col = src++;
667 fb_data *dst_col = dst++;
668 unsigned mask_col = mask;
669 unsigned data = 0;
670
671 for (y = ny; y >= 8; y -= 8)
672 {
673 data |= *src_col << shift;
674
675 if (mask_col & 0xFFu)
676 {
677 if (mask_col & 0x0F)
678 bfunc(dst_col, lcd_dibits[mask_col&0x0F], lcd_dibits[data&0x0F]);
679 bfunc(dst_col + LCD_WIDTH, lcd_dibits[(mask_col>>4)&0x0F],
680 lcd_dibits[(data>>4)&0x0F]);
681 mask_col = 0xFFu;
682 }
683 else
684 mask_col >>= 8;
685
686 src_col += stride;
687 dst_col += 2*LCD_WIDTH;
688 data >>= 8;
689 }
690 data |= *src_col << shift;
691 mask_bottom &= mask_col;
692 if (mask_bottom & 0x0F)
693 bfunc(dst_col, lcd_dibits[mask_bottom&0x0F], lcd_dibits[data&0x0F]);
694 if (mask_bottom & 0xF0)
695 bfunc(dst_col + LCD_WIDTH, lcd_dibits[(mask_bottom&0xF0)>>4],
696 lcd_dibits[(data>>4)&0x0F]);
697 }
698 while (dst < dst_end);
699 }
700}
701
702/* Draw a full monochrome bitmap */
703void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
704{
705 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
706}
707
708/* About Rockbox' internal native bitmap format:
709 *
710 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
711 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
712 * at top.
713 * The bytes are stored in row-major order, with byte 0 being top left,
714 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
715 * 0..3, the second row defines pixel row 4..7 etc.
716 *
717 * This is the same as the internal lcd hw format. */
718
719/* Draw a partial native bitmap */
720void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
721 int stride, int x, int y, int width, int height)
722 ICODE_ATTR;
723void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
724 int stride, int x, int y, int width, int height)
725{
726 int shift, ny;
727 fb_data *dst, *dst_end;
728 unsigned mask, mask_bottom;
729
730 /* nothing to draw? */
731 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
732 || (x + width <= 0) || (y + height <= 0))
733 return;
734
735 /* clipping */
736 if (x < 0)
737 {
738 width += x;
739 src_x -= x;
740 x = 0;
741 }
742 if (y < 0)
743 {
744 height += y;
745 src_y -= y;
746 y = 0;
747 }
748 if (x + width > LCD_WIDTH)
749 width = LCD_WIDTH - x;
750 if (y + height > LCD_HEIGHT)
751 height = LCD_HEIGHT - y;
752
753 src += stride * (src_y >> 2) + src_x; /* move starting point */
754 src_y &= 3;
755 y -= src_y;
756 dst = &lcd_framebuffer[y>>2][x];
757 shift = y & 3;
758 ny = height - 1 + shift + src_y;
759
760 mask = 0xFFu << (2 * (shift + src_y));
761 mask_bottom = 0xFFu >> (2 * (~ny & 3));
762
763 if (shift == 0)
764 {
765 for (; ny >= 4; ny -= 4)
766 {
767 if (mask == 0xFFu)
768 memcpy(dst, src, width);
769 else
770 {
771 const fb_data *src_row = src;
772 fb_data *dst_row = dst;
773
774 dst_end = dst_row + width;
775 do
776 setblock(dst_row++, mask, *src_row++);
777 while (dst_row < dst_end);
778 }
779 src += stride;
780 dst += LCD_WIDTH;
781 mask = 0xFFu;
782 }
783 mask &= mask_bottom;
784
785 if (mask == 0xFFu)
786 memcpy(dst, src, width);
787 else
788 {
789 dst_end = dst + width;
790 do
791 setblock(dst++, mask, *src++);
792 while (dst < dst_end);
793 }
794 }
795 else
796 {
797 shift *= 2;
798 dst_end = dst + width;
799 do
800 {
801 const fb_data *src_col = src++;
802 fb_data *dst_col = dst++;
803 unsigned mask_col = mask;
804 unsigned data = 0;
805
806 for (y = ny; y >= 4; y -= 4)
807 {
808 data |= *src_col << shift;
809
810 if (mask_col & 0xFFu)
811 {
812 setblock(dst_col, mask_col, data);
813 mask_col = 0xFFu;
814 }
815 else
816 mask_col >>= 8;
817
818 src_col += stride;
819 dst_col += LCD_WIDTH;
820 data >>= 8;
821 }
822 data |= *src_col << shift;
823 setblock(dst_col, mask_col & mask_bottom, data);
824 }
825 while (dst < dst_end);
826 }
827}
828
829/* Draw a full native bitmap */
830void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
831{
832 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
833}
834
835/* put a string at a given pixel position, skipping first ofs pixel columns */
836static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
837{
838 unsigned short ch;
839 unsigned short *ucs;
840 struct font* pf = font_get(curfont);
841
842 ucs = bidi_l2v(str, 1);
843
844 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
845 {
846 int width;
847 const unsigned char *bits;
848
849 /* get proportional width and glyph bits */
850 width = font_get_width(pf,ch);
851
852 if (ofs > width)
853 {
854 ofs -= width;
855 continue;
856 }
857
858 bits = font_get_bits(pf, ch);
859
860 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
861 pf->height);
862
863 x += width - ofs;
864 ofs = 0;
865 }
866}
867
868/* put a string at a given pixel position */
869void lcd_putsxy(int x, int y, const unsigned char *str)
870{
871 lcd_putsxyofs(x, y, 0, str);
872}
873
874/*** line oriented text output ***/
875
876/* put a string at a given char position */
877void lcd_puts(int x, int y, const unsigned char *str)
878{
879 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
880}
881
882void lcd_puts_style(int x, int y, const unsigned char *str, int style)
883{
884 lcd_puts_style_offset(x, y, str, style, 0);
885}
886
887void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
888{
889 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
890}
891
892/* put a string at a given char position, style, and pixel position,
893 * skipping first offset pixel columns */
894void lcd_puts_style_offset(int x, int y, const unsigned char *str,
895 int style, int offset)
896{
897 int xpos,ypos,w,h,xrect;
898 int lastmode = drawmode;
899
900 /* make sure scrolling is turned off on the line we are updating */
901 scrolling_lines &= ~(1 << y);
902
903 if(!str || !str[0])
904 return;
905
906 lcd_getstringsize(str, &w, &h);
907 xpos = xmargin + x*w / utf8length((char *)str);
908 ypos = ymargin + y*h;
909 drawmode = (style & STYLE_INVERT) ?
910 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
911 lcd_putsxyofs(xpos, ypos, offset, str);
912 drawmode ^= DRMODE_INVERSEVID;
913 xrect = xpos + MAX(w - offset, 0);
914 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
915 drawmode = lastmode;
916}
917
918/*** scrolling ***/
919
920/* Reverse the invert setting of the scrolling line (if any) at given char
921 position. Setting will go into affect next time line scrolls. */
922void lcd_invertscroll(int x, int y)
923{
924 struct scrollinfo* s;
925
926 (void)x;
927
928 s = &scroll[y];
929 s->invert = !s->invert;
930}
931
932void lcd_stop_scroll(void)
933{
934 scrolling_lines=0;
935}
936
937void lcd_scroll_speed(int speed)
938{
939 scroll_ticks = scroll_tick_table[speed];
940}
941
942void lcd_scroll_step(int step)
943{
944 scroll_step = step;
945}
946
947void lcd_scroll_delay(int ms)
948{
949 scroll_delay = ms / (HZ / 10);
950}
951
952void lcd_bidir_scroll(int percent)
953{
954 bidir_limit = percent;
955}
956
957void lcd_puts_scroll(int x, int y, const unsigned char *string)
958{
959 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
960}
961
962void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
963{
964 lcd_puts_scroll_style_offset(x, y, string, style, 0);
965}
966
967void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
968{
969 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
970}
971
972void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
973 int style, int offset)
974{
975 struct scrollinfo* s;
976 int w, h;
977
978 s = &scroll[y];
979
980 s->start_tick = current_tick + scroll_delay;
981 s->invert = false;
982 if (style & STYLE_INVERT) {
983 s->invert = true;
984 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
985 }
986 else
987 lcd_puts_offset(x,y,string,offset);
988
989 lcd_getstringsize(string, &w, &h);
990
991 if (LCD_WIDTH - x * 8 - xmargin < w) {
992 /* prepare scroll line */
993 char *end;
994
995 memset(s->line, 0, sizeof s->line);
996 strcpy(s->line, (char *)string);
997
998 /* get width */
999 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1000
1001 /* scroll bidirectional or forward only depending on the string
1002 width */
1003 if ( bidir_limit ) {
1004 s->bidir = s->width < (LCD_WIDTH - xmargin) *
1005 (100 + bidir_limit) / 100;
1006 }
1007 else
1008 s->bidir = false;
1009
1010 if (!s->bidir) { /* add spaces if scrolling in the round */
1011 strcat(s->line, " ");
1012 /* get new width incl. spaces */
1013 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1014 }
1015
1016 end = strchr(s->line, '\0');
1017 strncpy(end, (char *)string, LCD_WIDTH/2);
1018
1019 s->len = utf8length((char *)string);
1020 s->offset = offset;
1021 s->startx = xmargin + x * s->width / s->len;
1022 s->backward = false;
1023 scrolling_lines |= (1<<y);
1024 }
1025 else
1026 /* force a bit switch-off since it doesn't scroll */
1027 scrolling_lines &= ~(1<<y);
1028}
1029
1030static void scroll_thread(void)
1031{
1032 struct font* pf;
1033 struct scrollinfo* s;
1034 int index;
1035 int xpos, ypos;
1036 int lastmode;
1037
1038 /* initialize scroll struct array */
1039 scrolling_lines = 0;
1040
1041 while ( 1 ) {
1042 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
1043 /* really scroll? */
1044 if ( !(scrolling_lines&(1<<index)) )
1045 continue;
1046
1047 s = &scroll[index];
1048
1049 /* check pause */
1050 if (TIME_BEFORE(current_tick, s->start_tick))
1051 continue;
1052
1053 if (s->backward)
1054 s->offset -= scroll_step;
1055 else
1056 s->offset += scroll_step;
1057
1058 pf = font_get(curfont);
1059 xpos = s->startx;
1060 ypos = ymargin + index * pf->height;
1061
1062 if (s->bidir) { /* scroll bidirectional */
1063 if (s->offset <= 0) {
1064 /* at beginning of line */
1065 s->offset = 0;
1066 s->backward = false;
1067 s->start_tick = current_tick + scroll_delay * 2;
1068 }
1069 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
1070 /* at end of line */
1071 s->offset = s->width - (LCD_WIDTH - xpos);
1072 s->backward = true;
1073 s->start_tick = current_tick + scroll_delay * 2;
1074 }
1075 }
1076 else {
1077 /* scroll forward the whole time */
1078 if (s->offset >= s->width)
1079 s->offset %= s->width;
1080 }
1081
1082 lastmode = drawmode;
1083 drawmode = s->invert ?
1084 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1085 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
1086 drawmode = lastmode;
1087 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1088 }
1089
1090 sleep(scroll_ticks);
1091 }
1092}
1093
diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h
index 91b2cb7658..8ef227676a 100644
--- a/firmware/export/config-h100.h
+++ b/firmware/export/config-h100.h
@@ -55,8 +55,6 @@
55 55
56#define CONFIG_LCD LCD_S1D15E06 56#define CONFIG_LCD LCD_S1D15E06
57 57
58#define DEFAULT_REMOTE_CONTRAST_SETTING 42
59
60/* Define this for LCD backlight available */ 58/* Define this for LCD backlight available */
61#define CONFIG_BACKLIGHT BL_IRIVER_H100 /* port controlled */ 59#define CONFIG_BACKLIGHT BL_IRIVER_H100 /* port controlled */
62 60
@@ -156,9 +154,9 @@
156 154
157#endif /* !SIMULATOR */ 155#endif /* !SIMULATOR */
158 156
159#define DEFAULT_CONTRAST_SETTING 40 157#define DEFAULT_CONTRAST_SETTING 28
160#define MIN_CONTRAST_SETTING 5 158#define MIN_CONTRAST_SETTING 14 /* White screen a bit higher than this */
161#define MAX_CONTRAST_SETTING 63 159#define MAX_CONTRAST_SETTING 63 /* Black screen a bit lower than this*/
162#define DEFAULT_REMOTE_CONTRAST_SETTING 42 160#define DEFAULT_REMOTE_CONTRAST_SETTING 42
163#define MIN_REMOTE_CONTRAST_SETTING MIN_CONTRAST_SETTING 161#define MIN_REMOTE_CONTRAST_SETTING MIN_CONTRAST_SETTING
164#define MAX_REMOTE_CONTRAST_SETTING MAX_CONTRAST_SETTING 162#define MAX_REMOTE_CONTRAST_SETTING MAX_CONTRAST_SETTING
diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h
index ad73f379b9..e4c5cfe876 100644
--- a/firmware/export/config-h120.h
+++ b/firmware/export/config-h120.h
@@ -50,8 +50,6 @@
50 50
51#define CONFIG_LCD LCD_S1D15E06 51#define CONFIG_LCD LCD_S1D15E06
52 52
53#define DEFAULT_REMOTE_CONTRAST_SETTING 42
54
55/* Define this for LCD backlight available */ 53/* Define this for LCD backlight available */
56#define CONFIG_BACKLIGHT BL_IRIVER_H100 /* port controlled */ 54#define CONFIG_BACKLIGHT BL_IRIVER_H100 /* port controlled */
57 55
@@ -152,9 +150,9 @@
152 150
153#endif /* !SIMULATOR */ 151#endif /* !SIMULATOR */
154 152
155#define DEFAULT_CONTRAST_SETTING 40 153#define DEFAULT_CONTRAST_SETTING 28
156#define MIN_CONTRAST_SETTING 5 154#define MIN_CONTRAST_SETTING 14 /* White screen a bit higher than this */
157#define MAX_CONTRAST_SETTING 63 155#define MAX_CONTRAST_SETTING 63 /* Black screen a bit lower than this*/
158#define DEFAULT_REMOTE_CONTRAST_SETTING 42 156#define DEFAULT_REMOTE_CONTRAST_SETTING 42
159#define MIN_REMOTE_CONTRAST_SETTING MIN_CONTRAST_SETTING 157#define MIN_REMOTE_CONTRAST_SETTING MIN_CONTRAST_SETTING
160#define MAX_REMOTE_CONTRAST_SETTING MAX_CONTRAST_SETTING 158#define MAX_REMOTE_CONTRAST_SETTING MAX_CONTRAST_SETTING
diff --git a/firmware/target/coldfire/iriver/h100/lcd-h100.c b/firmware/target/coldfire/iriver/h100/lcd-h100.c
index 9ed63661a0..9d2f7dac6b 100644
--- a/firmware/target/coldfire/iriver/h100/lcd-h100.c
+++ b/firmware/target/coldfire/iriver/h100/lcd-h100.c
@@ -18,18 +18,9 @@
18 ****************************************************************************/ 18 ****************************************************************************/
19#include "config.h" 19#include "config.h"
20 20
21#include "cpu.h"
22#include "lcd.h"
23#include "kernel.h"
24#include "thread.h"
25#include <string.h>
26#include <stdlib.h>
27#include "file.h"
28#include "debug.h"
29#include "system.h" 21#include "system.h"
30#include "font.h" 22#include "kernel.h"
31#include "rbunicode.h" 23#include "lcd.h"
32#include "bidi.h"
33 24
34/*** definitions ***/ 25/*** definitions ***/
35 26
@@ -59,58 +50,23 @@
59#define LCD_CNTL_COLUMN 0x13 50#define LCD_CNTL_COLUMN 0x13
60#define LCD_CNTL_DATA_WRITE 0x1d 51#define LCD_CNTL_DATA_WRITE 0x1d
61 52
62#define SCROLLABLE_LINES 26 53/*** shared semi-private declarations ***/
63 54extern const unsigned char lcd_dibits[16] ICONST_ATTR;
64/*** globals ***/
65
66fb_data lcd_framebuffer[LCD_HEIGHT/4][LCD_WIDTH] IBSS_ATTR;
67
68static const unsigned char dibits[16] ICONST_ATTR = {
69 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
70 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
71};
72
73static const unsigned char pixmask[4] ICONST_ATTR = {
74 0x03, 0x0C, 0x30, 0xC0
75};
76
77static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
78static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
79static int drawmode = DRMODE_SOLID;
80static int xmargin = 0;
81static int ymargin = 0;
82static int curfont = FONT_SYSFIXED;
83
84/* scrolling */
85static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
86static void scroll_thread(void);
87static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
88static const char scroll_name[] = "scroll";
89static int scroll_ticks = 12; /* # of ticks between updates*/
90static int scroll_delay = HZ/2; /* ticks delay before start */
91static int scroll_step = 6; /* pixels per scroll step */
92static int bidir_limit = 50; /* percent */
93static struct scrollinfo scroll[SCROLLABLE_LINES];
94
95static const char scroll_tick_table[16] = {
96 /* Hz values:
97 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
98 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
99};
100
101/*** driver code is in lcd.S ***/
102 55
103/*** hardware configuration ***/ 56/*** hardware configuration ***/
104
105#ifndef SIMULATOR
106
107int lcd_default_contrast(void) 57int lcd_default_contrast(void)
108{ 58{
109 return 28; 59 return DEFAULT_CONTRAST_SETTING;
110} 60}
111 61
112void lcd_set_contrast(int val) 62void lcd_set_contrast(int val)
113{ 63{
64 /* Keep val in acceptable hw range */
65 if (val < 0)
66 val = 0;
67 else if (val > 127)
68 val = 127;
69
114 lcd_write_command_ex(LCD_CNTL_ELECTRONIC_VOLUME, val, -1); 70 lcd_write_command_ex(LCD_CNTL_ELECTRONIC_VOLUME, val, -1);
115} 71}
116 72
@@ -136,19 +92,7 @@ void lcd_set_flip(bool yesno)
136 } 92 }
137} 93}
138 94
139#endif /* !SIMULATOR */ 95void lcd_init_device(void)
140
141/* LCD init */
142#ifdef SIMULATOR
143
144void lcd_init(void)
145{
146 create_thread(scroll_thread, scroll_stack,
147 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
148}
149#else
150
151void lcd_init(void)
152{ 96{
153 /* GPO35 is the LCD A0 pin 97 /* GPO35 is the LCD A0 pin
154 GPO46 is LCD RESET */ 98 GPO46 is LCD RESET */
@@ -188,12 +132,8 @@ void lcd_init(void)
188 lcd_write_command_ex(LCD_CNTL_DISPLAY_MODE, 0, -1); /* Greyscale mode */ 132 lcd_write_command_ex(LCD_CNTL_DISPLAY_MODE, 0, -1); /* Greyscale mode */
189 lcd_write_command(LCD_CNTL_DATA_INPUT_DIR | 0); /* Column mode */ 133 lcd_write_command(LCD_CNTL_DATA_INPUT_DIR | 0); /* Column mode */
190 134
191 lcd_clear_display();
192 lcd_update(); 135 lcd_update();
193 lcd_write_command(LCD_CNTL_ON_OFF | 1); /* LCD ON */ 136 lcd_write_command(LCD_CNTL_ON_OFF | 1); /* LCD ON */
194
195 create_thread(scroll_thread, scroll_stack,
196 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
197} 137}
198 138
199/*** update functions ***/ 139/*** update functions ***/
@@ -220,9 +160,9 @@ void lcd_blit(const unsigned char* data, int x, int by, int width,
220 do 160 do
221 { 161 {
222 byte = *src++; 162 byte = *src++;
223 *dst_u++ = dibits[byte & 0x0F]; 163 *dst_u++ = lcd_dibits[byte & 0x0F];
224 byte >>= 4; 164 byte >>= 4;
225 *dst_l++ = dibits[byte & 0x0F]; 165 *dst_l++ = lcd_dibits[byte & 0x0F];
226 } 166 }
227 while (src < src_end); 167 while (src < src_end);
228 168
@@ -286,1013 +226,3 @@ void lcd_update_rect(int x, int y, int width, int height)
286 lcd_write_data (&lcd_framebuffer[y][x], width); 226 lcd_write_data (&lcd_framebuffer[y][x], width);
287 } 227 }
288} 228}
289#endif /* !SIMULATOR */
290
291/*** parameter handling ***/
292
293void lcd_set_drawmode(int mode)
294{
295 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
296}
297
298int lcd_get_drawmode(void)
299{
300 return drawmode;
301}
302
303void lcd_set_foreground(unsigned brightness)
304{
305 fg_pattern = 0x55 * (~brightness & 3);
306}
307
308unsigned lcd_get_foreground(void)
309{
310 return ~fg_pattern & 3;
311}
312
313void lcd_set_background(unsigned brightness)
314{
315 bg_pattern = 0x55 * (~brightness & 3);
316}
317
318unsigned lcd_get_background(void)
319{
320 return ~bg_pattern & 3;
321}
322
323void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
324{
325 lcd_set_drawmode(mode);
326 lcd_set_foreground(fg_brightness);
327 lcd_set_background(bg_brightness);
328}
329
330void lcd_setmargins(int x, int y)
331{
332 xmargin = x;
333 ymargin = y;
334}
335
336int lcd_getxmargin(void)
337{
338 return xmargin;
339}
340
341int lcd_getymargin(void)
342{
343 return ymargin;
344}
345
346void lcd_setfont(int newfont)
347{
348 curfont = newfont;
349}
350
351int lcd_getstringsize(const unsigned char *str, int *w, int *h)
352{
353 return font_getstringsize(str, w, h, curfont);
354}
355
356/*** low-level drawing functions ***/
357
358static void setpixel(int x, int y)
359{
360 unsigned mask = pixmask[y & 3];
361 fb_data *address = &lcd_framebuffer[y>>2][x];
362 unsigned data = *address;
363
364 *address = data ^ ((data ^ fg_pattern) & mask);
365}
366
367static void clearpixel(int x, int y)
368{
369 unsigned mask = pixmask[y & 3];
370 fb_data *address = &lcd_framebuffer[y>>2][x];
371 unsigned data = *address;
372
373 *address = data ^ ((data ^ bg_pattern) & mask);
374}
375
376static void flippixel(int x, int y)
377{
378 unsigned mask = pixmask[y & 3];
379 fb_data *address = &lcd_framebuffer[y>>2][x];
380
381 *address ^= mask;
382}
383
384static void nopixel(int x, int y)
385{
386 (void)x;
387 (void)y;
388}
389
390lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
391 flippixel, nopixel, setpixel, setpixel,
392 nopixel, clearpixel, nopixel, clearpixel
393};
394
395/* 'mask' and 'bits' contain 2 bits per pixel */
396static void flipblock(fb_data *address, unsigned mask, unsigned bits)
397 ICODE_ATTR;
398static void flipblock(fb_data *address, unsigned mask, unsigned bits)
399{
400 *address ^= bits & mask;
401}
402
403static void bgblock(fb_data *address, unsigned mask, unsigned bits)
404 ICODE_ATTR;
405static void bgblock(fb_data *address, unsigned mask, unsigned bits)
406{
407 unsigned data = *address;
408
409 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
410}
411
412static void fgblock(fb_data *address, unsigned mask, unsigned bits)
413 ICODE_ATTR;
414static void fgblock(fb_data *address, unsigned mask, unsigned bits)
415{
416 unsigned data = *address;
417
418 *address = data ^ ((data ^ fg_pattern) & mask & bits);
419}
420
421static void solidblock(fb_data *address, unsigned mask, unsigned bits)
422 ICODE_ATTR;
423static void solidblock(fb_data *address, unsigned mask, unsigned bits)
424{
425 unsigned data = *address;
426 unsigned bgp = bg_pattern;
427
428 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
429 *address = data ^ ((data ^ bits) & mask);
430}
431
432static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
433 ICODE_ATTR;
434static void flipinvblock(fb_data *address, unsigned mask, unsigned bits)
435{
436 *address ^= ~bits & mask;
437}
438
439static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
440 ICODE_ATTR;
441static void bginvblock(fb_data *address, unsigned mask, unsigned bits)
442{
443 unsigned data = *address;
444
445 *address = data ^ ((data ^ bg_pattern) & mask & bits);
446}
447
448static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
449 ICODE_ATTR;
450static void fginvblock(fb_data *address, unsigned mask, unsigned bits)
451{
452 unsigned data = *address;
453
454 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
455}
456
457static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
458 ICODE_ATTR;
459static void solidinvblock(fb_data *address, unsigned mask, unsigned bits)
460{
461 unsigned data = *address;
462 unsigned fgp = fg_pattern;
463
464 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
465 *address = data ^ ((data ^ bits) & mask);
466}
467
468lcd_blockfunc_type* const lcd_blockfuncs[8] = {
469 flipblock, bgblock, fgblock, solidblock,
470 flipinvblock, bginvblock, fginvblock, solidinvblock
471};
472
473static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
474{
475 unsigned data = *address;
476
477 bits ^= data;
478 *address = data ^ (bits & mask);
479}
480
481/*** drawing functions ***/
482
483/* Clear the whole display */
484void lcd_clear_display(void)
485{
486 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? fg_pattern : bg_pattern;
487
488 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
489 scrolling_lines = 0;
490}
491
492/* Set a single pixel */
493void lcd_drawpixel(int x, int y)
494{
495 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
496 lcd_pixelfuncs[drawmode](x, y);
497}
498
499/* Draw a line */
500void lcd_drawline(int x1, int y1, int x2, int y2)
501{
502 int numpixels;
503 int i;
504 int deltax, deltay;
505 int d, dinc1, dinc2;
506 int x, xinc1, xinc2;
507 int y, yinc1, yinc2;
508 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
509
510 deltax = abs(x2 - x1);
511 deltay = abs(y2 - y1);
512 xinc2 = 1;
513 yinc2 = 1;
514
515 if (deltax >= deltay)
516 {
517 numpixels = deltax;
518 d = 2 * deltay - deltax;
519 dinc1 = deltay * 2;
520 dinc2 = (deltay - deltax) * 2;
521 xinc1 = 1;
522 yinc1 = 0;
523 }
524 else
525 {
526 numpixels = deltay;
527 d = 2 * deltax - deltay;
528 dinc1 = deltax * 2;
529 dinc2 = (deltax - deltay) * 2;
530 xinc1 = 0;
531 yinc1 = 1;
532 }
533 numpixels++; /* include endpoints */
534
535 if (x1 > x2)
536 {
537 xinc1 = -xinc1;
538 xinc2 = -xinc2;
539 }
540
541 if (y1 > y2)
542 {
543 yinc1 = -yinc1;
544 yinc2 = -yinc2;
545 }
546
547 x = x1;
548 y = y1;
549
550 for (i = 0; i < numpixels; i++)
551 {
552 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
553 pfunc(x, y);
554
555 if (d < 0)
556 {
557 d += dinc1;
558 x += xinc1;
559 y += yinc1;
560 }
561 else
562 {
563 d += dinc2;
564 x += xinc2;
565 y += yinc2;
566 }
567 }
568}
569
570/* Draw a horizontal line (optimised) */
571void lcd_hline(int x1, int x2, int y)
572{
573 int x;
574 fb_data *dst, *dst_end;
575 unsigned mask;
576 lcd_blockfunc_type *bfunc;
577
578 /* direction flip */
579 if (x2 < x1)
580 {
581 x = x1;
582 x1 = x2;
583 x2 = x;
584 }
585
586 /* nothing to draw? */
587 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
588 return;
589
590 /* clipping */
591 if (x1 < 0)
592 x1 = 0;
593 if (x2 >= LCD_WIDTH)
594 x2 = LCD_WIDTH-1;
595
596 bfunc = lcd_blockfuncs[drawmode];
597 dst = &lcd_framebuffer[y>>2][x1];
598 mask = pixmask[y & 3];
599
600 dst_end = dst + x2 - x1;
601 do
602 bfunc(dst++, mask, 0xFFu);
603 while (dst <= dst_end);
604}
605
606/* Draw a vertical line (optimised) */
607void lcd_vline(int x, int y1, int y2)
608{
609 int ny;
610 fb_data *dst;
611 unsigned mask, mask_bottom;
612 lcd_blockfunc_type *bfunc;
613
614 /* direction flip */
615 if (y2 < y1)
616 {
617 ny = y1;
618 y1 = y2;
619 y2 = ny;
620 }
621
622 /* nothing to draw? */
623 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
624 return;
625
626 /* clipping */
627 if (y1 < 0)
628 y1 = 0;
629 if (y2 >= LCD_HEIGHT)
630 y2 = LCD_HEIGHT-1;
631
632 bfunc = lcd_blockfuncs[drawmode];
633 dst = &lcd_framebuffer[y1>>2][x];
634 ny = y2 - (y1 & ~3);
635 mask = 0xFFu << (2 * (y1 & 3));
636 mask_bottom = 0xFFu >> (2 * (~ny & 3));
637
638 for (; ny >= 4; ny -= 4)
639 {
640 bfunc(dst, mask, 0xFFu);
641 dst += LCD_WIDTH;
642 mask = 0xFFu;
643 }
644 mask &= mask_bottom;
645 bfunc(dst, mask, 0xFFu);
646}
647
648/* Draw a rectangular box */
649void lcd_drawrect(int x, int y, int width, int height)
650{
651 if ((width <= 0) || (height <= 0))
652 return;
653
654 int x2 = x + width - 1;
655 int y2 = y + height - 1;
656
657 lcd_vline(x, y, y2);
658 lcd_vline(x2, y, y2);
659 lcd_hline(x, x2, y);
660 lcd_hline(x, x2, y2);
661}
662
663/* Fill a rectangular area */
664void lcd_fillrect(int x, int y, int width, int height)
665{
666 int ny;
667 fb_data *dst, *dst_end;
668 unsigned mask, mask_bottom;
669 unsigned bits = 0;
670 lcd_blockfunc_type *bfunc;
671 bool fillopt = false;
672
673 /* nothing to draw? */
674 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
675 || (x + width <= 0) || (y + height <= 0))
676 return;
677
678 /* clipping */
679 if (x < 0)
680 {
681 width += x;
682 x = 0;
683 }
684 if (y < 0)
685 {
686 height += y;
687 y = 0;
688 }
689 if (x + width > LCD_WIDTH)
690 width = LCD_WIDTH - x;
691 if (y + height > LCD_HEIGHT)
692 height = LCD_HEIGHT - y;
693
694 if (drawmode & DRMODE_INVERSEVID)
695 {
696 if (drawmode & DRMODE_BG)
697 {
698 fillopt = true;
699 bits = bg_pattern;
700 }
701 }
702 else
703 {
704 if (drawmode & DRMODE_FG)
705 {
706 fillopt = true;
707 bits = fg_pattern;
708 }
709 }
710 bfunc = lcd_blockfuncs[drawmode];
711 dst = &lcd_framebuffer[y>>2][x];
712 ny = height - 1 + (y & 3);
713 mask = 0xFFu << (2 * (y & 3));
714 mask_bottom = 0xFFu >> (2 * (~ny & 3));
715
716 for (; ny >= 4; ny -= 4)
717 {
718 if (fillopt && (mask == 0xFFu))
719 memset(dst, bits, width);
720 else
721 {
722 fb_data *dst_row = dst;
723
724 dst_end = dst_row + width;
725 do
726 bfunc(dst_row++, mask, 0xFFu);
727 while (dst_row < dst_end);
728 }
729
730 dst += LCD_WIDTH;
731 mask = 0xFFu;
732 }
733 mask &= mask_bottom;
734
735 if (fillopt && (mask == 0xFFu))
736 memset(dst, bits, width);
737 else
738 {
739 dst_end = dst + width;
740 do
741 bfunc(dst++, mask, 0xFFu);
742 while (dst < dst_end);
743 }
744}
745
746/* About Rockbox' internal monochrome bitmap format:
747 *
748 * A bitmap contains one bit for every pixel that defines if that pixel is
749 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
750 * at top.
751 * The bytes are stored in row-major order, with byte 0 being top left,
752 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
753 * 0..7, the second row defines pixel row 8..15 etc.
754 *
755 * This is similar to the internal lcd hw format. */
756
757/* Draw a partial monochrome bitmap */
758void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
759 int stride, int x, int y, int width, int height)
760 ICODE_ATTR;
761void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
762 int stride, int x, int y, int width, int height)
763{
764 int shift, ny;
765 fb_data *dst, *dst_end;
766 unsigned mask, mask_bottom;
767 lcd_blockfunc_type *bfunc;
768
769 /* nothing to draw? */
770 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
771 || (x + width <= 0) || (y + height <= 0))
772 return;
773
774 /* clipping */
775 if (x < 0)
776 {
777 width += x;
778 src_x -= x;
779 x = 0;
780 }
781 if (y < 0)
782 {
783 height += y;
784 src_y -= y;
785 y = 0;
786 }
787 if (x + width > LCD_WIDTH)
788 width = LCD_WIDTH - x;
789 if (y + height > LCD_HEIGHT)
790 height = LCD_HEIGHT - y;
791
792 src += stride * (src_y >> 3) + src_x; /* move starting point */
793 src_y &= 7;
794 y -= src_y;
795 dst = &lcd_framebuffer[y>>2][x];
796 shift = y & 3;
797 ny = height - 1 + shift + src_y;
798
799 bfunc = lcd_blockfuncs[drawmode];
800 mask = 0xFFu << (shift + src_y);
801 mask_bottom = 0xFFu >> (~ny & 7);
802
803 if (shift == 0)
804 {
805 unsigned dmask1, dmask2, data;
806
807 for (; ny >= 8; ny -= 8)
808 {
809 const unsigned char *src_row = src;
810 fb_data *dst_row = dst + LCD_WIDTH;
811
812 dmask1 = dibits[mask&0x0F];
813 dmask2 = dibits[(mask>>4)&0x0F];
814 dst_end = dst_row + width;
815
816 if (dmask1 != 0)
817 {
818 do
819 {
820 data = *src_row++;
821 bfunc(dst_row - LCD_WIDTH, dmask1, dibits[data&0x0F]);
822 bfunc(dst_row++, dmask2, dibits[(data>>4)&0x0F]);
823 }
824 while (dst_row < dst_end);
825 }
826 else
827 {
828 do
829 bfunc(dst_row++, dmask2, dibits[((*src_row++)>>4)&0x0F]);
830 while (dst_row < dst_end);
831 }
832 src += stride;
833 dst += 2*LCD_WIDTH;
834 mask = 0xFFu;
835 }
836 mask &= mask_bottom;
837 dmask1 = dibits[mask&0x0F];
838 dmask2 = dibits[(mask>>4)&0x0F];
839 dst_end = dst + width;
840
841 if (dmask1 != 0)
842 {
843 if (dmask2 != 0)
844 {
845 do
846 {
847 data = *src++;
848 bfunc(dst, dmask1, dibits[data&0x0F]);
849 bfunc((dst++) + LCD_WIDTH, dmask2, dibits[(data>>4)&0x0F]);
850 }
851 while (dst < dst_end);
852 }
853 else
854 {
855 do
856 bfunc(dst++, dmask1, dibits[(*src++)&0x0F]);
857 while (dst < dst_end);
858 }
859 }
860 else
861 {
862 do
863 bfunc((dst++) + LCD_WIDTH, dmask2, dibits[((*src++)>>4)&0x0F]);
864 while (dst < dst_end);
865 }
866 }
867 else
868 {
869 dst_end = dst + width;
870 do
871 {
872 const unsigned char *src_col = src++;
873 fb_data *dst_col = dst++;
874 unsigned mask_col = mask;
875 unsigned data = 0;
876
877 for (y = ny; y >= 8; y -= 8)
878 {
879 data |= *src_col << shift;
880
881 if (mask_col & 0xFFu)
882 {
883 if (mask_col & 0x0F)
884 bfunc(dst_col, dibits[mask_col&0x0F], dibits[data&0x0F]);
885 bfunc(dst_col + LCD_WIDTH, dibits[(mask_col>>4)&0x0F],
886 dibits[(data>>4)&0x0F]);
887 mask_col = 0xFFu;
888 }
889 else
890 mask_col >>= 8;
891
892 src_col += stride;
893 dst_col += 2*LCD_WIDTH;
894 data >>= 8;
895 }
896 data |= *src_col << shift;
897 mask_bottom &= mask_col;
898 if (mask_bottom & 0x0F)
899 bfunc(dst_col, dibits[mask_bottom&0x0F], dibits[data&0x0F]);
900 if (mask_bottom & 0xF0)
901 bfunc(dst_col + LCD_WIDTH, dibits[(mask_bottom&0xF0)>>4],
902 dibits[(data>>4)&0x0F]);
903 }
904 while (dst < dst_end);
905 }
906}
907
908/* Draw a full monochrome bitmap */
909void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
910{
911 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
912}
913
914/* About Rockbox' internal native bitmap format:
915 *
916 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
917 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
918 * at top.
919 * The bytes are stored in row-major order, with byte 0 being top left,
920 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
921 * 0..3, the second row defines pixel row 4..7 etc.
922 *
923 * This is the same as the internal lcd hw format. */
924
925/* Draw a partial native bitmap */
926void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
927 int stride, int x, int y, int width, int height)
928 ICODE_ATTR;
929void lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
930 int stride, int x, int y, int width, int height)
931{
932 int shift, ny;
933 fb_data *dst, *dst_end;
934 unsigned mask, mask_bottom;
935
936 /* nothing to draw? */
937 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
938 || (x + width <= 0) || (y + height <= 0))
939 return;
940
941 /* clipping */
942 if (x < 0)
943 {
944 width += x;
945 src_x -= x;
946 x = 0;
947 }
948 if (y < 0)
949 {
950 height += y;
951 src_y -= y;
952 y = 0;
953 }
954 if (x + width > LCD_WIDTH)
955 width = LCD_WIDTH - x;
956 if (y + height > LCD_HEIGHT)
957 height = LCD_HEIGHT - y;
958
959 src += stride * (src_y >> 2) + src_x; /* move starting point */
960 src_y &= 3;
961 y -= src_y;
962 dst = &lcd_framebuffer[y>>2][x];
963 shift = y & 3;
964 ny = height - 1 + shift + src_y;
965
966 mask = 0xFFu << (2 * (shift + src_y));
967 mask_bottom = 0xFFu >> (2 * (~ny & 3));
968
969 if (shift == 0)
970 {
971 for (; ny >= 4; ny -= 4)
972 {
973 if (mask == 0xFFu)
974 memcpy(dst, src, width);
975 else
976 {
977 const fb_data *src_row = src;
978 fb_data *dst_row = dst;
979
980 dst_end = dst_row + width;
981 do
982 setblock(dst_row++, mask, *src_row++);
983 while (dst_row < dst_end);
984 }
985 src += stride;
986 dst += LCD_WIDTH;
987 mask = 0xFFu;
988 }
989 mask &= mask_bottom;
990
991 if (mask == 0xFFu)
992 memcpy(dst, src, width);
993 else
994 {
995 dst_end = dst + width;
996 do
997 setblock(dst++, mask, *src++);
998 while (dst < dst_end);
999 }
1000 }
1001 else
1002 {
1003 shift *= 2;
1004 dst_end = dst + width;
1005 do
1006 {
1007 const fb_data *src_col = src++;
1008 fb_data *dst_col = dst++;
1009 unsigned mask_col = mask;
1010 unsigned data = 0;
1011
1012 for (y = ny; y >= 4; y -= 4)
1013 {
1014 data |= *src_col << shift;
1015
1016 if (mask_col & 0xFFu)
1017 {
1018 setblock(dst_col, mask_col, data);
1019 mask_col = 0xFFu;
1020 }
1021 else
1022 mask_col >>= 8;
1023
1024 src_col += stride;
1025 dst_col += LCD_WIDTH;
1026 data >>= 8;
1027 }
1028 data |= *src_col << shift;
1029 setblock(dst_col, mask_col & mask_bottom, data);
1030 }
1031 while (dst < dst_end);
1032 }
1033}
1034
1035/* Draw a full native bitmap */
1036void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
1037{
1038 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
1039}
1040
1041/* put a string at a given pixel position, skipping first ofs pixel columns */
1042static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
1043{
1044 unsigned short ch;
1045 unsigned short *ucs;
1046 struct font* pf = font_get(curfont);
1047
1048 ucs = bidi_l2v(str, 1);
1049
1050 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
1051 {
1052 int width;
1053 const unsigned char *bits;
1054
1055 /* get proportional width and glyph bits */
1056 width = font_get_width(pf,ch);
1057
1058 if (ofs > width)
1059 {
1060 ofs -= width;
1061 continue;
1062 }
1063
1064 bits = font_get_bits(pf, ch);
1065
1066 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
1067 pf->height);
1068
1069 x += width - ofs;
1070 ofs = 0;
1071 }
1072}
1073
1074/* put a string at a given pixel position */
1075void lcd_putsxy(int x, int y, const unsigned char *str)
1076{
1077 lcd_putsxyofs(x, y, 0, str);
1078}
1079
1080/*** line oriented text output ***/
1081
1082/* put a string at a given char position */
1083void lcd_puts(int x, int y, const unsigned char *str)
1084{
1085 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
1086}
1087
1088void lcd_puts_style(int x, int y, const unsigned char *str, int style)
1089{
1090 lcd_puts_style_offset(x, y, str, style, 0);
1091}
1092
1093void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
1094{
1095 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
1096}
1097
1098/* put a string at a given char position, style, and pixel position,
1099 * skipping first offset pixel columns */
1100void lcd_puts_style_offset(int x, int y, const unsigned char *str,
1101 int style, int offset)
1102{
1103 int xpos,ypos,w,h,xrect;
1104 int lastmode = drawmode;
1105
1106 /* make sure scrolling is turned off on the line we are updating */
1107 scrolling_lines &= ~(1 << y);
1108
1109 if(!str || !str[0])
1110 return;
1111
1112 lcd_getstringsize(str, &w, &h);
1113 xpos = xmargin + x*w / utf8length((char *)str);
1114 ypos = ymargin + y*h;
1115 drawmode = (style & STYLE_INVERT) ?
1116 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1117 lcd_putsxyofs(xpos, ypos, offset, str);
1118 drawmode ^= DRMODE_INVERSEVID;
1119 xrect = xpos + MAX(w - offset, 0);
1120 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
1121 drawmode = lastmode;
1122}
1123
1124/*** scrolling ***/
1125
1126/* Reverse the invert setting of the scrolling line (if any) at given char
1127 position. Setting will go into affect next time line scrolls. */
1128void lcd_invertscroll(int x, int y)
1129{
1130 struct scrollinfo* s;
1131
1132 (void)x;
1133
1134 s = &scroll[y];
1135 s->invert = !s->invert;
1136}
1137
1138void lcd_stop_scroll(void)
1139{
1140 scrolling_lines=0;
1141}
1142
1143void lcd_scroll_speed(int speed)
1144{
1145 scroll_ticks = scroll_tick_table[speed];
1146}
1147
1148void lcd_scroll_step(int step)
1149{
1150 scroll_step = step;
1151}
1152
1153void lcd_scroll_delay(int ms)
1154{
1155 scroll_delay = ms / (HZ / 10);
1156}
1157
1158void lcd_bidir_scroll(int percent)
1159{
1160 bidir_limit = percent;
1161}
1162
1163void lcd_puts_scroll(int x, int y, const unsigned char *string)
1164{
1165 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
1166}
1167
1168void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
1169{
1170 lcd_puts_scroll_style_offset(x, y, string, style, 0);
1171}
1172
1173void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
1174{
1175 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
1176}
1177
1178void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
1179 int style, int offset)
1180{
1181 struct scrollinfo* s;
1182 int w, h;
1183
1184 s = &scroll[y];
1185
1186 s->start_tick = current_tick + scroll_delay;
1187 s->invert = false;
1188 if (style & STYLE_INVERT) {
1189 s->invert = true;
1190 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
1191 }
1192 else
1193 lcd_puts_offset(x,y,string,offset);
1194
1195 lcd_getstringsize(string, &w, &h);
1196
1197 if (LCD_WIDTH - x * 8 - xmargin < w) {
1198 /* prepare scroll line */
1199 char *end;
1200
1201 memset(s->line, 0, sizeof s->line);
1202 strcpy(s->line, (char *)string);
1203
1204 /* get width */
1205 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1206
1207 /* scroll bidirectional or forward only depending on the string
1208 width */
1209 if ( bidir_limit ) {
1210 s->bidir = s->width < (LCD_WIDTH - xmargin) *
1211 (100 + bidir_limit) / 100;
1212 }
1213 else
1214 s->bidir = false;
1215
1216 if (!s->bidir) { /* add spaces if scrolling in the round */
1217 strcat(s->line, " ");
1218 /* get new width incl. spaces */
1219 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1220 }
1221
1222 end = strchr(s->line, '\0');
1223 strncpy(end, (char *)string, LCD_WIDTH/2);
1224
1225 s->len = utf8length((char *)string);
1226 s->offset = offset;
1227 s->startx = xmargin + x * s->width / s->len;
1228 s->backward = false;
1229 scrolling_lines |= (1<<y);
1230 }
1231 else
1232 /* force a bit switch-off since it doesn't scroll */
1233 scrolling_lines &= ~(1<<y);
1234}
1235
1236static void scroll_thread(void)
1237{
1238 struct font* pf;
1239 struct scrollinfo* s;
1240 int index;
1241 int xpos, ypos;
1242 int lastmode;
1243
1244 /* initialize scroll struct array */
1245 scrolling_lines = 0;
1246
1247 while ( 1 ) {
1248 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
1249 /* really scroll? */
1250 if ( !(scrolling_lines&(1<<index)) )
1251 continue;
1252
1253 s = &scroll[index];
1254
1255 /* check pause */
1256 if (TIME_BEFORE(current_tick, s->start_tick))
1257 continue;
1258
1259 if (s->backward)
1260 s->offset -= scroll_step;
1261 else
1262 s->offset += scroll_step;
1263
1264 pf = font_get(curfont);
1265 xpos = s->startx;
1266 ypos = ymargin + index * pf->height;
1267
1268 if (s->bidir) { /* scroll bidirectional */
1269 if (s->offset <= 0) {
1270 /* at beginning of line */
1271 s->offset = 0;
1272 s->backward = false;
1273 s->start_tick = current_tick + scroll_delay * 2;
1274 }
1275 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
1276 /* at end of line */
1277 s->offset = s->width - (LCD_WIDTH - xpos);
1278 s->backward = true;
1279 s->start_tick = current_tick + scroll_delay * 2;
1280 }
1281 }
1282 else {
1283 /* scroll forward the whole time */
1284 if (s->offset >= s->width)
1285 s->offset %= s->width;
1286 }
1287
1288 lastmode = drawmode;
1289 drawmode = s->invert ?
1290 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1291 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
1292 drawmode = lastmode;
1293 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1294 }
1295
1296 sleep(scroll_ticks);
1297 }
1298}