diff options
Diffstat (limited to 'apps/plugins/imageviewer/jpeg/yuv2rgb.c')
-rw-r--r-- | apps/plugins/imageviewer/jpeg/yuv2rgb.c | 407 |
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 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* JPEG image viewer +* (This is a real mess if it has to be coded in one single C file) +* +* File scrolling addition (C) 2005 Alexander Spyridakis +* Copyright (C) 2004 Jörg Hohensohn aka [IDC]Dragon +* Heavily borrowed from the IJG implementation (C) Thomas G. Lane +* Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ + +#include "plugin.h" +#include "yuv2rgb.h" + +/* + * Conversion of full 0-255 range YCrCb to RGB: + * |R| |1.000000 -0.000001 1.402000| |Y'| + * |G| = |1.000000 -0.334136 -0.714136| |Pb| + * |B| |1.000000 1.772000 0.000000| |Pr| + * Scaled (yields s15-bit output): + * |R| |128 0 179| |Y | + * |G| = |128 -43 -91| |Cb - 128| + * |B| |128 227 0| |Cr - 128| + */ +#define YFAC 128 +#define RVFAC 179 +#define GUFAC (-43) +#define GVFAC (-91) +#define BUFAC 227 +#define YUV_WHITE (255*YFAC) +#define NODITHER_DELTA (127*YFAC) +#define COMPONENT_SHIFT 15 +#define MATRIX_SHIFT 7 + +static inline int clamp_component_bits(int x, int bits) +{ + if ((unsigned)x > (1u << bits) - 1) + x = x < 0 ? 0 : (1 << bits) - 1; + return x; +} + +static inline int component_to_lcd(int x, int bits, int delta) +{ + /* Formula used in core bitmap loader. */ + return (((1 << bits) - 1)*x + (x >> (8 - bits)) + delta) >> COMPONENT_SHIFT; +} + +static inline int lcd_to_component(int x, int bits, int delta) +{ + /* Reasonable, approximate reversal to get a full range back from the + quantized value. */ + return YUV_WHITE*x / ((1 << bits) - 1); + (void)delta; +} + +#define RED 0 +#define GRN 1 +#define BLU 2 + +struct rgb_err +{ + int16_t errbuf[LCD_WIDTH+2]; /* Error record for line below */ +} rgb_err_buffers[3]; + +struct rgb_pixel +{ + int r, g, b; /* Current pixel components in s16.0 */ + int inc; /* Current line increment (-1 or 1) */ + int row; /* Current row in source image */ + int col; /* Current column in source image */ + int ce[3]; /* Errors to apply to current pixel */ + struct rgb_err *e; /* RED, GRN, BLU */ + int epos; /* Current position in error record */ +}; + +struct rgb_pixel *pixel; + +/** round and truncate to lcd depth **/ +static fb_data pixel_to_lcd_colour(void) +{ + struct rgb_pixel *p = pixel; + int r, g, b; + + r = component_to_lcd(p->r, LCD_RED_BITS, NODITHER_DELTA); + r = clamp_component_bits(r, LCD_RED_BITS); + + g = component_to_lcd(p->g, LCD_GREEN_BITS, NODITHER_DELTA); + g = clamp_component_bits(g, LCD_GREEN_BITS); + + b = component_to_lcd(p->b, LCD_BLUE_BITS, NODITHER_DELTA); + b = clamp_component_bits(b, LCD_BLUE_BITS); + + return LCD_RGBPACK_LCD(r, g, b); +} + +/** write a monochrome pixel to the colour LCD **/ +static fb_data pixel_to_lcd_gray(void) +{ + int r, g, b; + + g = clamp_component(pixel->g); + r = component_to_lcd(g, LCD_RED_BITS, NODITHER_DELTA); + b = component_to_lcd(g, LCD_BLUE_BITS, NODITHER_DELTA); + g = component_to_lcd(g, LCD_GREEN_BITS, NODITHER_DELTA); + + return LCD_RGBPACK_LCD(r, g, b); +} + +/** + * Bayer ordered dithering - swiped from the core bitmap loader. + */ +static fb_data pixel_odither_to_lcd(void) +{ + /* canonical ordered dither matrix */ + static const unsigned char dither_matrix[16][16] = { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + }; + + struct rgb_pixel *p = pixel; + int r, g, b, delta; + + delta = dither_matrix[p->col & 15][p->row & 15] << MATRIX_SHIFT; + + r = component_to_lcd(p->r, LCD_RED_BITS, delta); + r = clamp_component_bits(r, LCD_RED_BITS); + + g = component_to_lcd(p->g, LCD_GREEN_BITS, delta); + g = clamp_component_bits(g, LCD_GREEN_BITS); + + b = component_to_lcd(p->b, LCD_BLUE_BITS, delta); + b = clamp_component_bits(b, LCD_BLUE_BITS); + + p->col += p->inc; + + return LCD_RGBPACK_LCD(r, g, b); +} + +/** + * Floyd/Steinberg dither to lcd depth. + * + * Apply filter to each component in serpentine pattern. Kernel shown for + * L->R scan. Kernel is reversed for R->L. + * * 7 + * 3 5 1 (1/16) + */ +static inline void distribute_error(int *ce, struct rgb_err *e, + int err, int epos, int inc) +{ + *ce = (7*err >> 4) + e->errbuf[epos+inc]; + e->errbuf[epos+inc] = err >> 4; + e->errbuf[epos] += 5*err >> 4; + e->errbuf[epos-inc] += 3*err >> 4; +} + +static fb_data pixel_fsdither_to_lcd(void) +{ + struct rgb_pixel *p = pixel; + int rc, gc, bc, r, g, b; + int inc, epos; + + /* Full components with error terms */ + rc = p->r + p->ce[RED]; + r = component_to_lcd(rc, LCD_RED_BITS, 0); + r = clamp_component_bits(r, LCD_RED_BITS); + + gc = p->g + p->ce[GRN]; + g = component_to_lcd(gc, LCD_GREEN_BITS, 0); + g = clamp_component_bits(g, LCD_GREEN_BITS); + + bc = p->b + p->ce[BLU]; + b = component_to_lcd(bc, LCD_BLUE_BITS, 0); + b = clamp_component_bits(b, LCD_BLUE_BITS); + + /* Get pixel errors */ + rc -= lcd_to_component(r, LCD_RED_BITS, 0); + gc -= lcd_to_component(g, LCD_GREEN_BITS, 0); + bc -= lcd_to_component(b, LCD_BLUE_BITS, 0); + + /* Spead error to surrounding pixels. */ + inc = p->inc; + epos = p->epos; + p->epos += inc; + + distribute_error(&p->ce[RED], &p->e[RED], rc, epos, inc); + distribute_error(&p->ce[GRN], &p->e[GRN], gc, epos, inc); + distribute_error(&p->ce[BLU], &p->e[BLU], bc, epos, inc); + + /* Pack and return pixel */ + return LCD_RGBPACK_LCD(r, g, b); +} + +/* Functions for each output mode, colour then grayscale. */ +static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) = +{ + [COLOURMODE_COLOUR] = + { + [DITHER_NONE] = pixel_to_lcd_colour, + [DITHER_ORDERED] = pixel_odither_to_lcd, + [DITHER_DIFFUSION] = pixel_fsdither_to_lcd, + }, + [COLOURMODE_GRAY] = + { + [DITHER_NONE] = pixel_to_lcd_gray, + [DITHER_ORDERED] = pixel_odither_to_lcd, + [DITHER_DIFFUSION] = pixel_fsdither_to_lcd, + }, +}; + +/* These defines are used fornormal horizontal strides and vertical strides. */ +#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE +#define LCDADDR(x, y) (rb->lcd_framebuffer + LCD_HEIGHT*(x) + (y)) +#define ROWENDOFFSET (width*LCD_HEIGHT) +#define ROWOFFSET (1) +#define COLOFFSET (LCD_HEIGHT) +#else +#define LCDADDR(x, y) (rb->lcd_framebuffer + LCD_WIDTH*(y) + (x)) +#define ROWENDOFFSET (width) +#define ROWOFFSET (LCD_WIDTH) +#define COLOFFSET (1) +#endif + +/** + * Draw a partial YUV colour bitmap + * + * Runs serpentine pattern when dithering is DITHER_DIFFUSION, else scan is + * always L->R. + */ +void yuv_bitmap_part(unsigned char *src[3], int csub_x, int csub_y, + int src_x, int src_y, int stride, + int x, int y, int width, int height, + int colour_mode, int dither_mode) +{ + fb_data *dst, *dst_end; + fb_data (*pixel_func)(void); + struct rgb_pixel px; + int dst_inc; + + if (x + width > LCD_WIDTH) + width = LCD_WIDTH - x; /* Clip right */ + if (x < 0) + width += x, x = 0; /* Clip left */ + if (width <= 0) + return; /* nothing left to do */ + + if (y + height > LCD_HEIGHT) + height = LCD_HEIGHT - y; /* Clip bottom */ + if (y < 0) + height += y, y = 0; /* Clip top */ + if (height <= 0) + return; /* nothing left to do */ + + pixel = &px; + + dst = LCDADDR(x, y); + dst_end = LCDADDR(x, y+height); + + if (colour_mode == COLOURMODE_GRAY) + csub_y = 0; /* Ignore Cb, Cr */ + + pixel_func = pixel_funcs[colour_mode] + [dither_mode]; + + if (dither_mode == DITHER_DIFFUSION) + { + /* Reset error terms. */ + px.e = rgb_err_buffers; + px.ce[RED] = px.ce[GRN] = px.ce[BLU] = 0; + rb->memset(px.e, 0, 3*sizeof (struct rgb_err)); + } + + do + { + fb_data *dst_row, *row_end; + const unsigned char *ysrc; + px.inc = 1; + + if (dither_mode == DITHER_DIFFUSION) + { + /* Use R->L scan on odd lines */ + px.inc -= (src_y & 1) << 1; + px.epos = x + 1; + + if (px.inc < 0) + px.epos += width - 1; + } + + if (px.inc == 1) + { + /* Scan is L->R */ + dst_inc = COLOFFSET; + dst_row = dst; + row_end = dst_row + ROWENDOFFSET; + px.col = src_x; + } + else + { + /* Scan is R->L */ + dst_inc = -COLOFFSET; + row_end = dst + dst_inc; + dst_row = row_end + ROWENDOFFSET; + px.col = src_x + width - 1; + } + + ysrc = src[0] + stride * src_y + px.col; + px.row = src_y; + + /* Do one row of pixels */ + if (csub_y) /* colour */ + { + /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */ + const unsigned char *usrc, *vsrc; + + usrc = src[1] + (stride/csub_x) * (src_y/csub_y) + + (px.col/csub_x); + vsrc = src[2] + (stride/csub_x) * (src_y/csub_y) + + (px.col/csub_x); + int xphase = px.col % csub_x; + int xphase_reset = px.inc * csub_x; + int y, v, u, rv, guv, bu; + + v = *vsrc - 128; + vsrc += px.inc; + u = *usrc - 128; + usrc += px.inc; + rv = RVFAC*v; + guv = GUFAC*u + GVFAC*v; + bu = BUFAC*u; + + while (1) + { + y = YFAC*(*ysrc); + ysrc += px.inc; + px.r = y + rv; + px.g = y + guv; + px.b = y + bu; + + *dst_row = pixel_func(); + dst_row += dst_inc; + + if (dst_row == row_end) + break; + + xphase += px.inc; + if ((unsigned)xphase < (unsigned)csub_x) + continue; + + /* fetch new chromas */ + v = *vsrc - 128; + vsrc += px.inc; + u = *usrc - 128; + usrc += px.inc; + rv = RVFAC*v; + guv = GUFAC*u + GVFAC*v; + bu = BUFAC*u; + + xphase -= xphase_reset; + } + } + else /* monochrome */ + { + do + { + /* Set all components the same for dithering purposes */ + px.g = px.r = px.b = YFAC*(*ysrc); + *dst_row = pixel_func(); + ysrc += px.inc; + dst_row += dst_inc; + } + while (dst_row != row_end); + } + + src_y++; + dst += ROWOFFSET; + } + while (dst < dst_end); +} |