FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / firmware / drivers / lcd-charcell.c
blob33337daf19478808d29640eeb725e8e6a83fc4c6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Jens Arnold
11 * Based on the work of Alan Korr, Kjell Ericson and others
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "config.h"
23 #include "hwcompat.h"
25 #include "lcd.h"
26 #include "kernel.h"
27 #include "thread.h"
28 #include <string.h>
29 #include <stdlib.h>
30 #include "file.h"
31 #include "system.h"
32 #include "lcd-charcell.h"
33 #include "rbunicode.h"
34 #include "scroll_engine.h"
36 /** definitions **/
38 #define VARIABLE_XCHARS 16 /* number of software user-definable characters */
39 /* There must be mappings for this many characters in the 0xe000 unicode range
40 * in lcd-charset-<target>.c */
42 #define NO_PATTERN (-1)
44 static int find_xchar(unsigned long ucs);
46 /** globals **/
48 unsigned char lcd_charbuffer[LCD_HEIGHT][LCD_WIDTH]; /* The "frame"buffer */
49 static unsigned char lcd_substbuffer[LCD_HEIGHT][LCD_WIDTH];
50 struct pattern_info lcd_patterns[MAX_HW_PATTERNS];
51 struct cursor_info lcd_cursor;
53 static unsigned char xfont_variable[VARIABLE_XCHARS][HW_PATTERN_SIZE];
54 static bool xfont_variable_locked[VARIABLE_XCHARS];
55 static int xspace; /* stores xhcar id of ' ' - often needed */
57 static struct viewport default_vp =
59 .x = 0,
60 .y = 0,
61 .width = LCD_WIDTH,
62 .height = LCD_HEIGHT,
65 static struct viewport* current_vp = &default_vp;
67 /* LCD init */
68 void lcd_init (void)
70 lcd_init_device();
71 lcd_charset_init();
72 memset(lcd_patterns, 0, sizeof(lcd_patterns));
73 xspace = find_xchar(' ');
74 memset(lcd_charbuffer, xchar_info[xspace].hw_char, sizeof(lcd_charbuffer));
75 scroll_init();
78 /* Viewports */
80 void lcd_set_viewport(struct viewport* vp)
82 if (vp == NULL)
83 current_vp = &default_vp;
84 else
85 current_vp = vp;
88 void lcd_update_viewport(void)
90 lcd_update();
93 /** parameter handling **/
95 int lcd_getwidth(void)
97 return current_vp->width;
100 int lcd_getheight(void)
102 return current_vp->height;
105 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
107 int width = utf8length(str);
109 if (w)
110 *w = width;
111 if (h)
112 *h = 1;
114 return width;
117 /** low-level functions **/
119 static int find_xchar(unsigned long ucs)
121 int low = 0;
122 int high = xchar_info_size - 1;
126 int probe = (low + high) >> 1;
128 if (xchar_info[probe].ucs < ucs)
129 low = probe + 1;
130 else if (xchar_info[probe].ucs > ucs)
131 high = probe - 1;
132 else
133 return probe;
135 while (low <= high);
137 /* Not found: return index of no-char symbol (last symbol, hardcoded). */
138 return xchar_info_size - 1;
141 static int glyph_to_pat(unsigned glyph)
143 int i;
145 for (i = 0; i < lcd_pattern_count; i++)
146 if (lcd_patterns[i].glyph == glyph)
147 return i;
149 return NO_PATTERN;
152 static void lcd_free_pat(int pat)
154 int x, y;
156 if (pat != NO_PATTERN)
158 for (x = 0; x < LCD_WIDTH; x++)
159 for (y = 0; y < LCD_HEIGHT; y++)
160 if (pat == lcd_charbuffer[y][x])
161 lcd_charbuffer[y][x] = lcd_substbuffer[y][x];
163 if (lcd_cursor.enabled && pat == lcd_cursor.hw_char)
164 lcd_cursor.hw_char = lcd_cursor.subst_char;
166 lcd_patterns[pat].count = 0;
170 static int lcd_get_free_pat(int xchar)
172 static int last_used_pat = 0;
174 int pat = last_used_pat; /* start from last used pattern */
175 int least_pat = pat; /* pattern with least priority */
176 int least_priority = lcd_patterns[pat].priority;
177 int i;
179 for (i = 0; i < lcd_pattern_count; i++)
181 if (++pat >= lcd_pattern_count) /* Keep 'pat' within limits */
182 pat = 0;
184 if (lcd_patterns[pat].count == 0)
186 last_used_pat = pat;
187 return pat;
189 if (lcd_patterns[pat].priority < least_priority)
191 least_priority = lcd_patterns[pat].priority;
192 least_pat = pat;
195 if (xchar_info[xchar].priority > least_priority) /* prioritized char */
197 lcd_free_pat(least_pat);
198 last_used_pat = least_pat;
199 return least_pat;
201 return NO_PATTERN;
204 static const unsigned char *glyph_to_pattern(unsigned glyph)
206 if (glyph & 0x8000)
207 return xfont_variable[glyph & 0x7fff];
208 else
209 return xfont_fixed[glyph];
212 static int map_xchar(int xchar, unsigned char *substitute)
214 int pat;
215 unsigned glyph;
217 if (xchar_info[xchar].priority > 0) /* soft char */
219 glyph = xchar_info[xchar].glyph;
220 pat = glyph_to_pat(glyph);
222 if (pat == NO_PATTERN) /* not yet mapped */
224 pat = lcd_get_free_pat(xchar); /* try to map */
226 if (pat == NO_PATTERN) /* failed: just use substitute */
227 return xchar_info[xchar].hw_char;
228 else
229 { /* define pattern */
230 lcd_patterns[pat].priority = xchar_info[xchar].priority;
231 lcd_patterns[pat].glyph = glyph;
232 memcpy(lcd_patterns[pat].pattern, glyph_to_pattern(glyph),
233 HW_PATTERN_SIZE);
236 lcd_patterns[pat].count++; /* increase reference count */
237 *substitute = xchar_info[xchar].hw_char;
238 return pat;
240 else /* hardware char */
241 return xchar_info[xchar].hw_char;
244 static void lcd_putxchar(int x, int y, int xchar)
246 int lcd_char;
248 /* Adjust for viewport */
249 x += current_vp->x;
250 y += current_vp->y;
252 lcd_char = lcd_charbuffer[y][x];
254 if (lcd_char < lcd_pattern_count) /* old char was soft */
255 lcd_patterns[lcd_char].count--; /* decrease old reference count */
257 lcd_charbuffer[y][x] = map_xchar(xchar, &lcd_substbuffer[y][x]);
260 /** user-definable pattern handling **/
262 unsigned long lcd_get_locked_pattern(void)
264 int i = 0;
266 for (i = 0; i < VARIABLE_XCHARS; i++)
268 if (!xfont_variable_locked[i])
270 xfont_variable_locked[i] = true;
271 return 0xe000 + i; /* hard-coded */
274 return 0;
277 void lcd_unlock_pattern(unsigned long ucs)
279 int xchar = find_xchar(ucs);
280 unsigned glyph = xchar_info[xchar].glyph;
282 if (glyph & 0x8000) /* variable extended char */
284 lcd_free_pat(glyph_to_pat(glyph));
285 xfont_variable_locked[glyph & 0x7fff] = false;
289 void lcd_define_pattern(unsigned long ucs, const char *pattern)
291 int xchar = find_xchar(ucs);
292 unsigned glyph = xchar_info[xchar].glyph;
293 int pat;
295 if (glyph & 0x8000) /* variable extended char */
297 memcpy(xfont_variable[glyph & 0x7fff], pattern, HW_PATTERN_SIZE);
298 pat = glyph_to_pat(glyph);
299 if (pat != NO_PATTERN)
300 memcpy(lcd_patterns[pat].pattern, pattern, HW_PATTERN_SIZE);
304 /** output functions **/
306 /* Clear the whole display */
307 void lcd_clear_display(void)
309 int x, y;
310 struct viewport* old_vp = current_vp;
312 lcd_scroll_info.lines = 0;
313 lcd_remove_cursor();
315 /* Set the default viewport - required for lcd_putxchar */
316 current_vp = &default_vp;
318 for (x = 0; x < LCD_WIDTH; x++)
319 for (y = 0; y < LCD_HEIGHT; y++)
320 lcd_putxchar(x, y, xspace);
322 current_vp = old_vp;
325 /* Clear the current viewport */
326 void lcd_clear_viewport(void)
328 int x, y;
330 if (current_vp == &default_vp)
332 lcd_clear_display();
334 else
336 /* Remove the cursor if it is within the current viewport */
337 if (lcd_cursor.enabled &&
338 (lcd_cursor.x >= current_vp->x) &&
339 (lcd_cursor.x <= current_vp->x + current_vp->width) &&
340 (lcd_cursor.y >= current_vp->y) &&
341 (lcd_cursor.y <= current_vp->y + current_vp->height))
343 lcd_remove_cursor();
346 for (x = 0; x < current_vp->width; x++)
347 for (y = 0; y < current_vp->height; y++)
348 lcd_putxchar(x, y, xspace);
350 lcd_scroll_stop(current_vp);
354 /* Put an unicode character at the given position */
355 void lcd_putc(int x, int y, unsigned long ucs)
357 if ((unsigned)x >= (unsigned)current_vp->width ||
358 (unsigned)y >= (unsigned)current_vp->height)
359 return;
361 lcd_putxchar(x, y, find_xchar(ucs));
364 /* Show cursor (alternating with existing character) at the given position */
365 void lcd_put_cursor(int x, int y, unsigned long cursor_ucs)
367 if ((unsigned)x >= (unsigned)current_vp->width ||
368 (unsigned)y >= (unsigned)current_vp->height ||
369 lcd_cursor.enabled)
370 return;
372 lcd_cursor.enabled = true;
373 lcd_cursor.visible = false;
374 lcd_cursor.hw_char = map_xchar(find_xchar(cursor_ucs), &lcd_cursor.subst_char);
375 lcd_cursor.x = current_vp->x + x;
376 lcd_cursor.y = current_vp->y + y;
377 lcd_cursor.downcount = 0;
378 lcd_cursor.divider = MAX((HZ/2) / lcd_scroll_info.ticks, 1);
381 /* Remove the cursor */
382 void lcd_remove_cursor(void)
384 if (lcd_cursor.enabled)
386 if (lcd_cursor.hw_char < lcd_pattern_count) /* soft char, unmap */
387 lcd_patterns[lcd_cursor.hw_char].count--;
389 lcd_cursor.enabled = lcd_cursor.visible = false;
393 /* Put a string at a given position, skipping first ofs chars */
394 static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
396 unsigned short ucs;
397 const unsigned char *utf8 = str;
399 while (*utf8 && x < current_vp->width)
401 utf8 = utf8decode(utf8, &ucs);
403 if (ofs > 0)
405 ofs--;
406 continue;
408 lcd_putxchar(x++, y, find_xchar(ucs));
410 return x;
413 /* Put a string at a given position */
414 void lcd_putsxy(int x, int y, const unsigned char *str)
416 if ((unsigned)y >= (unsigned)current_vp->height)
417 return;
419 lcd_putsxyofs(x, y, 0, str);
422 /*** Line oriented text output ***/
424 /* Put a string at a given char position */
425 void lcd_puts(int x, int y, const unsigned char *str)
427 lcd_puts_offset(x, y, str, 0);
430 /* Put a string at a given char position, skipping first offset chars */
431 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
433 if ((unsigned)y >= (unsigned)current_vp->height)
434 return;
436 /* make sure scrolling is turned off on the line we are updating */
437 lcd_scroll_stop_line(current_vp, y);
439 x = lcd_putsxyofs(x, y, offset, str);
440 while (x < current_vp->width)
441 lcd_putxchar(x++, y, xspace);
444 /** scrolling **/
445 void lcd_puts_scroll(int x, int y, const unsigned char *string)
447 lcd_puts_scroll_offset(x, y, string, 0);
450 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
451 int offset)
453 struct scrollinfo* s;
454 int len;
456 if ((unsigned)y >= (unsigned)current_vp->height)
457 return;
459 /* remove any previously scrolling line at the same location */
460 lcd_scroll_stop_line(current_vp, y);
462 if (lcd_scroll_info.lines >= LCD_SCROLLABLE_LINES) return;
464 s = &lcd_scroll_info.scroll[lcd_scroll_info.lines];
466 s->start_tick = current_tick + lcd_scroll_info.delay;
468 lcd_puts_offset(x, y, string, offset);
469 len = utf8length(string);
471 if (current_vp->width - x < len)
473 /* prepare scroll line */
474 char *end;
476 memset(s->line, 0, sizeof s->line);
477 strcpy(s->line, string);
479 /* get width */
480 s->len = utf8length(s->line);
482 /* scroll bidirectional or forward only depending on the string width */
483 if (lcd_scroll_info.bidir_limit)
485 s->bidir = s->len < (current_vp->width) *
486 (100 + lcd_scroll_info.bidir_limit) / 100;
488 else
489 s->bidir = false;
491 if (!s->bidir) /* add spaces if scrolling in the round */
493 strcat(s->line, " ");
494 /* get new width incl. spaces */
495 s->len += SCROLL_SPACING;
498 end = strchr(s->line, '\0');
499 strlcpy(end, string, utf8seek(s->line, current_vp->width));
501 s->vp = current_vp;
502 s->y = y;
503 s->offset = offset;
504 s->startx = x;
505 s->backward = false;
506 lcd_scroll_info.lines++;
510 void lcd_scroll_fn(void)
512 struct scrollinfo* s;
513 int index;
514 int xpos, ypos;
515 bool update;
516 struct viewport* old_vp = current_vp;
518 update = false;
519 for ( index = 0; index < lcd_scroll_info.lines; index++ ) {
520 s = &lcd_scroll_info.scroll[index];
522 /* check pause */
523 if (TIME_BEFORE(current_tick, s->start_tick))
524 continue;
526 lcd_set_viewport(s->vp);
528 if (s->backward)
529 s->offset--;
530 else
531 s->offset++;
533 xpos = s->startx;
534 ypos = s->y;
536 if (s->bidir) /* scroll bidirectional */
538 if (s->offset <= 0) {
539 /* at beginning of line */
540 s->offset = 0;
541 s->backward = false;
542 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
544 if (s->offset >= s->len - (current_vp->width - xpos)) {
545 /* at end of line */
546 s->offset = s->len - (current_vp->width - xpos);
547 s->backward = true;
548 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
551 else /* scroll forward the whole time */
553 if (s->offset >= s->len)
554 s->offset -= s->len;
557 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
558 update = true;
561 lcd_set_viewport(old_vp);
563 if (lcd_cursor.enabled)
565 if (--lcd_cursor.downcount <= 0)
567 lcd_cursor.downcount = lcd_cursor.divider;
568 lcd_cursor.visible = !lcd_cursor.visible;
569 update = true;
573 if (update)
574 lcd_update();