9 segment bitmap drawing:
[maemo-rb.git] / firmware / drivers / lcd-bitmap-common.c
bloba149e8aaa9b770c057556cf6d0aaed65102e86b5
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 void lcd_gradient_fillrect(int x, int y, int width, int height,
45 unsigned start_rgb, unsigned end_rgb)
47 int old_pattern = current_vp->fg_pattern;
48 int step_mul, i;
49 int x1, x2;
50 x1 = x;
51 x2 = x + width;
53 if (height == 0) return;
55 step_mul = (1 << 16) / height;
56 int h_r = RGB_UNPACK_RED(start_rgb);
57 int h_g = RGB_UNPACK_GREEN(start_rgb);
58 int h_b = RGB_UNPACK_BLUE(start_rgb);
59 int rstep = (h_r - RGB_UNPACK_RED(end_rgb)) * step_mul;
60 int gstep = (h_g - RGB_UNPACK_GREEN(end_rgb)) * step_mul;
61 int bstep = (h_b - RGB_UNPACK_BLUE(end_rgb)) * step_mul;
62 h_r = (h_r << 16) + (1 << 15);
63 h_g = (h_g << 16) + (1 << 15);
64 h_b = (h_b << 16) + (1 << 15);
66 for(i = y; i < y + height; i++) {
67 current_vp->fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
68 lcd_hline(x1, x2, i);
69 h_r -= rstep;
70 h_g -= gstep;
71 h_b -= bstep;
74 current_vp->fg_pattern = old_pattern;
77 /* Fill a text line with a gradient:
78 * x1, x2 - x pixel coordinates to start/stop
79 * y - y pixel to start from
80 * h - line height
81 * num_lines - number of lines to span the gradient over
82 * cur_line - current line being draw
84 static void lcd_do_gradient_line(int x1, int x2, int y, unsigned h,
85 int num_lines, int cur_line)
87 int step_mul;
88 if (h == 0) return;
90 num_lines *= h;
91 cur_line *= h;
92 step_mul = (1 << 16) / (num_lines);
93 int h_r = RGB_UNPACK_RED(current_vp->lss_pattern);
94 int h_g = RGB_UNPACK_GREEN(current_vp->lss_pattern);
95 int h_b = RGB_UNPACK_BLUE(current_vp->lss_pattern);
96 int rstep = (h_r - RGB_UNPACK_RED(current_vp->lse_pattern)) * step_mul;
97 int gstep = (h_g - RGB_UNPACK_GREEN(current_vp->lse_pattern)) * step_mul;
98 int bstep = (h_b - RGB_UNPACK_BLUE(current_vp->lse_pattern)) * step_mul;
99 unsigned start_rgb, end_rgb;
100 h_r = (h_r << 16) + (1 << 15);
101 h_g = (h_g << 16) + (1 << 15);
102 h_b = (h_b << 16) + (1 << 15);
103 if (cur_line)
105 h_r -= cur_line * rstep;
106 h_g -= cur_line * gstep;
107 h_b -= cur_line * bstep;
109 start_rgb = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
111 h_r -= h * rstep;
112 h_g -= h * gstep;
113 h_b -= h * bstep;
114 end_rgb = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
115 lcd_gradient_fillrect(x1, y, x2 - x1, h, start_rgb, end_rgb);
118 #endif
120 void LCDFN(set_framebuffer)(FBFN(data) *fb)
122 if (fb)
123 LCDFN(framebuffer) = fb;
124 else
125 LCDFN(framebuffer) = &LCDFN(static_framebuffer)[0][0];
129 * draws the borders of the current viewport
131 void LCDFN(draw_border_viewport)(void)
133 LCDFN(drawrect)(0, 0, current_vp->width, current_vp->height);
137 * fills the rectangle formed by current_vp
139 void LCDFN(fill_viewport)(void)
141 LCDFN(fillrect)(0, 0, current_vp->width, current_vp->height);
144 /* put a string at a given pixel position, skipping first ofs pixel columns */
145 static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str)
147 unsigned short *ucs;
148 font_lock(current_vp->font, true);
149 struct font* pf = font_get(current_vp->font);
150 int vp_flags = current_vp->flags;
151 int rtl_next_non_diac_width, last_non_diacritic_width;
153 if ((vp_flags & VP_FLAG_ALIGNMENT_MASK) != 0)
155 int w;
157 LCDFN(getstringsize)(str, &w, NULL);
158 /* center takes precedence */
159 if (vp_flags & VP_FLAG_ALIGN_CENTER)
161 x = ((current_vp->width - w)/ 2) + x;
162 if (x < 0)
163 x = 0;
165 else
167 x = current_vp->width - w - x;
168 x += ofs;
169 ofs = 0;
173 rtl_next_non_diac_width = 0;
174 last_non_diacritic_width = 0;
175 /* Mark diacritic and rtl flags for each character */
176 for (ucs = bidi_l2v(str, 1); *ucs; ucs++)
178 bool is_rtl, is_diac;
179 const unsigned char *bits;
180 int width, base_width, drawmode = 0, base_ofs = 0;
181 const unsigned short next_ch = ucs[1];
183 if (x >= current_vp->width)
184 break;
186 is_diac = is_diacritic(*ucs, &is_rtl);
188 /* Get proportional width and glyph bits */
189 width = font_get_width(pf, *ucs);
191 /* Calculate base width */
192 if (is_rtl)
194 /* Forward-seek the next non-diacritic character for base width */
195 if (is_diac)
197 if (!rtl_next_non_diac_width)
199 const unsigned short *u;
201 /* Jump to next non-diacritic char, and calc its width */
202 for (u = &ucs[1]; *u && is_diacritic(*u, NULL); u++);
204 rtl_next_non_diac_width = *u ? font_get_width(pf, *u) : 0;
206 base_width = rtl_next_non_diac_width;
208 else
210 rtl_next_non_diac_width = 0; /* Mark */
211 base_width = width;
214 else
216 if (!is_diac)
217 last_non_diacritic_width = width;
219 base_width = last_non_diacritic_width;
222 if (ofs > width)
224 ofs -= width;
225 continue;
228 if (is_diac)
230 /* XXX: Suggested by amiconn:
231 * This will produce completely wrong results if the original
232 * drawmode is DRMODE_COMPLEMENT. We need to pre-render the current
233 * character with all its diacritics at least (in mono) and then
234 * finally draw that. And we'll need an extra buffer that can hold
235 * one char's bitmap. Basically we can't just change the draw mode
236 * to something else irrespective of the original mode and expect
237 * the result to look as intended and with DRMODE_COMPLEMENT (which
238 * means XORing pixels), overdrawing this way will cause odd results
239 * if the diacritics and the base char both have common pixels set.
240 * So we need to combine the char and its diacritics in a temp
241 * buffer using OR, and then draw the final bitmap instead of the
242 * chars, without touching the drawmode
244 drawmode = current_vp->drawmode;
245 current_vp->drawmode = DRMODE_FG;
247 base_ofs = (base_width - width) / 2;
250 bits = font_get_bits(pf, *ucs);
252 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
253 if (pf->depth)
254 lcd_alpha_bitmap_part(bits, ofs, 0, width, x + base_ofs, y,
255 width - ofs, pf->height);
256 else
257 #endif
258 LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x + base_ofs,
259 y, width - ofs, pf->height);
260 if (is_diac)
262 current_vp->drawmode = drawmode;
265 if (next_ch)
267 bool next_is_rtl;
268 bool next_is_diacritic = is_diacritic(next_ch, &next_is_rtl);
270 /* Increment if:
271 * LTR: Next char is not diacritic,
272 * RTL: Current char is non-diacritic and next char is diacritic */
273 if ((is_rtl && !is_diac) ||
274 (!is_rtl && (!next_is_diacritic || next_is_rtl)))
276 x += base_width - ofs;
277 ofs = 0;
281 font_lock(current_vp->font, false);
284 /* put a string at a given pixel position */
285 void LCDFN(putsxy)(int x, int y, const unsigned char *str)
287 LCDFN(putsxyofs)(x, y, 0, str);
290 /* Formatting version of LCDFN(putsxy) */
291 void LCDFN(putsxyf)(int x, int y, const unsigned char *fmt, ...)
293 va_list ap;
294 char buf[256];
295 va_start(ap, fmt);
296 vsnprintf(buf, sizeof (buf), fmt, ap);
297 va_end(ap);
298 LCDFN(putsxy)(x, y, buf);
301 static void LCDFN(putsxyofs_style)(int xpos, int ypos,
302 const unsigned char *str, int style,
303 int h, int offset)
305 int lastmode = current_vp->drawmode;
306 int text_ypos = ypos;
307 int line_height = font_get(current_vp->font)->height;
308 text_ypos += h/2 - line_height/2; /* center the text in the line */
309 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
310 int oldfgcolor = current_vp->fg_pattern;
311 int oldbgcolor = current_vp->bg_pattern;
312 current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
313 DRMODE_INVERSEVID : 0);
314 if (style & STYLE_COLORED) {
315 if (current_vp->drawmode == DRMODE_SOLID)
316 current_vp->fg_pattern = style & STYLE_COLOR_MASK;
317 else
318 current_vp->bg_pattern = style & STYLE_COLOR_MASK;
320 current_vp->drawmode ^= DRMODE_INVERSEVID;
321 if (style & STYLE_GRADIENT) {
322 current_vp->drawmode = DRMODE_FG;
323 lcd_do_gradient_line(xpos, current_vp->width, ypos, h,
324 NUMLN_UNPACK(style), CURLN_UNPACK(style));
325 current_vp->fg_pattern = current_vp->lst_pattern;
327 else if (style & STYLE_COLORBAR) {
328 current_vp->drawmode = DRMODE_FG;
329 current_vp->fg_pattern = current_vp->lss_pattern;
330 lcd_fillrect(xpos, ypos, current_vp->width - xpos, h);
331 current_vp->fg_pattern = current_vp->lst_pattern;
333 else {
334 lcd_fillrect(xpos, ypos, current_vp->width - xpos, h);
335 current_vp->drawmode = (style & STYLE_INVERT) ?
336 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
338 if (str[0])
339 lcd_putsxyofs(xpos, text_ypos, offset, str);
340 current_vp->fg_pattern = oldfgcolor;
341 current_vp->bg_pattern = oldbgcolor;
342 #else
343 current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
344 0 : DRMODE_INVERSEVID);
345 LCDFN(fillrect)(xpos, ypos, current_vp->width - xpos, h);
346 current_vp->drawmode ^= DRMODE_INVERSEVID;
347 if (str[0])
348 LCDFN(putsxyofs)(xpos, text_ypos, offset, str);
349 #endif
350 current_vp->drawmode = lastmode;
353 /*** Line oriented text output ***/
355 /* put a string at a given char position */
356 void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
357 int style, int x_offset, int y_offset)
359 int xpos, ypos, h;
360 LCDFN(scroll_stop_line)(current_vp, y);
361 if(!str)
362 return;
364 h = current_vp->line_height ?: (int)font_get(current_vp->font)->height;
365 if ((style&STYLE_XY_PIXELS) == 0)
367 xpos = x * LCDFN(getstringsize)(" ", NULL, NULL);
368 ypos = y * h + y_offset;
370 else
372 xpos = x;
373 ypos = y + y_offset;
375 LCDFN(putsxyofs_style)(xpos, ypos, str, style, h, x_offset);
378 void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
379 int style, int x_offset)
381 LCDFN(puts_style_xyoffset)(x, y, str, style, x_offset, 0);
384 void LCDFN(puts)(int x, int y, const unsigned char *str)
386 LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, 0);
389 /* Formatting version of LCDFN(puts) */
390 void LCDFN(putsf)(int x, int y, const unsigned char *fmt, ...)
392 va_list ap;
393 char buf[256];
394 va_start(ap, fmt);
395 vsnprintf(buf, sizeof (buf), fmt, ap);
396 va_end(ap);
397 LCDFN(puts)(x, y, buf);
400 void LCDFN(puts_style)(int x, int y, const unsigned char *str, int style)
402 LCDFN(puts_style_offset)(x, y, str, style, 0);
405 void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)
407 LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, offset);
410 /*** scrolling ***/
412 static struct scrollinfo* find_scrolling_line(int line)
414 struct scrollinfo* s = NULL;
415 int i;
417 for(i=0; i<LCDFN(scroll_info).lines; i++)
419 s = &LCDFN(scroll_info).scroll[i];
420 if (s->y == line && s->vp == current_vp)
421 return s;
423 return NULL;
426 void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
427 int style, int x_offset, int y_offset)
429 struct scrollinfo* s;
430 char *end;
431 int w, h;
432 int len;
433 bool restart = false;
434 int space_width;
436 if (!string || ((unsigned)y >= (unsigned)current_vp->height))
437 return;
439 s = find_scrolling_line(y);
440 if (!s)
441 restart = true;
443 if (restart)
445 /* remove any previously scrolling line at the same location */
446 LCDFN(scroll_stop_line)(current_vp, y);
448 if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return;
449 LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset);
452 LCDFN(getstringsize)(string, &w, &h);
454 if (current_vp->width - x * 8 >= w)
455 return;
457 if (restart)
459 /* prepare scroll line */
460 s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
461 s->start_tick = current_tick + LCDFN(scroll_info).delay;
463 strlcpy(s->line, string, sizeof s->line);
464 space_width = LCDFN(getstringsize)(" ", NULL, NULL);
466 /* get width */
467 LCDFN(getstringsize)(s->line, &w, &h);
468 if (!restart && s->width > w)
470 if (s->startx > w)
471 s->startx = w;
473 s->width = w;
475 /* scroll bidirectional or forward only depending on the string
476 width */
477 if ( LCDFN(scroll_info).bidir_limit ) {
478 s->bidir = s->width < (current_vp->width) *
479 (100 + LCDFN(scroll_info).bidir_limit) / 100;
481 else
482 s->bidir = false;
484 if (!s->bidir) { /* add spaces if scrolling in the round */
485 strlcat(s->line, " ", sizeof s->line);
486 /* get new width incl. spaces */
487 s->width += space_width * 3;
490 end = strchr(s->line, '\0');
491 len = sizeof s->line - (end - s->line);
492 strlcpy(end, string, MIN(current_vp->width/2, len));
494 s->vp = current_vp;
495 s->y = y;
496 if (restart)
498 s->offset = x_offset;
499 s->startx = x * space_width;
500 s->backward = false;
501 s->style = style;
503 s->y_offset = y_offset;
505 if (restart)
506 LCDFN(scroll_info).lines++;
509 void LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
511 LCDFN(puts_scroll_style)(x, y, string, STYLE_DEFAULT);
514 void LCDFN(puts_scroll_style)(int x, int y, const unsigned char *string,
515 int style)
517 LCDFN(puts_scroll_style_offset)(x, y, string, style, 0);
520 void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string,
521 int offset)
523 LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset);
526 void LCDFN(scroll_fn)(void)
528 struct scrollinfo* s;
529 int index;
530 int xpos, ypos, height;
531 struct viewport* old_vp = current_vp;
532 bool makedelay;
534 for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) {
535 s = &LCDFN(scroll_info).scroll[index];
537 /* check pause */
538 if (TIME_BEFORE(current_tick, s->start_tick))
539 continue;
541 LCDFN(set_viewport)(s->vp);
542 height = s->vp->line_height ?: (int)font_get(s->vp->font)->height;
544 if (s->backward)
545 s->offset -= LCDFN(scroll_info).step;
546 else
547 s->offset += LCDFN(scroll_info).step;
549 xpos = s->startx;
550 ypos = s->y * height + s->y_offset;
552 makedelay = false;
553 if (s->bidir) { /* scroll bidirectional */
554 if (s->offset <= 0) {
555 /* at beginning of line */
556 s->offset = 0;
557 s->backward = false;
558 makedelay = true;
560 else if (s->offset >= s->width - (current_vp->width - xpos)) {
561 /* at end of line */
562 s->offset = s->width - (current_vp->width - xpos);
563 s->backward = true;
564 makedelay = true;
567 else {
568 /* scroll forward the whole time */
569 if (s->offset >= s->width) {
570 s->offset = 0;
571 makedelay = true;
575 if (makedelay)
576 s->start_tick = current_tick + LCDFN(scroll_info).delay +
577 LCDFN(scroll_info).ticks;
579 LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, height, s->offset);
580 LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width-xpos, height);
582 LCDFN(set_viewport)(old_vp);
585 void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
586 int style, int x_offset)
588 LCDFN(puts_scroll_style_xyoffset)(x, y, string, style, x_offset, 0);
591 #if !defined(HAVE_LCD_COLOR) || !defined(MAIN_LCD)
592 /* see lcd-16bit-common.c for others */
593 #ifdef MAIN_LCD
594 #define THIS_STRIDE STRIDE_MAIN
595 #else
596 #define THIS_STRIDE STRIDE_REMOTE
597 #endif
599 void LCDFN(bmp_part)(const struct bitmap* bm, int src_x, int src_y,
600 int x, int y, int width, int height)
602 #if LCDM(DEPTH) > 1
603 if (bm->format != FORMAT_MONO)
604 LCDFN(bitmap_part)((FBFN(data)*)(bm->data),
605 src_x, src_y, THIS_STRIDE(bm->width, bm->height), x, y, width, height);
606 else
607 #endif
608 LCDFN(mono_bitmap_part)(bm->data,
609 src_x, src_y, THIS_STRIDE(bm->width, bm->height), x, y, width, height);
612 void LCDFN(bmp)(const struct bitmap* bm, int x, int y)
614 LCDFN(bmp_part)(bm, 0, 0, x, y, bm->width, bm->height);
617 #endif
619 void LCDFN(nine_segment_bmp)(const struct bitmap* bm, int x, int y,
620 int width, int height)
622 int seg_w = bm->width / 3;
623 int seg_h = bm->height / 3;
624 int src_x, src_y, dst_x, dst_y;
626 /* top */
627 src_x = seg_w; src_y = 0;
628 dst_x = seg_w; dst_y = 0;
629 for (; dst_x < width - seg_w; dst_x += seg_w)
630 LCDFN(bmp_part)(bm, src_x, src_y, dst_x, dst_y, seg_w, seg_h);
631 /* bottom */
632 src_x = seg_w; src_y = bm->height - seg_h;
633 dst_x = seg_w; dst_y = height - seg_h;
634 for (; dst_x < width - seg_w; dst_x += seg_w)
635 LCDFN(bmp_part)(bm, src_x, src_y, dst_x, dst_y, seg_w, seg_h);
637 /* left */
638 src_x = 0; src_y = seg_h;
639 dst_x = 0; dst_y = seg_h;
640 for (; dst_y < height - seg_h; dst_y += seg_h)
641 LCDFN(bmp_part)(bm, src_x, src_y, dst_x, dst_y, seg_w, seg_h);
642 /* right */
643 src_x = bm->width - seg_w; src_y = seg_h;
644 dst_x = width - seg_w; dst_y = seg_h;
645 for (; dst_y < height - seg_h; dst_y += seg_h)
646 LCDFN(bmp_part)(bm, src_x, src_y, dst_x, dst_y, seg_w, seg_h);
647 /* center */
648 dst_y = seg_h; src_y = seg_h; src_x = seg_w;
649 for (; dst_y < height - seg_h; dst_y += seg_h)
651 dst_x = seg_w;
652 for (; dst_x < width - seg_w; dst_x += seg_w)
653 LCDFN(bmp_part)(bm, src_x, src_y, dst_x, dst_y, seg_w, seg_h);
656 /* 4 corners */
657 LCDFN(bmp_part)(bm, 0, 0, x, y, seg_w, seg_h);
658 LCDFN(bmp_part)(bm, bm->width - seg_w, 0, width - seg_w, 0, seg_w, seg_h);
659 LCDFN(bmp_part)(bm, 0, bm->width - seg_h, 0, height - seg_h, seg_w, seg_h);
660 LCDFN(bmp_part)(bm, bm->width - seg_w, bm->width - seg_h,
661 width - seg_w, height - seg_h, seg_w, seg_h);