ypr0: Correct .scrobbler.log path for YPR0
[maemo-rb.git] / firmware / drivers / lcd-16bit-common.c
blob06bb0a25fde28423a59adacad0ce4e016dac432c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Dave Chapman
12 * Rockbox driver for 16-bit colour LCDs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
25 /* to be #included by lcd-16bit*.c */
27 #if !defined(ROW_INC) || !defined(COL_INC)
28 #error ROW_INC or COL_INC not defined
29 #endif
31 enum fill_opt {
32 OPT_NONE = 0,
33 OPT_SET,
34 OPT_COPY
37 /*** globals ***/
38 fb_data lcd_static_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH]
39 IRAM_LCDFRAMEBUFFER CACHEALIGN_AT_LEAST_ATTR(16);
40 fb_data *lcd_framebuffer = &lcd_static_framebuffer[0][0];
42 static fb_data* lcd_backdrop = NULL;
43 static long lcd_backdrop_offset IDATA_ATTR = 0;
45 static struct viewport default_vp =
47 .x = 0,
48 .y = 0,
49 .width = LCD_WIDTH,
50 .height = LCD_HEIGHT,
51 .font = FONT_SYSFIXED,
52 .drawmode = DRMODE_SOLID,
53 .fg_pattern = LCD_DEFAULT_FG,
54 .bg_pattern = LCD_DEFAULT_BG,
55 .lss_pattern = LCD_DEFAULT_BG,
56 .lse_pattern = LCD_DEFAULT_BG,
57 .lst_pattern = LCD_DEFAULT_BG,
60 static struct viewport* current_vp IDATA_ATTR = &default_vp;
62 /* LCD init */
63 void lcd_init(void)
65 lcd_clear_display();
67 /* Call device specific init */
68 lcd_init_device();
69 scroll_init();
71 /*** Viewports ***/
73 void lcd_set_viewport(struct viewport* vp)
75 if (vp == NULL)
76 current_vp = &default_vp;
77 else
78 current_vp = vp;
80 #if defined(SIMULATOR)
81 /* Force the viewport to be within bounds. If this happens it should
82 * be considered an error - the viewport will not draw as it might be
83 * expected.
85 if((unsigned) current_vp->x > (unsigned) LCD_WIDTH
86 || (unsigned) current_vp->y > (unsigned) LCD_HEIGHT
87 || current_vp->x + current_vp->width > LCD_WIDTH
88 || current_vp->y + current_vp->height > LCD_HEIGHT)
90 #if !defined(HAVE_VIEWPORT_CLIP)
91 DEBUGF("ERROR: "
92 #else
93 DEBUGF("NOTE: "
94 #endif
95 "set_viewport out of bounds: x: %d y: %d width: %d height:%d\n",
96 current_vp->x, current_vp->y,
97 current_vp->width, current_vp->height);
100 #endif
103 void lcd_update_viewport(void)
105 lcd_update_rect(current_vp->x, current_vp->y,
106 current_vp->width, current_vp->height);
109 void lcd_update_viewport_rect(int x, int y, int width, int height)
111 lcd_update_rect(current_vp->x + x, current_vp->y + y, width, height);
114 /* Clear the current viewport */
115 void lcd_clear_viewport(void)
117 fb_data *dst, *dst_end;
118 int x, y, width, height;
119 int len, step;
121 x = current_vp->x;
122 y = current_vp->y;
123 width = current_vp->width;
124 height = current_vp->height;
126 #if defined(HAVE_VIEWPORT_CLIP)
127 /********************* Viewport on screen clipping ********************/
128 /* nothing to draw? */
129 if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
130 || (x + width <= 0) || (y + height <= 0))
131 return;
133 /* clip image in viewport in screen */
134 if (x < 0)
136 width += x;
137 x = 0;
139 if (y < 0)
141 height += y;
142 y = 0;
144 if (x + width > LCD_WIDTH)
145 width = LCD_WIDTH - x;
146 if (y + height > LCD_HEIGHT)
147 height = LCD_HEIGHT - y;
148 #endif
150 len = STRIDE_MAIN(width, height);
151 step = STRIDE_MAIN(ROW_INC, COL_INC);
153 dst = FBADDR(x, y);
154 dst_end = FBADDR(x + width - 1 , y + height - 1);
156 if (current_vp->drawmode & DRMODE_INVERSEVID)
160 memset16(dst, current_vp->fg_pattern, len);
161 dst += step;
163 while (dst <= dst_end);
165 else
167 if (!lcd_backdrop)
171 memset16(dst, current_vp->bg_pattern, len);
172 dst += step;
174 while (dst <= dst_end);
176 else
180 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
181 len * sizeof(fb_data));
182 dst += step;
184 while (dst <= dst_end);
188 if (current_vp == &default_vp)
190 lcd_scroll_info.lines = 0;
192 else
194 lcd_scroll_stop(current_vp);
198 /*** parameter handling ***/
200 void lcd_set_drawmode(int mode)
202 current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
205 int lcd_get_drawmode(void)
207 return current_vp->drawmode;
210 void lcd_set_foreground(unsigned color)
212 current_vp->fg_pattern = color;
215 unsigned lcd_get_foreground(void)
217 return current_vp->fg_pattern;
220 void lcd_set_background(unsigned color)
222 current_vp->bg_pattern = color;
225 unsigned lcd_get_background(void)
227 return current_vp->bg_pattern;
230 void lcd_set_selector_start(unsigned color)
232 current_vp->lss_pattern = color;
235 void lcd_set_selector_end(unsigned color)
237 current_vp->lse_pattern = color;
240 void lcd_set_selector_text(unsigned color)
242 current_vp->lst_pattern = color;
245 void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
247 lcd_set_drawmode(mode);
248 current_vp->fg_pattern = fg_color;
249 current_vp->bg_pattern = bg_color;
252 int lcd_getwidth(void)
254 return current_vp->width;
257 int lcd_getheight(void)
259 return current_vp->height;
262 void lcd_setfont(int newfont)
264 current_vp->font = newfont;
267 int lcd_getfont(void)
269 return current_vp->font;
272 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
274 return font_getstringsize(str, w, h, current_vp->font);
277 /*** low-level drawing functions ***/
279 static void ICODE_ATTR setpixel(fb_data *address)
281 *address = current_vp->fg_pattern;
284 static void ICODE_ATTR clearpixel(fb_data *address)
286 *address = current_vp->bg_pattern;
289 static void ICODE_ATTR clearimgpixel(fb_data *address)
291 *address = *(fb_data *)((long)address + lcd_backdrop_offset);
294 static void ICODE_ATTR flippixel(fb_data *address)
296 *address = ~(*address);
299 static void ICODE_ATTR nopixel(fb_data *address)
301 (void)address;
304 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_bgcolor[8] = {
305 flippixel, nopixel, setpixel, setpixel,
306 nopixel, clearpixel, nopixel, clearpixel
309 lcd_fastpixelfunc_type* const lcd_fastpixelfuncs_backdrop[8] = {
310 flippixel, nopixel, setpixel, setpixel,
311 nopixel, clearimgpixel, nopixel, clearimgpixel
314 lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
316 void lcd_set_backdrop(fb_data* backdrop)
318 lcd_backdrop = backdrop;
319 if (backdrop)
321 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
322 lcd_fastpixelfuncs = lcd_fastpixelfuncs_backdrop;
324 else
326 lcd_backdrop_offset = 0;
327 lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
331 fb_data* lcd_get_backdrop(void)
333 return lcd_backdrop;
336 /* Clear the whole display */
337 void lcd_clear_display(void)
339 struct viewport* old_vp = current_vp;
341 current_vp = &default_vp;
343 lcd_clear_viewport();
345 current_vp = old_vp;
348 /* Set a single pixel */
349 void lcd_drawpixel(int x, int y)
351 if ( ((unsigned)x < (unsigned)current_vp->width)
352 && ((unsigned)y < (unsigned)current_vp->height)
353 #if defined(HAVE_VIEWPORT_CLIP)
354 && ((unsigned)x < (unsigned)LCD_WIDTH)
355 && ((unsigned)y < (unsigned)LCD_HEIGHT)
356 #endif
358 lcd_fastpixelfuncs[current_vp->drawmode](FBADDR(current_vp->x+x, current_vp->y+y));
361 /* Draw a line */
362 void lcd_drawline(int x1, int y1, int x2, int y2)
364 int numpixels;
365 int i;
366 int deltax, deltay;
367 int d, dinc1, dinc2;
368 int x, xinc1, xinc2;
369 int y, yinc1, yinc2;
370 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[current_vp->drawmode];
372 deltay = abs(y2 - y1);
373 if (deltay == 0)
375 /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
376 lcd_hline(x1, x2, y1);
377 return;
379 deltax = abs(x2 - x1);
380 if (deltax == 0)
382 /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
383 lcd_vline(x1, y1, y2);
384 return;
386 xinc2 = 1;
387 yinc2 = 1;
389 if (deltax >= deltay)
391 numpixels = deltax;
392 d = 2 * deltay - deltax;
393 dinc1 = deltay * 2;
394 dinc2 = (deltay - deltax) * 2;
395 xinc1 = 1;
396 yinc1 = 0;
398 else
400 numpixels = deltay;
401 d = 2 * deltax - deltay;
402 dinc1 = deltax * 2;
403 dinc2 = (deltax - deltay) * 2;
404 xinc1 = 0;
405 yinc1 = 1;
407 numpixels++; /* include endpoints */
409 if (x1 > x2)
411 xinc1 = -xinc1;
412 xinc2 = -xinc2;
415 if (y1 > y2)
417 yinc1 = -yinc1;
418 yinc2 = -yinc2;
421 x = x1;
422 y = y1;
424 for (i = 0; i < numpixels; i++)
426 if ( ((unsigned)x < (unsigned)current_vp->width)
427 && ((unsigned)y < (unsigned)current_vp->height)
428 #if defined(HAVE_VIEWPORT_CLIP)
429 && ((unsigned)x < (unsigned)LCD_WIDTH)
430 && ((unsigned)y < (unsigned)LCD_HEIGHT)
431 #endif
433 pfunc(FBADDR(x + current_vp->x, y + current_vp->y));
435 if (d < 0)
437 d += dinc1;
438 x += xinc1;
439 y += yinc1;
441 else
443 d += dinc2;
444 x += xinc2;
445 y += yinc2;
450 /* Draw a rectangular box */
451 void lcd_drawrect(int x, int y, int width, int height)
453 if ((width <= 0) || (height <= 0))
454 return;
456 int x2 = x + width - 1;
457 int y2 = y + height - 1;
459 lcd_vline(x, y, y2);
460 lcd_vline(x2, y, y2);
461 lcd_hline(x, x2, y);
462 lcd_hline(x, x2, y2);
465 /* Fill a rectangular area */
466 void lcd_fillrect(int x, int y, int width, int height)
468 unsigned bits = 0;
469 enum fill_opt fillopt = OPT_NONE;
470 fb_data *dst, *dst_end;
471 int len, step;
473 /******************** In viewport clipping **********************/
474 /* nothing to draw? */
475 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
476 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
477 return;
479 if (x < 0)
481 width += x;
482 x = 0;
484 if (y < 0)
486 height += y;
487 y = 0;
489 if (x + width > current_vp->width)
490 width = current_vp->width - x;
491 if (y + height > current_vp->height)
492 height = current_vp->height - y;
494 /* adjust for viewport */
495 x += current_vp->x;
496 y += current_vp->y;
498 #if defined(HAVE_VIEWPORT_CLIP)
499 /********************* Viewport on screen clipping ********************/
500 /* nothing to draw? */
501 if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
502 || (x + width <= 0) || (y + height <= 0))
503 return;
505 /* clip image in viewport in screen */
506 if (x < 0)
508 width += x;
509 x = 0;
511 if (y < 0)
513 height += y;
514 y = 0;
516 if (x + width > LCD_WIDTH)
517 width = LCD_WIDTH - x;
518 if (y + height > LCD_HEIGHT)
519 height = LCD_HEIGHT - y;
520 #endif
522 /* drawmode and optimisation */
523 if (current_vp->drawmode & DRMODE_INVERSEVID)
525 if (current_vp->drawmode & DRMODE_BG)
527 if (!lcd_backdrop)
529 fillopt = OPT_SET;
530 bits = current_vp->bg_pattern;
532 else
533 fillopt = OPT_COPY;
536 else
538 if (current_vp->drawmode & DRMODE_FG)
540 fillopt = OPT_SET;
541 bits = current_vp->fg_pattern;
544 if (fillopt == OPT_NONE && current_vp->drawmode != DRMODE_COMPLEMENT)
545 return;
547 dst = FBADDR(x, y);
548 dst_end = FBADDR(x + width - 1, y + height - 1);
550 len = STRIDE_MAIN(width, height);
551 step = STRIDE_MAIN(ROW_INC, COL_INC);
555 switch (fillopt)
557 case OPT_SET:
558 memset16(dst, bits, len);
559 break;
561 case OPT_COPY:
562 memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
563 len * sizeof(fb_data));
564 break;
566 case OPT_NONE: /* DRMODE_COMPLEMENT */
568 fb_data *start = dst;
569 fb_data *end = start + len;
571 *start = ~(*start);
572 while (++start < end);
573 break;
576 dst += step;
578 while (dst <= dst_end);
581 /* About Rockbox' internal monochrome bitmap format:
583 * A bitmap contains one bit for every pixel that defines if that pixel is
584 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
585 * at top.
586 * The bytes are stored in row-major order, with byte 0 being top left,
587 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
588 * 0..7, the second row defines pixel row 8..15 etc.
590 * This is the mono bitmap format used on all other targets so far; the
591 * pixel packing doesn't really matter on a 8bit+ target. */
593 /* Draw a partial monochrome bitmap */
595 void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
596 int src_y, int stride, int x, int y,
597 int width, int height)
599 const unsigned char *src_end;
600 fb_data *dst, *dst_col;
601 unsigned dmask = 0x100; /* bit 8 == sentinel */
602 int drmode = current_vp->drawmode;
603 int row;
605 /******************** Image in viewport clipping **********************/
606 /* nothing to draw? */
607 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
608 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
609 return;
611 if (x < 0)
613 width += x;
614 src_x -= x;
615 x = 0;
617 if (y < 0)
619 height += y;
620 src_y -= y;
621 y = 0;
623 if (x + width > current_vp->width)
624 width = current_vp->width - x;
625 if (y + height > current_vp->height)
626 height = current_vp->height - y;
628 /* adjust for viewport */
629 x += current_vp->x;
630 y += current_vp->y;
632 #if defined(HAVE_VIEWPORT_CLIP)
633 /********************* Viewport on screen clipping ********************/
634 /* nothing to draw? */
635 if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
636 || (x + width <= 0) || (y + height <= 0))
637 return;
639 /* clip image in viewport in screen */
640 if (x < 0)
642 width += x;
643 src_x -= x;
644 x = 0;
646 if (y < 0)
648 height += y;
649 src_y -= y;
650 y = 0;
652 if (x + width > LCD_WIDTH)
653 width = LCD_WIDTH - x;
654 if (y + height > LCD_HEIGHT)
655 height = LCD_HEIGHT - y;
656 #endif
658 src += stride * (src_y >> 3) + src_x; /* move starting point */
659 src_y &= 7;
660 src_end = src + width;
661 dst_col = FBADDR(x, y);
664 if (drmode & DRMODE_INVERSEVID)
666 dmask = 0x1ff; /* bit 8 == sentinel */
667 drmode &= DRMODE_SOLID; /* mask out inversevid */
670 /* go through each column and update each pixel */
673 const unsigned char *src_col = src++;
674 unsigned data = (*src_col ^ dmask) >> src_y;
675 int fg, bg;
676 long bo;
678 dst = dst_col;
679 dst_col += COL_INC;
680 row = height;
682 #define UPDATE_SRC do { \
683 data >>= 1; \
684 if (data == 0x001) { \
685 src_col += stride; \
686 data = *src_col ^ dmask; \
688 } while (0)
690 switch (drmode)
692 case DRMODE_COMPLEMENT:
695 if (data & 0x01)
696 *dst = ~(*dst);
698 dst += ROW_INC;
699 UPDATE_SRC;
701 while (--row);
702 break;
704 case DRMODE_BG:
705 if (lcd_backdrop)
707 bo = lcd_backdrop_offset;
710 if (!(data & 0x01))
711 *dst = *(fb_data *)((long)dst + bo);
713 dst += ROW_INC;
714 UPDATE_SRC;
716 while (--row);
718 else
720 bg = current_vp->bg_pattern;
723 if (!(data & 0x01))
724 *dst = bg;
726 dst += ROW_INC;
727 UPDATE_SRC;
729 while (--row);
731 break;
733 case DRMODE_FG:
734 fg = current_vp->fg_pattern;
737 if (data & 0x01)
738 *dst = fg;
740 dst += ROW_INC;
741 UPDATE_SRC;
743 while (--row);
744 break;
746 case DRMODE_SOLID:
747 fg = current_vp->fg_pattern;
748 if (lcd_backdrop)
750 bo = lcd_backdrop_offset;
753 *dst = (data & 0x01) ? fg
754 : *(fb_data *)((long)dst + bo);
755 dst += ROW_INC;
756 UPDATE_SRC;
758 while (--row);
760 else
762 bg = current_vp->bg_pattern;
765 *dst = (data & 0x01) ? fg : bg;
766 dst += ROW_INC;
767 UPDATE_SRC;
769 while (--row);
771 break;
774 while (src < src_end);
776 /* Draw a full monochrome bitmap */
777 void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
779 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
783 /* About Rockbox' internal alpha channel format (for ALPHA_COLOR_FONT_DEPTH == 2)
785 * For each pixel, 4bit of alpha information is stored in a byte-stream,
786 * so two pixels are packed into one byte.
787 * The lower nibble is the first pixel, the upper one the second. The stride is
788 * horizontal. E.g row0: pixel0: byte0[0:3], pixel1: byte0[4:7], pixel2: byte1[0:3],...
789 * The format is independant of the internal display orientation and color
790 * representation, as to support the same font files on all displays.
791 * The values go linear from 0 (fully transparent) to 15 (fully opaque).
793 * This might suggest that rows need to have an even number of pixels.
794 * However this is generally not the case. lcd_alpha_bitmap_part_mix() can deal
795 * with uneven colums (i.e. two rows can share one byte). And font files do
796 * exploit this.
797 * However, this is difficult to do for image files, especially bottom-up bitmaps,
798 * so lcd_bmp() do expect even rows.
801 #define ALPHA_COLOR_FONT_DEPTH 2
802 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
803 #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
804 #define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
805 #define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
806 #ifdef CPU_ARM
807 #define BLEND_INIT do {} while (0)
808 #define BLEND_FINISH do {} while(0)
809 #define BLEND_START(acc, color, alpha) \
810 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
811 #define BLEND_CONT(acc, color, alpha) \
812 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
813 #define BLEND_OUT(acc) do {} while (0)
814 #elif defined(CPU_COLDFIRE)
815 #define ALPHA_BITMAP_READ_WORDS
816 #define BLEND_INIT \
817 unsigned long _macsr = coldfire_get_macsr(); \
818 coldfire_set_macsr(EMAC_UNSIGNED)
819 #define BLEND_FINISH \
820 coldfire_set_macsr(_macsr)
821 #define BLEND_START(acc, color, alpha) \
822 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
823 #define BLEND_CONT BLEND_START
824 #define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
825 #else
826 #define BLEND_INIT do {} while (0)
827 #define BLEND_FINISH do {} while(0)
828 #define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
829 #define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
830 #define BLEND_OUT(acc) do {} while (0)
831 #endif
833 /* Blend the given two colors */
834 static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
836 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
837 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
838 c1 = swap16(c1);
839 c2 = swap16(c2);
840 #endif
841 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
842 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
843 unsigned p;
844 BLEND_START(p, c1l, a);
845 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
846 BLEND_OUT(p);
847 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
848 p |= (p >> 16);
849 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
850 return swap16(p);
851 #else
852 return p;
853 #endif
856 /* Blend the given color with the value from the alpha_color_lookup table */
857 static inline unsigned blend_color(unsigned c, unsigned a)
859 return blend_two_colors(c, current_vp->fg_pattern, a);
862 /* Blend an image with an alpha channel
863 * if image is NULL, drawing will happen according to the drawmode
864 * src is the alpha channel (4bit per pixel) */
865 static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
866 const unsigned char *src, int src_x,
867 int src_y, int x, int y,
868 int width, int height,
869 int stride_image, int stride_src)
871 fb_data *dst, *dst_row;
872 const fb_data *image_row;
873 unsigned dmask = 0x00000000;
874 int drmode = current_vp->drawmode;
875 /* nothing to draw? */
876 if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
877 (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
878 return;
879 /* initialize blending */
880 BLEND_INIT;
882 /* clipping */
883 if (x < 0)
885 width += x;
886 src_x -= x;
887 x = 0;
889 if (y < 0)
891 height += y;
892 src_y -= y;
893 y = 0;
895 if (x + width > current_vp->width)
896 width = current_vp->width - x;
897 if (y + height > current_vp->height)
898 height = current_vp->height - y;
900 /* adjust for viewport */
901 x += current_vp->x;
902 y += current_vp->y;
904 #if defined(HAVE_VIEWPORT_CLIP)
905 /********************* Viewport on screen clipping ********************/
906 /* nothing to draw? */
907 if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
908 || (x + width <= 0) || (y + height <= 0))
910 BLEND_FINISH;
911 return;
914 /* clip image in viewport in screen */
915 if (x < 0)
917 width += x;
918 src_x -= x;
919 x = 0;
921 if (y < 0)
923 height += y;
924 src_y -= y;
925 y = 0;
927 if (x + width > LCD_WIDTH)
928 width = LCD_WIDTH - x;
929 if (y + height > LCD_HEIGHT)
930 height = LCD_HEIGHT - y;
931 #endif
933 if (drmode & DRMODE_INVERSEVID)
935 dmask = 0xffffffff;
936 drmode &= DRMODE_SOLID; /* mask out inversevid */
938 /* sourcing from an image ignore drawmode.
939 * Set to DRMODE_BG as we use its code path in the switch below */
940 if (image != NULL)
942 drmode = DRMODE_BG;
944 if (drmode == DRMODE_BG)
946 dmask = ~dmask;
949 dst_row = FBADDR(x, y);
951 int col, row = height;
952 unsigned data, pixels;
953 unsigned skip_end = (stride_src - width);
954 unsigned skip_start = src_y * stride_src + src_x;
955 unsigned skip_start_image = STRIDE_MAIN(src_y * stride_image + src_x,
956 src_x * stride_image + src_y);
958 #ifdef ALPHA_BITMAP_READ_WORDS
959 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
960 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
961 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
962 data = letoh32(*src_w++) ^ dmask;
963 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
964 #else
965 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
966 data = *src ^ dmask;
967 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_BYTE;
968 #endif
969 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
970 #ifdef ALPHA_BITMAP_READ_WORDS
971 pixels = 8 - pixels;
972 #endif
973 if (image)
974 image += skip_start_image;
975 image_row = image;
977 /* go through the rows and update each pixel */
980 col = width;
981 dst = dst_row;
982 dst_row += ROW_INC;
983 if (image_row) {
984 image = image_row;
985 image_row += STRIDE_MAIN(stride_image,1);
987 else
988 image = dst;
989 #ifdef ALPHA_BITMAP_READ_WORDS
990 #define UPDATE_SRC_ALPHA do { \
991 if (--pixels) \
992 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
993 else \
995 data = letoh32(*src_w++) ^ dmask; \
996 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
998 } while (0)
999 #elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
1000 #define UPDATE_SRC_ALPHA do { \
1001 if (pixels ^= 1) \
1002 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1003 else \
1004 data = *(++src) ^ dmask; \
1005 } while (0)
1006 #else
1007 #define UPDATE_SRC_ALPHA do { \
1008 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
1009 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1010 else \
1011 data = *(++src) ^ dmask; \
1012 } while (0)
1013 #endif
1014 /* we don't want to have this in our inner
1015 * loop and the codesize increase is minimal */
1016 switch (drmode)
1018 case DRMODE_COMPLEMENT:
1021 *dst = blend_two_colors(*dst, ~(*dst),
1022 data & ALPHA_COLOR_LOOKUP_SIZE );
1023 dst += COL_INC;
1024 UPDATE_SRC_ALPHA;
1026 while (--col);
1027 break;
1028 case DRMODE_BG:
1029 if(lcd_backdrop)
1031 uintptr_t bo = lcd_backdrop_offset;
1034 *dst = blend_two_colors(*(fb_data *)((uintptr_t)dst + bo),
1035 *image, data & ALPHA_COLOR_LOOKUP_SIZE );
1037 dst += COL_INC;
1038 image += STRIDE_MAIN(1, stride_image);
1039 UPDATE_SRC_ALPHA;
1041 while (--col);
1043 else
1047 *dst = blend_two_colors(current_vp->bg_pattern,
1048 *image, data & ALPHA_COLOR_LOOKUP_SIZE );
1049 dst += COL_INC;
1050 image += STRIDE_MAIN(1, stride_image);
1051 UPDATE_SRC_ALPHA;
1053 while (--col);
1055 break;
1056 case DRMODE_FG:
1059 *dst = blend_color(*dst, data & ALPHA_COLOR_LOOKUP_SIZE );
1060 dst += COL_INC;
1061 UPDATE_SRC_ALPHA;
1063 while (--col);
1064 break;
1065 case DRMODE_SOLID:
1066 if(lcd_backdrop)
1068 uintptr_t bo = lcd_backdrop_offset;
1071 *dst = blend_color(*(fb_data *)((uintptr_t)dst + bo),
1072 data & ALPHA_COLOR_LOOKUP_SIZE );
1073 dst += COL_INC;
1074 UPDATE_SRC_ALPHA;
1076 while (--col);
1078 else
1082 *dst = blend_color(current_vp->bg_pattern,
1083 data & ALPHA_COLOR_LOOKUP_SIZE );
1084 dst += COL_INC;
1085 UPDATE_SRC_ALPHA;
1087 while (--col);
1089 break;
1091 #ifdef ALPHA_BITMAP_READ_WORDS
1092 if (skip_end < pixels)
1094 pixels -= skip_end;
1095 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1096 } else {
1097 pixels = skip_end - pixels;
1098 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
1099 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
1100 data = letoh32(*src_w++) ^ dmask;
1101 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1102 pixels = 8 - pixels;
1104 #else
1105 if (skip_end)
1107 pixels += skip_end;
1108 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
1110 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
1111 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
1112 data = *src ^ dmask;
1113 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1114 } else
1115 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1117 #endif
1118 } while (--row);
1120 BLEND_FINISH;
1123 /* Draw a full native bitmap */
1124 void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
1126 lcd_bitmap_part(src, 0, 0, STRIDE(SCREEN_MAIN, width, height), x, y, width, height);
1129 /* Draw a full native bitmap with a transparent color */
1130 void lcd_bitmap_transparent(const fb_data *src, int x, int y,
1131 int width, int height)
1133 lcd_bitmap_transparent_part(src, 0, 0,
1134 STRIDE(SCREEN_MAIN, width, height), x, y, width, height);
1137 /* draw alpha bitmap for anti-alias font */
1138 void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x,
1139 int src_y, int stride, int x, int y,
1140 int width, int height)
1142 lcd_alpha_bitmap_part_mix(NULL, src, src_x, src_y, x, y, width, height, 0, stride);
1145 /* Draw a partial bitmap (mono or native) including alpha channel */
1146 void ICODE_ATTR lcd_bmp_part(const struct bitmap* bm, int src_x, int src_y,
1147 int x, int y, int width, int height)
1149 int bitmap_stride = STRIDE_MAIN(bm->width, bm->height);
1150 if (bm->format == FORMAT_MONO)
1151 lcd_mono_bitmap_part(bm->data, src_x, src_y, bitmap_stride, x, y, width, height);
1152 else if (bm->alpha_offset > 0)
1153 lcd_alpha_bitmap_part_mix((fb_data*)bm->data, bm->data+bm->alpha_offset,
1154 src_x, src_y, x, y, width, height,
1155 bitmap_stride, ALIGN_UP(bm->width, 2));
1156 else
1157 lcd_bitmap_transparent_part((fb_data*)bm->data,
1158 src_x, src_y, bitmap_stride, x, y, width, height);
1161 /* Draw a native bitmap with alpha channel */
1162 void ICODE_ATTR lcd_bmp(const struct bitmap *bmp, int x, int y)
1164 lcd_bmp_part(bmp, 0, 0, x, y, bmp->width, bmp->height);
1168 * |R| |1.000000 -0.000001 1.402000| |Y'|
1169 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
1170 * |B| |1.000000 1.772000 0.000000| |Pr|
1171 * Scaled, normalized, rounded and tweaked to yield RGB 565:
1172 * |R| |74 0 101| |Y' - 16| >> 9
1173 * |G| = |74 -24 -51| |Cb - 128| >> 8
1174 * |B| |74 128 0| |Cr - 128| >> 9
1176 #define YFAC (74)
1177 #define RVFAC (101)
1178 #define GUFAC (-24)
1179 #define GVFAC (-51)
1180 #define BUFAC (128)
1182 static inline int clamp(int val, int min, int max)
1184 if (val < min)
1185 val = min;
1186 else if (val > max)
1187 val = max;
1188 return val;
1191 #ifndef _WIN32
1193 * weak attribute doesn't work for win32 as of gcc 4.6.2 and binutils 2.21.52
1194 * When building win32 simulators, we won't be using an optimized version of
1195 * lcd_blit_yuv(), so just don't use the weak attribute.
1197 __attribute__((weak))
1198 #endif
1199 void lcd_yuv_set_options(unsigned options)
1201 (void)options;
1204 /* Draw a partial YUV colour bitmap */
1205 #ifndef _WIN32
1206 __attribute__((weak))
1207 #endif
1208 void lcd_blit_yuv(unsigned char * const src[3],
1209 int src_x, int src_y, int stride,
1210 int x, int y, int width, int height)
1212 const unsigned char *ysrc, *usrc, *vsrc;
1213 int linecounter;
1214 fb_data *dst, *row_end;
1215 long z;
1217 /* width and height must be >= 2 and an even number */
1218 width &= ~1;
1219 linecounter = height >> 1;
1221 #if LCD_WIDTH >= LCD_HEIGHT
1222 dst = FBADDR(x, y);
1223 row_end = dst + width;
1224 #else
1225 dst = FBADDR(LCD_WIDTH - y - 1, x);
1226 row_end = dst + LCD_WIDTH * width;
1227 #endif
1229 z = stride * src_y;
1230 ysrc = src[0] + z + src_x;
1231 usrc = src[1] + (z >> 2) + (src_x >> 1);
1232 vsrc = src[2] + (usrc - src[1]);
1234 /* stride => amount to jump from end of last row to start of next */
1235 stride -= width;
1237 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
1243 int y, cb, cr, rv, guv, bu, r, g, b;
1245 y = YFAC*(*ysrc++ - 16);
1246 cb = *usrc++ - 128;
1247 cr = *vsrc++ - 128;
1249 rv = RVFAC*cr;
1250 guv = GUFAC*cb + GVFAC*cr;
1251 bu = BUFAC*cb;
1253 r = y + rv;
1254 g = y + guv;
1255 b = y + bu;
1257 if ((unsigned)(r | g | b) > 64*256-1)
1259 r = clamp(r, 0, 64*256-1);
1260 g = clamp(g, 0, 64*256-1);
1261 b = clamp(b, 0, 64*256-1);
1264 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
1266 #if LCD_WIDTH >= LCD_HEIGHT
1267 dst++;
1268 #else
1269 dst += LCD_WIDTH;
1270 #endif
1272 y = YFAC*(*ysrc++ - 16);
1273 r = y + rv;
1274 g = y + guv;
1275 b = y + bu;
1277 if ((unsigned)(r | g | b) > 64*256-1)
1279 r = clamp(r, 0, 64*256-1);
1280 g = clamp(g, 0, 64*256-1);
1281 b = clamp(b, 0, 64*256-1);
1284 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
1286 #if LCD_WIDTH >= LCD_HEIGHT
1287 dst++;
1288 #else
1289 dst += LCD_WIDTH;
1290 #endif
1292 while (dst < row_end);
1294 ysrc += stride;
1295 usrc -= width >> 1;
1296 vsrc -= width >> 1;
1298 #if LCD_WIDTH >= LCD_HEIGHT
1299 row_end += LCD_WIDTH;
1300 dst += LCD_WIDTH - width;
1301 #else
1302 row_end -= 1;
1303 dst -= LCD_WIDTH*width + 1;
1304 #endif
1308 int y, cb, cr, rv, guv, bu, r, g, b;
1310 y = YFAC*(*ysrc++ - 16);
1311 cb = *usrc++ - 128;
1312 cr = *vsrc++ - 128;
1314 rv = RVFAC*cr;
1315 guv = GUFAC*cb + GVFAC*cr;
1316 bu = BUFAC*cb;
1318 r = y + rv;
1319 g = y + guv;
1320 b = y + bu;
1322 if ((unsigned)(r | g | b) > 64*256-1)
1324 r = clamp(r, 0, 64*256-1);
1325 g = clamp(g, 0, 64*256-1);
1326 b = clamp(b, 0, 64*256-1);
1329 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
1331 #if LCD_WIDTH >= LCD_HEIGHT
1332 dst++;
1333 #else
1334 dst += LCD_WIDTH;
1335 #endif
1337 y = YFAC*(*ysrc++ - 16);
1338 r = y + rv;
1339 g = y + guv;
1340 b = y + bu;
1342 if ((unsigned)(r | g | b) > 64*256-1)
1344 r = clamp(r, 0, 64*256-1);
1345 g = clamp(g, 0, 64*256-1);
1346 b = clamp(b, 0, 64*256-1);
1349 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
1351 #if LCD_WIDTH >= LCD_HEIGHT
1352 dst++;
1353 #else
1354 dst += LCD_WIDTH;
1355 #endif
1357 while (dst < row_end);
1359 ysrc += stride;
1360 usrc += stride >> 1;
1361 vsrc += stride >> 1;
1363 #if LCD_WIDTH >= LCD_HEIGHT
1364 row_end += LCD_WIDTH;
1365 dst += LCD_WIDTH - width;
1366 #else
1367 row_end -= 1;
1368 dst -= LCD_WIDTH*width + 1;
1369 #endif
1371 while (--linecounter > 0);
1373 #if LCD_WIDTH >= LCD_HEIGHT
1374 lcd_update_rect(x, y, width, height);
1375 #else
1376 lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
1377 #endif