rockbox/firmware/drivers/lcd-bitmap-common.c
Thomas Martitz a79dc87761 Add a center flag, next to the rtl flag, for viewports. That results in any text being drawn centered. It overrides the RTL flag if set.
Simplify splashes and time menu by using it.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23105 a1c6a512-1295-4272-9138-f99709370657
2009-10-11 12:21:27 +00:00

344 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Dave Chapman
* Text rendering
* Copyright (C) 2006 Shachar Liberman
* Offset text, scrolling
* Copyright (C) 2007 Nicolas Pennequin, Tom Ross, Ken Fazzone, Akio Idehara
* Color gradient background
* Copyright (C) 2009 Andrew Mahone
* Merged common LCD bitmap code
*
* Rockbox common bitmap LCD functions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */
#define LCDFN(fn) lcd_ ## fn
#define FBFN(fn) fb_ ## fn
#define LCDM(ma) LCD_ ## ma
#define LCDNAME "lcd_"
#define MAIN_LCD
#endif
#if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
/* Fill a rectangle with a gradient */
static void lcd_gradient_rect(int x1, int x2, int y, unsigned h,
int num_lines, int cur_line)
{
int old_pattern = current_vp->fg_pattern;
int step_mul;
if (h == 0) return;
num_lines *= h;
cur_line *= h;
step_mul = (1 << 16) / (num_lines);
int h_r = RGB_UNPACK_RED(current_vp->lss_pattern);
int h_g = RGB_UNPACK_GREEN(current_vp->lss_pattern);
int h_b = RGB_UNPACK_BLUE(current_vp->lss_pattern);
int rstep = (h_r - RGB_UNPACK_RED(current_vp->lse_pattern)) * step_mul;
int gstep = (h_g - RGB_UNPACK_GREEN(current_vp->lse_pattern)) * step_mul;
int bstep = (h_b - RGB_UNPACK_BLUE(current_vp->lse_pattern)) * step_mul;
h_r = (h_r << 16) + (1 << 15);
h_g = (h_g << 16) + (1 << 15);
h_b = (h_b << 16) + (1 << 15);
if (cur_line)
{
h_r -= cur_line * rstep;
h_g -= cur_line * gstep;
h_b -= cur_line * bstep;
}
unsigned count;
for(count = 0; count < h; count++) {
current_vp->fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16);
lcd_hline(x1, x2, y + count);
h_r -= rstep;
h_g -= gstep;
h_b -= bstep;
}
current_vp->fg_pattern = old_pattern;
}
#endif
/* put a string at a given pixel position, skipping first ofs pixel columns */
static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str)
{
unsigned short ch;
unsigned short *ucs;
struct font* pf = font_get(current_vp->font);
int vp_flags = current_vp->flags;
ucs = bidi_l2v(str, 1);
if ((vp_flags & VP_FLAG_ALIGNMENT_MASK) != 0)
{
int w;
LCDFN(getstringsize)(str, &w, NULL);
/* center takes precedence */
if (vp_flags & VP_FLAG_CENTER_ALIGN)
x = ((current_vp->width - w)/ 2) + x;
else
x = current_vp->width - w - x;
}
while ((ch = *ucs++) != 0 && x < current_vp->width)
{
int width;
const unsigned char *bits;
/* get proportional width and glyph bits */
width = font_get_width(pf, ch);
if (ofs > width)
{
ofs -= width;
continue;
}
bits = font_get_bits(pf, ch);
LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x, y, width - ofs,
pf->height);
x += width - ofs;
ofs = 0;
}
}
/* put a string at a given pixel position */
void LCDFN(putsxy)(int x, int y, const unsigned char *str)
{
LCDFN(putsxyofs)(x, y, 0, str);
}
static void LCDFN(putsxyofs_style)(int xpos, int ypos,
const unsigned char *str, int style,
int w, int h, int offset)
{
int lastmode = current_vp->drawmode;
int xrect = xpos + MAX(w - offset, 0);
#if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
int oldfgcolor = current_vp->fg_pattern;
int oldbgcolor = current_vp->bg_pattern;
current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
DRMODE_INVERSEVID : 0);
if (style & STYLE_COLORED) {
if (current_vp->drawmode == DRMODE_SOLID)
current_vp->fg_pattern = style & STYLE_COLOR_MASK;
else
current_vp->bg_pattern = style & STYLE_COLOR_MASK;
}
current_vp->drawmode ^= DRMODE_INVERSEVID;
if (style & STYLE_GRADIENT) {
current_vp->drawmode = DRMODE_FG;
lcd_gradient_rect(xpos, current_vp->width, ypos, h,
NUMLN_UNPACK(style), CURLN_UNPACK(style));
current_vp->fg_pattern = current_vp->lst_pattern;
}
else if (style & STYLE_COLORBAR) {
current_vp->drawmode = DRMODE_FG;
current_vp->fg_pattern = current_vp->lss_pattern;
lcd_fillrect(xpos, ypos, current_vp->width - xpos, h);
current_vp->fg_pattern = current_vp->lst_pattern;
}
else {
lcd_fillrect(xrect, ypos, current_vp->width - xrect, h);
current_vp->drawmode = (style & STYLE_INVERT) ?
(DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
}
lcd_putsxyofs(xpos, ypos, offset, str);
current_vp->fg_pattern = oldfgcolor;
current_vp->bg_pattern = oldbgcolor;
#else
current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ?
0 : DRMODE_INVERSEVID);
LCDFN(fillrect)(xrect, ypos, current_vp->width - xrect, h);
current_vp->drawmode ^= DRMODE_INVERSEVID;
LCDFN(putsxyofs)(xpos, ypos, offset, str);
#endif
current_vp->drawmode = lastmode;
}
/*** Line oriented text output ***/
/* put a string at a given char position */
void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
int style, int offset)
{
int xpos, ypos, w, h;
unsigned long chars_in_str;
LCDFN(scroll_stop_line)(current_vp, y);
if(!str || !str[0])
return;
chars_in_str = utf8length((char *)str);
LCDFN(getstringsize)(str, &w, &h);
xpos = x * w / chars_in_str;
ypos = y * h;
LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, offset);
}
void LCDFN(puts)(int x, int y, const unsigned char *str)
{
LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, 0);
}
void LCDFN(puts_style)(int x, int y, const unsigned char *str, int style)
{
LCDFN(puts_style_offset)(x, y, str, style, 0);
}
void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)
{
LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, offset);
}
/*** scrolling ***/
void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
int style, int offset)
{
int w, h;
if ((unsigned)y >= (unsigned)current_vp->height)
return;
/* remove any previously scrolling line at the same location */
lcd_scroll_stop_line(current_vp, y);
if (LCDFN(scroll_info.lines) >= LCDM(SCROLLABLE_LINES)) return;
if (!string)
return;
LCDFN(puts_style_offset)(x, y, string, style, offset);
LCDFN(getstringsize)(string, &w, &h);
if (current_vp->width - x * 8 < w) {
/* prepare scroll line */
struct scrollinfo* s;
s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
s->start_tick = current_tick + LCDFN(scroll_info).delay;
s->style = style;
char *end;
memset(s->line, 0, sizeof s->line);
strcpy(s->line, string);
/* get width */
s->width = LCDFN(getstringsize)(s->line, &w, &h);
/* scroll bidirectional or forward only depending on the string
width */
if ( LCDFN(scroll_info).bidir_limit ) {
s->bidir = s->width < (current_vp->width) *
(100 + LCDFN(scroll_info).bidir_limit) / 100;
}
else
s->bidir = false;
if (!s->bidir) { /* add spaces if scrolling in the round */
strcat(s->line, " ");
/* get new width incl. spaces */
s->width = LCDFN(getstringsize)(s->line, &w, &h);
}
end = strchr(s->line, '\0');
strlcpy(end, string, current_vp->width/2);
s->vp = current_vp;
s->y = y;
s->len = utf8length(string);
s->offset = offset;
s->startx = x * s->width / s->len;
s->backward = false;
LCDFN(scroll_info).lines++;
}
}
void LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
{
LCDFN(puts_scroll_style)(x, y, string, STYLE_DEFAULT);
}
void LCDFN(puts_scroll_style)(int x, int y, const unsigned char *string,
int style)
{
LCDFN(puts_scroll_style_offset)(x, y, string, style, 0);
}
void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string,
int offset)
{
LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset);
}
void LCDFN(scroll_fn)(void)
{
struct font* pf;
struct scrollinfo* s;
int index;
int xpos, ypos;
struct viewport* old_vp = current_vp;
for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) {
s = &LCDFN(scroll_info).scroll[index];
/* check pause */
if (TIME_BEFORE(current_tick, s->start_tick))
continue;
LCDFN(set_viewport)(s->vp);
if (s->backward ^ VP_IS_RTL(current_vp))
/* contrary to LTR, this is "forward" in RTL */
s->offset -= LCDFN(scroll_info).step;
else
s->offset += LCDFN(scroll_info).step;
pf = font_get(current_vp->font);
xpos = s->startx;
ypos = s->y * pf->height;
if (s->bidir) { /* scroll bidirectional */
if (abs(s->offset) <= 0) {
/* at beginning of line */
s->offset = 0;
s->backward = false;
s->start_tick = current_tick + LCDFN(scroll_info).delay * 2;
}
if (abs(s->offset) >= s->width - (current_vp->width - xpos)) {
/* at end of line */
s->backward = true;
s->start_tick = current_tick + LCDFN(scroll_info).delay * 2;
}
}
else {
/* scroll forward the whole time */
if (abs(s->offset) >= s->width)
s->offset %= s->width;
}
LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, s->width,
pf->height, s->offset);
LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width - xpos,
pf->height);
}
LCDFN(set_viewport)(old_vp);
}