Add a get_offset API call to make codec_advance_buffer_loc_callback work.
[Rockbox.git] / firmware / drivers / lcd-1bit-vert.c
blobbd62e105cbee81773243356db69370f91576021f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Alan Korr
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "config.h"
21 #include "lcd.h"
22 #include "kernel.h"
23 #include "thread.h"
24 #include <string.h>
25 #include <stdlib.h>
26 #include "file.h"
27 #include "debug.h"
28 #include "system.h"
29 #include "font.h"
30 #include "rbunicode.h"
31 #include "bidi.h"
32 #include "scroll_engine.h"
34 /*** globals ***/
36 unsigned char lcd_framebuffer[LCD_FBHEIGHT][LCD_FBWIDTH];
38 static int drawmode = DRMODE_SOLID;
39 static int xmargin = 0;
40 static int ymargin = 0;
41 static int curfont = FONT_SYSFIXED;
43 /* LCD init */
44 void lcd_init(void)
46 lcd_clear_display();
47 /* Call device specific init */
48 lcd_init_device();
49 scroll_init();
52 /*** parameter handling ***/
54 void lcd_set_drawmode(int mode)
56 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
59 int lcd_get_drawmode(void)
61 return drawmode;
64 void lcd_setmargins(int x, int y)
66 xmargin = x;
67 ymargin = y;
70 int lcd_getxmargin(void)
72 return xmargin;
75 int lcd_getymargin(void)
77 return ymargin;
80 void lcd_setfont(int newfont)
82 curfont = newfont;
85 int lcd_getstringsize(const unsigned char *str, int *w, int *h)
87 return font_getstringsize(str, w, h, curfont);
90 /*** low-level drawing functions ***/
92 static void setpixel(int x, int y)
94 lcd_framebuffer[y>>3][x] |= 1 << (y & 7);
97 static void clearpixel(int x, int y)
99 lcd_framebuffer[y>>3][x] &= ~(1 << (y & 7));
102 static void flippixel(int x, int y)
104 lcd_framebuffer[y>>3][x] ^= 1 << (y & 7);
107 static void nopixel(int x, int y)
109 (void)x;
110 (void)y;
113 lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
114 flippixel, nopixel, setpixel, setpixel,
115 nopixel, clearpixel, nopixel, clearpixel
118 static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
119 ICODE_ATTR;
120 static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
122 *address ^= bits & mask;
125 static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
126 ICODE_ATTR;
127 static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
129 *address &= bits | ~mask;
132 static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
133 ICODE_ATTR;
134 static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
136 *address |= bits & mask;
139 static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
140 ICODE_ATTR;
141 static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
143 unsigned data = *(char*)address;
145 bits ^= data;
146 *address = data ^ (bits & mask);
149 static void flipinvblock(unsigned char *address, unsigned mask, unsigned bits)
150 ICODE_ATTR;
151 static void flipinvblock(unsigned char *address, unsigned mask, unsigned bits)
153 *address ^= ~bits & mask;
156 static void bginvblock(unsigned char *address, unsigned mask, unsigned bits)
157 ICODE_ATTR;
158 static void bginvblock(unsigned char *address, unsigned mask, unsigned bits)
160 *address &= ~(bits & mask);
163 static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
164 ICODE_ATTR;
165 static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
167 *address |= ~bits & mask;
170 static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
171 ICODE_ATTR;
172 static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
174 unsigned data = *(char *)address;
176 bits = ~bits ^ data;
177 *address = data ^ (bits & mask);
180 lcd_blockfunc_type* const lcd_blockfuncs[8] = {
181 flipblock, bgblock, fgblock, solidblock,
182 flipinvblock, bginvblock, fginvblock, solidinvblock
185 /*** drawing functions ***/
187 /* Clear the whole display */
188 void lcd_clear_display(void)
190 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
192 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
193 lcd_scroll_info.lines = 0;
196 /* Set a single pixel */
197 void lcd_drawpixel(int x, int y)
199 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
200 lcd_pixelfuncs[drawmode](x, y);
203 /* Draw a line */
204 void lcd_drawline(int x1, int y1, int x2, int y2)
206 int numpixels;
207 int i;
208 int deltax, deltay;
209 int d, dinc1, dinc2;
210 int x, xinc1, xinc2;
211 int y, yinc1, yinc2;
212 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
214 deltax = abs(x2 - x1);
215 deltay = abs(y2 - y1);
216 xinc2 = 1;
217 yinc2 = 1;
219 if (deltax >= deltay)
221 numpixels = deltax;
222 d = 2 * deltay - deltax;
223 dinc1 = deltay * 2;
224 dinc2 = (deltay - deltax) * 2;
225 xinc1 = 1;
226 yinc1 = 0;
228 else
230 numpixels = deltay;
231 d = 2 * deltax - deltay;
232 dinc1 = deltax * 2;
233 dinc2 = (deltax - deltay) * 2;
234 xinc1 = 0;
235 yinc1 = 1;
237 numpixels++; /* include endpoints */
239 if (x1 > x2)
241 xinc1 = -xinc1;
242 xinc2 = -xinc2;
245 if (y1 > y2)
247 yinc1 = -yinc1;
248 yinc2 = -yinc2;
251 x = x1;
252 y = y1;
254 for (i = 0; i < numpixels; i++)
256 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
257 pfunc(x, y);
259 if (d < 0)
261 d += dinc1;
262 x += xinc1;
263 y += yinc1;
265 else
267 d += dinc2;
268 x += xinc2;
269 y += yinc2;
274 /* Draw a horizontal line (optimised) */
275 void lcd_hline(int x1, int x2, int y)
277 int x;
278 unsigned char *dst, *dst_end;
279 unsigned mask;
280 lcd_blockfunc_type *bfunc;
282 /* direction flip */
283 if (x2 < x1)
285 x = x1;
286 x1 = x2;
287 x2 = x;
290 /* nothing to draw? */
291 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
292 return;
294 /* clipping */
295 if (x1 < 0)
296 x1 = 0;
297 if (x2 >= LCD_WIDTH)
298 x2 = LCD_WIDTH-1;
300 bfunc = lcd_blockfuncs[drawmode];
301 dst = &lcd_framebuffer[y>>3][x1];
302 mask = 1 << (y & 7);
304 dst_end = dst + x2 - x1;
306 bfunc(dst++, mask, 0xFFu);
307 while (dst <= dst_end);
310 /* Draw a vertical line (optimised) */
311 void lcd_vline(int x, int y1, int y2)
313 int ny;
314 unsigned char *dst;
315 unsigned mask, mask_bottom;
316 lcd_blockfunc_type *bfunc;
318 /* direction flip */
319 if (y2 < y1)
321 ny = y1;
322 y1 = y2;
323 y2 = ny;
326 /* nothing to draw? */
327 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
328 return;
330 /* clipping */
331 if (y1 < 0)
332 y1 = 0;
333 if (y2 >= LCD_HEIGHT)
334 y2 = LCD_HEIGHT-1;
336 bfunc = lcd_blockfuncs[drawmode];
337 dst = &lcd_framebuffer[y1>>3][x];
338 ny = y2 - (y1 & ~7);
339 mask = 0xFFu << (y1 & 7);
340 mask_bottom = 0xFFu >> (~ny & 7);
342 for (; ny >= 8; ny -= 8)
344 bfunc(dst, mask, 0xFFu);
345 dst += LCD_WIDTH;
346 mask = 0xFFu;
348 mask &= mask_bottom;
349 bfunc(dst, mask, 0xFFu);
352 /* Draw a rectangular box */
353 void lcd_drawrect(int x, int y, int width, int height)
355 if ((width <= 0) || (height <= 0))
356 return;
358 int x2 = x + width - 1;
359 int y2 = y + height - 1;
361 lcd_vline(x, y, y2);
362 lcd_vline(x2, y, y2);
363 lcd_hline(x, x2, y);
364 lcd_hline(x, x2, y2);
367 /* Fill a rectangular area */
368 void lcd_fillrect(int x, int y, int width, int height)
370 int ny;
371 unsigned char *dst, *dst_end;
372 unsigned mask, mask_bottom;
373 unsigned bits = 0;
374 lcd_blockfunc_type *bfunc;
375 bool fillopt = false;
377 /* nothing to draw? */
378 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
379 || (x + width <= 0) || (y + height <= 0))
380 return;
382 /* clipping */
383 if (x < 0)
385 width += x;
386 x = 0;
388 if (y < 0)
390 height += y;
391 y = 0;
393 if (x + width > LCD_WIDTH)
394 width = LCD_WIDTH - x;
395 if (y + height > LCD_HEIGHT)
396 height = LCD_HEIGHT - y;
398 if (drawmode & DRMODE_INVERSEVID)
400 if (drawmode & DRMODE_BG)
402 fillopt = true;
405 else
407 if (drawmode & DRMODE_FG)
409 fillopt = true;
410 bits = 0xFFu;
413 bfunc = lcd_blockfuncs[drawmode];
414 dst = &lcd_framebuffer[y>>3][x];
415 ny = height - 1 + (y & 7);
416 mask = 0xFFu << (y & 7);
417 mask_bottom = 0xFFu >> (~ny & 7);
419 for (; ny >= 8; ny -= 8)
421 if (fillopt && (mask == 0xFFu))
422 memset(dst, bits, width);
423 else
425 unsigned char *dst_row = dst;
427 dst_end = dst_row + width;
429 bfunc(dst_row++, mask, 0xFFu);
430 while (dst_row < dst_end);
433 dst += LCD_WIDTH;
434 mask = 0xFFu;
436 mask &= mask_bottom;
438 if (fillopt && (mask == 0xFFu))
439 memset(dst, bits, width);
440 else
442 dst_end = dst + width;
444 bfunc(dst++, mask, 0xFFu);
445 while (dst < dst_end);
449 /* About Rockbox' internal bitmap format:
451 * A bitmap contains one bit for every pixel that defines if that pixel is
452 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
453 * at top.
454 * The bytes are stored in row-major order, with byte 0 being top left,
455 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
456 * 0..7, the second row defines pixel row 8..15 etc.
458 * This is the same as the internal lcd hw format. */
460 /* Draw a partial bitmap */
461 void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
462 int stride, int x, int y, int width, int height)
463 ICODE_ATTR;
464 void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
465 int stride, int x, int y, int width, int height)
467 int shift, ny;
468 unsigned char *dst, *dst_end;
469 unsigned mask, mask_bottom;
470 lcd_blockfunc_type *bfunc;
472 /* nothing to draw? */
473 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
474 || (x + width <= 0) || (y + height <= 0))
475 return;
477 /* clipping */
478 if (x < 0)
480 width += x;
481 src_x -= x;
482 x = 0;
484 if (y < 0)
486 height += y;
487 src_y -= y;
488 y = 0;
490 if (x + width > LCD_WIDTH)
491 width = LCD_WIDTH - x;
492 if (y + height > LCD_HEIGHT)
493 height = LCD_HEIGHT - y;
495 src += stride * (src_y >> 3) + src_x; /* move starting point */
496 src_y &= 7;
497 y -= src_y;
498 dst = &lcd_framebuffer[y>>3][x];
499 shift = y & 7;
500 ny = height - 1 + shift + src_y;
502 bfunc = lcd_blockfuncs[drawmode];
503 mask = 0xFFu << (shift + src_y);
504 mask_bottom = 0xFFu >> (~ny & 7);
506 if (shift == 0)
508 bool copyopt = (drawmode == DRMODE_SOLID);
510 for (; ny >= 8; ny -= 8)
512 if (copyopt && (mask == 0xFFu))
513 memcpy(dst, src, width);
514 else
516 const unsigned char *src_row = src;
517 unsigned char *dst_row = dst;
519 dst_end = dst_row + width;
521 bfunc(dst_row++, mask, *src_row++);
522 while (dst_row < dst_end);
525 src += stride;
526 dst += LCD_WIDTH;
527 mask = 0xFFu;
529 mask &= mask_bottom;
531 if (copyopt && (mask == 0xFFu))
532 memcpy(dst, src, width);
533 else
535 dst_end = dst + width;
537 bfunc(dst++, mask, *src++);
538 while (dst < dst_end);
541 else
543 dst_end = dst + width;
546 const unsigned char *src_col = src++;
547 unsigned char *dst_col = dst++;
548 unsigned mask_col = mask;
549 unsigned data = 0;
551 for (y = ny; y >= 8; y -= 8)
553 data |= *src_col << shift;
555 if (mask_col & 0xFFu)
557 bfunc(dst_col, mask_col, data);
558 mask_col = 0xFFu;
560 else
561 mask_col >>= 8;
563 src_col += stride;
564 dst_col += LCD_WIDTH;
565 data >>= 8;
567 data |= *src_col << shift;
568 bfunc(dst_col, mask_col & mask_bottom, data);
570 while (dst < dst_end);
574 /* Draw a full bitmap */
575 void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
577 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
580 /* put a string at a given pixel position, skipping first ofs pixel columns */
581 static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
583 unsigned short ch;
584 unsigned short *ucs;
585 struct font* pf = font_get(curfont);
587 ucs = bidi_l2v(str, 1);
589 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
591 int width;
592 const unsigned char *bits;
594 /* get proportional width and glyph bits */
595 width = font_get_width(pf,ch);
597 if (ofs > width)
599 ofs -= width;
600 continue;
603 bits = font_get_bits(pf, ch);
605 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
606 pf->height);
608 x += width - ofs;
609 ofs = 0;
612 /* put a string at a given pixel position */
613 void lcd_putsxy(int x, int y, const unsigned char *str)
615 lcd_putsxyofs(x, y, 0, str);
618 /*** Line oriented text output ***/
620 /* put a string at a given char position */
621 void lcd_puts(int x, int y, const unsigned char *str)
623 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
626 void lcd_puts_style(int x, int y, const unsigned char *str, int style)
628 lcd_puts_style_offset(x, y, str, style, 0);
631 void lcd_puts_offset(int x, int y, const unsigned char *str, int offset)
633 lcd_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
636 /* put a string at a given char position, style, and pixel position,
637 * skipping first offset pixel columns */
638 void lcd_puts_style_offset(int x, int y, const unsigned char *str,
639 int style, int offset)
641 int xpos,ypos,w,h,xrect;
642 int lastmode = drawmode;
644 /* make sure scrolling is turned off on the line we are updating */
645 lcd_scroll_info.lines &= ~(1 << y);
647 if(!str || !str[0])
648 return;
650 lcd_getstringsize(str, &w, &h);
651 xpos = xmargin + x*w / utf8length(str);
652 ypos = ymargin + y*h;
653 drawmode = (style & STYLE_INVERT) ?
654 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
655 lcd_putsxyofs(xpos, ypos, offset, str);
656 drawmode ^= DRMODE_INVERSEVID;
657 xrect = xpos + MAX(w - offset, 0);
658 lcd_fillrect(xrect, ypos, LCD_WIDTH - xrect, h);
659 drawmode = lastmode;
662 /*** scrolling ***/
663 void lcd_puts_scroll(int x, int y, const unsigned char *string)
665 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
668 void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
670 lcd_puts_scroll_style_offset(x, y, string, style, 0);
673 void lcd_puts_scroll_offset(int x, int y, const unsigned char *string,
674 int offset)
676 lcd_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
679 void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
680 int style, int offset)
682 struct scrollinfo* s;
683 int w, h;
685 if(y>=LCD_SCROLLABLE_LINES) return;
687 s = &lcd_scroll_info.scroll[y];
689 s->start_tick = current_tick + lcd_scroll_info.delay;
690 s->style = style;
691 if (style & STYLE_INVERT) {
692 lcd_puts_style_offset(x,y,string,STYLE_INVERT,offset);
694 else
695 lcd_puts_offset(x,y,string,offset);
697 lcd_getstringsize(string, &w, &h);
699 if (LCD_WIDTH - x * 8 - xmargin < w) {
700 /* prepare scroll line */
701 char *end;
703 memset(s->line, 0, sizeof s->line);
704 strcpy(s->line, string);
706 /* get width */
707 s->width = lcd_getstringsize(s->line, &w, &h);
709 /* scroll bidirectional or forward only depending on the string
710 width */
711 if ( lcd_scroll_info.bidir_limit ) {
712 s->bidir = s->width < (LCD_WIDTH - xmargin) *
713 (100 + lcd_scroll_info.bidir_limit) / 100;
715 else
716 s->bidir = false;
718 if (!s->bidir) { /* add spaces if scrolling in the round */
719 strcat(s->line, " ");
720 /* get new width incl. spaces */
721 s->width = lcd_getstringsize(s->line, &w, &h);
724 end = strchr(s->line, '\0');
725 strncpy(end, string, LCD_WIDTH/2);
727 s->len = utf8length(string);
728 s->offset = offset;
729 s->startx = xmargin + x * s->width / s->len;;
730 s->backward = false;
731 lcd_scroll_info.lines |= (1<<y);
733 else
734 /* force a bit switch-off since it doesn't scroll */
735 lcd_scroll_info.lines &= ~(1<<y);
738 void lcd_scroll_fn(void)
740 struct font* pf;
741 struct scrollinfo* s;
742 int index;
743 int xpos, ypos;
744 int lastmode;
746 for ( index = 0; index < LCD_SCROLLABLE_LINES; index++ ) {
747 /* really scroll? */
748 if ((lcd_scroll_info.lines & (1 << index)) == 0)
749 continue;
751 s = &lcd_scroll_info.scroll[index];
753 /* check pause */
754 if (TIME_BEFORE(current_tick, s->start_tick))
755 continue;
757 if (s->backward)
758 s->offset -= lcd_scroll_info.step;
759 else
760 s->offset += lcd_scroll_info.step;
762 pf = font_get(curfont);
763 xpos = s->startx;
764 ypos = ymargin + index * pf->height;
766 if (s->bidir) { /* scroll bidirectional */
767 if (s->offset <= 0) {
768 /* at beginning of line */
769 s->offset = 0;
770 s->backward = false;
771 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
773 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
774 /* at end of line */
775 s->offset = s->width - (LCD_WIDTH - xpos);
776 s->backward = true;
777 s->start_tick = current_tick + lcd_scroll_info.delay * 2;
780 else {
781 /* scroll forward the whole time */
782 if (s->offset >= s->width)
783 s->offset %= s->width;
786 lastmode = drawmode;
787 drawmode = (s->style&STYLE_INVERT) ?
788 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
789 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
790 drawmode = lastmode;
791 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);