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 /* 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
;
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);
66 h_r
-= cur_line
* rstep
;
67 h_g
-= cur_line
* gstep
;
68 h_b
-= cur_line
* bstep
;
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
);
80 current_vp
->fg_pattern
= old_pattern
;
85 * draws the borders of the current viewport
87 void LCDFN(draw_border_viewport
)(void)
89 LCDFN(drawrect
)(0, 0, current_vp
->width
, current_vp
->height
);
93 * fills the rectangle formed by current_vp
95 void LCDFN(fill_viewport
)(void)
97 LCDFN(fillrect
)(0, 0, current_vp
->width
, current_vp
->height
);
100 /* put a string at a given pixel position, skipping first ofs pixel columns */
101 static void LCDFN(putsxyofs
)(int x
, int y
, int ofs
, const unsigned char *str
)
104 struct font
* pf
= font_get(current_vp
->font
);
105 int vp_flags
= current_vp
->flags
;
106 int rtl_next_non_diac_width
, last_non_diacritic_width
;
108 if ((vp_flags
& VP_FLAG_ALIGNMENT_MASK
) != 0)
112 LCDFN(getstringsize
)(str
, &w
, NULL
);
113 /* center takes precedence */
114 if (vp_flags
& VP_FLAG_ALIGN_CENTER
)
116 x
= ((current_vp
->width
- w
)/ 2) + x
;
122 x
= current_vp
->width
- w
- x
;
128 rtl_next_non_diac_width
= 0;
129 last_non_diacritic_width
= 0;
130 /* Mark diacritic and rtl flags for each character */
131 for (ucs
= bidi_l2v(str
, 1); *ucs
; ucs
++)
133 bool is_rtl
, is_diac
;
134 const unsigned char *bits
;
135 int width
, base_width
, drawmode
= 0, base_ofs
= 0;
136 const unsigned short next_ch
= ucs
[1];
138 if (x
>= current_vp
->width
)
141 is_diac
= is_diacritic(*ucs
, &is_rtl
);
143 /* Get proportional width and glyph bits */
144 width
= font_get_width(pf
, *ucs
);
146 /* Calculate base width */
149 /* Forward-seek the next non-diacritic character for base width */
152 if (!rtl_next_non_diac_width
)
154 const unsigned short *u
;
156 /* Jump to next non-diacritic char, and calc its width */
157 for (u
= &ucs
[1]; *u
&& is_diacritic(*u
, NULL
); u
++);
159 rtl_next_non_diac_width
= *u
? font_get_width(pf
, *u
) : 0;
161 base_width
= rtl_next_non_diac_width
;
165 rtl_next_non_diac_width
= 0; /* Mark */
172 last_non_diacritic_width
= width
;
174 base_width
= last_non_diacritic_width
;
185 /* XXX: Suggested by amiconn:
186 * This will produce completely wrong results if the original
187 * drawmode is DRMODE_COMPLEMENT. We need to pre-render the current
188 * character with all its diacritics at least (in mono) and then
189 * finally draw that. And we'll need an extra buffer that can hold
190 * one char's bitmap. Basically we can't just change the draw mode
191 * to something else irrespective of the original mode and expect
192 * the result to look as intended and with DRMODE_COMPLEMENT (which
193 * means XORing pixels), overdrawing this way will cause odd results
194 * if the diacritics and the base char both have common pixels set.
195 * So we need to combine the char and its diacritics in a temp
196 * buffer using OR, and then draw the final bitmap instead of the
197 * chars, without touching the drawmode
199 drawmode
= current_vp
->drawmode
;
200 current_vp
->drawmode
= DRMODE_FG
;
202 base_ofs
= (base_width
- width
) / 2;
205 bits
= font_get_bits(pf
, *ucs
);
207 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
209 lcd_alpha_bitmap_part(bits
, ofs
, 0, width
, x
+ base_ofs
, y
,
210 width
- ofs
, pf
->height
);
213 LCDFN(mono_bitmap_part
)(bits
, ofs
, 0, width
, x
+ base_ofs
,
214 y
, width
- ofs
, pf
->height
);
217 current_vp
->drawmode
= drawmode
;
223 bool next_is_diacritic
= is_diacritic(next_ch
, &next_is_rtl
);
226 * LTR: Next char is not diacritic,
227 * RTL: Current char is non-diacritic and next char is diacritic */
228 if ((is_rtl
&& !is_diac
) ||
229 (!is_rtl
&& (!next_is_diacritic
|| next_is_rtl
)))
231 x
+= base_width
- ofs
;
238 /* put a string at a given pixel position */
239 void LCDFN(putsxy
)(int x
, int y
, const unsigned char *str
)
241 LCDFN(putsxyofs
)(x
, y
, 0, str
);
244 /* Formatting version of LCDFN(putsxy) */
245 void LCDFN(putsxyf
)(int x
, int y
, const unsigned char *fmt
, ...)
250 vsnprintf(buf
, sizeof (buf
), fmt
, ap
);
252 LCDFN(putsxy
)(x
, y
, buf
);
255 static void LCDFN(putsxyofs_style
)(int xpos
, int ypos
,
256 const unsigned char *str
, int style
,
257 int w
, int h
, int offset
)
259 int lastmode
= current_vp
->drawmode
;
260 int xrect
= xpos
+ MAX(w
- offset
, 0);
261 int x
= VP_IS_RTL(current_vp
) ? xpos
: xrect
;
262 #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
263 int oldfgcolor
= current_vp
->fg_pattern
;
264 int oldbgcolor
= current_vp
->bg_pattern
;
265 current_vp
->drawmode
= DRMODE_SOLID
| ((style
& STYLE_INVERT
) ?
266 DRMODE_INVERSEVID
: 0);
267 if (style
& STYLE_COLORED
) {
268 if (current_vp
->drawmode
== DRMODE_SOLID
)
269 current_vp
->fg_pattern
= style
& STYLE_COLOR_MASK
;
271 current_vp
->bg_pattern
= style
& STYLE_COLOR_MASK
;
273 current_vp
->drawmode
^= DRMODE_INVERSEVID
;
274 if (style
& STYLE_GRADIENT
) {
275 current_vp
->drawmode
= DRMODE_FG
;
276 lcd_gradient_rect(xpos
, current_vp
->width
, ypos
, h
,
277 NUMLN_UNPACK(style
), CURLN_UNPACK(style
));
278 current_vp
->fg_pattern
= current_vp
->lst_pattern
;
280 else if (style
& STYLE_COLORBAR
) {
281 current_vp
->drawmode
= DRMODE_FG
;
282 current_vp
->fg_pattern
= current_vp
->lss_pattern
;
283 lcd_fillrect(xpos
, ypos
, current_vp
->width
- xpos
, h
);
284 current_vp
->fg_pattern
= current_vp
->lst_pattern
;
287 lcd_fillrect(x
, ypos
, current_vp
->width
- xrect
, h
);
288 current_vp
->drawmode
= (style
& STYLE_INVERT
) ?
289 (DRMODE_SOLID
|DRMODE_INVERSEVID
) : DRMODE_SOLID
;
292 lcd_putsxyofs(xpos
, ypos
, offset
, str
);
293 current_vp
->fg_pattern
= oldfgcolor
;
294 current_vp
->bg_pattern
= oldbgcolor
;
296 current_vp
->drawmode
= DRMODE_SOLID
| ((style
& STYLE_INVERT
) ?
297 0 : DRMODE_INVERSEVID
);
298 LCDFN(fillrect
)(x
, ypos
, current_vp
->width
- xrect
, h
);
299 current_vp
->drawmode
^= DRMODE_INVERSEVID
;
301 LCDFN(putsxyofs
)(xpos
, ypos
, offset
, str
);
303 current_vp
->drawmode
= lastmode
;
306 /*** Line oriented text output ***/
308 /* put a string at a given char position */
309 void LCDFN(puts_style_xyoffset
)(int x
, int y
, const unsigned char *str
,
310 int style
, int x_offset
, int y_offset
)
312 int xpos
, ypos
, w
, h
;
313 LCDFN(scroll_stop_line
)(current_vp
, y
);
317 LCDFN(getstringsize
)(str
, &w
, &h
);
318 xpos
= x
* LCDFN(getstringsize
)(" ", NULL
, NULL
);
319 ypos
= y
* h
+ y_offset
;
320 LCDFN(putsxyofs_style
)(xpos
, ypos
, str
, style
, w
, h
, x_offset
);
323 void LCDFN(puts_style_offset
)(int x
, int y
, const unsigned char *str
,
324 int style
, int x_offset
)
326 LCDFN(puts_style_xyoffset
)(x
, y
, str
, style
, x_offset
, 0);
329 void LCDFN(puts
)(int x
, int y
, const unsigned char *str
)
331 LCDFN(puts_style_offset
)(x
, y
, str
, STYLE_DEFAULT
, 0);
334 /* Formatting version of LCDFN(puts) */
335 void LCDFN(putsf
)(int x
, int y
, const unsigned char *fmt
, ...)
340 vsnprintf(buf
, sizeof (buf
), fmt
, ap
);
342 LCDFN(puts
)(x
, y
, buf
);
345 void LCDFN(puts_style
)(int x
, int y
, const unsigned char *str
, int style
)
347 LCDFN(puts_style_offset
)(x
, y
, str
, style
, 0);
350 void LCDFN(puts_offset
)(int x
, int y
, const unsigned char *str
, int offset
)
352 LCDFN(puts_style_offset
)(x
, y
, str
, STYLE_DEFAULT
, offset
);
357 void LCDFN(puts_scroll_style_xyoffset
)(int x
, int y
, const unsigned char *string
,
358 int style
, int x_offset
, int y_offset
)
360 struct scrollinfo
* s
;
365 if ((unsigned)y
>= (unsigned)current_vp
->height
)
368 /* remove any previously scrolling line at the same location */
369 LCDFN(scroll_stop_line
)(current_vp
, y
);
371 if (LCDFN(scroll_info
).lines
>= LCDM(SCROLLABLE_LINES
)) return;
374 LCDFN(puts_style_xyoffset
)(x
, y
, string
, style
, x_offset
, y_offset
);
376 LCDFN(getstringsize
)(string
, &w
, &h
);
378 if (current_vp
->width
- x
* 8 >= w
)
381 /* prepare scroll line */
382 s
= &LCDFN(scroll_info
).scroll
[LCDFN(scroll_info
).lines
];
383 s
->start_tick
= current_tick
+ LCDFN(scroll_info
).delay
;
386 strlcpy(s
->line
, string
, sizeof s
->line
);
389 s
->width
= LCDFN(getstringsize
)(s
->line
, &w
, &h
);
391 /* scroll bidirectional or forward only depending on the string
393 if ( LCDFN(scroll_info
).bidir_limit
) {
394 s
->bidir
= s
->width
< (current_vp
->width
) *
395 (100 + LCDFN(scroll_info
).bidir_limit
) / 100;
400 if (!s
->bidir
) { /* add spaces if scrolling in the round */
401 strlcat(s
->line
, " ", sizeof s
->line
);
402 /* get new width incl. spaces */
403 s
->width
= LCDFN(getstringsize
)(s
->line
, &w
, &h
);
406 end
= strchr(s
->line
, '\0');
407 len
= sizeof s
->line
- (end
- s
->line
);
408 strlcpy(end
, string
, MIN(current_vp
->width
/2, len
));
412 s
->offset
= x_offset
;
413 s
->startx
= x
* LCDFN(getstringsize
)(" ", NULL
, NULL
);
414 s
->y_offset
= y_offset
;
417 LCDFN(scroll_info
).lines
++;
420 void LCDFN(puts_scroll
)(int x
, int y
, const unsigned char *string
)
422 LCDFN(puts_scroll_style
)(x
, y
, string
, STYLE_DEFAULT
);
425 void LCDFN(puts_scroll_style
)(int x
, int y
, const unsigned char *string
,
428 LCDFN(puts_scroll_style_offset
)(x
, y
, string
, style
, 0);
431 void LCDFN(puts_scroll_offset
)(int x
, int y
, const unsigned char *string
,
434 LCDFN(puts_scroll_style_offset
)(x
, y
, string
, STYLE_DEFAULT
, offset
);
437 void LCDFN(scroll_fn
)(void)
440 struct scrollinfo
* s
;
443 struct viewport
* old_vp
= current_vp
;
446 for ( index
= 0; index
< LCDFN(scroll_info
).lines
; index
++ ) {
447 s
= &LCDFN(scroll_info
).scroll
[index
];
450 if (TIME_BEFORE(current_tick
, s
->start_tick
))
453 LCDFN(set_viewport
)(s
->vp
);
456 s
->offset
-= LCDFN(scroll_info
).step
;
458 s
->offset
+= LCDFN(scroll_info
).step
;
460 pf
= font_get(current_vp
->font
);
462 ypos
= s
->y
* pf
->height
+ s
->y_offset
;
465 if (s
->bidir
) { /* scroll bidirectional */
466 if (s
->offset
<= 0) {
467 /* at beginning of line */
472 else if (s
->offset
>= s
->width
- (current_vp
->width
- xpos
)) {
474 s
->offset
= s
->width
- (current_vp
->width
- xpos
);
480 /* scroll forward the whole time */
481 if (s
->offset
>= s
->width
) {
488 s
->start_tick
= current_tick
+ LCDFN(scroll_info
).delay
+
489 LCDFN(scroll_info
).ticks
;
491 LCDFN(putsxyofs_style
)(xpos
, ypos
, s
->line
, s
->style
, s
->width
,
492 pf
->height
, s
->offset
);
493 LCDFN(update_viewport_rect
)(xpos
, ypos
, current_vp
->width
- xpos
,
496 LCDFN(set_viewport
)(old_vp
);
499 void LCDFN(puts_scroll_style_offset
)(int x
, int y
, const unsigned char *string
,
500 int style
, int x_offset
)
502 LCDFN(puts_scroll_style_xyoffset
)(x
, y
, string
, style
, x_offset
, 0);