summaryrefslogtreecommitdiff
path: root/apps/plugins/jpeg.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-04-01 00:41:17 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-04-01 00:41:17 +0000
commit9f2b5cce840102f68f93d4de3380075913def3f3 (patch)
treeca880f51921192a0ac242fc9f91b2c44133cf105 /apps/plugins/jpeg.c
parentc26a6bb57a8e6d324122bc74dbc15ebc931c0529 (diff)
downloadrockbox-9f2b5cce840102f68f93d4de3380075913def3f3.tar.gz
rockbox-9f2b5cce840102f68f93d4de3380075913def3f3.zip
JPEG Viewer on color LCDs: Finish up another old project. Add grayscale display option. Add ordered and diffusion dither modes to output. These options are under 'Display Options' in the viewer menu and are persisted in jpeg.cfg. Default setting is color display without dithering as before.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12987 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/jpeg.c')
-rw-r--r--apps/plugins/jpeg.c625
1 files changed, 507 insertions, 118 deletions
diff --git a/apps/plugins/jpeg.c b/apps/plugins/jpeg.c
index dbe7270d18..a330748404 100644
--- a/apps/plugins/jpeg.c
+++ b/apps/plugins/jpeg.c
@@ -31,6 +31,10 @@
31#include "gray.h" 31#include "gray.h"
32#include "xlcd.h" 32#include "xlcd.h"
33 33
34#ifdef HAVE_LCD_COLOR
35#include "lib/configfile.h"
36#endif
37
34PLUGIN_HEADER 38PLUGIN_HEADER
35 39
36/* variable button definitions */ 40/* variable button definitions */
@@ -192,7 +196,49 @@ static int running_slideshow = false; /* loading image because of slideshw */
192#ifndef SIMULATOR 196#ifndef SIMULATOR
193static int immediate_ata_off = false; /* power down disk after loading */ 197static int immediate_ata_off = false; /* power down disk after loading */
194#endif 198#endif
195static int button_timeout = HZ*5; 199static int button_timeout = HZ*5;
200
201#ifdef HAVE_LCD_COLOR
202
203/* Persistent configuration - only needed for color displays atm */
204#define JPEG_CONFIGFILE "jpeg.cfg"
205#define JPEG_SETTINGS_MINVERSION 1
206#define JPEG_SETTINGS_VERSION 1
207
208enum color_modes
209{
210 COLOURMODE_COLOUR = 0,
211 COLOURMODE_GRAY,
212 COLOUR_NUM_MODES
213};
214
215enum dither_modes
216{
217 DITHER_NONE = 0, /* No dithering */
218 DITHER_ORDERED, /* Bayer ordered */
219 DITHER_DIFFUSION, /* Floyd/Steinberg error diffusion */
220 DITHER_NUM_MODES
221};
222
223struct jpeg_settings
224{
225 int colour_mode;
226 int dither_mode;
227};
228
229static struct jpeg_settings jpeg_settings =
230 { COLOURMODE_COLOUR, DITHER_NONE };
231static struct jpeg_settings old_settings;
232
233static struct configdata jpeg_config[] =
234{
235 { TYPE_ENUM, 0, COLOUR_NUM_MODES, &jpeg_settings.colour_mode,
236 "Colour Mode", (char *[]){ "Colour", "Grayscale" }, NULL },
237 { TYPE_ENUM, 0, DITHER_NUM_MODES, &jpeg_settings.dither_mode,
238 "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" }, NULL },
239};
240
241#endif /* HAVE_LCD_COLOR */
196#if LCD_DEPTH > 1 242#if LCD_DEPTH > 1
197fb_data* old_backdrop; 243fb_data* old_backdrop;
198#endif 244#endif
@@ -1852,143 +1898,369 @@ bool plug_buf = false;
1852/************************* Implementation ***************************/ 1898/************************* Implementation ***************************/
1853 1899
1854#ifdef HAVE_LCD_COLOR 1900#ifdef HAVE_LCD_COLOR
1901/*
1902 * Conversion of full 0-255 range YCrCb to RGB:
1903 * |R| |1.000000 -0.000001 1.402000| |Y'|
1904 * |G| = |1.000000 -0.334136 -0.714136| |Pb|
1905 * |B| |1.000000 1.772000 0.000000| |Pr|
1906 * Scaled (yields s15-bit output):
1907 * |R| |128 0 179| |Y |
1908 * |G| = |128 -43 -91| |Cb - 128|
1909 * |B| |128 227 0| |Cr - 128|
1910 */
1911#define YFAC 128
1912#define RVFAC 179
1913#define GUFAC (-43)
1914#define GVFAC (-91)
1915#define BUFAC 227
1916#define YUV_WHITE (255*YFAC)
1917#define NODITHER_DELTA (127*YFAC)
1918#define COMPONENT_SHIFT 15
1919#define MATRIX_SHIFT 7
1920
1921static inline int clamp_component(int x)
1922{
1923 if ((unsigned)x > YUV_WHITE)
1924 x = x < 0 ? 0 : YUV_WHITE;
1925 return x;
1926}
1855 1927
1856#if (LCD_DEPTH == 16) && \ 1928static inline int clamp_component_bits(int x, int bits)
1857 ((LCD_PIXELFORMAT == RGB565) || (LCD_PIXELFORMAT == RGB565SWAPPED)) 1929{
1858#define RYFAC (31*257) 1930 if ((unsigned)x > (1u << bits) - 1)
1859#define GYFAC (63*257) 1931 x = x < 0 ? 0 : (1 << bits) - 1;
1860#define BYFAC (31*257) 1932 return x;
1861#define RVFAC 11170 /* 31 * 257 * 1.402 */ 1933}
1862#define GVFAC (-11563) /* 63 * 257 * -0.714136 */ 1934
1863#define GUFAC (-5572) /* 63 * 257 * -0.344136 */ 1935static inline int component_to_lcd(int x, int bits, int delta)
1864#define BUFAC 14118 /* 31 * 257 * 1.772 */ 1936{
1865#endif 1937 /* Formula used in core bitmap loader. */
1938 return (((1 << bits) - 1)*x + (x >> (8 - bits)) + delta) >> COMPONENT_SHIFT;
1939}
1940
1941static inline int lcd_to_component(int x, int bits, int delta)
1942{
1943 /* Reasonable, approximate reversal to get a full range back from the
1944 quantized value. */
1945 return YUV_WHITE*x / ((1 << bits) - 1);
1946 (void)delta;
1947}
1948
1949#define RED 0
1950#define GRN 1
1951#define BLU 2
1952
1953struct rgb_err
1954{
1955 int16_t errbuf[LCD_WIDTH+2]; /* Error record for line below */
1956} rgb_err_buffers[3];
1957
1958fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when
1959 DITHER_DIFFUSION is set */
1960
1961struct rgb_pixel
1962{
1963 int r, g, b; /* Current pixel components in s16.0 */
1964 int inc; /* Current line increment (-1 or 1) */
1965 int row; /* Current row in source image */
1966 int col; /* Current column in source image */
1967 int ce[3]; /* Errors to apply to current pixel */
1968 struct rgb_err *e; /* RED, GRN, BLU */
1969 int epos; /* Current position in error record */
1970};
1971
1972struct rgb_pixel *pixel;
1973
1974/** round and truncate to lcd depth **/
1975static fb_data pixel_to_lcd_colour(void)
1976{
1977 struct rgb_pixel *p = pixel;
1978 int r, g, b;
1979
1980 r = component_to_lcd(p->r, LCD_RED_BITS, NODITHER_DELTA);
1981 r = clamp_component_bits(r, LCD_RED_BITS);
1866 1982
1867#define ROUNDOFFS (127*257) 1983 g = component_to_lcd(p->g, LCD_GREEN_BITS, NODITHER_DELTA);
1984 g = clamp_component_bits(g, LCD_GREEN_BITS);
1868 1985
1869/* Draw a partial YUV colour bitmap */ 1986 b = component_to_lcd(p->b, LCD_BLUE_BITS, NODITHER_DELTA);
1987 b = clamp_component_bits(b, LCD_BLUE_BITS);
1988
1989 return LCD_RGBPACK_LCD(r, g, b);
1990}
1991
1992/** write a monochrome pixel to the colour LCD **/
1993static fb_data pixel_to_lcd_gray(void)
1994{
1995 int r, g, b;
1996
1997 g = clamp_component(pixel->g);
1998 r = component_to_lcd(g, LCD_RED_BITS, NODITHER_DELTA);
1999 b = component_to_lcd(g, LCD_BLUE_BITS, NODITHER_DELTA);
2000 g = component_to_lcd(g, LCD_GREEN_BITS, NODITHER_DELTA);
2001
2002 return LCD_RGBPACK_LCD(r, g, b);
2003}
2004
2005/**
2006 * Bayer ordered dithering - swiped from the core bitmap loader.
2007 */
2008static fb_data pixel_odither_to_lcd(void)
2009{
2010 /* canonical ordered dither matrix */
2011 static const unsigned char dither_matrix[16][16] = {
2012 { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
2013 { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
2014 { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
2015 { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
2016 { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
2017 { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
2018 { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
2019 { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
2020 { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
2021 { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
2022 { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
2023 { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
2024 { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
2025 { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
2026 { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
2027 { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
2028 };
2029
2030 struct rgb_pixel *p = pixel;
2031 int r, g, b, delta;
2032
2033 delta = dither_matrix[p->col & 15][p->row & 15] << MATRIX_SHIFT;
2034
2035 r = component_to_lcd(p->r, LCD_RED_BITS, delta);
2036 r = clamp_component_bits(r, LCD_RED_BITS);
2037
2038 g = component_to_lcd(p->g, LCD_GREEN_BITS, delta);
2039 g = clamp_component_bits(g, LCD_GREEN_BITS);
2040
2041 b = component_to_lcd(p->b, LCD_BLUE_BITS, delta);
2042 b = clamp_component_bits(b, LCD_BLUE_BITS);
2043
2044 p->col += p->inc;
2045
2046 return LCD_RGBPACK_LCD(r, g, b);
2047}
2048
2049/**
2050 * Floyd/Steinberg dither to lcd depth.
2051 *
2052 * Apply filter to each component in serpentine pattern. Kernel shown for
2053 * L->R scan. Kernel is reversed for R->L.
2054 * * 7
2055 * 3 5 1 (1/16)
2056 */
2057static inline void distribute_error(int *ce, struct rgb_err *e,
2058 int err, int epos, int inc)
2059{
2060 *ce = (7*err >> 4) + e->errbuf[epos+inc];
2061 e->errbuf[epos+inc] = err >> 4;
2062 e->errbuf[epos] += 5*err >> 4;
2063 e->errbuf[epos-inc] += 3*err >> 4;
2064}
2065
2066static fb_data pixel_fsdither_to_lcd(void)
2067{
2068 struct rgb_pixel *p = pixel;
2069 int rc, gc, bc, r, g, b;
2070 int inc, epos;
2071
2072 /* Full components with error terms */
2073 rc = p->r + p->ce[RED];
2074 r = component_to_lcd(rc, LCD_RED_BITS, 0);
2075 r = clamp_component_bits(r, LCD_RED_BITS);
2076
2077 gc = p->g + p->ce[GRN];
2078 g = component_to_lcd(gc, LCD_GREEN_BITS, 0);
2079 g = clamp_component_bits(g, LCD_GREEN_BITS);
2080
2081 bc = p->b + p->ce[BLU];
2082 b = component_to_lcd(bc, LCD_BLUE_BITS, 0);
2083 b = clamp_component_bits(b, LCD_BLUE_BITS);
2084
2085 /* Get pixel errors */
2086 rc -= lcd_to_component(r, LCD_RED_BITS, 0);
2087 gc -= lcd_to_component(g, LCD_GREEN_BITS, 0);
2088 bc -= lcd_to_component(b, LCD_BLUE_BITS, 0);
2089
2090 /* Spead error to surrounding pixels. */
2091 inc = p->inc;
2092 epos = p->epos;
2093 p->epos += inc;
2094
2095 distribute_error(&p->ce[RED], &p->e[RED], rc, epos, inc);
2096 distribute_error(&p->ce[GRN], &p->e[GRN], gc, epos, inc);
2097 distribute_error(&p->ce[BLU], &p->e[BLU], bc, epos, inc);
2098
2099 /* Pack and return pixel */
2100 return LCD_RGBPACK_LCD(r, g, b);
2101}
2102
2103/* Functions for each output mode, colour then grayscale. */
2104static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) =
2105{
2106 [COLOURMODE_COLOUR] =
2107 {
2108 [DITHER_NONE] = pixel_to_lcd_colour,
2109 [DITHER_ORDERED] = pixel_odither_to_lcd,
2110 [DITHER_DIFFUSION] = pixel_fsdither_to_lcd,
2111 },
2112 [COLOURMODE_GRAY] =
2113 {
2114 [DITHER_NONE] = pixel_to_lcd_gray,
2115 [DITHER_ORDERED] = pixel_odither_to_lcd,
2116 [DITHER_DIFFUSION] = pixel_fsdither_to_lcd,
2117 },
2118};
2119
2120/**
2121 * Draw a partial YUV colour bitmap
2122 *
2123 * Runs serpentine pattern when dithering is DITHER_DIFFUSION, else scan is
2124 * always L->R.
2125 */
1870void yuv_bitmap_part(unsigned char *src[3], int csub_x, int csub_y, 2126void yuv_bitmap_part(unsigned char *src[3], int csub_x, int csub_y,
1871 int src_x, int src_y, int stride, 2127 int src_x, int src_y, int stride,
1872 int x, int y, int width, int height) 2128 int x, int y, int width, int height)
1873{ 2129{
1874 fb_data *dst, *dst_end; 2130 fb_data *dst, *dst_end;
2131 fb_data (*pixel_func)(void);
2132 struct rgb_pixel px;
1875 2133
1876 /* nothing to draw? */
1877 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
1878 || (x + width <= 0) || (y + height <= 0))
1879 return;
1880
1881 /* clipping */
1882 if (x < 0)
1883 {
1884 width += x;
1885 src_x -= x;
1886 x = 0;
1887 }
1888 if (y < 0)
1889 {
1890 height += y;
1891 src_y -= y;
1892 y = 0;
1893 }
1894 if (x + width > LCD_WIDTH) 2134 if (x + width > LCD_WIDTH)
1895 width = LCD_WIDTH - x; 2135 width = LCD_WIDTH - x; /* Clip right */
2136 if (x < 0)
2137 width += x, x = 0; /* Clip left */
2138 if (width <= 0)
2139 return; /* nothing left to do */
2140
1896 if (y + height > LCD_HEIGHT) 2141 if (y + height > LCD_HEIGHT)
1897 height = LCD_HEIGHT - y; 2142 height = LCD_HEIGHT - y; /* Clip bottom */
2143 if (y < 0)
2144 height += y, y = 0; /* Clip top */
2145 if (height <= 0)
2146 return; /* nothing left to do */
2147
2148 pixel = &px;
1898 2149
1899 dst = rb->lcd_framebuffer + LCD_WIDTH * y + x; 2150 dst = rb->lcd_framebuffer + LCD_WIDTH * y + x;
1900 dst_end = dst + LCD_WIDTH * height; 2151 dst_end = dst + LCD_WIDTH * height;
1901 2152
2153 if (jpeg_settings.colour_mode == COLOURMODE_GRAY)
2154 csub_y = 0; /* Ignore Cb, Cr */
2155
2156 pixel_func = pixel_funcs[jpeg_settings.colour_mode]
2157 [jpeg_settings.dither_mode];
2158
2159 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
2160 {
2161 /* Reset error terms. */
2162 px.e = rgb_err_buffers;
2163 px.ce[RED] = px.ce[GRN] = px.ce[BLU] = 0;
2164 rb->memset(px.e, 0, 3*sizeof (struct rgb_err));
2165 }
2166
1902 do 2167 do
1903 { 2168 {
1904 fb_data *dst_row = dst; 2169 fb_data *dst_row, *row_end;
1905 fb_data *row_end = dst_row + width; 2170 const unsigned char *ysrc;
1906 const unsigned char *ysrc = src[0] + stride * src_y + src_x; 2171 px.inc = 1;
1907 int y, u, v; 2172
1908 int red, green, blue; 2173 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
1909 unsigned rbits, gbits, bbits; 2174 {
2175 /* Use R->L scan on odd lines */
2176 px.inc -= (src_y & 1) << 1;
2177 px.epos = x + 1;
2178
2179 if (px.inc < 0)
2180 px.epos += width - 1;
2181 }
2182
2183 if (px.inc == 1)
2184 {
2185 /* Scan is L->R */
2186 dst_row = dst;
2187 row_end = dst_row + width;
2188 px.col = src_x;
2189 }
2190 else
2191 {
2192 /* Scan is R->L */
2193 row_end = dst - 1;
2194 dst_row = row_end + width;
2195 px.col = src_x + width - 1;
2196 }
1910 2197
2198 ysrc = src[0] + stride * src_y + px.col;
2199 px.row = src_y;
2200
2201 /* Do one row of pixels */
1911 if (csub_y) /* colour */ 2202 if (csub_y) /* colour */
1912 { 2203 {
1913 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */ 2204 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
1914 const unsigned char *usrc = src[1] + (stride/csub_x) * (src_y/csub_y) 2205 const unsigned char *usrc, *vsrc;
1915 + (src_x/csub_x); 2206
1916 const unsigned char *vsrc = src[2] + (stride/csub_x) * (src_y/csub_y) 2207 usrc = src[1] + (stride/csub_x) * (src_y/csub_y)
1917 + (src_x/csub_x); 2208 + (px.col/csub_x);
1918 int xphase = src_x % csub_x; 2209 vsrc = src[2] + (stride/csub_x) * (src_y/csub_y)
1919 int rc, gc, bc; 2210 + (px.col/csub_x);
1920 2211 int xphase = px.col % csub_x;
1921 u = *usrc++ - 128; 2212 int xphase_reset = px.inc * csub_x;
1922 v = *vsrc++ - 128; 2213 int y, v, u, rv, guv, bu;
1923 rc = RVFAC * v + ROUNDOFFS; 2214
1924 gc = GVFAC * v + GUFAC * u + ROUNDOFFS; 2215 v = *vsrc - 128;
1925 bc = BUFAC * u + ROUNDOFFS; 2216 vsrc += px.inc;
2217 u = *usrc - 128;
2218 usrc += px.inc;
2219 rv = RVFAC*v;
2220 guv = GUFAC*u + GVFAC*v;
2221 bu = BUFAC*u;
1926 2222
1927 do 2223 while (1)
1928 { 2224 {
1929 y = *ysrc++; 2225 y = YFAC*(*ysrc);
1930 red = RYFAC * y + rc; 2226 ysrc += px.inc;
1931 green = GYFAC * y + gc; 2227 px.r = y + rv;
1932 blue = BYFAC * y + bc; 2228 px.g = y + guv;
2229 px.b = y + bu;
1933 2230
1934 if ((unsigned)red > (RYFAC*255+ROUNDOFFS)) 2231 *dst_row = pixel_func();
1935 { 2232 dst_row += px.inc;
1936 if (red < 0)
1937 red = 0;
1938 else
1939 red = (RYFAC*255+ROUNDOFFS);
1940 }
1941 if ((unsigned)green > (GYFAC*255+ROUNDOFFS))
1942 {
1943 if (green < 0)
1944 green = 0;
1945 else
1946 green = (GYFAC*255+ROUNDOFFS);
1947 }
1948 if ((unsigned)blue > (BYFAC*255+ROUNDOFFS))
1949 {
1950 if (blue < 0)
1951 blue = 0;
1952 else
1953 blue = (BYFAC*255+ROUNDOFFS);
1954 }
1955 rbits = ((unsigned)red) >> 16 ;
1956 gbits = ((unsigned)green) >> 16 ;
1957 bbits = ((unsigned)blue) >> 16 ;
1958#if LCD_PIXELFORMAT == RGB565
1959 *dst_row++ = (rbits << 11) | (gbits << 5) | bbits;
1960#elif LCD_PIXELFORMAT == RGB565SWAPPED
1961 *dst_row++ = swap16((rbits << 11) | (gbits << 5) | bbits);
1962#endif
1963 2233
1964 if (++xphase >= csub_x) 2234 if (dst_row == row_end)
1965 { 2235 break;
1966 u = *usrc++ - 128; 2236
1967 v = *vsrc++ - 128; 2237 xphase += px.inc;
1968 rc = RVFAC * v + ROUNDOFFS; 2238 if ((unsigned)xphase < (unsigned)csub_x)
1969 gc = GVFAC * v + GUFAC * u + ROUNDOFFS; 2239 continue;
1970 bc = BUFAC * u + ROUNDOFFS; 2240
1971 xphase = 0; 2241 /* fetch new chromas */
1972 } 2242 v = *vsrc - 128;
2243 vsrc += px.inc;
2244 u = *usrc - 128;
2245 usrc += px.inc;
2246 rv = RVFAC*v;
2247 guv = GUFAC*u + GVFAC*v;
2248 bu = BUFAC*u;
2249
2250 xphase -= xphase_reset;
1973 } 2251 }
1974 while (dst_row < row_end);
1975 } 2252 }
1976 else /* monochrome */ 2253 else /* monochrome */
1977 { 2254 {
1978 do 2255 do
1979 { 2256 {
1980 y = *ysrc++; 2257 /* Set all components the same for dithering purposes */
1981 red = RYFAC * y + ROUNDOFFS; /* blue == red */ 2258 px.g = px.r = px.b = YFAC*(*ysrc);
1982 green = GYFAC * y + ROUNDOFFS; 2259 *dst_row = pixel_func();
1983 rbits = ((unsigned)red) >> 16; 2260 ysrc += px.inc;
1984 gbits = ((unsigned)green) >> 16; 2261 dst_row += px.inc;
1985#if LCD_PIXELFORMAT == RGB565
1986 *dst_row++ = (rbits << 11) | (gbits << 5) | rbits;
1987#elif LCD_PIXELFORMAT == RGB565SWAPPED
1988 *dst_row++ = swap16((rbits << 11) | (gbits << 5) | rbits);
1989#endif
1990 } 2262 }
1991 while (dst_row < row_end); 2263 while (dst_row != row_end);
1992 } 2264 }
1993 2265
1994 src_y++; 2266 src_y++;
@@ -1996,7 +2268,8 @@ void yuv_bitmap_part(unsigned char *src[3], int csub_x, int csub_y,
1996 } 2268 }
1997 while (dst < dst_end); 2269 while (dst < dst_end);
1998} 2270}
1999#endif 2271
2272#endif /* HAVE_LCD_COLOR */
2000 2273
2001 2274
2002/* support function for qsort() */ 2275/* support function for qsort() */
@@ -2114,6 +2387,42 @@ void cleanup(void *parameter)
2114#define ZOOM_IN 100 /* return codes for below function */ 2387#define ZOOM_IN 100 /* return codes for below function */
2115#define ZOOM_OUT 101 2388#define ZOOM_OUT 101
2116 2389
2390#ifdef HAVE_LCD_COLOR
2391bool set_option_grayscale(void)
2392{
2393 bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY;
2394 rb->set_bool("Grayscale", &gray);
2395 jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR;
2396 return false;
2397}
2398
2399bool set_option_dithering(void)
2400{
2401 static const struct opt_items dithering[DITHER_NUM_MODES] = {
2402 [DITHER_NONE] = { "Off", -1 },
2403 [DITHER_ORDERED] = { "Ordered", -1 },
2404 [DITHER_DIFFUSION] = { "Diffusion", -1 },
2405 };
2406
2407 rb->set_option("Dithering", &jpeg_settings.dither_mode, INT,
2408 dithering, DITHER_NUM_MODES, NULL);
2409 return false;
2410}
2411
2412static void display_options(void)
2413{
2414 static const struct menu_item items[] = {
2415 { "Grayscale", set_option_grayscale },
2416 { "Dithering", set_option_dithering },
2417 };
2418
2419 int m = rb->menu_init(items, ARRAYLEN(items),
2420 NULL, NULL, NULL, NULL);
2421 rb->menu_run(m);
2422 rb->menu_exit(m);
2423}
2424#endif /* HAVE_LCD_COLOR */
2425
2117int show_menu(void) /* return 1 to quit */ 2426int show_menu(void) /* return 1 to quit */
2118{ 2427{
2119#if LCD_DEPTH > 1 2428#if LCD_DEPTH > 1
@@ -2128,19 +2437,45 @@ int show_menu(void) /* return 1 to quit */
2128#endif 2437#endif
2129 int m; 2438 int m;
2130 int result; 2439 int result;
2440
2441 enum menu_id
2442 {
2443 MIID_QUIT = 0,
2444 MIID_TOGGLE_SS_MODE,
2445 MIID_CHANGE_SS_MODE,
2446#if PLUGIN_BUFFER_SIZE >= MIN_MEM
2447 MIID_SHOW_PLAYBACK_MENU,
2448#endif
2449#ifdef HAVE_LCD_COLOR
2450 MIID_DISPLAY_OPTIONS,
2451#endif
2452 MIID_RETURN,
2453 };
2454
2131 static const struct menu_item items[] = { 2455 static const struct menu_item items[] = {
2132 { "Quit", NULL }, 2456 [MIID_QUIT] =
2133 { "Toggle Slideshow Mode", NULL }, 2457 { "Quit", NULL },
2134 { "Change Slideshow Time", NULL }, 2458 [MIID_TOGGLE_SS_MODE] =
2459 { "Toggle Slideshow Mode", NULL },
2460 [MIID_CHANGE_SS_MODE] =
2461 { "Change Slideshow Time", NULL },
2135#if PLUGIN_BUFFER_SIZE >= MIN_MEM 2462#if PLUGIN_BUFFER_SIZE >= MIN_MEM
2136 { "Show Playback Menu", NULL }, 2463 [MIID_SHOW_PLAYBACK_MENU] =
2464 { "Show Playback Menu", NULL },
2137#endif 2465#endif
2138 { "Return", NULL }, 2466#ifdef HAVE_LCD_COLOR
2467 [MIID_DISPLAY_OPTIONS] =
2468 { "Display Options", NULL },
2469#endif
2470 [MIID_RETURN] =
2471 { "Return", NULL },
2139 }; 2472 };
2473
2140 static const struct opt_items slideshow[2] = { 2474 static const struct opt_items slideshow[2] = {
2141 { "Disable", -1 }, 2475 { "Disable", -1 },
2142 { "Enable", -1 }, 2476 { "Enable", -1 },
2143 }; 2477 };
2478
2144 static const struct opt_items timeout[12] = { 2479 static const struct opt_items timeout[12] = {
2145 { "1 second", -1 }, 2480 { "1 second", -1 },
2146 { "2 seconds", -1 }, 2481 { "2 seconds", -1 },
@@ -2155,20 +2490,22 @@ int show_menu(void) /* return 1 to quit */
2155 { "15 seconds", -1 }, 2490 { "15 seconds", -1 },
2156 { "20 seconds", -1 }, 2491 { "20 seconds", -1 },
2157 }; 2492 };
2493
2158 m = rb->menu_init(items, sizeof(items) / sizeof(*items), 2494 m = rb->menu_init(items, sizeof(items) / sizeof(*items),
2159 NULL, NULL, NULL, NULL); 2495 NULL, NULL, NULL, NULL);
2160 result=rb->menu_show(m); 2496 result=rb->menu_show(m);
2497
2161 switch (result) 2498 switch (result)
2162 { 2499 {
2163 case 0: 2500 case MIID_QUIT:
2164 rb->menu_exit(m); 2501 rb->menu_exit(m);
2165 return 1; 2502 return 1;
2166 break; 2503 break;
2167 case 1: //toggle slideshow 2504 case MIID_TOGGLE_SS_MODE:
2168 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, 2505 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
2169 slideshow , 2, NULL); 2506 slideshow , 2, NULL);
2170 break; 2507 break;
2171 case 2: 2508 case MIID_CHANGE_SS_MODE:
2172 switch (button_timeout/HZ) 2509 switch (button_timeout/HZ)
2173 { 2510 {
2174 case 10: result = 9; break; 2511 case 10: result = 9; break;
@@ -2186,12 +2523,17 @@ int show_menu(void) /* return 1 to quit */
2186 default: button_timeout = (result+1)*HZ; break; 2523 default: button_timeout = (result+1)*HZ; break;
2187 } 2524 }
2188 break; 2525 break;
2189 case 3:
2190#if PLUGIN_BUFFER_SIZE >= MIN_MEM 2526#if PLUGIN_BUFFER_SIZE >= MIN_MEM
2527 case MIID_SHOW_PLAYBACK_MENU:
2191 playback_control(rb); 2528 playback_control(rb);
2192 break; 2529 break;
2193 case 4:
2194#endif 2530#endif
2531#ifdef HAVE_LCD_COLOR
2532 case MIID_DISPLAY_OPTIONS:
2533 display_options();
2534 break;
2535#endif
2536 case MIID_RETURN:
2195 break; 2537 break;
2196 } 2538 }
2197 2539
@@ -2300,6 +2642,13 @@ int scroll_bmp(struct t_disp* pdisp)
2300 MYXLCD(scroll_down)(move); /* scroll down */ 2642 MYXLCD(scroll_down)(move); /* scroll down */
2301 pdisp->y -= move; 2643 pdisp->y -= move;
2302#ifdef HAVE_LCD_COLOR 2644#ifdef HAVE_LCD_COLOR
2645 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
2646 {
2647 /* Draw over the band at the top of the last update
2648 caused by lack of error history on line zero. */
2649 move = MIN(move + 1, pdisp->y + pdisp->height);
2650 }
2651
2303 yuv_bitmap_part( 2652 yuv_bitmap_part(
2304 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, 2653 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
2305 pdisp->x, pdisp->y, pdisp->stride, 2654 pdisp->x, pdisp->y, pdisp->stride,
@@ -2323,11 +2672,32 @@ int scroll_bmp(struct t_disp* pdisp)
2323 MYXLCD(scroll_up)(move); /* scroll up */ 2672 MYXLCD(scroll_up)(move); /* scroll up */
2324 pdisp->y += move; 2673 pdisp->y += move;
2325#ifdef HAVE_LCD_COLOR 2674#ifdef HAVE_LCD_COLOR
2326 yuv_bitmap_part( 2675 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
2676 {
2677 /* Save the line that was on the last line of the display
2678 and draw one extra line above then recover the line with
2679 image data that had an error history when it was drawn.
2680 */
2681 move++, pdisp->y--;
2682 MEMCPY(rgb_linebuf,
2683 rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
2684 LCD_WIDTH*sizeof (fb_data));
2685 }
2686
2687 yuv_bitmap_part(
2327 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, pdisp->x, 2688 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, pdisp->x,
2328 pdisp->y + LCD_HEIGHT - move, pdisp->stride, 2689 pdisp->y + LCD_HEIGHT - move, pdisp->stride,
2329 MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */ 2690 MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */
2330 MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ 2691 MIN(LCD_WIDTH, pdisp->width), move); /* w, h */
2692
2693 if (jpeg_settings.dither_mode == DITHER_DIFFUSION)
2694 {
2695 /* Cover the first row drawn with previous image data. */
2696 MEMCPY(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH,
2697 rgb_linebuf,
2698 LCD_WIDTH*sizeof (fb_data));
2699 pdisp->y++;
2700 }
2331#else 2701#else
2332 MYXLCD(gray_bitmap_part)( 2702 MYXLCD(gray_bitmap_part)(
2333 pdisp->bitmap[0], pdisp->x, 2703 pdisp->bitmap[0], pdisp->x,
@@ -2909,6 +3279,15 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
2909 xlcd_init(rb); 3279 xlcd_init(rb);
2910#endif 3280#endif
2911 3281
3282#ifdef HAVE_LCD_COLOR
3283 /* should be ok to just load settings since a parameter is present
3284 here and the drive should be spinning */
3285 configfile_init(rb);
3286 configfile_load(JPEG_CONFIGFILE, jpeg_config,
3287 ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION);
3288 old_settings = jpeg_settings;
3289#endif
3290
2912 buf_images = buf; buf_images_size = buf_size; 3291 buf_images = buf; buf_images_size = buf_size;
2913 3292
2914 /* make sure the backlight is always on when viewing pictures 3293 /* make sure the backlight is always on when viewing pictures
@@ -2926,6 +3305,16 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
2926 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED 3305 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2927 && condition != PLUGIN_ERROR); 3306 && condition != PLUGIN_ERROR);
2928 3307
3308#ifdef HAVE_LCD_COLOR
3309 if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings)))
3310 {
3311 /* Just in case drive has to spin, keep it from looking locked */
3312 rb->splash(0, "Saving Settings");
3313 configfile_save(JPEG_CONFIGFILE, jpeg_config,
3314 ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION);
3315 }
3316#endif
3317
2929#ifndef SIMULATOR 3318#ifndef SIMULATOR
2930 /* set back ata spindown time in case we changed it */ 3319 /* set back ata spindown time in case we changed it */
2931 rb->ata_spindown(rb->global_settings->disk_spindown); 3320 rb->ata_spindown(rb->global_settings->disk_spindown);