clean up some debugging output.
[Rockbox.git] / firmware / drivers / lcd-2bit-vert.c
blobd12ebbc9752f8b3811028e1a3d094b62e4429576
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 by Linus Nielsen Feltzing
12 * 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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
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"
35 #include "scroll_engine.h"
37 /*** globals ***/
39 fb_data lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH] IBSS_ATTR;
41 const 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
46 static const unsigned char pixmask[4] ICONST_ATTR = {
47 0x03, 0x0C, 0x30, 0xC0
50 static fb_data* lcd_backdrop = NULL;
51 static long lcd_backdrop_offset IDATA_ATTR = 0;
53 static struct viewport default_vp =
55 .x = 0,
56 .y = 0,
57 .width = LCD_WIDTH,
58 .height = LCD_HEIGHT,
59 .font = FONT_SYSFIXED,
60 .drawmode = DRMODE_SOLID,
61 .fg_pattern = LCD_DEFAULT_FG,
62 .bg_pattern = LCD_DEFAULT_BG
65 static struct viewport* current_vp IBSS_ATTR;
66 static unsigned fg_pattern IBSS_ATTR;
67 static unsigned bg_pattern IBSS_ATTR;
69 /* LCD init */
70 void lcd_init(void)
72 /* Initialise the viewport */
73 lcd_set_viewport(NULL);
75 lcd_clear_display();
76 /* Call device specific init */
77 lcd_init_device();
78 scroll_init();
81 /*** Viewports ***/
83 void lcd_set_viewport(struct viewport* vp)
85 if (vp == NULL)
86 current_vp = &default_vp;
87 else
88 current_vp = vp;
90 fg_pattern = 0x55 * (~current_vp->fg_pattern & 3);
91 bg_pattern = 0x55 * (~current_vp->bg_pattern & 3);
94 void lcd_update_viewport(void)
96 lcd_update_rect(current_vp->x, current_vp->y,
97 current_vp->width, current_vp->height);
100 void lcd_update_viewport_rect(int x, int y, int width, int height)
102 lcd_update_rect(current_vp->x + x, current_vp->y + y, width, height);
106 /*** parameter handling ***/
108 void lcd_set_drawmode(int mode)
110 current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
113 int lcd_get_drawmode(void)
115 return current_vp->drawmode;
118 void lcd_set_foreground(unsigned brightness)
120 current_vp->fg_pattern = brightness;
121 fg_pattern = 0x55 * (~brightness & 3);
124 unsigned lcd_get_foreground(void)
126 return current_vp->fg_pattern;
129 void lcd_set_background(unsigned brightness)
131 current_vp->bg_pattern = brightness;
132 bg_pattern = 0x55 * (~brightness & 3);
135 unsigned lcd_get_background(void)
137 return current_vp->bg_pattern;
140 void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
142 lcd_set_drawmode(mode);
143 lcd_set_foreground(fg_brightness);
144 lcd_set_background(bg_brightness);
147 int lcd_getwidth(void)
149 return current_vp->width;
152 int lcd_getheight(void)
154 return current_vp->height;
157 void lcd_setfont(int newfont)
159 current_vp->font = newfont;
162 int lcd_getfont(void)
164 return current_vp->font;
167 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
169 return font_getstringsize(str, w, h, current_vp->font);
172 /*** low-level drawing functions ***/
174 static void setpixel(int x, int y)
176 unsigned mask = pixmask[y & 3];
177 fb_data *address = &lcd_framebuffer[y>>2][x];
178 unsigned data = *address;
180 *address = data ^ ((data ^ fg_pattern) & mask);
183 static void clearpixel(int x, int y)
185 unsigned mask = pixmask[y & 3];
186 fb_data *address = &lcd_framebuffer[y>>2][x];
187 unsigned data = *address;
189 *address = data ^ ((data ^ bg_pattern) & mask);
192 static void clearimgpixel(int x, int y)
194 unsigned mask = pixmask[y & 3];
195 fb_data *address = &lcd_framebuffer[y>>2][x];
196 unsigned data = *address;
198 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask);
201 static void flippixel(int x, int y)
203 unsigned mask = pixmask[y & 3];
204 fb_data *address = &lcd_framebuffer[y>>2][x];
206 *address ^= mask;
209 static void nopixel(int x, int y)
211 (void)x;
212 (void)y;
215 lcd_pixelfunc_type* const lcd_pixelfuncs_bgcolor[8] = {
216 flippixel, nopixel, setpixel, setpixel,
217 nopixel, clearpixel, nopixel, clearpixel
220 lcd_pixelfunc_type* const lcd_pixelfuncs_backdrop[8] = {
221 flippixel, nopixel, setpixel, setpixel,
222 nopixel, clearimgpixel, nopixel, clearimgpixel
226 lcd_pixelfunc_type* const * lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
228 /* 'mask' and 'bits' contain 2 bits per pixel */
229 static void ICODE_ATTR flipblock(fb_data *address, unsigned mask,
230 unsigned bits)
232 *address ^= bits & mask;
235 static void ICODE_ATTR bgblock(fb_data *address, unsigned mask,
236 unsigned bits)
238 unsigned data = *address;
240 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
243 static void ICODE_ATTR bgimgblock(fb_data *address, unsigned mask,
244 unsigned bits)
246 unsigned data = *address;
248 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & ~bits);
251 static void ICODE_ATTR fgblock(fb_data *address, unsigned mask,
252 unsigned bits)
254 unsigned data = *address;
256 *address = data ^ ((data ^ fg_pattern) & mask & bits);
259 static void ICODE_ATTR solidblock(fb_data *address, unsigned mask,
260 unsigned bits)
262 unsigned data = *address;
263 unsigned bgp = bg_pattern;
265 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
266 *address = data ^ ((data ^ bits) & mask);
269 static void ICODE_ATTR solidimgblock(fb_data *address, unsigned mask,
270 unsigned bits)
272 unsigned data = *address;
273 unsigned bgp = *(address + lcd_backdrop_offset);
275 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
276 *address = data ^ ((data ^ bits) & mask);
279 static void ICODE_ATTR flipinvblock(fb_data *address, unsigned mask,
280 unsigned bits)
282 *address ^= ~bits & mask;
285 static void ICODE_ATTR bginvblock(fb_data *address, unsigned mask,
286 unsigned bits)
288 unsigned data = *address;
290 *address = data ^ ((data ^ bg_pattern) & mask & bits);
293 static void ICODE_ATTR bgimginvblock(fb_data *address, unsigned mask,
294 unsigned bits)
296 unsigned data = *address;
298 *address = data ^ ((data ^ *(address + lcd_backdrop_offset)) & mask & bits);
301 static void ICODE_ATTR fginvblock(fb_data *address, unsigned mask,
302 unsigned bits)
304 unsigned data = *address;
306 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
309 static void ICODE_ATTR solidinvblock(fb_data *address, unsigned mask,
310 unsigned bits)
312 unsigned data = *address;
313 unsigned fgp = fg_pattern;
315 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
316 *address = data ^ ((data ^ bits) & mask);
319 static void ICODE_ATTR solidimginvblock(fb_data *address, unsigned mask,
320 unsigned bits)
322 unsigned data = *address;
323 unsigned fgp = fg_pattern;
325 bits = fgp ^ ((fgp ^ *(address + lcd_backdrop_offset)) & bits);
326 *address = data ^ ((data ^ bits) & mask);
329 lcd_blockfunc_type* const lcd_blockfuncs_bgcolor[8] = {
330 flipblock, bgblock, fgblock, solidblock,
331 flipinvblock, bginvblock, fginvblock, solidinvblock
334 lcd_blockfunc_type* const lcd_blockfuncs_backdrop[8] = {
335 flipblock, bgimgblock, fgblock, solidimgblock,
336 flipinvblock, bgimginvblock, fginvblock, solidimginvblock
339 lcd_blockfunc_type* const * lcd_blockfuncs = lcd_blockfuncs_bgcolor;
342 void lcd_set_backdrop(fb_data* backdrop)
344 lcd_backdrop = backdrop;
345 if (backdrop)
347 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
348 lcd_pixelfuncs = lcd_pixelfuncs_backdrop;
349 lcd_blockfuncs = lcd_blockfuncs_backdrop;
351 else
353 lcd_backdrop_offset = 0;
354 lcd_pixelfuncs = lcd_pixelfuncs_bgcolor;
355 lcd_blockfuncs = lcd_blockfuncs_bgcolor;
359 fb_data* lcd_get_backdrop(void)
361 return lcd_backdrop;
365 static inline void setblock(fb_data *address, unsigned mask, unsigned bits)
367 unsigned data = *address;
369 bits ^= data;
370 *address = data ^ (bits & mask);
373 /*** drawing functions ***/
375 /* Clear the whole display */
376 void lcd_clear_display(void)
378 if (current_vp->drawmode & DRMODE_INVERSEVID)
380 memset(lcd_framebuffer, fg_pattern, sizeof lcd_framebuffer);
382 else
384 if (lcd_backdrop)
385 memcpy(lcd_framebuffer, lcd_backdrop, sizeof lcd_framebuffer);
386 else
387 memset(lcd_framebuffer, bg_pattern, sizeof lcd_framebuffer);
390 lcd_scroll_info.lines = 0;
393 /* Clear the current viewport */
394 void lcd_clear_viewport(void)
396 int lastmode;
398 if (current_vp == &default_vp)
400 lcd_clear_display();
402 else
404 lastmode = current_vp->drawmode;
406 /* Invert the INVERSEVID bit and set basic mode to SOLID */
407 current_vp->drawmode = (~lastmode & DRMODE_INVERSEVID) |
408 DRMODE_SOLID;
410 lcd_fillrect(0, 0, current_vp->width, current_vp->height);
412 current_vp->drawmode = lastmode;
414 lcd_scroll_stop(current_vp);
418 /* Set a single pixel */
419 void lcd_drawpixel(int x, int y)
421 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);
426 /* Draw a line */
427 void lcd_drawline(int x1, int y1, int x2, int y2)
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;
435 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[current_vp->drawmode];
437 deltax = abs(x2 - x1);
438 if (deltax == 0)
440 DEBUGF("lcd_drawline() called for vertical line - optimisation.\n");
441 lcd_vline(x1, y1, y2);
442 return;
444 deltay = abs(y2 - y1);
445 if (deltay == 0)
447 DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n");
448 lcd_hline(x1, x2, y1);
449 return;
451 xinc2 = 1;
452 yinc2 = 1;
454 if (deltax >= deltay)
456 numpixels = deltax;
457 d = 2 * deltay - deltax;
458 dinc1 = deltay * 2;
459 dinc2 = (deltay - deltax) * 2;
460 xinc1 = 1;
461 yinc1 = 0;
463 else
465 numpixels = deltay;
466 d = 2 * deltax - deltay;
467 dinc1 = deltax * 2;
468 dinc2 = (deltax - deltay) * 2;
469 xinc1 = 0;
470 yinc1 = 1;
472 numpixels++; /* include endpoints */
474 if (x1 > x2)
476 xinc1 = -xinc1;
477 xinc2 = -xinc2;
480 if (y1 > y2)
482 yinc1 = -yinc1;
483 yinc2 = -yinc2;
486 x = x1;
487 y = y1;
489 for (i = 0; i < numpixels; i++)
491 if (((unsigned)x < (unsigned)current_vp->width) &&
492 ((unsigned)y < (unsigned)current_vp->height))
493 pfunc(current_vp->x + x, current_vp->y + y);
495 if (d < 0)
497 d += dinc1;
498 x += xinc1;
499 y += yinc1;
501 else
503 d += dinc2;
504 x += xinc2;
505 y += yinc2;
510 /* Draw a horizontal line (optimised) */
511 void lcd_hline(int x1, int x2, int y)
513 int x;
514 int width;
515 fb_data *dst, *dst_end;
516 unsigned mask;
517 lcd_blockfunc_type *bfunc;
519 /* direction flip */
520 if (x2 < x1)
522 x = x1;
523 x1 = x2;
524 x2 = x;
527 /* nothing to draw? */
528 if (((unsigned)y >= (unsigned)current_vp->height) || (x1 >= current_vp->width)
529 || (x2 < 0))
530 return;
532 /* clipping */
533 if (x1 < 0)
534 x1 = 0;
535 if (x2 >= current_vp->width)
536 x2 = current_vp->width-1;
538 width = x2 - x1 + 1;
540 /* adjust x1 and y to viewport */
541 x1 += current_vp->x;
542 y += current_vp->y;
544 bfunc = lcd_blockfuncs[current_vp->drawmode];
545 dst = &lcd_framebuffer[y>>2][x1];
546 mask = pixmask[y & 3];
548 dst_end = dst + width;
550 bfunc(dst++, mask, 0xFFu);
551 while (dst < dst_end);
554 /* Draw a vertical line (optimised) */
555 void lcd_vline(int x, int y1, int y2)
557 int ny;
558 fb_data *dst;
559 unsigned mask, mask_bottom;
560 lcd_blockfunc_type *bfunc;
562 /* direction flip */
563 if (y2 < y1)
565 ny = y1;
566 y1 = y2;
567 y2 = ny;
570 /* nothing to draw? */
571 if (((unsigned)x >= (unsigned)current_vp->width) || (y1 >= current_vp->height)
572 || (y2 < 0))
573 return;
575 /* clipping */
576 if (y1 < 0)
577 y1 = 0;
578 if (y2 >= current_vp->height)
579 y2 = current_vp->height-1;
581 /* adjust for viewport */
582 y1 += current_vp->y;
583 y2 += current_vp->y;
584 x += current_vp->x;
586 bfunc = lcd_blockfuncs[current_vp->drawmode];
587 dst = &lcd_framebuffer[y1>>2][x];
588 ny = y2 - (y1 & ~3);
589 mask = 0xFFu << (2 * (y1 & 3));
590 mask_bottom = 0xFFu >> (2 * (~ny & 3));
592 for (; ny >= 4; ny -= 4)
594 bfunc(dst, mask, 0xFFu);
595 dst += LCD_WIDTH;
596 mask = 0xFFu;
598 mask &= mask_bottom;
599 bfunc(dst, mask, 0xFFu);
602 /* Draw a rectangular box */
603 void lcd_drawrect(int x, int y, int width, int height)
605 if ((width <= 0) || (height <= 0))
606 return;
608 int x2 = x + width - 1;
609 int y2 = y + height - 1;
611 lcd_vline(x, y, y2);
612 lcd_vline(x2, y, y2);
613 lcd_hline(x, x2, y);
614 lcd_hline(x, x2, y2);
617 /* Fill a rectangular area */
618 void lcd_fillrect(int x, int y, int width, int height)
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;
627 /* nothing to draw? */
628 if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
629 || (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
630 return;
632 /* clipping */
633 if (x < 0)
635 width += x;
636 x = 0;
638 if (y < 0)
640 height += y;
641 y = 0;
643 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;
648 /* adjust for viewport */
649 x += current_vp->x;
650 y += current_vp->y;
652 if (current_vp->drawmode & DRMODE_INVERSEVID)
654 if ((current_vp->drawmode & DRMODE_BG) && !lcd_backdrop)
656 fillopt = true;
657 bits = bg_pattern;
660 else
662 if (current_vp->drawmode & DRMODE_FG)
664 fillopt = true;
665 bits = fg_pattern;
668 bfunc = lcd_blockfuncs[current_vp->drawmode];
669 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));
674 for (; ny >= 4; ny -= 4)
676 if (fillopt && (mask == 0xFFu))
677 memset(dst, bits, width);
678 else
680 fb_data *dst_row = dst;
682 dst_end = dst_row + width;
684 bfunc(dst_row++, mask, 0xFFu);
685 while (dst_row < dst_end);
688 dst += LCD_WIDTH;
689 mask = 0xFFu;
691 mask &= mask_bottom;
693 if (fillopt && (mask == 0xFFu))
694 memset(dst, bits, width);
695 else
697 dst_end = dst + width;
699 bfunc(dst++, mask, 0xFFu);
700 while (dst < dst_end);
704 /* About Rockbox' internal monochrome bitmap format:
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.
713 * This is similar to the internal lcd hw format. */
715 /* Draw a partial monochrome bitmap */
716 void 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)
720 int shift, ny;
721 fb_data *dst, *dst_end;
722 unsigned mask, mask_bottom;
723 lcd_blockfunc_type *bfunc;
725 /* nothing to draw? */
726 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
727 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
728 return;
730 /* clipping */
731 if (x < 0)
733 width += x;
734 src_x -= x;
735 x = 0;
737 if (y < 0)
739 height += y;
740 src_y -= y;
741 y = 0;
743 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;
748 /* adjust for viewport */
749 x += current_vp->x;
750 y += current_vp->y;
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;
759 bfunc = lcd_blockfuncs[current_vp->drawmode];
760 mask = 0xFFu << (shift + src_y);
761 mask_bottom = 0xFFu >> (~ny & 7);
763 if (shift == 0)
765 unsigned dmask1, dmask2, data;
767 for (; ny >= 8; ny -= 8)
769 const unsigned char *src_row = src;
770 fb_data *dst_row = dst + LCD_WIDTH;
772 dmask1 = lcd_dibits[mask&0x0F];
773 dmask2 = lcd_dibits[(mask>>4)&0x0F];
774 dst_end = dst_row + width;
776 if (dmask1 != 0)
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]);
784 while (dst_row < dst_end);
786 else
789 bfunc(dst_row++, dmask2, lcd_dibits[((*src_row++)>>4)&0x0F]);
790 while (dst_row < dst_end);
792 src += stride;
793 dst += 2*LCD_WIDTH;
794 mask = 0xFFu;
796 mask &= mask_bottom;
797 dmask1 = lcd_dibits[mask&0x0F];
798 dmask2 = lcd_dibits[(mask>>4)&0x0F];
799 dst_end = dst + width;
801 if (dmask1 != 0)
803 if (dmask2 != 0)
807 data = *src++;
808 bfunc(dst, dmask1, lcd_dibits[data&0x0F]);
809 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[(data>>4)&0x0F]);
811 while (dst < dst_end);
813 else
816 bfunc(dst++, dmask1, lcd_dibits[(*src++)&0x0F]);
817 while (dst < dst_end);
820 else
823 bfunc((dst++) + LCD_WIDTH, dmask2, lcd_dibits[((*src++)>>4)&0x0F]);
824 while (dst < dst_end);
827 else
829 dst_end = dst + width;
832 const unsigned char *src_col = src++;
833 fb_data *dst_col = dst++;
834 unsigned mask_col = mask;
835 unsigned data = 0;
837 for (y = ny; y >= 8; y -= 8)
839 data |= *src_col << shift;
841 if (mask_col & 0xFFu)
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;
849 else
850 mask_col >>= 8;
852 src_col += stride;
853 dst_col += 2*LCD_WIDTH;
854 data >>= 8;
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]);
864 while (dst < dst_end);
868 /* Draw a full monochrome bitmap */
869 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
871 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
874 /* About Rockbox' internal native bitmap format:
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.
883 * This is the same as the internal lcd hw format. */
885 /* Draw a partial native bitmap */
886 void 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)
890 int shift, ny;
891 fb_data *dst, *dst_end;
892 unsigned mask, mask_bottom;
894 /* nothing to draw? */
895 if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
896 || (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
897 return;
899 /* clipping */
900 if (x < 0)
902 width += x;
903 src_x -= x;
904 x = 0;
906 if (y < 0)
908 height += y;
909 src_y -= y;
910 y = 0;
912 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;
917 /* adjust for viewport */
918 x += current_vp->x;
919 y += current_vp->y;
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;
928 mask = 0xFFu << (2 * (shift + src_y));
929 mask_bottom = 0xFFu >> (2 * (~ny & 3));
931 if (shift == 0)
933 for (; ny >= 4; ny -= 4)
935 if (mask == 0xFFu)
936 memcpy(dst, src, width);
937 else
939 const fb_data *src_row = src;
940 fb_data *dst_row = dst;
942 dst_end = dst_row + width;
944 setblock(dst_row++, mask, *src_row++);
945 while (dst_row < dst_end);
947 src += stride;
948 dst += LCD_WIDTH;
949 mask = 0xFFu;
951 mask &= mask_bottom;
953 if (mask == 0xFFu)
954 memcpy(dst, src, width);
955 else
957 dst_end = dst + width;
959 setblock(dst++, mask, *src++);
960 while (dst < dst_end);
963 else
965 shift *= 2;
966 dst_end = dst + width;
969 const fb_data *src_col = src++;
970 fb_data *dst_col = dst++;
971 unsigned mask_col = mask;
972 unsigned data = 0;
974 for (y = ny; y >= 4; y -= 4)
976 data |= *src_col << shift;
978 if (mask_col & 0xFFu)
980 setblock(dst_col, mask_col, data);
981 mask_col = 0xFFu;
983 else
984 mask_col >>= 8;
986 src_col += stride;
987 dst_col += LCD_WIDTH;
988 data >>= 8;
990 data |= *src_col << shift;
991 setblock(dst_col, mask_col & mask_bottom, data);
993 while (dst < dst_end);
997 /* Draw a full native bitmap */
998 void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
1000 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
1003 /* put a string at a given pixel position, skipping first ofs pixel columns */
1004 static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
1006 unsigned short ch;
1007 unsigned short *ucs;
1008 struct font* pf = font_get(current_vp->font);
1010 ucs = bidi_l2v(str, 1);
1012 while ((ch = *ucs++) != 0 && x < current_vp->width)
1014 int width;
1015 const unsigned char *bits;
1017 /* get proportional width and glyph bits */
1018 width = font_get_width(pf,ch);
1020 if (ofs > width)
1022 ofs -= width;
1023 continue;
1026 bits = font_get_bits(pf, ch);
1028 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
1029 pf->height);
1031 x += width - ofs;
1032 ofs = 0;
1036 /* put a string at a given pixel position */
1037 void lcd_putsxy(int x, int y, const unsigned char *str)
1039 lcd_putsxyofs(x, y, 0, str);
1042 /*** line oriented text output ***/
1044 /* put a string at a given char position */
1045 void lcd_puts(int x, int y, const unsigned char *str)
1047 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
1050 void lcd_puts_style(int x, int y, const unsigned char *str, int style)
1052 lcd_puts_style_offset(x, y, str, style, 0);
1055 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
1057 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
1060 /* put a string at a given char position, style, and pixel position,
1061 * skipping first offset pixel columns */
1062 void lcd_puts_style_offset(int x, int y, const unsigned char *str,
1063 int style, int offset)
1065 int xpos,ypos,w,h,xrect;
1066 int lastmode = current_vp->drawmode;
1068 /* make sure scrolling is turned off on the line we are updating */
1069 lcd_scroll_stop_line(current_vp, y);
1071 if(!str || !str[0])
1072 return;
1074 lcd_getstringsize(str, &w, &h);
1075 xpos = x*w / utf8length((char *)str);
1076 ypos = y*h;
1077 current_vp->drawmode = (style & STYLE_INVERT) ?
1078 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1079 lcd_putsxyofs(xpos, ypos, offset, str);
1080 current_vp->drawmode ^= DRMODE_INVERSEVID;
1081 xrect = xpos + MAX(w - offset, 0);
1082 lcd_fillrect(xrect, ypos, current_vp->width - xrect, h);
1083 current_vp->drawmode = lastmode;
1086 /*** scrolling ***/
1088 void lcd_puts_scroll(int x, int y, const unsigned char *string)
1090 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
1093 void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
1095 lcd_puts_scroll_style_offset(x, y, string, style, 0);
1098 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
1100 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
1103 void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
1104 int style, int offset)
1106 struct scrollinfo* s;
1107 int w, h;
1109 if ((unsigned)y >= (unsigned)current_vp->height)
1110 return;
1112 /* remove any previously scrolling line at the same location */
1113 lcd_scroll_stop_line(current_vp, y);
1115 if (lcd_scroll_info.lines >= LCD_SCROLLABLE_LINES) return;
1117 s = &lcd_scroll_info.scroll[lcd_scroll_info.lines];
1119 s->start_tick = current_tick + lcd_scroll_info.delay;
1120 s->style = style;
1121 if (style & STYLE_INVERT) {
1122 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
1124 else
1125 lcd_puts_offset(x,y,string,offset);
1127 lcd_getstringsize(string, &w, &h);
1129 if (current_vp->width - x * 8< w) {
1130 /* prepare scroll line */
1131 char *end;
1133 memset(s->line, 0, sizeof s->line);
1134 strcpy(s->line, (char *)string);
1136 /* get width */
1137 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
1139 /* scroll bidirectional or forward only depending on the string
1140 width */
1141 if ( lcd_scroll_info.bidir_limit ) {
1142 s->bidir = s->width < (current_vp->width) *
1143 (100 + lcd_scroll_info.bidir_limit) / 100;
1145 else
1146 s->bidir = false;
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);
1154 end = strchr(s->line, '\0');
1155 strncpy(end, (char *)string, current_vp->width/2);
1157 s->vp = current_vp;
1158 s->y = y;
1159 s->len = utf8length((char *)string);
1160 s->offset = offset;
1161 s->startx = x * s->width / s->len;
1162 s->backward = false;
1164 lcd_scroll_info.lines++;
1168 void lcd_scroll_fn(void)
1170 struct font* pf;
1171 struct scrollinfo* s;
1172 int index;
1173 int xpos, ypos;
1174 int lastmode;
1175 struct viewport* old_vp = current_vp;
1177 for ( index = 0; index < lcd_scroll_info.lines; index++ ) {
1178 s = &lcd_scroll_info.scroll[index];
1180 /* check pause */
1181 if (TIME_BEFORE(current_tick, s->start_tick))
1182 continue;
1184 lcd_set_viewport(s->vp);
1186 if (s->backward)
1187 s->offset -= lcd_scroll_info.step;
1188 else
1189 s->offset += lcd_scroll_info.step;
1191 pf = font_get(current_vp->font);
1192 xpos = s->startx;
1193 ypos = s->y * pf->height;
1195 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;
1202 if (s->offset >= s->width - (current_vp->width - xpos)) {
1203 /* at end of line */
1204 s->offset = s->width - (current_vp->width - xpos);
1205 s->backward = true;
1206 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
1209 else {
1210 /* scroll forward the whole time */
1211 if (s->offset >= s->width)
1212 s->offset %= s->width;
1215 lastmode = current_vp->drawmode;
1216 current_vp->drawmode = (s->style&STYLE_INVERT) ?
1217 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1218 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
1219 current_vp->drawmode = lastmode;
1220 lcd_update_viewport_rect(xpos, ypos,
1221 current_vp->width - xpos, pf->height);
1224 lcd_set_viewport(old_vp);