lcd/skin_engine: Add the ability to draw onto the backdrop layer
[maemo-rb.git] / firmware / drivers / lcd-bitmap-common.c
blobb0be687ed2c7e3fb3251f3e771b15ca2973e0b90
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Dave Chapman
11 * Text rendering
12 * Copyright (C) 2006 Shachar Liberman
13 * Offset text, scrolling
14 * Copyright (C) 2007 Nicolas Pennequin, Tom Ross, Ken Fazzone, Akio Idehara
15 * Color gradient background
16 * Copyright (C) 2009 Andrew Mahone
17 * Merged common LCD bitmap code
19 * Rockbox common bitmap LCD functions
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
27 * KIND, either express or implied.
29 ****************************************************************************/
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include "string-extra.h"
33 #include "diacritic.h"
35 #ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */
36 #define LCDFN(fn) lcd_ ## fn
37 #define FBFN(fn) fb_ ## fn
38 #define LCDM(ma) LCD_ ## ma
39 #define LCDNAME "lcd_"
40 #define MAIN_LCD
41 #endif
43 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
44 /* Fill a rectangle with a gradient */
45 static void lcd_gradient_rect(int x1, int x2, int y, unsigned h,
46 int num_lines, int cur_line)
48 int old_pattern = current_vp->fg_pattern;
49 int step_mul;
50 if (h == 0) return;
52 num_lines *= h;
53 cur_line *= h;
54 step_mul = (1 << 16) / (num_lines);
55 int h_r = RGB_UNPACK_RED(current_vp->lss_pattern);
56 int h_g = RGB_UNPACK_GREEN(current_vp->lss_pattern);
57 int h_b = RGB_UNPACK_BLUE(current_vp->lss_pattern);
58 int rstep = (h_r - RGB_UNPACK_RED(current_vp->lse_pattern)) * step_mul;
59 int gstep = (h_g - RGB_UNPACK_GREEN(current_vp->lse_pattern)) * step_mul;
60 int bstep = (h_b - RGB_UNPACK_BLUE(current_vp->lse_pattern)) * step_mul;
61 h_r = (h_r << 16) + (1 << 15);
62 h_g = (h_g << 16) + (1 << 15);
63 h_b = (h_b << 16) + (1 << 15);
64 if (cur_line)
66 h_r -= cur_line * rstep;
67 h_g -= cur_line * gstep;
68 h_b -= cur_line * bstep;
70 unsigned count;
72 for(count = 0; count < h; count++) {
73 current_vp->fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
74 lcd_hline(x1, x2, y + count);
75 h_r -= rstep;
76 h_g -= gstep;
77 h_b -= bstep;
80 current_vp->fg_pattern = old_pattern;
82 #endif
84 void LCDFN(set_framebuffer)(FBFN(data) *fb)
86 if (fb)
87 LCDFN(framebuffer) = fb;
88 else
89 LCDFN(framebuffer) = &LCDFN(static_framebuffer)[0][0];
93 * draws the borders of the current viewport
94 **/
95 void LCDFN(draw_border_viewport)(void)
97 LCDFN(drawrect)(0, 0, current_vp->width, current_vp->height);
101 * fills the rectangle formed by current_vp
103 void LCDFN(fill_viewport)(void)
105 LCDFN(fillrect)(0, 0, current_vp->width, current_vp->height);
108 /* put a string at a given pixel position, skipping first ofs pixel columns */
109 static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str)
111 unsigned short *ucs;
112 font_lock(current_vp->font, true);
113 struct font* pf = font_get(current_vp->font);
114 int vp_flags = current_vp->flags;
115 int rtl_next_non_diac_width, last_non_diacritic_width;
117 if ((vp_flags & VP_FLAG_ALIGNMENT_MASK) != 0)
119 int w;
121 LCDFN(getstringsize)(str, &w, NULL);
122 /* center takes precedence */
123 if (vp_flags & VP_FLAG_ALIGN_CENTER)
125 x = ((current_vp->width - w)/ 2) + x;
126 if (x < 0)
127 x = 0;
129 else
131 x = current_vp->width - w - x;
132 x += ofs;
133 ofs = 0;
137 rtl_next_non_diac_width = 0;
138 last_non_diacritic_width = 0;
139 /* Mark diacritic and rtl flags for each character */
140 for (ucs = bidi_l2v(str, 1); *ucs; ucs++)
142 bool is_rtl, is_diac;
143 const unsigned char *bits;
144 int width, base_width, drawmode = 0, base_ofs = 0;
145 const unsigned short next_ch = ucs[1];
147 if (x >= current_vp->width)
148 break;
150 is_diac = is_diacritic(*ucs, &is_rtl);
152 /* Get proportional width and glyph bits */
153 width = font_get_width(pf, *ucs);
155 /* Calculate base width */
156 if (is_rtl)
158 /* Forward-seek the next non-diacritic character for base width */
159 if (is_diac)
161 if (!rtl_next_non_diac_width)
163 const unsigned short *u;
165 /* Jump to next non-diacritic char, and calc its width */
166 for (u = &ucs[1]; *u && is_diacritic(*u, NULL); u++);
168 rtl_next_non_diac_width = *u ? font_get_width(pf, *u) : 0;
170 base_width = rtl_next_non_diac_width;
172 else
174 rtl_next_non_diac_width = 0; /* Mark */
175 base_width = width;
178 else
180 if (!is_diac)
181 last_non_diacritic_width = width;
183 base_width = last_non_diacritic_width;
186 if (ofs > width)
188 ofs -= width;
189 continue;
192 if (is_diac)
194 /* XXX: Suggested by amiconn:
195 * This will produce completely wrong results if the original
196 * drawmode is DRMODE_COMPLEMENT. We need to pre-render the current
197 * character with all its diacritics at least (in mono) and then
198 * finally draw that. And we'll need an extra buffer that can hold
199 * one char's bitmap. Basically we can't just change the draw mode
200 * to something else irrespective of the original mode and expect
201 * the result to look as intended and with DRMODE_COMPLEMENT (which
202 * means XORing pixels), overdrawing this way will cause odd results
203 * if the diacritics and the base char both have common pixels set.
204 * So we need to combine the char and its diacritics in a temp
205 * buffer using OR, and then draw the final bitmap instead of the
206 * chars, without touching the drawmode
208 drawmode = current_vp->drawmode;
209 current_vp->drawmode = DRMODE_FG;
211 base_ofs = (base_width - width) / 2;
214 bits = font_get_bits(pf, *ucs);
216 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
217 if (pf->depth)
218 lcd_alpha_bitmap_part(bits, ofs, 0, width, x + base_ofs, y,
219 width - ofs, pf->height);
220 else
221 #endif
222 LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x + base_ofs,
223 y, width - ofs, pf->height);
224 if (is_diac)
226 current_vp->drawmode = drawmode;
229 if (next_ch)
231 bool next_is_rtl;
232 bool next_is_diacritic = is_diacritic(next_ch, &next_is_rtl);
234 /* Increment if:
235 * LTR: Next char is not diacritic,
236 * RTL: Current char is non-diacritic and next char is diacritic */
237 if ((is_rtl && !is_diac) ||
238 (!is_rtl && (!next_is_diacritic || next_is_rtl)))
240 x += base_width - ofs;
241 ofs = 0;
245 font_lock(current_vp->font, false);
248 /* put a string at a given pixel position */
249 void LCDFN(putsxy)(int x, int y, const unsigned char *str)
251 LCDFN(putsxyofs)(x, y, 0, str);
254 /* Formatting version of LCDFN(putsxy) */
255 void LCDFN(putsxyf)(int x, int y, const unsigned char *fmt, ...)
257 va_list ap;
258 char buf[256];
259 va_start(ap, fmt);
260 vsnprintf(buf, sizeof (buf), fmt, ap);
261 va_end(ap);
262 LCDFN(putsxy)(x, y, buf);
265 static void LCDFN(putsxyofs_style)(int xpos, int ypos,
266 const unsigned char *str, int style,
267 int h, int offset)
269 int lastmode = current_vp->drawmode;
270 int text_ypos = ypos;
271 int line_height = font_get(current_vp->font)->height;
272 text_ypos += h/2 - line_height/2; /* center the text in the line */
273 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
274 int oldfgcolor = current_vp->fg_pattern;
275 int oldbgcolor = current_vp->bg_pattern;
276 current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
277 DRMODE_INVERSEVID : 0);
278 if (style & STYLE_COLORED) {
279 if (current_vp->drawmode == DRMODE_SOLID)
280 current_vp->fg_pattern = style & STYLE_COLOR_MASK;
281 else
282 current_vp->bg_pattern = style & STYLE_COLOR_MASK;
284 current_vp->drawmode ^= DRMODE_INVERSEVID;
285 if (style & STYLE_GRADIENT) {
286 current_vp->drawmode = DRMODE_FG;
287 lcd_gradient_rect(xpos, current_vp->width, ypos, h,
288 NUMLN_UNPACK(style), CURLN_UNPACK(style));
289 current_vp->fg_pattern = current_vp->lst_pattern;
291 else if (style & STYLE_COLORBAR) {
292 current_vp->drawmode = DRMODE_FG;
293 current_vp->fg_pattern = current_vp->lss_pattern;
294 lcd_fillrect(xpos, ypos, current_vp->width - xpos, h);
295 current_vp->fg_pattern = current_vp->lst_pattern;
297 else {
298 lcd_fillrect(xpos, ypos, current_vp->width - xpos, h);
299 current_vp->drawmode = (style & STYLE_INVERT) ?
300 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
302 if (str[0])
303 lcd_putsxyofs(xpos, text_ypos, offset, str);
304 current_vp->fg_pattern = oldfgcolor;
305 current_vp->bg_pattern = oldbgcolor;
306 #else
307 current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
308 0 : DRMODE_INVERSEVID);
309 LCDFN(fillrect)(xpos, ypos, current_vp->width - xpos, h);
310 current_vp->drawmode ^= DRMODE_INVERSEVID;
311 if (str[0])
312 LCDFN(putsxyofs)(xpos, text_ypos, offset, str);
313 #endif
314 current_vp->drawmode = lastmode;
317 /*** Line oriented text output ***/
319 /* put a string at a given char position */
320 void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
321 int style, int x_offset, int y_offset)
323 int xpos, ypos, h;
324 LCDFN(scroll_stop_line)(current_vp, y);
325 if(!str)
326 return;
328 h = current_vp->line_height ?: (int)font_get(current_vp->font)->height;
329 if ((style&STYLE_XY_PIXELS) == 0)
331 xpos = x * LCDFN(getstringsize)(" ", NULL, NULL);
332 ypos = y * h + y_offset;
334 else
336 xpos = x;
337 ypos = y + y_offset;
339 LCDFN(putsxyofs_style)(xpos, ypos, str, style, h, x_offset);
342 void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
343 int style, int x_offset)
345 LCDFN(puts_style_xyoffset)(x, y, str, style, x_offset, 0);
348 void LCDFN(puts)(int x, int y, const unsigned char *str)
350 LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, 0);
353 /* Formatting version of LCDFN(puts) */
354 void LCDFN(putsf)(int x, int y, const unsigned char *fmt, ...)
356 va_list ap;
357 char buf[256];
358 va_start(ap, fmt);
359 vsnprintf(buf, sizeof (buf), fmt, ap);
360 va_end(ap);
361 LCDFN(puts)(x, y, buf);
364 void LCDFN(puts_style)(int x, int y, const unsigned char *str, int style)
366 LCDFN(puts_style_offset)(x, y, str, style, 0);
369 void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)
371 LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, offset);
374 /*** scrolling ***/
376 static struct scrollinfo* find_scrolling_line(int line)
378 struct scrollinfo* s = NULL;
379 int i;
381 for(i=0; i<LCDFN(scroll_info).lines; i++)
383 s = &LCDFN(scroll_info).scroll[i];
384 if (s->y == line && s->vp == current_vp)
385 return s;
387 return NULL;
390 void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
391 int style, int x_offset, int y_offset)
393 struct scrollinfo* s;
394 char *end;
395 int w, h;
396 int len;
397 bool restart = false;
398 int space_width;
400 if (!string || ((unsigned)y >= (unsigned)current_vp->height))
401 return;
403 s = find_scrolling_line(y);
404 if (!s)
405 restart = true;
407 if (restart)
409 /* remove any previously scrolling line at the same location */
410 LCDFN(scroll_stop_line)(current_vp, y);
412 if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return;
413 LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset);
416 LCDFN(getstringsize)(string, &w, &h);
418 if (current_vp->width - x * 8 >= w)
419 return;
421 if (restart)
423 /* prepare scroll line */
424 s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
425 s->start_tick = current_tick + LCDFN(scroll_info).delay;
427 strlcpy(s->line, string, sizeof s->line);
428 space_width = LCDFN(getstringsize)(" ", NULL, NULL);
430 /* get width */
431 LCDFN(getstringsize)(s->line, &w, &h);
432 if (!restart && s->width > w)
434 if (s->startx > w)
435 s->startx = w;
437 s->width = w;
439 /* scroll bidirectional or forward only depending on the string
440 width */
441 if ( LCDFN(scroll_info).bidir_limit ) {
442 s->bidir = s->width < (current_vp->width) *
443 (100 + LCDFN(scroll_info).bidir_limit) / 100;
445 else
446 s->bidir = false;
448 if (!s->bidir) { /* add spaces if scrolling in the round */
449 strlcat(s->line, " ", sizeof s->line);
450 /* get new width incl. spaces */
451 s->width += space_width * 3;
454 end = strchr(s->line, '\0');
455 len = sizeof s->line - (end - s->line);
456 strlcpy(end, string, MIN(current_vp->width/2, len));
458 s->vp = current_vp;
459 s->y = y;
460 if (restart)
462 s->offset = x_offset;
463 s->startx = x * space_width;
464 s->backward = false;
465 s->style = style;
467 s->y_offset = y_offset;
469 if (restart)
470 LCDFN(scroll_info).lines++;
473 void LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
475 LCDFN(puts_scroll_style)(x, y, string, STYLE_DEFAULT);
478 void LCDFN(puts_scroll_style)(int x, int y, const unsigned char *string,
479 int style)
481 LCDFN(puts_scroll_style_offset)(x, y, string, style, 0);
484 void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string,
485 int offset)
487 LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset);
490 void LCDFN(scroll_fn)(void)
492 struct scrollinfo* s;
493 int index;
494 int xpos, ypos, height;
495 struct viewport* old_vp = current_vp;
496 bool makedelay;
498 for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) {
499 s = &LCDFN(scroll_info).scroll[index];
501 /* check pause */
502 if (TIME_BEFORE(current_tick, s->start_tick))
503 continue;
505 LCDFN(set_viewport)(s->vp);
506 height = s->vp->line_height ?: (int)font_get(s->vp->font)->height;
508 if (s->backward)
509 s->offset -= LCDFN(scroll_info).step;
510 else
511 s->offset += LCDFN(scroll_info).step;
513 xpos = s->startx;
514 ypos = s->y * height + s->y_offset;
516 makedelay = false;
517 if (s->bidir) { /* scroll bidirectional */
518 if (s->offset <= 0) {
519 /* at beginning of line */
520 s->offset = 0;
521 s->backward = false;
522 makedelay = true;
524 else if (s->offset >= s->width - (current_vp->width - xpos)) {
525 /* at end of line */
526 s->offset = s->width - (current_vp->width - xpos);
527 s->backward = true;
528 makedelay = true;
531 else {
532 /* scroll forward the whole time */
533 if (s->offset >= s->width) {
534 s->offset = 0;
535 makedelay = true;
539 if (makedelay)
540 s->start_tick = current_tick + LCDFN(scroll_info).delay +
541 LCDFN(scroll_info).ticks;
543 LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, height, s->offset);
544 LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width-xpos, height);
546 LCDFN(set_viewport)(old_vp);
549 void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
550 int style, int x_offset)
552 LCDFN(puts_scroll_style_xyoffset)(x, y, string, style, x_offset, 0);
555 #if !defined(HAVE_LCD_COLOR) || !defined(MAIN_LCD)
556 /* see lcd-16bit-common.c for others */
557 #ifdef MAIN_LCD
558 #define THIS_STRIDE STRIDE_MAIN
559 #else
560 #define THIS_STRIDE STRIDE_REMOTE
561 #endif
563 void LCDFN(bmp_part)(const struct bitmap* bm, int src_x, int src_y,
564 int x, int y, int width, int height)
566 #if LCDM(DEPTH) > 1
567 if (bm->format != FORMAT_MONO)
568 LCDFN(bitmap_part)((FBFN(data)*)(bm->data),
569 src_x, src_y, THIS_STRIDE(bm->width, bm->height), x, y, width, height);
570 else
571 #endif
572 LCDFN(mono_bitmap_part)(bm->data,
573 src_x, src_y, THIS_STRIDE(bm->width, bm->height), x, y, width, height);
576 void LCDFN(bmp)(const struct bitmap* bm, int x, int y)
578 LCDFN(bmp_part)(bm, 0, 0, x, y, bm->width, bm->height);
581 #endif