Use pre-multiplication in scaler to save one multiply per color component on ARM...
[kugel-rb.git] / apps / plugins / lib / grey_draw.c
blobc1e6376cfe1557a7f9ae9f05a76ccf878dc2aab2
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 /*** low-level drawing functions ***/
33 static void setpixel(unsigned char *address)
35 *address = _grey_info.fg_brightness;
38 static void clearpixel(unsigned char *address)
40 *address = _grey_info.bg_brightness;
43 static void flippixel(unsigned char *address)
45 *address = ~(*address);
48 static void nopixel(unsigned char *address)
50 (void)address;
53 void (* const _grey_pixelfuncs[8])(unsigned char *address) = {
54 flippixel, nopixel, setpixel, setpixel,
55 nopixel, clearpixel, nopixel, clearpixel
58 /*** Drawing functions ***/
60 /* Clear the whole display */
61 void grey_clear_display(void)
63 int value = (_grey_info.drawmode & DRMODE_INVERSEVID) ?
64 _grey_info.fg_brightness : _grey_info.bg_brightness;
66 rb->memset(_grey_info.buffer, value,
67 _GREY_MULUQ(_grey_info.width, _grey_info.height));
70 /* Set a single pixel */
71 void grey_drawpixel(int x, int y)
73 if (((unsigned)x < (unsigned)_grey_info.width)
74 && ((unsigned)y < (unsigned)_grey_info.height))
75 _grey_pixelfuncs[_grey_info.drawmode](&_grey_info.buffer[_GREY_MULUQ(
76 _grey_info.width, y) + x]);
79 /* Draw a line */
80 void grey_drawline(int x1, int y1, int x2, int y2)
82 int numpixels;
83 int i;
84 int deltax, deltay;
85 int d, dinc1, dinc2;
86 int x, xinc1, xinc2;
87 int y, yinc1, yinc2;
88 void (*pfunc)(unsigned char *address) = _grey_pixelfuncs[_grey_info.drawmode];
90 deltax = abs(x2 - x1);
91 deltay = abs(y2 - y1);
92 xinc2 = 1;
93 yinc2 = 1;
95 if (deltax >= deltay)
97 numpixels = deltax;
98 d = 2 * deltay - deltax;
99 dinc1 = deltay * 2;
100 dinc2 = (deltay - deltax) * 2;
101 xinc1 = 1;
102 yinc1 = 0;
104 else
106 numpixels = deltay;
107 d = 2 * deltax - deltay;
108 dinc1 = deltax * 2;
109 dinc2 = (deltax - deltay) * 2;
110 xinc1 = 0;
111 yinc1 = 1;
113 numpixels++; /* include endpoints */
115 if (x1 > x2)
117 xinc1 = -xinc1;
118 xinc2 = -xinc2;
121 if (y1 > y2)
123 yinc1 = -yinc1;
124 yinc2 = -yinc2;
127 x = x1;
128 y = y1;
130 for (i = 0; i < numpixels; i++)
132 if (((unsigned)x < (unsigned)_grey_info.width)
133 && ((unsigned)y < (unsigned)_grey_info.height))
134 pfunc(&_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x]);
136 if (d < 0)
138 d += dinc1;
139 x += xinc1;
140 y += yinc1;
142 else
144 d += dinc2;
145 x += xinc2;
146 y += yinc2;
151 /* Draw a horizontal line (optimised) */
152 void grey_hline(int x1, int x2, int y)
154 int x;
155 int value = 0;
156 unsigned char *dst;
157 bool fillopt = false;
159 /* direction flip */
160 if (x2 < x1)
162 x = x1;
163 x1 = x2;
164 x2 = x;
167 /* nothing to draw? */
168 if (((unsigned)y >= (unsigned)_grey_info.height)
169 || (x1 >= _grey_info.width) || (x2 < 0))
170 return;
172 /* drawmode and optimisation */
173 if (_grey_info.drawmode & DRMODE_INVERSEVID)
175 if (_grey_info.drawmode & DRMODE_BG)
177 fillopt = true;
178 value = _grey_info.bg_brightness;
181 else
183 if (_grey_info.drawmode & DRMODE_FG)
185 fillopt = true;
186 value = _grey_info.fg_brightness;
189 if (!fillopt && _grey_info.drawmode != DRMODE_COMPLEMENT)
190 return;
192 /* clipping */
193 if (x1 < 0)
194 x1 = 0;
195 if (x2 >= _grey_info.width)
196 x2 = _grey_info.width - 1;
198 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x1];
200 if (fillopt)
201 rb->memset(dst, value, x2 - x1 + 1);
202 else /* DRMODE_COMPLEMENT */
204 unsigned char *dst_end = dst + x2 - x1;
206 *dst = ~(*dst);
207 while (++dst <= dst_end);
211 /* Draw a vertical line (optimised) */
212 void grey_vline(int x, int y1, int y2)
214 int y;
215 unsigned char *dst, *dst_end;
216 void (*pfunc)(unsigned char *address);
218 /* direction flip */
219 if (y2 < y1)
221 y = y1;
222 y1 = y2;
223 y2 = y;
226 /* nothing to draw? */
227 if (((unsigned)x >= (unsigned)_grey_info.width)
228 || (y1 >= _grey_info.height) || (y2 < 0))
229 return;
231 /* clipping */
232 if (y1 < 0)
233 y1 = 0;
234 if (y2 >= _grey_info.height)
235 y2 = _grey_info.height - 1;
237 pfunc = _grey_pixelfuncs[_grey_info.drawmode];
238 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y1) + x];
240 dst_end = dst + _GREY_MULUQ(_grey_info.width, y2 - y1);
243 pfunc(dst);
244 dst += _grey_info.width;
246 while (dst <= dst_end);
249 /* Draw a filled triangle */
250 void grey_filltriangle(int x1, int y1, int x2, int y2, int x3, int y3)
252 int x, y;
253 long fp_x1, fp_x2, fp_dx1, fp_dx2;
255 /* sort vertices by increasing y value */
256 if (y1 > y3)
258 if (y2 < y3) /* y2 < y3 < y1 */
260 x = x1; x1 = x2; x2 = x3; x3 = x;
261 y = y1; y1 = y2; y2 = y3; y3 = y;
263 else if (y2 > y1) /* y3 < y1 < y2 */
265 x = x1; x1 = x3; x3 = x2; x2 = x;
266 y = y1; y1 = y3; y3 = y2; y2 = y;
268 else /* y3 <= y2 <= y1 */
270 x = x1; x1 = x3; x3 = x;
271 y = y1; y1 = y3; y3 = y;
274 else
276 if (y2 < y1) /* y2 < y1 <= y3 */
278 x = x1; x1 = x2; x2 = x;
279 y = y1; y1 = y2; y2 = y;
281 else if (y2 > y3) /* y1 <= y3 < y2 */
283 x = x2; x2 = x3; x3 = x;
284 y = y2; y2 = y3; y3 = y;
286 /* else already sorted */
289 if (y1 < y3) /* draw */
291 fp_dx1 = ((x3 - x1) << 16) / (y3 - y1);
292 fp_x1 = (x1 << 16) + (1<<15) + (fp_dx1 >> 1);
294 if (y1 < y2) /* first part */
296 fp_dx2 = ((x2 - x1) << 16) / (y2 - y1);
297 fp_x2 = (x1 << 16) + (1<<15) + (fp_dx2 >> 1);
298 for (y = y1; y < y2; y++)
300 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
301 fp_x1 += fp_dx1;
302 fp_x2 += fp_dx2;
305 if (y2 < y3) /* second part */
307 fp_dx2 = ((x3 - x2) << 16) / (y3 - y2);
308 fp_x2 = (x2 << 16) + (1<<15) + (fp_dx2 >> 1);
309 for (y = y2; y < y3; y++)
311 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
312 fp_x1 += fp_dx1;
313 fp_x2 += fp_dx2;
319 /* Draw a rectangular box */
320 void grey_drawrect(int x, int y, int width, int height)
322 if ((width <= 0) || (height <= 0))
323 return;
325 int x2 = x + width - 1;
326 int y2 = y + height - 1;
328 grey_vline(x, y, y2);
329 grey_vline(x2, y, y2);
330 grey_hline(x, x2, y);
331 grey_hline(x, x2, y2);
334 /* Fill a rectangular area */
335 void grey_fillrect(int x, int y, int width, int height)
337 int value = 0;
338 unsigned char *dst, *dst_end;
339 bool fillopt = false;
341 /* nothing to draw? */
342 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
343 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
344 return;
346 /* drawmode and optimisation */
347 if (_grey_info.drawmode & DRMODE_INVERSEVID)
349 if (_grey_info.drawmode & DRMODE_BG)
351 fillopt = true;
352 value = _grey_info.bg_brightness;
355 else
357 if (_grey_info.drawmode & DRMODE_FG)
359 fillopt = true;
360 value = _grey_info.fg_brightness;
363 if (!fillopt && _grey_info.drawmode != DRMODE_COMPLEMENT)
364 return;
366 /* clipping */
367 if (x < 0)
369 width += x;
370 x = 0;
372 if (y < 0)
374 height += y;
375 y = 0;
377 if (x + width > _grey_info.width)
378 width = _grey_info.width - x;
379 if (y + height > _grey_info.height)
380 height = _grey_info.height - y;
382 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x];
383 dst_end = dst + _GREY_MULUQ(_grey_info.width, height);
387 if (fillopt)
388 rb->memset(dst, value, width);
389 else /* DRMODE_COMPLEMENT */
391 unsigned char *dst_row = dst;
392 unsigned char *row_end = dst_row + width;
395 *dst_row = ~(*dst_row);
396 while (++dst_row < row_end);
398 dst += _grey_info.width;
400 while (dst < dst_end);
403 /* About Rockbox' internal monochrome bitmap format:
405 * A bitmap contains one bit for every pixel that defines if that pixel is
406 * foreground (1) or background (0). Bits within a byte are arranged
407 * vertically, LSB at top.
408 * The bytes are stored in row-major order, with byte 0 being top left,
409 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
410 * 0..7, the second row defines pixel row 8..15 etc. */
412 /* Draw a partial monochrome bitmap */
413 void grey_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
414 int stride, int x, int y, int width, int height)
416 const unsigned char *src_end;
417 unsigned char *dst, *dst_end;
418 unsigned dmask = 0x100; /* bit 8 == sentinel */
419 int drmode = _grey_info.drawmode;
420 int dwidth;
422 /* nothing to draw? */
423 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
424 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
425 return;
427 /* clipping */
428 if (x < 0)
430 width += x;
431 src_x -= x;
432 x = 0;
434 if (y < 0)
436 height += y;
437 src_y -= y;
438 y = 0;
440 if (x + width > _grey_info.width)
441 width = _grey_info.width - x;
442 if (y + height > _grey_info.height)
443 height = _grey_info.height - y;
445 src += _GREY_MULUQ(stride, src_y >> 3) + src_x; /* move starting point */
446 src_y &= 7;
447 src_end = src + width;
448 dwidth = _grey_info.width;
449 dst = &_grey_info.buffer[_GREY_MULUQ(dwidth, y) + x];
451 if (drmode & DRMODE_INVERSEVID)
453 dmask = 0x1ff; /* bit 8 == sentinel */
454 drmode &= DRMODE_SOLID; /* mask out inversevid */
459 const unsigned char *src_col = src++;
460 unsigned char *dst_col = dst++;
461 unsigned data = (*src_col ^ dmask) >> src_y;
462 int fg, bg;
464 dst_end = dst_col + _GREY_MULUQ(dwidth, height);
466 #define UPDATE_SRC do { \
467 data >>= 1; \
468 if (data == 0x001) { \
469 src_col += stride; \
470 data = *src_col ^ dmask; \
472 } while (0)
474 switch (drmode)
476 case DRMODE_COMPLEMENT:
479 if (data & 0x01)
480 *dst_col = ~(*dst_col);
482 dst_col += dwidth;
483 UPDATE_SRC;
485 while (dst_col < dst_end);
486 break;
488 case DRMODE_BG:
489 bg = _grey_info.bg_brightness;
492 if (!(data & 0x01))
493 *dst_col = bg;
495 dst_col += dwidth;
496 UPDATE_SRC;
498 while (dst_col < dst_end);
499 break;
501 case DRMODE_FG:
502 fg = _grey_info.fg_brightness;
505 if (data & 0x01)
506 *dst_col = fg;
508 dst_col += dwidth;
509 UPDATE_SRC;
511 while (dst_col < dst_end);
512 break;
514 case DRMODE_SOLID:
515 fg = _grey_info.fg_brightness;
516 bg = _grey_info.bg_brightness;
519 *dst_col = (data & 0x01) ? fg : bg;
520 dst_col += dwidth;
521 UPDATE_SRC;
523 while (dst_col < dst_end);
524 break;
527 while (src < src_end);
530 /* Draw a full monochrome bitmap */
531 void grey_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
533 grey_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
536 /* Draw a partial greyscale bitmap, canonical format */
537 void grey_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
538 int stride, int x, int y, int width, int height)
540 unsigned char *dst, *dst_end;
542 /* nothing to draw? */
543 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
544 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
545 return;
547 /* clipping */
548 if (x < 0)
550 width += x;
551 src_x -= x;
552 x = 0;
554 if (y < 0)
556 height += y;
557 src_y -= y;
558 y = 0;
560 if (x + width > _grey_info.width)
561 width = _grey_info.width - x;
562 if (y + height > _grey_info.height)
563 height = _grey_info.height - y;
565 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
566 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x];
567 dst_end = dst + _GREY_MULUQ(_grey_info.width, height);
571 rb->memcpy(dst, src, width);
572 dst += _grey_info.width;
573 src += stride;
575 while (dst < dst_end);
578 /* Draw a full greyscale bitmap, canonical format */
579 void grey_gray_bitmap(const unsigned char *src, int x, int y, int width,
580 int height)
582 grey_gray_bitmap_part(src, 0, 0, width, x, y, width, height);
585 /* Put a string at a given pixel position, skipping first ofs pixel columns */
586 void grey_putsxyofs(int x, int y, int ofs, const unsigned char *str)
588 int ch;
589 unsigned short *ucs;
590 struct font* pf = rb->font_get(_grey_info.curfont);
592 ucs = rb->bidi_l2v(str, 1);
594 while ((ch = *ucs++) != 0 && x < _grey_info.width)
596 int width;
597 const unsigned char *bits;
599 /* get proportional width and glyph bits */
600 width = rb->font_get_width(pf, ch);
602 if (ofs > width)
604 ofs -= width;
605 continue;
608 bits = rb->font_get_bits(pf, ch);
610 grey_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
612 x += width - ofs;
613 ofs = 0;
617 /* Put a string at a given pixel position */
618 void grey_putsxy(int x, int y, const unsigned char *str)
620 grey_putsxyofs(x, y, 0, str);
623 /*** Unbuffered drawing functions ***/
625 /* Clear the greyscale display (sets all pixels to white) */
626 void grey_ub_clear_display(void)
628 int value = _grey_info.gvalue[(_grey_info.drawmode & DRMODE_INVERSEVID) ?
629 _grey_info.fg_brightness :
630 _grey_info.bg_brightness];
632 rb->memset(_grey_info.values, value,
633 _GREY_MULUQ(_grey_info.width, _grey_info.height));
634 #ifdef SIMULATOR
635 rb->sim_lcd_ex_update_rect(_grey_info.x, _grey_info.y,
636 _grey_info.width, _grey_info.height);
637 #endif
640 /* Assembler optimised helper function for copying a single line to the
641 * greyvalue buffer. */
642 void _grey_line1(int width, unsigned char *dst, const unsigned char *src,
643 const unsigned char *lut);
645 /* Draw a partial greyscale bitmap, canonical format */
646 void grey_ub_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
647 int stride, int x, int y, int width, int height)
649 int yc, ye;
650 unsigned char *dst;
652 /* nothing to draw? */
653 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
654 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
655 return;
657 /* clipping */
658 if (x < 0)
660 width += x;
661 src_x -= x;
662 x = 0;
664 if (y < 0)
666 height += y;
667 src_y -= y;
668 y = 0;
670 if (x + width > _grey_info.width)
671 width = _grey_info.width - x;
672 if (y + height > _grey_info.height)
673 height = _grey_info.height - y;
675 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
676 yc = y;
677 ye = y + height;
678 dst = _grey_info.values + (x << _GREY_BSHIFT);
682 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING
683 int idx = _GREY_MULUQ(_grey_info.width, yc);
684 #else /* vertical packing or vertical interleaved */
685 int idx = _GREY_MULUQ(_grey_info.width, yc & ~_GREY_BMASK)
686 + (~yc & _GREY_BMASK);
687 #endif /* LCD_PIXELFORMAT */
689 #if ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 1) && (CONFIG_CPU == SH7034)) \
690 || ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 2) && defined(CPU_COLDFIRE)) \
691 || ((LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && defined(CPU_COLDFIRE))
692 _grey_line1(width, dst + idx, src, _grey_info.gvalue);
693 #else
694 unsigned char *dst_row = dst + idx;
695 const unsigned char *src_row = src;
696 const unsigned char *src_end = src + width;
700 *dst_row = _grey_info.gvalue[*src_row++];
701 dst_row += _GREY_BSIZE;
703 while (src_row < src_end);
704 #endif
706 src += stride;
708 while (++yc < ye);
709 #ifdef SIMULATOR
710 rb->sim_lcd_ex_update_rect(_grey_info.x + x, _grey_info.y + y,
711 width, height);
712 #endif
715 /* Draw a full greyscale bitmap, canonical format */
716 void grey_ub_gray_bitmap(const unsigned char *src, int x, int y, int width,
717 int height)
719 grey_ub_gray_bitmap_part(src, 0, 0, width, x, y, width, height);
722 static void output_row_grey_8(uint32_t row, void * row_in,
723 struct scaler_context *ctx)
725 uint8_t *dest = (uint8_t*)ctx->bm->data + ctx->bm->width * row;
726 rb->memcpy(dest, row_in, ctx->bm->width);
729 static void output_row_grey_32(uint32_t row, void * row_in,
730 struct scaler_context *ctx)
732 int col;
733 uint32_t *qp = (uint32_t*)row_in;
734 uint8_t *dest = (uint8_t*)ctx->bm->data + ctx->bm->width * row;
735 for (col = 0; col < ctx->bm->width; col++)
736 *dest++ = SC_OUT(*qp++, ctx);
739 static unsigned int get_size_grey(struct bitmap *bm)
741 return bm->width * bm->height;
744 const struct custom_format format_grey = {
745 .output_row_8 = output_row_grey_8,
746 .output_row_32 = output_row_grey_32,
747 .get_size = get_size_grey