summaryrefslogtreecommitdiff
path: root/apps/plugins/imageviewer/jpeg/yuv2rgb.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/imageviewer/jpeg/yuv2rgb.c')
-rw-r--r--apps/plugins/imageviewer/jpeg/yuv2rgb.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/apps/plugins/imageviewer/jpeg/yuv2rgb.c b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
new file mode 100644
index 0000000000..2395f232b2
--- /dev/null
+++ b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
@@ -0,0 +1,407 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* JPEG image viewer
11* (This is a real mess if it has to be coded in one single C file)
12*
13* File scrolling addition (C) 2005 Alexander Spyridakis
14* Copyright (C) 2004 Jörg Hohensohn aka [IDC]Dragon
15* Heavily borrowed from the IJG implementation (C) Thomas G. Lane
16* Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org
17*
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.
22*
23* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24* KIND, either express or implied.
25*
26****************************************************************************/
27
28#include "plugin.h"
29#include "yuv2rgb.h"
30
31/*
32 * Conversion of full 0-255 range YCrCb to RGB:
33 * |R| |1.000000 -0.000001 1.402000| |Y'|
34 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
35 * |B| |1.000000 1.772000 0.000000| |Pr|
36 * Scaled (yields s15-bit output):
37 * |R| |128 0 179| |Y |
38 * |G| = |128 -43 -91| |Cb - 128|
39 * |B| |128 227 0| |Cr - 128|
40 */
41#define YFAC 128
42#define RVFAC 179
43#define GUFAC (-43)
44#define GVFAC (-91)
45#define BUFAC 227
46#define YUV_WHITE (255*YFAC)
47#define NODITHER_DELTA (127*YFAC)
48#define COMPONENT_SHIFT 15
49#define MATRIX_SHIFT 7
50
51static inline int clamp_component_bits(int x, int bits)
52{
53 if ((unsigned)x > (1u << bits) - 1)
54 x = x < 0 ? 0 : (1 << bits) - 1;
55 return x;
56}
57
58static inline int component_to_lcd(int x, int bits, int delta)
59{
60 /* Formula used in core bitmap loader. */
61 return (((1 << bits) - 1)*x + (x >> (8 - bits)) + delta) >> COMPONENT_SHIFT;
62}
63
64static inline int lcd_to_component(int x, int bits, int delta)
65{
66 /* Reasonable, approximate reversal to get a full range back from the
67 quantized value. */
68 return YUV_WHITE*x / ((1 << bits) - 1);
69 (void)delta;
70}
71
72#define RED 0
73#define GRN 1
74#define BLU 2
75
76struct rgb_err
77{
78 int16_t errbuf[LCD_WIDTH+2]; /* Error record for line below */
79} rgb_err_buffers[3];
80
81struct rgb_pixel
82{
83 int r, g, b; /* Current pixel components in s16.0 */
84 int inc; /* Current line increment (-1 or 1) */
85 int row; /* Current row in source image */
86 int col; /* Current column in source image */
87 int ce[3]; /* Errors to apply to current pixel */
88 struct rgb_err *e; /* RED, GRN, BLU */
89 int epos; /* Current position in error record */
90};
91
92struct rgb_pixel *pixel;
93
94/** round and truncate to lcd depth **/
95static fb_data pixel_to_lcd_colour(void)
96{
97 struct rgb_pixel *p = pixel;
98 int r, g, b;
99
100 r = component_to_lcd(p->r, LCD_RED_BITS, NODITHER_DELTA);
101 r = clamp_component_bits(r, LCD_RED_BITS);
102
103 g = component_to_lcd(p->g, LCD_GREEN_BITS, NODITHER_DELTA);
104 g = clamp_component_bits(g, LCD_GREEN_BITS);
105
106 b = component_to_lcd(p->b, LCD_BLUE_BITS, NODITHER_DELTA);
107 b = clamp_component_bits(b, LCD_BLUE_BITS);
108
109 return LCD_RGBPACK_LCD(r, g, b);
110}
111
112/** write a monochrome pixel to the colour LCD **/
113static fb_data pixel_to_lcd_gray(void)
114{
115 int r, g, b;
116
117 g = clamp_component(pixel->g);
118 r = component_to_lcd(g, LCD_RED_BITS, NODITHER_DELTA);
119 b = component_to_lcd(g, LCD_BLUE_BITS, NODITHER_DELTA);
120 g = component_to_lcd(g, LCD_GREEN_BITS, NODITHER_DELTA);
121
122 return LCD_RGBPACK_LCD(r, g, b);
123}
124
125/**
126 * Bayer ordered dithering - swiped from the core bitmap loader.
127 */
128static fb_data pixel_odither_to_lcd(void)
129{
130 /* canonical ordered dither matrix */
131 static const unsigned char dither_matrix[16][16] = {
132 { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
133 { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
134 { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
135 { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
136 { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
137 { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
138 { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
139 { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
140 { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
141 { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
142 { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
143 { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
144 { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
145 { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
146 { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
147 { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
148 };
149
150 struct rgb_pixel *p = pixel;
151 int r, g, b, delta;
152
153 delta = dither_matrix[p->col & 15][p->row & 15] << MATRIX_SHIFT;
154
155 r = component_to_lcd(p->r, LCD_RED_BITS, delta);
156 r = clamp_component_bits(r, LCD_RED_BITS);
157
158 g = component_to_lcd(p->g, LCD_GREEN_BITS, delta);
159 g = clamp_component_bits(g, LCD_GREEN_BITS);
160
161 b = component_to_lcd(p->b, LCD_BLUE_BITS, delta);
162 b = clamp_component_bits(b, LCD_BLUE_BITS);
163
164 p->col += p->inc;
165
166 return LCD_RGBPACK_LCD(r, g, b);
167}
168
169/**
170 * Floyd/Steinberg dither to lcd depth.
171 *
172 * Apply filter to each component in serpentine pattern. Kernel shown for
173 * L->R scan. Kernel is reversed for R->L.
174 * * 7
175 * 3 5 1 (1/16)
176 */
177static inline void distribute_error(int *ce, struct rgb_err *e,
178 int err, int epos, int inc)
179{
180 *ce = (7*err >> 4) + e->errbuf[epos+inc];
181 e->errbuf[epos+inc] = err >> 4;
182 e->errbuf[epos] += 5*err >> 4;
183 e->errbuf[epos-inc] += 3*err >> 4;
184}
185
186static fb_data pixel_fsdither_to_lcd(void)
187{
188 struct rgb_pixel *p = pixel;
189 int rc, gc, bc, r, g, b;
190 int inc, epos;
191
192 /* Full components with error terms */
193 rc = p->r + p->ce[RED];
194 r = component_to_lcd(rc, LCD_RED_BITS, 0);
195 r = clamp_component_bits(r, LCD_RED_BITS);
196
197 gc = p->g + p->ce[GRN];
198 g = component_to_lcd(gc, LCD_GREEN_BITS, 0);
199 g = clamp_component_bits(g, LCD_GREEN_BITS);
200
201 bc = p->b + p->ce[BLU];
202 b = component_to_lcd(bc, LCD_BLUE_BITS, 0);
203 b = clamp_component_bits(b, LCD_BLUE_BITS);
204
205 /* Get pixel errors */
206 rc -= lcd_to_component(r, LCD_RED_BITS, 0);
207 gc -= lcd_to_component(g, LCD_GREEN_BITS, 0);
208 bc -= lcd_to_component(b, LCD_BLUE_BITS, 0);
209
210 /* Spead error to surrounding pixels. */
211 inc = p->inc;
212 epos = p->epos;
213 p->epos += inc;
214
215 distribute_error(&p->ce[RED], &p->e[RED], rc, epos, inc);
216 distribute_error(&p->ce[GRN], &p->e[GRN], gc, epos, inc);
217 distribute_error(&p->ce[BLU], &p->e[BLU], bc, epos, inc);
218
219 /* Pack and return pixel */
220 return LCD_RGBPACK_LCD(r, g, b);
221}
222
223/* Functions for each output mode, colour then grayscale. */
224static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) =
225{
226 [COLOURMODE_COLOUR] =
227 {
228 [DITHER_NONE] = pixel_to_lcd_colour,
229 [DITHER_ORDERED] = pixel_odither_to_lcd,
230 [DITHER_DIFFUSION] = pixel_fsdither_to_lcd,
231 },
232 [COLOURMODE_GRAY] =
233 {
234 [DITHER_NONE] = pixel_to_lcd_gray,
235 [DITHER_ORDERED] = pixel_odither_to_lcd,
236 [DITHER_DIFFUSION] = pixel_fsdither_to_lcd,
237 },
238};
239
240/* These defines are used fornormal horizontal strides and vertical strides. */
241#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
242#define LCDADDR(x, y) (rb->lcd_framebuffer + LCD_HEIGHT*(x) + (y))
243#define ROWENDOFFSET (width*LCD_HEIGHT)
244#define ROWOFFSET (1)
245#define COLOFFSET (LCD_HEIGHT)
246#else
247#define LCDADDR(x, y) (rb->lcd_framebuffer + LCD_WIDTH*(y) + (x))
248#define ROWENDOFFSET (width)
249#define ROWOFFSET (LCD_WIDTH)
250#define COLOFFSET (1)
251#endif
252
253/**
254 * Draw a partial YUV colour bitmap
255 *
256 * Runs serpentine pattern when dithering is DITHER_DIFFUSION, else scan is
257 * always L->R.
258 */
259void yuv_bitmap_part(unsigned char *src[3], int csub_x, int csub_y,
260 int src_x, int src_y, int stride,
261 int x, int y, int width, int height,
262 int colour_mode, int dither_mode)
263{
264 fb_data *dst, *dst_end;
265 fb_data (*pixel_func)(void);
266 struct rgb_pixel px;
267 int dst_inc;
268
269 if (x + width > LCD_WIDTH)
270 width = LCD_WIDTH - x; /* Clip right */
271 if (x < 0)
272 width += x, x = 0; /* Clip left */
273 if (width <= 0)
274 return; /* nothing left to do */
275
276 if (y + height > LCD_HEIGHT)
277 height = LCD_HEIGHT - y; /* Clip bottom */
278 if (y < 0)
279 height += y, y = 0; /* Clip top */
280 if (height <= 0)
281 return; /* nothing left to do */
282
283 pixel = &px;
284
285 dst = LCDADDR(x, y);
286 dst_end = LCDADDR(x, y+height);
287
288 if (colour_mode == COLOURMODE_GRAY)
289 csub_y = 0; /* Ignore Cb, Cr */
290
291 pixel_func = pixel_funcs[colour_mode]
292 [dither_mode];
293
294 if (dither_mode == DITHER_DIFFUSION)
295 {
296 /* Reset error terms. */
297 px.e = rgb_err_buffers;
298 px.ce[RED] = px.ce[GRN] = px.ce[BLU] = 0;
299 rb->memset(px.e, 0, 3*sizeof (struct rgb_err));
300 }
301
302 do
303 {
304 fb_data *dst_row, *row_end;
305 const unsigned char *ysrc;
306 px.inc = 1;
307
308 if (dither_mode == DITHER_DIFFUSION)
309 {
310 /* Use R->L scan on odd lines */
311 px.inc -= (src_y & 1) << 1;
312 px.epos = x + 1;
313
314 if (px.inc < 0)
315 px.epos += width - 1;
316 }
317
318 if (px.inc == 1)
319 {
320 /* Scan is L->R */
321 dst_inc = COLOFFSET;
322 dst_row = dst;
323 row_end = dst_row + ROWENDOFFSET;
324 px.col = src_x;
325 }
326 else
327 {
328 /* Scan is R->L */
329 dst_inc = -COLOFFSET;
330 row_end = dst + dst_inc;
331 dst_row = row_end + ROWENDOFFSET;
332 px.col = src_x + width - 1;
333 }
334
335 ysrc = src[0] + stride * src_y + px.col;
336 px.row = src_y;
337
338 /* Do one row of pixels */
339 if (csub_y) /* colour */
340 {
341 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
342 const unsigned char *usrc, *vsrc;
343
344 usrc = src[1] + (stride/csub_x) * (src_y/csub_y)
345 + (px.col/csub_x);
346 vsrc = src[2] + (stride/csub_x) * (src_y/csub_y)
347 + (px.col/csub_x);
348 int xphase = px.col % csub_x;
349 int xphase_reset = px.inc * csub_x;
350 int y, v, u, rv, guv, bu;
351
352 v = *vsrc - 128;
353 vsrc += px.inc;
354 u = *usrc - 128;
355 usrc += px.inc;
356 rv = RVFAC*v;
357 guv = GUFAC*u + GVFAC*v;
358 bu = BUFAC*u;
359
360 while (1)
361 {
362 y = YFAC*(*ysrc);
363 ysrc += px.inc;
364 px.r = y + rv;
365 px.g = y + guv;
366 px.b = y + bu;
367
368 *dst_row = pixel_func();
369 dst_row += dst_inc;
370
371 if (dst_row == row_end)
372 break;
373
374 xphase += px.inc;
375 if ((unsigned)xphase < (unsigned)csub_x)
376 continue;
377
378 /* fetch new chromas */
379 v = *vsrc - 128;
380 vsrc += px.inc;
381 u = *usrc - 128;
382 usrc += px.inc;
383 rv = RVFAC*v;
384 guv = GUFAC*u + GVFAC*v;
385 bu = BUFAC*u;
386
387 xphase -= xphase_reset;
388 }
389 }
390 else /* monochrome */
391 {
392 do
393 {
394 /* Set all components the same for dithering purposes */
395 px.g = px.r = px.b = YFAC*(*ysrc);
396 *dst_row = pixel_func();
397 ysrc += px.inc;
398 dst_row += dst_inc;
399 }
400 while (dst_row != row_end);
401 }
402
403 src_y++;
404 dst += ROWOFFSET;
405 }
406 while (dst < dst_end);
407}