Colour targets: Revert an optimisation from almost 18 months ago that actually turned...
[Rockbox.git] / apps / plugins / lib / grey_draw.c
blobe9812b67844c6c10cfde824e5ecb647519df2039
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 _grey_info.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;
158 void (*pfunc)(unsigned char *address);
160 /* direction flip */
161 if (x2 < x1)
163 x = x1;
164 x1 = x2;
165 x2 = x;
168 /* nothing to draw? */
169 if (((unsigned)y >= (unsigned)_grey_info.height)
170 || (x1 >= _grey_info.width) || (x2 < 0))
171 return;
173 /* clipping */
174 if (x1 < 0)
175 x1 = 0;
176 if (x2 >= _grey_info.width)
177 x2 = _grey_info.width - 1;
179 if (_grey_info.drawmode & DRMODE_INVERSEVID)
181 if (_grey_info.drawmode & DRMODE_BG)
183 fillopt = true;
184 value = _grey_info.bg_brightness;
187 else
189 if (_grey_info.drawmode & DRMODE_FG)
191 fillopt = true;
192 value = _grey_info.fg_brightness;
195 pfunc = _grey_pixelfuncs[_grey_info.drawmode];
196 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x1];
198 if (fillopt)
199 _grey_info.rb->memset(dst, value, x2 - x1 + 1);
200 else
202 unsigned char *dst_end = dst + x2 - x1;
204 pfunc(dst++);
205 while (dst <= dst_end);
209 /* Draw a vertical line (optimised) */
210 void grey_vline(int x, int y1, int y2)
212 int y;
213 unsigned char *dst, *dst_end;
214 void (*pfunc)(unsigned char *address);
216 /* direction flip */
217 if (y2 < y1)
219 y = y1;
220 y1 = y2;
221 y2 = y;
224 /* nothing to draw? */
225 if (((unsigned)x >= (unsigned)_grey_info.width)
226 || (y1 >= _grey_info.height) || (y2 < 0))
227 return;
229 /* clipping */
230 if (y1 < 0)
231 y1 = 0;
232 if (y2 >= _grey_info.height)
233 y2 = _grey_info.height - 1;
235 pfunc = _grey_pixelfuncs[_grey_info.drawmode];
236 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y1) + x];
238 dst_end = dst + _GREY_MULUQ(_grey_info.width, y2 - y1);
241 pfunc(dst);
242 dst += _grey_info.width;
244 while (dst <= dst_end);
247 /* Draw a filled triangle */
248 void grey_filltriangle(int x1, int y1, int x2, int y2, int x3, int y3)
250 int x, y;
251 long fp_x1, fp_x2, fp_dx1, fp_dx2;
253 /* sort vertices by increasing y value */
254 if (y1 > y3)
256 if (y2 < y3) /* y2 < y3 < y1 */
258 x = x1; x1 = x2; x2 = x3; x3 = x;
259 y = y1; y1 = y2; y2 = y3; y3 = y;
261 else if (y2 > y1) /* y3 < y1 < y2 */
263 x = x1; x1 = x3; x3 = x2; x2 = x;
264 y = y1; y1 = y3; y3 = y2; y2 = y;
266 else /* y3 <= y2 <= y1 */
268 x = x1; x1 = x3; x3 = x;
269 y = y1; y1 = y3; y3 = y;
272 else
274 if (y2 < y1) /* y2 < y1 <= y3 */
276 x = x1; x1 = x2; x2 = x;
277 y = y1; y1 = y2; y2 = y;
279 else if (y2 > y3) /* y1 <= y3 < y2 */
281 x = x2; x2 = x3; x3 = x;
282 y = y2; y2 = y3; y3 = y;
284 /* else already sorted */
287 if (y1 < y3) /* draw */
289 fp_dx1 = ((x3 - x1) << 16) / (y3 - y1);
290 fp_x1 = (x1 << 16) + (1<<15) + (fp_dx1 >> 1);
292 if (y1 < y2) /* first part */
294 fp_dx2 = ((x2 - x1) << 16) / (y2 - y1);
295 fp_x2 = (x1 << 16) + (1<<15) + (fp_dx2 >> 1);
296 for (y = y1; y < y2; y++)
298 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
299 fp_x1 += fp_dx1;
300 fp_x2 += fp_dx2;
303 if (y2 < y3) /* second part */
305 fp_dx2 = ((x3 - x2) << 16) / (y3 - y2);
306 fp_x2 = (x2 << 16) + (1<<15) + (fp_dx2 >> 1);
307 for (y = y2; y < y3; y++)
309 grey_hline(fp_x1 >> 16, fp_x2 >> 16, y);
310 fp_x1 += fp_dx1;
311 fp_x2 += fp_dx2;
317 /* Draw a rectangular box */
318 void grey_drawrect(int x, int y, int width, int height)
320 if ((width <= 0) || (height <= 0))
321 return;
323 int x2 = x + width - 1;
324 int y2 = y + height - 1;
326 grey_vline(x, y, y2);
327 grey_vline(x2, y, y2);
328 grey_hline(x, x2, y);
329 grey_hline(x, x2, y2);
332 /* Fill a rectangular area */
333 void grey_fillrect(int x, int y, int width, int height)
335 int value = 0;
336 unsigned char *dst, *dst_end;
337 bool fillopt = false;
338 void (*pfunc)(unsigned char *address);
340 /* nothing to draw? */
341 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
342 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
343 return;
345 /* clipping */
346 if (x < 0)
348 width += x;
349 x = 0;
351 if (y < 0)
353 height += y;
354 y = 0;
356 if (x + width > _grey_info.width)
357 width = _grey_info.width - x;
358 if (y + height > _grey_info.height)
359 height = _grey_info.height - y;
361 if (_grey_info.drawmode & DRMODE_INVERSEVID)
363 if (_grey_info.drawmode & DRMODE_BG)
365 fillopt = true;
366 value = _grey_info.bg_brightness;
369 else
371 if (_grey_info.drawmode & DRMODE_FG)
373 fillopt = true;
374 value = _grey_info.fg_brightness;
377 pfunc = _grey_pixelfuncs[_grey_info.drawmode];
378 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x];
379 dst_end = dst + _GREY_MULUQ(_grey_info.width, height);
383 if (fillopt)
384 _grey_info.rb->memset(dst, value, width);
385 else
387 unsigned char *dst_row = dst;
388 unsigned char *row_end = dst_row + width;
391 pfunc(dst_row++);
392 while (dst_row < row_end);
394 dst += _grey_info.width;
396 while (dst < dst_end);
399 /* About Rockbox' internal monochrome bitmap format:
401 * A bitmap contains one bit for every pixel that defines if that pixel is
402 * foreground (1) or background (0). Bits within a byte are arranged
403 * vertically, LSB at top.
404 * The bytes are stored in row-major order, with byte 0 being top left,
405 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
406 * 0..7, the second row defines pixel row 8..15 etc. */
408 /* Draw a partial monochrome bitmap */
409 void grey_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
410 int stride, int x, int y, int width, int height)
412 const unsigned char *src_end;
413 unsigned char *dst, *dst_end;
414 void (*fgfunc)(unsigned char *address);
415 void (*bgfunc)(unsigned char *address);
417 /* nothing to draw? */
418 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
419 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
420 return;
422 /* clipping */
423 if (x < 0)
425 width += x;
426 src_x -= x;
427 x = 0;
429 if (y < 0)
431 height += y;
432 src_y -= y;
433 y = 0;
435 if (x + width > _grey_info.width)
436 width = _grey_info.width - x;
437 if (y + height > _grey_info.height)
438 height = _grey_info.height - y;
440 src += _GREY_MULUQ(stride, src_y >> 3) + src_x; /* move starting point */
441 src_y &= 7;
442 src_end = src + width;
444 fgfunc = _grey_pixelfuncs[_grey_info.drawmode];
445 bgfunc = _grey_pixelfuncs[_grey_info.drawmode ^ DRMODE_INVERSEVID];
446 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x];
450 const unsigned char *src_col = src++;
451 unsigned char *dst_col = dst++;
452 unsigned data = *src_col >> src_y;
453 int numbits = 8 - src_y;
455 dst_end = dst_col + _GREY_MULUQ(_grey_info.width, height);
458 if (data & 0x01)
459 fgfunc(dst_col);
460 else
461 bgfunc(dst_col);
463 dst_col += _grey_info.width;
465 data >>= 1;
466 if (--numbits == 0)
468 src_col += stride;
469 data = *src_col;
470 numbits = 8;
473 while (dst_col < dst_end);
475 while (src < src_end);
478 /* Draw a full monochrome bitmap */
479 void grey_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
481 grey_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
484 /* Draw a partial greyscale bitmap, canonical format */
485 void grey_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
486 int stride, int x, int y, int width, int height)
488 unsigned char *dst, *dst_end;
490 /* nothing to draw? */
491 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
492 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
493 return;
495 /* clipping */
496 if (x < 0)
498 width += x;
499 src_x -= x;
500 x = 0;
502 if (y < 0)
504 height += y;
505 src_y -= y;
506 y = 0;
508 if (x + width > _grey_info.width)
509 width = _grey_info.width - x;
510 if (y + height > _grey_info.height)
511 height = _grey_info.height - y;
513 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
514 dst = &_grey_info.buffer[_GREY_MULUQ(_grey_info.width, y) + x];
515 dst_end = dst + _GREY_MULUQ(_grey_info.width, height);
519 _grey_info.rb->memcpy(dst, src, width);
520 dst += _grey_info.width;
521 src += stride;
523 while (dst < dst_end);
526 /* Draw a full greyscale bitmap, canonical format */
527 void grey_gray_bitmap(const unsigned char *src, int x, int y, int width,
528 int height)
530 grey_gray_bitmap_part(src, 0, 0, width, x, y, width, height);
533 /* Put a string at a given pixel position, skipping first ofs pixel columns */
534 void grey_putsxyofs(int x, int y, int ofs, const unsigned char *str)
536 int ch;
537 struct font* pf = _grey_info.rb->font_get(_grey_info.curfont);
539 while ((ch = *str++) != '\0' && x < _grey_info.width)
541 int width;
542 const unsigned char *bits;
544 /* check input range */
545 if (ch < pf->firstchar || ch >= pf->firstchar+pf->size)
546 ch = pf->defaultchar;
547 ch -= pf->firstchar;
549 /* get proportional width and glyph bits */
550 width = pf->width ? pf->width[ch] : pf->maxwidth;
552 if (ofs > width)
554 ofs -= width;
555 continue;
558 bits = pf->bits + (pf->offset ?
559 pf->offset[ch] : (((pf->height + 7) >> 3) * pf->maxwidth * ch));
561 grey_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
563 x += width - ofs;
564 ofs = 0;
568 /* Put a string at a given pixel position */
569 void grey_putsxy(int x, int y, const unsigned char *str)
571 grey_putsxyofs(x, y, 0, str);
574 /*** Unbuffered drawing functions ***/
576 /* Clear the greyscale display (sets all pixels to white) */
577 void grey_ub_clear_display(void)
579 int value = _grey_info.gvalue[(_grey_info.drawmode & DRMODE_INVERSEVID) ?
580 _grey_info.fg_brightness :
581 _grey_info.bg_brightness];
583 _grey_info.rb->memset(_grey_info.values, value,
584 _GREY_MULUQ(_grey_info.width, _grey_info.height));
585 #ifdef SIMULATOR
586 _grey_info.rb->sim_lcd_ex_update_rect(_grey_info.x, _grey_info.y,
587 _grey_info.width, _grey_info.height);
588 #endif
591 /* Assembler optimised helper function for copying a single line to the
592 * greyvalue buffer. */
593 void _grey_line1(int width, unsigned char *dst, const unsigned char *src,
594 const unsigned char *lut);
596 /* Draw a partial greyscale bitmap, canonical format */
597 void grey_ub_gray_bitmap_part(const unsigned char *src, int src_x, int src_y,
598 int stride, int x, int y, int width, int height)
600 int yc, ye;
601 unsigned char *dst;
603 /* nothing to draw? */
604 if ((width <= 0) || (height <= 0) || (x >= _grey_info.width)
605 || (y >= _grey_info.height) || (x + width <= 0) || (y + height <= 0))
606 return;
608 /* clipping */
609 if (x < 0)
611 width += x;
612 src_x -= x;
613 x = 0;
615 if (y < 0)
617 height += y;
618 src_y -= y;
619 y = 0;
621 if (x + width > _grey_info.width)
622 width = _grey_info.width - x;
623 if (y + height > _grey_info.height)
624 height = _grey_info.height - y;
626 src += _GREY_MULUQ(stride, src_y) + src_x; /* move starting point */
627 yc = y;
628 ye = y + height;
629 dst = _grey_info.values + (x << _GREY_BSHIFT);
633 #if LCD_PIXELFORMAT == HORIZONTAL_PACKING
634 int idx = _GREY_MULUQ(_grey_info.width, yc);
635 #else /* vertical packing or vertical interleaved */
636 int idx = _GREY_MULUQ(_grey_info.width, yc & ~_GREY_BMASK)
637 + (~yc & _GREY_BMASK);
638 #endif /* LCD_PIXELFORMAT */
640 #if ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 1) && (CONFIG_CPU == SH7034)) \
641 || ((LCD_PIXELFORMAT == VERTICAL_PACKING) && (LCD_DEPTH == 2) && defined(CPU_COLDFIRE)) \
642 || ((LCD_PIXELFORMAT == VERTICAL_INTERLEAVED) && defined(CPU_COLDFIRE))
643 _grey_line1(width, dst + idx, src, _grey_info.gvalue);
644 #else
645 unsigned char *dst_row = dst + idx;
646 const unsigned char *src_row = src;
647 const unsigned char *src_end = src + width;
651 *dst_row = _grey_info.gvalue[*src_row++];
652 dst_row += _GREY_BSIZE;
654 while (src_row < src_end);
655 #endif
657 src += stride;
659 while (++yc < ye);
660 #ifdef SIMULATOR
661 _grey_info.rb->sim_lcd_ex_update_rect(_grey_info.x + x, _grey_info.y + y,
662 width, height);
663 #endif
666 /* Draw a full greyscale bitmap, canonical format */
667 void grey_ub_gray_bitmap(const unsigned char *src, int x, int y, int width,
668 int height)
670 grey_ub_gray_bitmap_part(src, 0, 0, width, x, y, width, height);