Andrew Mahone | e04f95e | 2009-08-13 08:02:29 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 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 |
| 18 | * |
| 19 | * Rockbox common bitmap LCD functions |
| 20 | * |
| 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. |
| 25 | * |
| 26 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 27 | * KIND, either express or implied. |
| 28 | * |
| 29 | ****************************************************************************/ |
| 30 | |
| 31 | #ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */ |
| 32 | #define LCDFN(fn) lcd_ ## fn |
| 33 | #define FBFN(fn) fb_ ## fn |
| 34 | #define LCDM(ma) LCD_ ## ma |
| 35 | #define LCDNAME "lcd_" |
| 36 | #define MAIN_LCD |
| 37 | #endif |
| 38 | |
| 39 | #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR) |
| 40 | /* Fill a rectangle with a gradient */ |
| 41 | static void lcd_gradient_rect(int x1, int x2, int y, unsigned h, |
| 42 | int num_lines, int cur_line) |
| 43 | { |
| 44 | int old_pattern = current_vp->fg_pattern; |
| 45 | int step_mul; |
| 46 | if (h == 0) return; |
| 47 | |
| 48 | num_lines *= h; |
| 49 | cur_line *= h; |
| 50 | step_mul = (1 << 16) / (num_lines); |
| 51 | int h_r = RGB_UNPACK_RED(current_vp->lss_pattern); |
| 52 | int h_g = RGB_UNPACK_GREEN(current_vp->lss_pattern); |
| 53 | int h_b = RGB_UNPACK_BLUE(current_vp->lss_pattern); |
| 54 | int rstep = (h_r - RGB_UNPACK_RED(current_vp->lse_pattern)) * step_mul; |
| 55 | int gstep = (h_g - RGB_UNPACK_GREEN(current_vp->lse_pattern)) * step_mul; |
| 56 | int bstep = (h_b - RGB_UNPACK_BLUE(current_vp->lse_pattern)) * step_mul; |
| 57 | h_r = (h_r << 16) + (1 << 15); |
| 58 | h_g = (h_g << 16) + (1 << 15); |
| 59 | h_b = (h_b << 16) + (1 << 15); |
| 60 | if (cur_line) |
| 61 | { |
| 62 | h_r -= cur_line * rstep; |
| 63 | h_g -= cur_line * gstep; |
| 64 | h_b -= cur_line * bstep; |
| 65 | } |
| 66 | unsigned count; |
| 67 | |
| 68 | for(count = 0; count < h; count++) { |
| 69 | current_vp->fg_pattern = LCD_RGBPACK(h_r >> 16, h_g >> 16, h_b >> 16); |
| 70 | lcd_hline(x1, x2, y + count); |
| 71 | h_r -= rstep; |
| 72 | h_g -= gstep; |
| 73 | h_b -= bstep; |
| 74 | } |
| 75 | |
| 76 | current_vp->fg_pattern = old_pattern; |
| 77 | } |
| 78 | #endif |
| 79 | |
| 80 | /* put a string at a given pixel position, skipping first ofs pixel columns */ |
| 81 | static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str) |
| 82 | { |
| 83 | unsigned short ch; |
| 84 | unsigned short *ucs; |
| 85 | struct font* pf = font_get(current_vp->font); |
| 86 | |
| 87 | ucs = bidi_l2v(str, 1); |
| 88 | |
| 89 | while ((ch = *ucs++) != 0 && x < current_vp->width) |
| 90 | { |
| 91 | int width; |
| 92 | const unsigned char *bits; |
| 93 | |
| 94 | /* get proportional width and glyph bits */ |
| 95 | width = font_get_width(pf, ch); |
| 96 | |
| 97 | if (ofs > width) |
| 98 | { |
| 99 | ofs -= width; |
| 100 | continue; |
| 101 | } |
| 102 | |
| 103 | bits = font_get_bits(pf, ch); |
| 104 | |
| 105 | LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x, y, width - ofs, |
| 106 | pf->height); |
| 107 | |
| 108 | x += width - ofs; |
| 109 | ofs = 0; |
| 110 | } |
| 111 | } |
| 112 | /* put a string at a given pixel position */ |
| 113 | void LCDFN(putsxy)(int x, int y, const unsigned char *str) |
| 114 | { |
| 115 | LCDFN(putsxyofs)(x, y, 0, str); |
| 116 | } |
| 117 | |
| 118 | static void LCDFN(putsxyofs_style)(int xpos, int ypos, |
| 119 | const unsigned char *str, int style, |
| 120 | int w, int h, int offset) |
| 121 | { |
| 122 | int lastmode = current_vp->drawmode; |
| 123 | int xrect = xpos + MAX(w - offset, 0); |
| 124 | #if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR) |
| 125 | int oldfgcolor = current_vp->fg_pattern; |
| 126 | int oldbgcolor = current_vp->bg_pattern; |
| 127 | current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ? |
| 128 | DRMODE_INVERSEVID : 0); |
| 129 | if (style & STYLE_COLORED) { |
| 130 | if (current_vp->drawmode == DRMODE_SOLID) |
| 131 | current_vp->fg_pattern = style & STYLE_COLOR_MASK; |
| 132 | else |
| 133 | current_vp->bg_pattern = style & STYLE_COLOR_MASK; |
| 134 | } |
| 135 | current_vp->drawmode ^= DRMODE_INVERSEVID; |
| 136 | if (style & STYLE_GRADIENT) { |
| 137 | current_vp->drawmode = DRMODE_FG; |
| 138 | lcd_gradient_rect(xpos, current_vp->width, ypos, h, |
| 139 | NUMLN_UNPACK(style), CURLN_UNPACK(style)); |
| 140 | current_vp->fg_pattern = current_vp->lst_pattern; |
| 141 | } |
| 142 | else if (style & STYLE_COLORBAR) { |
| 143 | current_vp->drawmode = DRMODE_FG; |
| 144 | current_vp->fg_pattern = current_vp->lss_pattern; |
| 145 | lcd_fillrect(xpos, ypos, current_vp->width - xpos, h); |
| 146 | current_vp->fg_pattern = current_vp->lst_pattern; |
| 147 | } |
| 148 | else { |
| 149 | lcd_fillrect(xrect, ypos, current_vp->width - xrect, h); |
| 150 | current_vp->drawmode = (style & STYLE_INVERT) ? |
| 151 | (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID; |
| 152 | } |
| 153 | lcd_putsxyofs(xpos, ypos, offset, str); |
| 154 | current_vp->fg_pattern = oldfgcolor; |
| 155 | current_vp->bg_pattern = oldbgcolor; |
| 156 | #else |
| 157 | current_vp->drawmode = DRMODE_SOLID | ((style & STYLE_INVERT) ? |
| 158 | 0 : DRMODE_INVERSEVID); |
| 159 | LCDFN(fillrect)(xrect, ypos, current_vp->width - xrect, h); |
| 160 | current_vp->drawmode ^= DRMODE_INVERSEVID; |
| 161 | LCDFN(putsxyofs)(xpos, ypos, offset, str); |
| 162 | #endif |
| 163 | current_vp->drawmode = lastmode; |
| 164 | } |
| 165 | |
| 166 | /*** Line oriented text output ***/ |
| 167 | |
| 168 | /* put a string at a given char position */ |
| 169 | void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str, |
| 170 | int style, int offset) |
| 171 | { |
| 172 | int xpos, ypos, w, h; |
| 173 | LCDFN(scroll_stop_line)(current_vp, y); |
| 174 | if(!str || !str[0]) |
| 175 | return; |
| 176 | LCDFN(getstringsize)(str, &w, &h); |
| 177 | xpos = x * w / utf8length((char *)str); |
| 178 | ypos = y * h; |
| 179 | LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, offset); |
| 180 | } |
| 181 | |
| 182 | void LCDFN(puts)(int x, int y, const unsigned char *str) |
| 183 | { |
| 184 | LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, 0); |
| 185 | } |
| 186 | |
| 187 | void LCDFN(puts_style)(int x, int y, const unsigned char *str, int style) |
| 188 | { |
| 189 | LCDFN(puts_style_offset)(x, y, str, style, 0); |
| 190 | } |
| 191 | |
| 192 | void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset) |
| 193 | { |
| 194 | LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, offset); |
| 195 | } |
| 196 | |
| 197 | /*** scrolling ***/ |
| 198 | |
| 199 | void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string, |
| 200 | int style, int offset) |
| 201 | { |
| 202 | int w, h; |
| 203 | |
| 204 | if ((unsigned)y >= (unsigned)current_vp->height) |
| 205 | return; |
| 206 | |
| 207 | /* remove any previously scrolling line at the same location */ |
| 208 | lcd_scroll_stop_line(current_vp, y); |
| 209 | |
| 210 | if (LCDFN(scroll_info.lines) >= LCDM(SCROLLABLE_LINES)) return; |
| 211 | if (!string) |
| 212 | return; |
| 213 | LCDFN(puts_style_offset)(x, y, string, style, offset); |
| 214 | |
| 215 | LCDFN(getstringsize)(string, &w, &h); |
| 216 | |
| 217 | if (current_vp->width - x * 8 < w) { |
| 218 | /* prepare scroll line */ |
| 219 | struct scrollinfo* s; |
| 220 | s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines]; |
| 221 | s->start_tick = current_tick + LCDFN(scroll_info).delay; |
| 222 | s->style = style; |
| 223 | |
| 224 | char *end; |
| 225 | |
| 226 | memset(s->line, 0, sizeof s->line); |
| 227 | strcpy(s->line, string); |
| 228 | |
| 229 | /* get width */ |
| 230 | s->width = LCDFN(getstringsize)(s->line, &w, &h); |
| 231 | |
| 232 | /* scroll bidirectional or forward only depending on the string |
| 233 | width */ |
| 234 | if ( LCDFN(scroll_info).bidir_limit ) { |
| 235 | s->bidir = s->width < (current_vp->width) * |
| 236 | (100 + LCDFN(scroll_info).bidir_limit) / 100; |
| 237 | } |
| 238 | else |
| 239 | s->bidir = false; |
| 240 | |
| 241 | if (!s->bidir) { /* add spaces if scrolling in the round */ |
| 242 | strcat(s->line, " "); |
| 243 | /* get new width incl. spaces */ |
| 244 | s->width = LCDFN(getstringsize)(s->line, &w, &h); |
| 245 | } |
| 246 | |
| 247 | end = strchr(s->line, '\0'); |
| 248 | strlcpy(end, string, current_vp->width/2); |
| 249 | |
| 250 | s->vp = current_vp; |
| 251 | s->y = y; |
| 252 | s->len = utf8length(string); |
| 253 | s->offset = offset; |
| 254 | s->startx = x * s->width / s->len; |
| 255 | s->backward = false; |
| 256 | |
| 257 | LCDFN(scroll_info).lines++; |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | void LCDFN(puts_scroll)(int x, int y, const unsigned char *string) |
| 262 | { |
| 263 | LCDFN(puts_scroll_style)(x, y, string, STYLE_DEFAULT); |
| 264 | } |
| 265 | |
| 266 | void LCDFN(puts_scroll_style)(int x, int y, const unsigned char *string, |
| 267 | int style) |
| 268 | { |
| 269 | LCDFN(puts_scroll_style_offset)(x, y, string, style, 0); |
| 270 | } |
| 271 | |
| 272 | void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string, |
| 273 | int offset) |
| 274 | { |
| 275 | LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset); |
| 276 | } |
| 277 | |
| 278 | void LCDFN(scroll_fn)(void) |
| 279 | { |
| 280 | struct font* pf; |
| 281 | struct scrollinfo* s; |
| 282 | int index; |
| 283 | int xpos, ypos; |
| 284 | struct viewport* old_vp = current_vp; |
| 285 | |
| 286 | for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) { |
| 287 | s = &LCDFN(scroll_info).scroll[index]; |
| 288 | |
| 289 | /* check pause */ |
| 290 | if (TIME_BEFORE(current_tick, s->start_tick)) |
| 291 | continue; |
| 292 | |
| 293 | LCDFN(set_viewport)(s->vp); |
| 294 | |
| 295 | if (s->backward) |
| 296 | s->offset -= LCDFN(scroll_info).step; |
| 297 | else |
| 298 | s->offset += LCDFN(scroll_info).step; |
| 299 | |
| 300 | pf = font_get(current_vp->font); |
| 301 | xpos = s->startx; |
| 302 | ypos = s->y * pf->height; |
| 303 | |
| 304 | if (s->bidir) { /* scroll bidirectional */ |
| 305 | if (s->offset <= 0) { |
| 306 | /* at beginning of line */ |
| 307 | s->offset = 0; |
| 308 | s->backward = false; |
| 309 | s->start_tick = current_tick + LCDFN(scroll_info).delay * 2; |
| 310 | } |
| 311 | if (s->offset >= s->width - (current_vp->width - xpos)) { |
| 312 | /* at end of line */ |
| 313 | s->offset = s->width - (current_vp->width - xpos); |
| 314 | s->backward = true; |
| 315 | s->start_tick = current_tick + LCDFN(scroll_info).delay * 2; |
| 316 | } |
| 317 | } |
| 318 | else { |
| 319 | /* scroll forward the whole time */ |
| 320 | if (s->offset >= s->width) |
| 321 | s->offset %= s->width; |
| 322 | } |
| 323 | LCDFN(putsxyofs_style)(xpos, ypos, s->line, s->style, s->width, |
| 324 | pf->height, s->offset); |
| 325 | LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width - xpos, |
| 326 | pf->height); |
| 327 | } |
| 328 | LCDFN(set_viewport)(old_vp); |
| 329 | } |