summaryrefslogtreecommitdiff
path: root/firmware/drivers/lcd-color-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/lcd-color-common.c')
-rw-r--r--firmware/drivers/lcd-color-common.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-color-common.c b/firmware/drivers/lcd-color-common.c
new file mode 100644
index 0000000000..e171f08465
--- /dev/null
+++ b/firmware/drivers/lcd-color-common.c
@@ -0,0 +1,605 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Dave Chapman
11 * Copyright (C) 2009 by Karl Kurbjun
12 *
13 * Rockbox driver for 16-bit colour LCDs
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25
26/* to be #included by lcd-16bit*.c */
27
28#if !defined(ROW_INC) || !defined(COL_INC)
29#error ROW_INC or COL_INC not defined
30#endif
31
32enum fill_opt {
33 OPT_NONE = 0,
34 OPT_SET,
35 OPT_COPY
36};
37
38/*** globals ***/
39fb_data lcd_static_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH]
40 IRAM_LCDFRAMEBUFFER CACHEALIGN_AT_LEAST_ATTR(16);
41fb_data *lcd_framebuffer = &lcd_static_framebuffer[0][0];
42
43static fb_data* lcd_backdrop = NULL;
44static long lcd_backdrop_offset IDATA_ATTR = 0;
45
46static struct viewport default_vp =
47{
48 .x = 0,
49 .y = 0,
50 .width = LCD_WIDTH,
51 .height = LCD_HEIGHT,
52 .font = FONT_SYSFIXED,
53 .drawmode = DRMODE_SOLID,
54 .fg_pattern = LCD_DEFAULT_FG,
55 .bg_pattern = LCD_DEFAULT_BG,
56};
57
58static struct viewport* current_vp IDATA_ATTR = &default_vp;
59
60/* LCD init */
61void lcd_init(void)
62{
63 lcd_clear_display();
64
65 /* Call device specific init */
66 lcd_init_device();
67 scroll_init();
68}
69
70/* Clear the whole display */
71void lcd_clear_display(void)
72{
73 struct viewport* old_vp = current_vp;
74
75 current_vp = &default_vp;
76
77 lcd_clear_viewport();
78
79 current_vp = old_vp;
80}
81
82/*** parameter handling ***/
83
84void lcd_set_drawmode(int mode)
85{
86 current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
87}
88
89int lcd_get_drawmode(void)
90{
91 return current_vp->drawmode;
92}
93
94void lcd_set_foreground(unsigned color)
95{
96 current_vp->fg_pattern = color;
97}
98
99unsigned lcd_get_foreground(void)
100{
101 return current_vp->fg_pattern;
102}
103
104void lcd_set_background(unsigned color)
105{
106 current_vp->bg_pattern = color;
107}
108
109unsigned lcd_get_background(void)
110{
111 return current_vp->bg_pattern;
112}
113
114void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
115{
116 lcd_set_drawmode(mode);
117 current_vp->fg_pattern = fg_color;
118 current_vp->bg_pattern = bg_color;
119}
120
121int lcd_getwidth(void)
122{
123 return current_vp->width;
124}
125
126int lcd_getheight(void)
127{
128 return current_vp->height;
129}
130
131void lcd_setfont(int newfont)
132{
133 current_vp->font = newfont;
134}
135
136int lcd_getfont(void)
137{
138 return current_vp->font;
139}
140
141int lcd_getstringsize(const unsigned char *str, int *w, int *h)
142{
143 return font_getstringsize(str, w, h, current_vp->font);
144}
145
146void lcd_set_backdrop(fb_data* backdrop)
147{
148 lcd_backdrop = backdrop;
149 if (backdrop)
150 {
151 lcd_backdrop_offset = (long)backdrop - (long)lcd_framebuffer;
152 lcd_fastpixelfuncs = lcd_fastpixelfuncs_backdrop;
153 }
154 else
155 {
156 lcd_backdrop_offset = 0;
157 lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
158 }
159}
160
161fb_data* lcd_get_backdrop(void)
162{
163 return lcd_backdrop;
164}
165
166/* Set a single pixel */
167void lcd_drawpixel(int x, int y)
168{
169 if ( ((unsigned)x < (unsigned)current_vp->width)
170 && ((unsigned)y < (unsigned)current_vp->height)
171#if defined(HAVE_VIEWPORT_CLIP)
172 && ((unsigned)x < (unsigned)LCD_WIDTH)
173 && ((unsigned)y < (unsigned)LCD_HEIGHT)
174#endif
175 )
176 lcd_fastpixelfuncs[current_vp->drawmode](FBADDR(current_vp->x+x, current_vp->y+y));
177}
178
179/* Draw a line */
180void lcd_drawline(int x1, int y1, int x2, int y2)
181{
182 int numpixels;
183 int i;
184 int deltax, deltay;
185 int d, dinc1, dinc2;
186 int x, xinc1, xinc2;
187 int y, yinc1, yinc2;
188 lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[current_vp->drawmode];
189
190 deltay = abs(y2 - y1);
191 if (deltay == 0)
192 {
193 /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
194 lcd_hline(x1, x2, y1);
195 return;
196 }
197 deltax = abs(x2 - x1);
198 if (deltax == 0)
199 {
200 /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
201 lcd_vline(x1, y1, y2);
202 return;
203 }
204 xinc2 = 1;
205 yinc2 = 1;
206
207 if (deltax >= deltay)
208 {
209 numpixels = deltax;
210 d = 2 * deltay - deltax;
211 dinc1 = deltay * 2;
212 dinc2 = (deltay - deltax) * 2;
213 xinc1 = 1;
214 yinc1 = 0;
215 }
216 else
217 {
218 numpixels = deltay;
219 d = 2 * deltax - deltay;
220 dinc1 = deltax * 2;
221 dinc2 = (deltax - deltay) * 2;
222 xinc1 = 0;
223 yinc1 = 1;
224 }
225 numpixels++; /* include endpoints */
226
227 if (x1 > x2)
228 {
229 xinc1 = -xinc1;
230 xinc2 = -xinc2;
231 }
232
233 if (y1 > y2)
234 {
235 yinc1 = -yinc1;
236 yinc2 = -yinc2;
237 }
238
239 x = x1;
240 y = y1;
241
242 for (i = 0; i < numpixels; i++)
243 {
244 if ( ((unsigned)x < (unsigned)current_vp->width)
245 && ((unsigned)y < (unsigned)current_vp->height)
246#if defined(HAVE_VIEWPORT_CLIP)
247 && ((unsigned)x < (unsigned)LCD_WIDTH)
248 && ((unsigned)y < (unsigned)LCD_HEIGHT)
249#endif
250 )
251 pfunc(FBADDR(x + current_vp->x, y + current_vp->y));
252
253 if (d < 0)
254 {
255 d += dinc1;
256 x += xinc1;
257 y += yinc1;
258 }
259 else
260 {
261 d += dinc2;
262 x += xinc2;
263 y += yinc2;
264 }
265 }
266}
267
268/* Draw a rectangular box */
269void lcd_drawrect(int x, int y, int width, int height)
270{
271 if ((width <= 0) || (height <= 0))
272 return;
273
274 int x2 = x + width - 1;
275 int y2 = y + height - 1;
276
277 lcd_vline(x, y, y2);
278 lcd_vline(x2, y, y2);
279 lcd_hline(x, x2, y);
280 lcd_hline(x, x2, y2);
281}
282
283
284/* Draw a full native bitmap */
285void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
286{
287 lcd_bitmap_part(src, 0, 0, STRIDE(SCREEN_MAIN, width, height), x, y, width, height);
288}
289
290/* Draw a full native bitmap with a transparent color */
291void lcd_bitmap_transparent(const fb_data *src, int x, int y,
292 int width, int height)
293{
294 lcd_bitmap_transparent_part(src, 0, 0,
295 STRIDE(SCREEN_MAIN, width, height), x, y, width, height);
296}
297
298/* draw alpha bitmap for anti-alias font */
299void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x,
300 int src_y, int stride, int x, int y,
301 int width, int height)
302{
303 lcd_alpha_bitmap_part_mix(NULL, src, src_x, src_y, x, y, width, height, 0, stride);
304}
305
306/* Draw a partial bitmap (mono or native) including alpha channel */
307void ICODE_ATTR lcd_bmp_part(const struct bitmap* bm, int src_x, int src_y,
308 int x, int y, int width, int height)
309{
310 int bitmap_stride = STRIDE_MAIN(bm->width, bm->height);
311 if (bm->format == FORMAT_MONO)
312 lcd_mono_bitmap_part(bm->data, src_x, src_y, bitmap_stride, x, y, width, height);
313 else if (bm->alpha_offset > 0)
314 lcd_alpha_bitmap_part_mix((fb_data*)bm->data, bm->data+bm->alpha_offset,
315 src_x, src_y, x, y, width, height,
316 bitmap_stride, ALIGN_UP(bm->width, 2));
317 else
318 lcd_bitmap_transparent_part((fb_data*)bm->data,
319 src_x, src_y, bitmap_stride, x, y, width, height);
320}
321
322/* Draw a native bitmap with alpha channel */
323void ICODE_ATTR lcd_bmp(const struct bitmap *bmp, int x, int y)
324{
325 lcd_bmp_part(bmp, 0, 0, x, y, bmp->width, bmp->height);
326}
327
328/**
329 * |R| |1.000000 -0.000001 1.402000| |Y'|
330 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
331 * |B| |1.000000 1.772000 0.000000| |Pr|
332 * Scaled, normalized, rounded and tweaked to yield RGB 565:
333 * |R| |74 0 101| |Y' - 16| >> 9
334 * |G| = |74 -24 -51| |Cb - 128| >> 8
335 * |B| |74 128 0| |Cr - 128| >> 9
336 */
337#define YFAC (74)
338#define RVFAC (101)
339#define GUFAC (-24)
340#define GVFAC (-51)
341#define BUFAC (128)
342
343static inline int clamp(int val, int min, int max)
344{
345 if (val < min)
346 val = min;
347 else if (val > max)
348 val = max;
349 return val;
350}
351
352#ifndef _WIN32
353/*
354 * weak attribute doesn't work for win32 as of gcc 4.6.2 and binutils 2.21.52
355 * When building win32 simulators, we won't be using an optimized version of
356 * lcd_blit_yuv(), so just don't use the weak attribute.
357 */
358__attribute__((weak))
359#endif
360void lcd_yuv_set_options(unsigned options)
361{
362 (void)options;
363}
364
365/* Draw a partial YUV colour bitmap */
366#ifndef _WIN32
367__attribute__((weak))
368#endif
369void lcd_blit_yuv(unsigned char * const src[3],
370 int src_x, int src_y, int stride,
371 int x, int y, int width, int height)
372{
373 const unsigned char *ysrc, *usrc, *vsrc;
374 int linecounter;
375 fb_data *dst, *row_end;
376 long z;
377
378 /* width and height must be >= 2 and an even number */
379 width &= ~1;
380 linecounter = height >> 1;
381
382#if LCD_WIDTH >= LCD_HEIGHT
383 dst = FBADDR(x, y);
384 row_end = dst + width;
385#else
386 dst = FBADDR(LCD_WIDTH - y - 1, x);
387 row_end = dst + LCD_WIDTH * width;
388#endif
389
390 z = stride * src_y;
391 ysrc = src[0] + z + src_x;
392 usrc = src[1] + (z >> 2) + (src_x >> 1);
393 vsrc = src[2] + (usrc - src[1]);
394
395 /* stride => amount to jump from end of last row to start of next */
396 stride -= width;
397
398 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
399
400 do
401 {
402 do
403 {
404 int y, cb, cr, rv, guv, bu, r, g, b;
405
406 y = YFAC*(*ysrc++ - 16);
407 cb = *usrc++ - 128;
408 cr = *vsrc++ - 128;
409
410 rv = RVFAC*cr;
411 guv = GUFAC*cb + GVFAC*cr;
412 bu = BUFAC*cb;
413
414 r = y + rv;
415 g = y + guv;
416 b = y + bu;
417
418 if ((unsigned)(r | g | b) > 64*256-1)
419 {
420 r = clamp(r, 0, 64*256-1);
421 g = clamp(g, 0, 64*256-1);
422 b = clamp(b, 0, 64*256-1);
423 }
424
425 *dst = FB_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
426
427#if LCD_WIDTH >= LCD_HEIGHT
428 dst++;
429#else
430 dst += LCD_WIDTH;
431#endif
432
433 y = YFAC*(*ysrc++ - 16);
434 r = y + rv;
435 g = y + guv;
436 b = y + bu;
437
438 if ((unsigned)(r | g | b) > 64*256-1)
439 {
440 r = clamp(r, 0, 64*256-1);
441 g = clamp(g, 0, 64*256-1);
442 b = clamp(b, 0, 64*256-1);
443 }
444
445 *dst = FB_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
446
447#if LCD_WIDTH >= LCD_HEIGHT
448 dst++;
449#else
450 dst += LCD_WIDTH;
451#endif
452 }
453 while (dst < row_end);
454
455 ysrc += stride;
456 usrc -= width >> 1;
457 vsrc -= width >> 1;
458
459#if LCD_WIDTH >= LCD_HEIGHT
460 row_end += LCD_WIDTH;
461 dst += LCD_WIDTH - width;
462#else
463 row_end -= 1;
464 dst -= LCD_WIDTH*width + 1;
465#endif
466
467 do
468 {
469 int y, cb, cr, rv, guv, bu, r, g, b;
470
471 y = YFAC*(*ysrc++ - 16);
472 cb = *usrc++ - 128;
473 cr = *vsrc++ - 128;
474
475 rv = RVFAC*cr;
476 guv = GUFAC*cb + GVFAC*cr;
477 bu = BUFAC*cb;
478
479 r = y + rv;
480 g = y + guv;
481 b = y + bu;
482
483 if ((unsigned)(r | g | b) > 64*256-1)
484 {
485 r = clamp(r, 0, 64*256-1);
486 g = clamp(g, 0, 64*256-1);
487 b = clamp(b, 0, 64*256-1);
488 }
489
490 *dst = FB_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
491
492#if LCD_WIDTH >= LCD_HEIGHT
493 dst++;
494#else
495 dst += LCD_WIDTH;
496#endif
497
498 y = YFAC*(*ysrc++ - 16);
499 r = y + rv;
500 g = y + guv;
501 b = y + bu;
502
503 if ((unsigned)(r | g | b) > 64*256-1)
504 {
505 r = clamp(r, 0, 64*256-1);
506 g = clamp(g, 0, 64*256-1);
507 b = clamp(b, 0, 64*256-1);
508 }
509
510 *dst = FB_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
511
512#if LCD_WIDTH >= LCD_HEIGHT
513 dst++;
514#else
515 dst += LCD_WIDTH;
516#endif
517 }
518 while (dst < row_end);
519
520 ysrc += stride;
521 usrc += stride >> 1;
522 vsrc += stride >> 1;
523
524#if LCD_WIDTH >= LCD_HEIGHT
525 row_end += LCD_WIDTH;
526 dst += LCD_WIDTH - width;
527#else
528 row_end -= 1;
529 dst -= LCD_WIDTH*width + 1;
530#endif
531 }
532 while (--linecounter > 0);
533
534#if LCD_WIDTH >= LCD_HEIGHT
535 lcd_update_rect(x, y, width, height);
536#else
537 lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
538#endif
539}
540
541/* Fill a rectangle with a gradient. This function draws only the partial
542 * gradient. It assumes the original gradient is src_height high and skips
543 * the first few rows. This is useful for drawing only the bottom half of
544 * a full gradient.
545 *
546 * height == src_height and row_skip == 0 will draw the full gradient
547 *
548 * x, y, width, height - dimensions describing the rectangle
549 * start_rgb - beginning color of the gradient
550 * end_rgb - end color of the gradient
551 * src_height - assumed original height (only height rows will be drawn)
552 * row_skip - how many rows of the original gradient to skip
553 */
554void lcd_gradient_fillrect_part(int x, int y, int width, int height,
555 unsigned start_rgb, unsigned end_rgb, int src_height, int row_skip)
556{
557 int old_pattern = current_vp->fg_pattern;
558 int step_mul, i;
559 int x1, x2;
560 x1 = x;
561 x2 = x + width;
562
563 if (height == 0) return;
564
565 step_mul = (1 << 16) / src_height;
566 int h_r = RGB_UNPACK_RED(start_rgb);
567 int h_g = RGB_UNPACK_GREEN(start_rgb);
568 int h_b = RGB_UNPACK_BLUE(start_rgb);
569 int rstep = (h_r - RGB_UNPACK_RED(end_rgb)) * step_mul;
570 int gstep = (h_g - RGB_UNPACK_GREEN(end_rgb)) * step_mul;
571 int bstep = (h_b - RGB_UNPACK_BLUE(end_rgb)) * step_mul;
572 h_r = (h_r << 16) + (1 << 15);
573 h_g = (h_g << 16) + (1 << 15);
574 h_b = (h_b << 16) + (1 << 15);
575
576 if (row_skip > 0)
577 {
578 h_r -= rstep * row_skip;
579 h_g -= gstep * row_skip;
580 h_b -= bstep * row_skip;
581 }
582
583 for(i = y; i < y + height; i++) {
584 current_vp->fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
585 lcd_hline(x1, x2, i);
586 h_r -= rstep;
587 h_g -= gstep;
588 h_b -= bstep;
589 }
590
591 current_vp->fg_pattern = old_pattern;
592}
593
594/* Fill a rectangle with a gradient. The gradient's color will fade from
595 * start_rgb to end_rgb over the height of the rectangle
596 *
597 * x, y, width, height - dimensions describing the rectangle
598 * start_rgb - beginning color of the gradient
599 * end_rgb - end color of the gradient
600 */
601void lcd_gradient_fillrect(int x, int y, int width, int height,
602 unsigned start_rgb, unsigned end_rgb)
603{
604 lcd_gradient_fillrect_part(x, y, width, height, start_rgb, end_rgb, height, 0);
605}