Prepare new maemo release
[maemo-rb.git] / apps / plugins / lib / grey_draw.c
blob171d2734cf453492a1bc71a98f8124f80c2f15e2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * New greyscale framework
11 * Drawing functions
13 * This is a generic framework to display 129 shades of grey on low-depth
14 * bitmap LCDs (Archos b&w, Iriver & Ipod 4-grey) within plugins.
16 * Copyright (C) 2008 Jens Arnold
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24 * KIND, either express or implied.
26 ****************************************************************************/
28 #include "plugin.h"
29 #include "grey.h"
31 extern struct viewport _grey_default_vp;
33 /*** low-level drawing functions ***/
35 static void setpixel(unsigned char *address)
37 *address = _GREY_FG_BRIGHTNESS(_grey_info.vp);
40 static void clearpixel(unsigned char *address)
42 *address = _GREY_BG_BRIGHTNESS(_grey_info.vp);
45 static void flippixel(unsigned char *address)
47 *address = ~(*address);
50 static void nopixel(unsigned char *address)
52 (void)address;
55 void (* const _grey_pixelfuncs[8])(unsigned char *address) = {
56 flippixel, nopixel, setpixel, setpixel,
57 nopixel, clearpixel, nopixel, clearpixel
60 /*** Drawing functions ***/
62 /* Clear the current viewport */
63 void grey_clear_viewport(void)
65 struct viewport *vp = _grey_info.vp;
66 int drawmode = vp->drawmode;
67 vp->drawmode = DRMODE_SOLID | DRMODE_INVERSEVID;
68 grey_fillrect(0, 0, vp->width, vp->height);
69 vp->drawmode = drawmode;
72 /* Clear the whole display */
73 void grey_clear_display(void)
75 struct viewport *vp = &_grey_default_vp;
77 int value = (vp->drawmode & DRMODE_INVERSEVID) ?
78 _GREY_FG_BRIGHTNESS(vp) : _GREY_BG_BRIGHTNESS(vp);
80 rb->memset(_grey_info.curbuffer, value,
81 _GREY_MULUQ(_grey_info.cb_width, _grey_info.cb_height));
84 /* Set a single pixel */
85 void grey_drawpixel(int x, int y)
87 if (x >= _grey_info.clip_l && x < _grey_info.clip_r &&
88 y >= _grey_info.clip_t && y < _grey_info.clip_b)
90 int dst_stride = _grey_info.cb_width;
91 struct viewport *vp = _grey_info.vp;
92 _grey_pixelfuncs[vp->drawmode](
93 &_grey_info.curbuffer[
94 _GREY_MULUQ(dst_stride, vp->y - _grey_info.cb_y + y) +
95 vp->x - _grey_info.cb_x + x]);
99 /* Draw a line */
100 void grey_drawline(int x1, int y1, int x2, int y2)
102 struct viewport *vp = _grey_info.vp;
103 int numpixels;
104 int i;
105 int deltax, deltay;
106 int d, dinc1, dinc2;
107 int x, xinc1, xinc2;
108 int y, yinc1, yinc2;
109 void (*pfunc)(unsigned char *address) = _grey_pixelfuncs[vp->drawmode];
110 int dwidth;
111 int xoffs, yoffs;
113 deltax = abs(x2 - x1);
114 deltay = abs(y2 - y1);
115 xinc2 = 1;
116 yinc2 = 1;
118 if (deltax >= deltay)
120 numpixels = deltax;
121 d = 2 * deltay - deltax;
122 dinc1 = deltay * 2;
123 dinc2 = (deltay - deltax) * 2;
124 xinc1 = 1;
125 yinc1 = 0;
127 else
129 numpixels = deltay;
130 d = 2 * deltax - deltay;
131 dinc1 = deltax * 2;
132 dinc2 = (deltax - deltay) * 2;
133 xinc1 = 0;
134 yinc1 = 1;
136 numpixels++; /* include endpoints */
138 if (x1 > x2)
140 xinc1 = -xinc1;
141 xinc2 = -xinc2;
144 if (y1 > y2)
146 yinc1 = -yinc1;
147 yinc2 = -yinc2;
150 x = x1;
151 y = y1;
153 dwidth = _grey_info.cb_width;
154 xoffs = vp->x - _grey_info.cb_x;
155 yoffs = vp->y - _grey_info.cb_y;
157 for (i = 0; i < numpixels; i++)
159 if (x >= _grey_info.clip_l && x < _grey_info.clip_r &&
160 y >= _grey_info.clip_t && y < _grey_info.clip_b)
162 pfunc(&_grey_info.curbuffer[_GREY_MULUQ(dwidth, yoffs + y) +
163 xoffs + x]);
166 if (d < 0)
168 d += dinc1;
169 x += xinc1;
170 y += yinc1;
172 else
174 d += dinc2;
175 x += xinc2;
176 y += yinc2;
181 /* Draw a horizontal line (optimised) */
182 void grey_hline(int x1, int x2, int y)
184 struct viewport *vp = _grey_info.vp;
185 int x;
186 int value = 0;
187 unsigned char *dst;
188 bool fillopt = false;
189 int dwidth;
191 /* direction flip */
192 if (x2 < x1)
194 x = x1;
195 x1 = x2;
196 x2 = x;
199 /* nothing to draw? */
200 if (y < _grey_info.clip_t || y >= _grey_info.clip_b ||
201 x1 >= _grey_info.clip_r || x2 < _grey_info.clip_l)
202 return;
204 /* drawmode and optimisation */
205 if (vp->drawmode & DRMODE_INVERSEVID)
207 if (vp->drawmode & DRMODE_BG)
209 fillopt = true;
210 value = _GREY_BG_BRIGHTNESS(vp);
213 else
215 if (vp->drawmode & DRMODE_FG)
217 fillopt = true;
218 value = _GREY_FG_BRIGHTNESS(vp);
221 if (!fillopt && vp->drawmode != DRMODE_COMPLEMENT)
222 return;
224 /* clipping */
225 if (x1 < _grey_info.clip_l)
226 x1 = _grey_info.clip_l;
227 if (x2 >= _grey_info.clip_r)
228 x2 = _grey_info.clip_r - 1;
230 dwidth = _grey_info.cb_width;
231 dst = &_grey_info.curbuffer[
232 _GREY_MULUQ(dwidth, vp->y - _grey_info.cb_y + y) +
233 vp->x - _grey_info.cb_x + x1];
235 if (fillopt)
236 rb->memset(dst, value, x2 - x1 + 1);
237 else /* DRMODE_COMPLEMENT */
239 unsigned char *dst_end = dst + x2 - x1;
241 *dst = ~(*dst);
242 while (++dst <= dst_end);
246 /* Draw a vertical line (optimised) */
247 void grey_vline(int x, int y1, int y2)
249 struct viewport *vp = _grey_info.vp;
250 int y;
251 unsigned char *dst, *dst_end;
252 void (*pfunc)(unsigned char *address);
253 int dwidth;
255 /* direction flip */
256 if (y2 < y1)
258 y = y1;
259 y1 = y2;
260 y2 = y;
263 /* nothing to draw? */
264 if (x < _grey_info.clip_l || x >= _grey_info.clip_r ||
265 y1 >= _grey_info.clip_b || y2 < _grey_info.clip_t)
266 return;
268 /* clipping */
269 if (y1 < _grey_info.clip_t)
270 y1 = _grey_info.clip_t;
271 if (y2 >= _grey_info.clip_b)
272 y2 = _grey_info.clip_b - 1;
274 dwidth = _grey_info.cb_width;
275 pfunc = _grey_pixelfuncs[vp->drawmode];
276 dst = &_grey_info.curbuffer[
277 _GREY_MULUQ(dwidth, vp->y - _grey_info.cb_y + y1) +
278 vp->x - _grey_info.cb_x + x];
280 dst_end = dst + _GREY_MULUQ(dwidth, y2 - y1);
283 pfunc(dst);
284 dst += dwidth;
286 while (dst <= dst_end);
289 /* Draw a filled triangle */
290 void grey_filltriangle(int x1, int y1, int x2, int y2, int x3, int y3)
292 int x, y;
293 long fp_x1, fp_x2, fp_dx1, fp_dx2;
295 /* sort vertices by increasing y value */
296 if (y1 > y3)
298 if (y2 < y3) /* y2 < y3 < y1 */
300 x = x1; x1 = x2; x2 = x3; x3 = x;
301 y = y1; y1 = y2; y2 = y3; y3 = y;
303 else if (y2 > y1) /* y3 < y1 < y2 */
305 x = x1; x1 = x3; x3 = x2; x2 = x;
306 y = y1; y1 = y3; y3 = y2; y2 = y;
308 else /* y3 <= y2 <= y1 */
310 x = x1; x1 = x3; x3 = x;
311 y = y1; y1 = y3; y3 = y;
314 else
316 if (y2 < y1) /* y2 < y1 <= y3 */
318 x = x1; x1 = x2; x2 = x;
319 y = y1; y1 = y2; y2 = y;
321 else if (y2 > y3) /* y1 <= y3 < y2 */
323 x = x2; x2 = x3; x3 = x;
324 y = y2; y2 = y3; y3 = y;
326 /* else already sorted */
329 if (y1 < y3) /* draw */
331 fp_dx1 = ((x3 - x1) << 16) / (y3 - y1);
332 fp_x1 = (x1 << 16) + (1<<15) + (fp_dx1 >> 1);
334 if (y1 < y2) /* first part */
336 fp_dx2 = ((x2 - x1) << 16) / (y2 - y1);
337 fp_x2 = (x1 << 16) + (1<<15) + (fp_dx2 >> 1);
338 for (y = y1; y < y2; y++)
340 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
341 fp_x1 += fp_dx1;
342 fp_x2 += fp_dx2;
345 if (y2 < y3) /* second part */
347 fp_dx2 = ((x3 - x2) << 16) / (y3 - y2);
348 fp_x2 = (x2 << 16) + (1<<15) + (fp_dx2 >> 1);
349 for (y = y2; y < y3; y++)
351 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
352 fp_x1 += fp_dx1;
353 fp_x2 += fp_dx2;
359 /* Draw a rectangular box */
360 void grey_drawrect(int x, int y, int width, int height)
362 if ((width <= 0) || (height <= 0))
363 return;
365 int x2 = x + width - 1;
366 int y2 = y + height - 1;
368 grey_vline(x, y, y2);
369 grey_vline(x2, y, y2);
370 grey_hline(x, x2, y);
371 grey_hline(x, x2, y2);
374 /* Fill a rectangular area */
375 void grey_fillrect(int x, int y, int width, int height)
377 struct viewport *vp = _grey_info.vp;
378 int value = 0;
379 unsigned char *dst, *dst_end;
380 bool fillopt = false;
381 int dwidth;
383 /* drawmode and optimisation */
384 if (vp->drawmode & DRMODE_INVERSEVID)
386 if (vp->drawmode & DRMODE_BG)
388 fillopt = true;
389 value = _GREY_BG_BRIGHTNESS(vp);
392 else
394 if (vp->drawmode & DRMODE_FG)
396 fillopt = true;
397 value = _GREY_FG_BRIGHTNESS(vp);
401 if (!fillopt && vp->drawmode != DRMODE_COMPLEMENT)
402 return;
404 /* clipping */
405 if (x < _grey_info.clip_l)
407 width += x - _grey_info.clip_l;
408 x = _grey_info.clip_l;
411 if (x + width > _grey_info.clip_r)
412 width = _grey_info.clip_r - x;
414 if (width <= 0)
415 return;
417 if (y < _grey_info.clip_t)
419 height += y - _grey_info.clip_t;
420 y = _grey_info.clip_t;
423 if (y + height > _grey_info.clip_b)
424 height = _grey_info.clip_b - y;
426 if (height <= 0)
427 return;
429 dwidth = _grey_info.cb_width;
430 dst = &_grey_info.curbuffer[
431 _GREY_MULUQ(dwidth, _grey_info.vp->y - _grey_info.cb_y + y) +
432 _grey_info.vp->x - _grey_info.cb_x + x];
433 dst_end = dst + _GREY_MULUQ(dwidth, height);
437 if (fillopt)
438 rb->memset(dst, value, width);
439 else /* DRMODE_COMPLEMENT */
441 unsigned char *dst_row = dst;
442 unsigned char *row_end = dst_row + width;
445 *dst_row = ~(*dst_row);
446 while (++dst_row < row_end);
448 dst += dwidth;
450 while (dst < dst_end);
453 /* About Rockbox' internal monochrome bitmap format:
455 * A bitmap contains one bit for every pixel that defines if that pixel is
456 * foreground (1) or background (0). Bits within a byte are arranged
457 * vertically, LSB at top.
458 * The bytes are stored in row-major order, with byte 0 being top left,
459 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
460 * 0..7, the second row defines pixel row 8..15 etc. */
462 /* Draw a partial monochrome bitmap */
463 void grey_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
464 int stride, int x, int y, int width, int height)
466 struct viewport *vp = _grey_info.vp;
467 const unsigned char *src_end;
468 unsigned char *dst, *dst_end;
469 unsigned dmask = 0x100; /* bit 8 == sentinel */
470 int drmode = vp->drawmode;
471 int dwidth;
473 /* clipping */
474 if (x < _grey_info.clip_l)
476 int dx = x - _grey_info.clip_l;
477 width += dx;
478 src_x -= dx;
479 x = _grey_info.clip_l;
482 if (x + width > _grey_info.clip_r)
483 width = _grey_info.clip_r - x;
485 if (width <= 0)
486 return;
488 if (y < _grey_info.clip_t)
490 int dy = y - _grey_info.clip_t;
491 height += dy;
492 src_y += dy;
493 y = _grey_info.clip_t;
496 if (y + height > _grey_info.clip_b)
497 height = _grey_info.clip_b - y;
499 if (height <= 0)
500 return;
502 src += _GREY_MULUQ(stride, src_y >> 3) + src_x; /* move starting point */
503 src_y &= 7;
504 src_end = src + width;
505 dwidth = _grey_info.cb_width;
506 dst = &_grey_info.curbuffer[
507 _GREY_MULUQ(dwidth, vp->y - _grey_info.cb_y + y) +
508 vp->x - _grey_info.cb_x + x];
509 dst_end = dst + _GREY_MULUQ(dwidth, height);
511 if (drmode & DRMODE_INVERSEVID)
513 dmask = 0x1ff; /* bit 8 == sentinel */
514 drmode &= DRMODE_SOLID; /* mask out inversevid */
519 const unsigned char *src_col = src++;
520 unsigned char *dst_col = dst++;
521 unsigned data = (*src_col ^ dmask) >> src_y;
522 int fg, bg;
524 #define UPDATE_SRC do { \
525 data >>= 1; \
526 if (data == 0x001) { \
527 src_col += stride; \
528 data = *src_col ^ dmask; \
530 } while (0)
532 switch (drmode)
534 case DRMODE_COMPLEMENT:
537 if (data & 0x01)
538 *dst_col = ~(*dst_col);
540 dst_col += dwidth;
541 UPDATE_SRC;
543 while (dst_col < dst_end);
544 break;
546 case DRMODE_BG:
547 bg = _GREY_BG_BRIGHTNESS(vp);
550 if (!(data & 0x01))
551 *dst_col = bg;
553 dst_col += dwidth;
554 UPDATE_SRC;
556 while (dst_col < dst_end);
557 break;
559 case DRMODE_FG:
560 fg = _GREY_FG_BRIGHTNESS(vp);
563 if (data & 0x01)
564 *dst_col = fg;
566 dst_col += dwidth;
567 UPDATE_SRC;
569 while (dst_col < dst_end);
570 break;
572 case DRMODE_SOLID:
573 fg = _GREY_FG_BRIGHTNESS(vp);
574 bg = _GREY_BG_BRIGHTNESS(vp);
577 *dst_col = (data & 0x01) ? fg : bg;
578 dst_col += dwidth;
579 UPDATE_SRC;
581 while (dst_col < dst_end);
582 break;
585 while (src < src_end);
588 /* Draw a full monochrome bitmap */
589 void grey_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
591 grey_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
594 /* Draw a partial greyscale bitmap, canonical format */
595 void grey_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
596 int stride, int x, int y, int width, int height)
598 unsigned char *dst, *dst_end;
599 int dwidth;
601 /* clipping */
602 if (x < _grey_info.clip_l)
604 int dx = x - _grey_info.clip_l;
605 width += dx;
606 src_x -= dx;
607 x = _grey_info.clip_l;
610 if (x + width > _grey_info.clip_r)
611 width = _grey_info.clip_r - x;
613 if (width <= 0)
614 return;
616 if (y < 0)
618 int dy = y - _grey_info.clip_t;
619 height += dy;
620 src_y -= dy;
621 y = _grey_info.clip_t;
624 if (y + height > _grey_info.clip_b)
625 height = _grey_info.clip_b - y;
627 if (height <= 0)
628 return;
630 dwidth = _grey_info.cb_width;
631 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
632 dst = &_grey_info.curbuffer[
633 _GREY_MULUQ(dwidth, _grey_info.vp->y - _grey_info.cb_y + y) +
634 _grey_info.vp->x - _grey_info.cb_x + x];
635 dst_end = dst + _GREY_MULUQ(dwidth, height);
639 rb->memcpy(dst, src, width);
640 dst += dwidth;
641 src += stride;
643 while (dst < dst_end);
646 /* Draw a full greyscale bitmap, canonical format */
647 void grey_gray_bitmap(const unsigned char *src, int x, int y, int width,
648 int height)
650 grey_gray_bitmap_part(src, 0, 0, width, x, y, width, height);
653 /* Put a string at a given pixel position, skipping first ofs pixel columns */
654 void grey_putsxyofs(int x, int y, int ofs, const unsigned char *str)
656 int ch;
657 unsigned short *ucs;
658 struct font* pf;
660 if (_grey_info.clip_b <= _grey_info.clip_t)
661 return;
663 pf = rb->font_get(_grey_info.vp->font);
664 ucs = rb->bidi_l2v(str, 1);
666 while ((ch = *ucs++) != 0 && x < _grey_info.clip_r)
668 int width;
669 const unsigned char *bits;
671 /* get proportional width and glyph bits */
672 width = rb->font_get_width(pf, ch);
674 if (ofs > width)
676 ofs -= width;
677 continue;
680 bits = rb->font_get_bits(pf, ch);
682 grey_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
684 x += width - ofs;
685 ofs = 0;
689 /* Put a string at a given pixel position */
690 void grey_putsxy(int x, int y, const unsigned char *str)
692 grey_putsxyofs(x, y, 0, str);
695 /*** Unbuffered drawing functions ***/
697 /* Clear the greyscale display (sets all pixels to white) */
698 void grey_ub_clear_display(void)
700 struct viewport *vp = &_grey_default_vp;
701 int value = _grey_info.gvalue[(vp->drawmode & DRMODE_INVERSEVID) ?
702 _GREY_FG_BRIGHTNESS(vp) :
703 _GREY_BG_BRIGHTNESS(vp)];
705 rb->memset(_grey_info.values, value,
706 _GREY_MULUQ(_grey_info.width, _grey_info.height));
707 #ifdef SIMULATOR
708 rb->sim_lcd_ex_update_rect(_grey_info.x, _grey_info.y,
709 _grey_info.width, _grey_info.height);
710 #endif
713 /* Assembler optimised helper function for copying a single line to the
714 * greyvalue buffer. */
715 void _grey_line1(int width, unsigned char *dst, const unsigned char *src,
716 const unsigned char *lut);
718 /* Draw a partial greyscale bitmap, canonical format */
719 void grey_ub_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
720 int stride, int x, int y, int width, int height)
722 int yc, ye;
723 unsigned char *dst;
725 /* nothing to draw? */
726 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
727 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
728 return;
730 /* clipping */
731 if (x < 0)
733 width += x;
734 src_x -= x;
735 x = 0;
737 if (y < 0)
739 height += y;
740 src_y -= y;
741 y = 0;
743 if (x + width > _grey_info.width)
744 width = _grey_info.width - x;
745 if (y + height > _grey_info.height)
746 height = _grey_info.height - y;
748 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
749 yc = y;
750 ye = y + height;
751 dst = _grey_info.values + (x << _GREY_BSHIFT);
755 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING
756 int idx = _GREY_MULUQ(_grey_info.width, yc);
757 #else /* vertical packing or vertical interleaved */
758 int idx = _GREY_MULUQ(_grey_info.width, yc & ~_GREY_BMASK)
759 + (~yc & _GREY_BMASK);
760 #endif /* LCD_PIXELFORMAT */
762 #if ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 1) && (CONFIG_CPU == SH7034)) \
763 || ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 2) && defined(CPU_COLDFIRE)) \
764 || ((LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && defined(CPU_COLDFIRE))
765 _grey_line1(width, dst + idx, src, _grey_info.gvalue);
766 #else
767 unsigned char *dst_row = dst + idx;
768 const unsigned char *src_row = src;
769 const unsigned char *src_end = src + width;
773 *dst_row = _grey_info.gvalue[*src_row++];
774 dst_row += _GREY_BSIZE;
776 while (src_row < src_end);
777 #endif
779 src += stride;
781 while (++yc < ye);
782 #ifdef SIMULATOR
783 rb->sim_lcd_ex_update_rect(_grey_info.x + x, _grey_info.y + y,
784 width, height);
785 #endif
788 /* Draw a full greyscale bitmap, canonical format */
789 void grey_ub_gray_bitmap(const unsigned char *src, int x, int y, int width,
790 int height)
792 grey_ub_gray_bitmap_part(src, 0, 0, width, x, y, width, height);
795 static void output_row_grey_8(uint32_t row, void * row_in,
796 struct scaler_context *ctx)
798 uint8_t *dest = (uint8_t*)ctx->bm->data + ctx->bm->width * row;
799 rb->memcpy(dest, row_in, ctx->bm->width);
802 static void output_row_grey_32(uint32_t row, void * row_in,
803 struct scaler_context *ctx)
805 int col;
806 uint32_t *qp = (uint32_t*)row_in;
807 uint8_t *dest = (uint8_t*)ctx->bm->data + ctx->bm->width * row;
808 for (col = 0; col < ctx->bm->width; col++)
809 *dest++ = SC_OUT(*qp++, ctx);
812 static unsigned int get_size_grey(struct bitmap *bm)
814 return bm->width * bm->height;
817 const struct custom_format format_grey = {
818 .output_row_8 = output_row_grey_8,
819 .output_row_32 = output_row_grey_32,
820 .get_size = get_size_grey