1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 Dave Chapman
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 ****************************************************************************/
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_"
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
;
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);
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
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
)
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);
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);
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
);
120 void LCDFN(set_framebuffer
)(FBFN(data
) *fb
)
123 LCDFN(framebuffer
) = fb
;
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
)
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)
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
;
167 x
= current_vp
->width
- w
- x
;
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
)
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 */
194 /* Forward-seek the next non-diacritic character for base width */
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
;
210 rtl_next_non_diac_width
= 0; /* Mark */
217 last_non_diacritic_width
= width
;
219 base_width
= last_non_diacritic_width
;
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)
254 lcd_alpha_bitmap_part(bits
, ofs
, 0, width
, x
+ base_ofs
, y
,
255 width
- ofs
, pf
->height
);
258 LCDFN(mono_bitmap_part
)(bits
, ofs
, 0, width
, x
+ base_ofs
,
259 y
, width
- ofs
, pf
->height
);
262 current_vp
->drawmode
= drawmode
;
268 bool next_is_diacritic
= is_diacritic(next_ch
, &next_is_rtl
);
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
;
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
, ...)
296 vsnprintf(buf
, sizeof (buf
), fmt
, ap
);
298 LCDFN(putsxy
)(x
, y
, buf
);
301 static void LCDFN(putsxyofs_style
)(int xpos
, int ypos
,
302 const unsigned char *str
, int style
,
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
;
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
;
334 lcd_fillrect(xpos
, ypos
, current_vp
->width
- xpos
, h
);
335 current_vp
->drawmode
= (style
& STYLE_INVERT
) ?
336 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
339 lcd_putsxyofs(xpos
, text_ypos
, offset
, str
);
340 current_vp
->fg_pattern
= oldfgcolor
;
341 current_vp
->bg_pattern
= oldbgcolor
;
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
;
348 LCDFN(putsxyofs
)(xpos
, text_ypos
, offset
, str
);
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
)
360 LCDFN(scroll_stop_line
)(current_vp
, y
);
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
;
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
, ...)
395 vsnprintf(buf
, sizeof (buf
), fmt
, 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
);
412 static struct scrollinfo
* find_scrolling_line(int line
)
414 struct scrollinfo
* s
= NULL
;
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
)
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
;
433 bool restart
= false;
436 if (!string
|| ((unsigned)y
>= (unsigned)current_vp
->height
))
439 s
= find_scrolling_line(y
);
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
)
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
);
467 LCDFN(getstringsize
)(s
->line
, &w
, &h
);
468 if (!restart
&& s
->width
> w
)
475 /* scroll bidirectional or forward only depending on the string
477 if ( LCDFN(scroll_info
).bidir_limit
) {
478 s
->bidir
= s
->width
< (current_vp
->width
) *
479 (100 + LCDFN(scroll_info
).bidir_limit
) / 100;
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
));
498 s
->offset
= x_offset
;
499 s
->startx
= x
* space_width
;
503 s
->y_offset
= y_offset
;
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
,
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
,
523 LCDFN(puts_scroll_style_offset
)(x
, y
, string
, STYLE_DEFAULT
, offset
);
526 void LCDFN(scroll_fn
)(void)
528 struct scrollinfo
* s
;
530 int xpos
, ypos
, height
;
531 struct viewport
* old_vp
= current_vp
;
534 for ( index
= 0; index
< LCDFN(scroll_info
).lines
; index
++ ) {
535 s
= &LCDFN(scroll_info
).scroll
[index
];
538 if (TIME_BEFORE(current_tick
, s
->start_tick
))
541 LCDFN(set_viewport
)(s
->vp
);
542 height
= s
->vp
->line_height
?: (int)font_get(s
->vp
->font
)->height
;
545 s
->offset
-= LCDFN(scroll_info
).step
;
547 s
->offset
+= LCDFN(scroll_info
).step
;
550 ypos
= s
->y
* height
+ s
->y_offset
;
553 if (s
->bidir
) { /* scroll bidirectional */
554 if (s
->offset
<= 0) {
555 /* at beginning of line */
560 else if (s
->offset
>= s
->width
- (current_vp
->width
- xpos
)) {
562 s
->offset
= s
->width
- (current_vp
->width
- xpos
);
568 /* scroll forward the whole time */
569 if (s
->offset
>= s
->width
) {
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 */
594 #define THIS_STRIDE STRIDE_MAIN
596 #define THIS_STRIDE STRIDE_REMOTE
599 void LCDFN(bmp_part
)(const struct bitmap
* bm
, int src_x
, int src_y
,
600 int x
, int y
, int width
, int height
)
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
);
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
);
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
;
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
);
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
);
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
);
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
);
648 dst_y
= seg_h
; src_y
= seg_h
; src_x
= seg_w
;
649 for (; dst_y
< height
- seg_h
; dst_y
+= seg_h
)
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
);
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
);