summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd-2bit-vert.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/lcd-2bit-vert.c')
-rw-r--r--firmware/drivers/lcd-2bit-vert.c1093
1 files changed, 1093 insertions, 0 deletions
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