diff options
author | Dave Chapman <dave@dchapman.com> | 2008-10-21 16:05:46 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2008-10-21 16:05:46 +0000 |
commit | 76596deaf1c90f107d43542bf60c9bad1a7ffd68 (patch) | |
tree | ba3a919baa3a3ddd3064f86d98bc220b4daee654 | |
parent | b0b3f0339ab928530ceac34a0d2714b266f8d831 (diff) | |
download | rockbox-76596deaf1c90f107d43542bf60c9bad1a7ffd68.tar.gz rockbox-76596deaf1c90f107d43542bf60c9bad1a7ffd68.zip |
Move the monolithic jpeg viewer into its own subdirectory and split it into three (for now - maybe it should be split further) files - jpeg.c (the main plugin/viewer parts), jpeg_decoder.c (the actual decoder) and. for colour targets only, yuv2rgb.c. The intention of this commit is as a first step towards abstracting this viewer into a reusable jpeg decoder and a multi-format image viewer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18853 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/plugins/SOURCES | 6 | ||||
-rw-r--r-- | apps/plugins/SUBDIRS | 1 | ||||
-rw-r--r-- | apps/plugins/jpeg.c | 3430 | ||||
-rw-r--r-- | apps/plugins/jpeg/Makefile | 75 | ||||
-rw-r--r-- | apps/plugins/jpeg/SOURCES | 5 | ||||
-rw-r--r-- | apps/plugins/jpeg/jpeg.c | 1235 | ||||
-rw-r--r-- | apps/plugins/jpeg/jpeg.h | 256 | ||||
-rw-r--r-- | apps/plugins/jpeg/jpeg_decoder.c | 1540 | ||||
-rw-r--r-- | apps/plugins/jpeg/jpeg_decoder.h | 142 | ||||
-rw-r--r-- | apps/plugins/jpeg/yuv2rgb.c | 401 | ||||
-rw-r--r-- | apps/plugins/jpeg/yuv2rgb.h | 51 |
11 files changed, 3706 insertions, 3436 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 4bcc144ed8..b1b397d062 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -24,11 +24,6 @@ viewer.c lamp.c #endif /* HAVE_BACKLIGHT */ -#ifdef OLYMPUS_MROBE_500 -/* remove these once the plugins before it are compileable */ -jpeg.c -#endif - #ifndef OLYMPUS_MROBE_500 #if (CONFIG_CODEC == SWCODEC) || !defined(SIMULATOR) @@ -62,7 +57,6 @@ ppmviewer.c /* Plugins needing the grayscale lib on low-depth LCDs */ fire.c -jpeg.c mandelbrot.c plasma.c diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS index 1b3e2af252..22ddcef4cb 100644 --- a/apps/plugins/SUBDIRS +++ b/apps/plugins/SUBDIRS @@ -17,6 +17,7 @@ rockboy /* For all targets with a bitmap display */ #ifdef HAVE_LCD_BITMAP chessbox +jpeg sudoku reversi #ifndef OLYMPUS_MROBE_500 diff --git a/apps/plugins/jpeg.c b/apps/plugins/jpeg.c deleted file mode 100644 index 397fab7bd6..0000000000 --- a/apps/plugins/jpeg.c +++ /dev/null @@ -1,3430 +0,0 @@ -/*************************************************************************** -* __________ __ ___. -* 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 "playback_control.h" -#include "oldmenuapi.h" -#include "helper.h" -#include "lib/configfile.h" - -#ifdef HAVE_LCD_BITMAP -#include "grey.h" -#include "xlcd.h" - -PLUGIN_HEADER - -/* variable button definitions */ -#if CONFIG_KEYPAD == RECORDER_PAD -#define JPEG_ZOOM_IN BUTTON_PLAY -#define JPEG_ZOOM_OUT BUTTON_ON -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_NEXT BUTTON_F3 -#define JPEG_PREVIOUS BUTTON_F2 -#define JPEG_MENU BUTTON_OFF - -#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD -#define JPEG_ZOOM_IN BUTTON_SELECT -#define JPEG_ZOOM_OUT BUTTON_ON -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_NEXT BUTTON_F3 -#define JPEG_PREVIOUS BUTTON_F2 -#define JPEG_MENU BUTTON_OFF - -#elif CONFIG_KEYPAD == ONDIO_PAD -#define JPEG_ZOOM_PRE BUTTON_MENU -#define JPEG_ZOOM_IN (BUTTON_MENU | BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_MENU | BUTTON_DOWN) -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_NEXT (BUTTON_MENU | BUTTON_RIGHT) -#define JPEG_PREVIOUS (BUTTON_MENU | BUTTON_LEFT) -#define JPEG_MENU BUTTON_OFF - -#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ - (CONFIG_KEYPAD == IRIVER_H300_PAD) -#define JPEG_ZOOM_IN BUTTON_SELECT -#define JPEG_ZOOM_OUT BUTTON_MODE -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#if (CONFIG_KEYPAD == IRIVER_H100_PAD) -#define JPEG_NEXT BUTTON_ON -#define JPEG_PREVIOUS BUTTON_REC -#else -#define JPEG_NEXT BUTTON_REC -#define JPEG_PREVIOUS BUTTON_ON -#endif -#define JPEG_MENU BUTTON_OFF -#define JPEG_RC_MENU BUTTON_RC_STOP - -#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ - (CONFIG_KEYPAD == IPOD_1G2G_PAD) -#define JPEG_ZOOM_IN BUTTON_SCROLL_FWD -#define JPEG_ZOOM_OUT BUTTON_SCROLL_BACK -#define JPEG_UP BUTTON_MENU -#define JPEG_DOWN BUTTON_PLAY -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU (BUTTON_SELECT | BUTTON_MENU) -#define JPEG_NEXT (BUTTON_SELECT | BUTTON_RIGHT) -#define JPEG_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT) - -#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD -#define JPEG_ZOOM_PRE BUTTON_SELECT -#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_POWER -#define JPEG_NEXT BUTTON_PLAY -#define JPEG_PREVIOUS BUTTON_REC - -#elif CONFIG_KEYPAD == GIGABEAT_PAD -#define JPEG_ZOOM_IN BUTTON_VOL_UP -#define JPEG_ZOOM_OUT BUTTON_VOL_DOWN -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_MENU -#define JPEG_NEXT (BUTTON_A | BUTTON_RIGHT) -#define JPEG_PREVIOUS (BUTTON_A | BUTTON_LEFT) - -#elif CONFIG_KEYPAD == SANSA_E200_PAD -#define JPEG_ZOOM_PRE BUTTON_SELECT -#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_POWER -#define JPEG_SLIDE_SHOW BUTTON_REC -#define JPEG_NEXT BUTTON_SCROLL_FWD -#define JPEG_NEXT_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT) -#define JPEG_PREVIOUS BUTTON_SCROLL_BACK -#define JPEG_PREVIOUS_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT) - -#elif CONFIG_KEYPAD == SANSA_C200_PAD -#define JPEG_ZOOM_PRE BUTTON_SELECT -#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_POWER -#define JPEG_SLIDE_SHOW BUTTON_REC -#define JPEG_NEXT BUTTON_VOL_UP -#define JPEG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) -#define JPEG_PREVIOUS BUTTON_VOL_DOWN -#define JPEG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) - -#elif CONFIG_KEYPAD == IRIVER_H10_PAD -#define JPEG_ZOOM_PRE BUTTON_PLAY -#define JPEG_ZOOM_IN (BUTTON_PLAY | BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_PLAY | BUTTON_REPEAT) -#define JPEG_UP BUTTON_SCROLL_UP -#define JPEG_DOWN BUTTON_SCROLL_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_POWER -#define JPEG_NEXT BUTTON_FF -#define JPEG_PREVIOUS BUTTON_REW - -#elif CONFIG_KEYPAD == MROBE500_PAD -#define JPEG_ZOOM_IN BUTTON_RC_VOL_UP -#define JPEG_ZOOM_OUT BUTTON_RC_VOL_DOWN -#define JPEG_UP BUTTON_RC_PLAY -#define JPEG_DOWN BUTTON_RC_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_POWER -#define JPEG_NEXT BUTTON_RC_HEART -#define JPEG_PREVIOUS BUTTON_RC_MODE - -#elif CONFIG_KEYPAD == GIGABEAT_S_PAD -#define JPEG_ZOOM_IN BUTTON_VOL_UP -#define JPEG_ZOOM_OUT BUTTON_VOL_DOWN -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_MENU -#define JPEG_NEXT BUTTON_NEXT -#define JPEG_PREVIOUS BUTTON_PREV - -#elif CONFIG_KEYPAD == MROBE100_PAD -#define JPEG_ZOOM_IN BUTTON_SELECT -#define JPEG_ZOOM_OUT BUTTON_PLAY -#define JPEG_UP BUTTON_UP -#define JPEG_DOWN BUTTON_DOWN -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_MENU -#define JPEG_NEXT (BUTTON_DISPLAY | BUTTON_RIGHT) -#define JPEG_PREVIOUS (BUTTON_DISPLAY | BUTTON_LEFT) - -#elif CONFIG_KEYPAD == IAUDIO_M3_PAD -#define JPEG_ZOOM_PRE BUTTON_RC_PLAY -#define JPEG_ZOOM_IN (BUTTON_RC_PLAY|BUTTON_REL) -#define JPEG_ZOOM_OUT (BUTTON_RC_PLAY|BUTTON_REPEAT) -#define JPEG_UP BUTTON_RC_VOL_UP -#define JPEG_DOWN BUTTON_RC_VOL_DOWN -#define JPEG_LEFT BUTTON_RC_REW -#define JPEG_RIGHT BUTTON_RC_FF -#define JPEG_MENU BUTTON_RC_REC -#define JPEG_NEXT BUTTON_RC_MODE -#define JPEG_PREVIOUS BUTTON_RC_MENU - -#elif CONFIG_KEYPAD == COWOND2_PAD - -#elif CONFIG_KEYPAD == IAUDIO67_PAD -#define JPEG_ZOOM_IN BUTTON_VOLUP -#define JPEG_ZOOM_OUT BUTTON_VOLDOWN -#define JPEG_UP BUTTON_STOP -#define JPEG_DOWN BUTTON_PLAY -#define JPEG_LEFT BUTTON_LEFT -#define JPEG_RIGHT BUTTON_RIGHT -#define JPEG_MENU BUTTON_MENU -#define JPEG_NEXT (BUTTON_PLAY|BUTTON_VOLUP) -#define JPEG_PREVIOUS (BUTTON_PLAY|BUTTON_VOLDOWN) - -#else -#error No keymap defined! -#endif - -#ifdef HAVE_TOUCHSCREEN -#ifndef JPEG_UP -#define JPEG_UP BUTTON_TOPMIDDLE -#endif -#ifndef JPEG_DOWN -#define JPEG_DOWN BUTTON_BOTTOMMIDDLE -#endif -#ifndef JPEG_LEFT -#define JPEG_LEFT BUTTON_MIDLEFT -#endif -#ifndef JPEG_RIGHT -#define JPEG_RIGHT BUTTON_MIDRIGHT -#endif -#ifndef JPEG_ZOOM_IN -#define JPEG_ZOOM_IN BUTTON_TOPRIGHT -#endif -#ifndef JPEG_ZOOM_OUT -#define JPEG_ZOOM_OUT BUTTON_TOPLEFT -#endif -#ifndef JPEG_MENU -#define JPEG_MENU (BUTTON_CENTER|BUTTON_REL) -#endif -#ifndef JPEG_NEXT -#define JPEG_NEXT BUTTON_BOTTOMRIGHT -#endif -#ifndef JPEG_PREVIOUS -#define JPEG_PREVIOUS BUTTON_BOTTOMLEFT -#endif -#endif - -/* different graphics libraries */ -#if LCD_DEPTH < 8 -#define USEGSLIB -GREY_INFO_STRUCT -#define MYLCD(fn) grey_ub_ ## fn -#define MYLCD_UPDATE() -#define MYXLCD(fn) grey_ub_ ## fn -#else -#define MYLCD(fn) rb->lcd_ ## fn -#define MYLCD_UPDATE() rb->lcd_update(); -#define MYXLCD(fn) xlcd_ ## fn -#endif - -#define MAX_X_SIZE LCD_WIDTH*8 - -/* Min memory allowing us to use the plugin buffer - * and thus not stopping the music - * *Very* rough estimation: - * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes - * + 20k code size = 60 000 - * + 50k min for jpeg = 120 000 - */ -#define MIN_MEM 120000 - -/* Headings */ -#define DIR_PREV 1 -#define DIR_NEXT -1 -#define DIR_NONE 0 - -#define PLUGIN_OTHER 10 /* State code for output with return. */ - -/******************************* Globals ***********************************/ - -static const struct plugin_api* rb; -MEM_FUNCTION_WRAPPERS(rb); - -/* for portability of below JPEG code */ -#define MEMSET(p,v,c) rb->memset(p,v,c) -#define MEMCPY(d,s,c) rb->memcpy(d,s,c) -#define INLINE static inline -#define ENDIAN_SWAP16(n) n /* only for poor little endian machines */ - -static int slideshow_enabled = false; /* run slideshow */ -static int running_slideshow = false; /* loading image because of slideshw */ -#ifndef SIMULATOR -static int immediate_ata_off = false; /* power down disk after loading */ -#endif - -/* Persistent configuration */ -#define JPEG_CONFIGFILE "jpeg.cfg" -#define JPEG_SETTINGS_MINVERSION 1 -#define JPEG_SETTINGS_VERSION 2 - -/* Slideshow times */ -#define SS_MIN_TIMEOUT 1 -#define SS_MAX_TIMEOUT 20 -#define SS_DEFAULT_TIMEOUT 5 - -enum color_modes -{ - COLOURMODE_COLOUR = 0, - COLOURMODE_GRAY, - COLOUR_NUM_MODES -}; - -enum dither_modes -{ - DITHER_NONE = 0, /* No dithering */ - DITHER_ORDERED, /* Bayer ordered */ - DITHER_DIFFUSION, /* Floyd/Steinberg error diffusion */ - DITHER_NUM_MODES -}; - -struct jpeg_settings -{ - int colour_mode; - int dither_mode; - int ss_timeout; -}; - -static struct jpeg_settings jpeg_settings = - { COLOURMODE_COLOUR, DITHER_NONE, SS_DEFAULT_TIMEOUT }; -static struct jpeg_settings old_settings; - -static struct configdata jpeg_config[] = -{ -#ifdef HAVE_LCD_COLOR - { TYPE_ENUM, 0, COLOUR_NUM_MODES, &jpeg_settings.colour_mode, - "Colour Mode", (char *[]){ "Colour", "Grayscale" }, NULL }, - { TYPE_ENUM, 0, DITHER_NUM_MODES, &jpeg_settings.dither_mode, - "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" }, NULL }, -#endif - { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, &jpeg_settings.ss_timeout, - "Slideshow Time", NULL, NULL}, -}; - -#if LCD_DEPTH > 1 -fb_data* old_backdrop; -#endif - -/**************** begin JPEG code ********************/ - -INLINE unsigned range_limit(int value) -{ -#if CONFIG_CPU == SH7034 - unsigned tmp; - asm ( /* Note: Uses knowledge that only low byte of result is used */ - "mov #-128,%[t] \n" - "sub %[t],%[v] \n" /* value -= -128; equals value += 128; */ - "extu.b %[v],%[t] \n" - "cmp/eq %[v],%[t] \n" /* low byte == whole number ? */ - "bt 1f \n" /* yes: no overflow */ - "cmp/pz %[v] \n" /* overflow: positive? */ - "subc %[v],%[v] \n" /* %[r] now either 0 or 0xffffffff */ - "1: \n" - : /* outputs */ - [v]"+r"(value), - [t]"=&r"(tmp) - ); - return value; -#elif defined(CPU_COLDFIRE) - asm ( /* Note: Uses knowledge that only the low byte of the result is used */ - "add.l #128,%[v] \n" /* value += 128; */ - "cmp.l #255,%[v] \n" /* overflow? */ - "bls.b 1f \n" /* no: return value */ - "spl.b %[v] \n" /* yes: set low byte to appropriate boundary */ - "1: \n" - : /* outputs */ - [v]"+d"(value) - ); - return value; -#elif defined(CPU_ARM) - asm ( /* Note: Uses knowledge that only the low byte of the result is used */ - "add %[v], %[v], #128 \n" /* value += 128 */ - "cmp %[v], #255 \n" /* out of range 0..255? */ - "mvnhi %[v], %[v], asr #31 \n" /* yes: set all bits to ~(sign_bit) */ - : /* outputs */ - [v]"+r"(value) - ); - return value; -#else - value += 128; - - if ((unsigned)value <= 255) - return value; - - if (value < 0) - return 0; - - return 255; -#endif -} - -/* IDCT implementation */ - - -#define CONST_BITS 13 -#define PASS1_BITS 2 - - -/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus -* causing a lot of useless floating-point operations at run time. -* To get around this we use the following pre-calculated constants. -* If you change CONST_BITS you may want to add appropriate values. -* (With a reasonable C compiler, you can just rely on the FIX() macro...) -*/ -#define FIX_0_298631336 2446 /* FIX(0.298631336) */ -#define FIX_0_390180644 3196 /* FIX(0.390180644) */ -#define FIX_0_541196100 4433 /* FIX(0.541196100) */ -#define FIX_0_765366865 6270 /* FIX(0.765366865) */ -#define FIX_0_899976223 7373 /* FIX(0.899976223) */ -#define FIX_1_175875602 9633 /* FIX(1.175875602) */ -#define FIX_1_501321110 12299 /* FIX(1.501321110) */ -#define FIX_1_847759065 15137 /* FIX(1.847759065) */ -#define FIX_1_961570560 16069 /* FIX(1.961570560) */ -#define FIX_2_053119869 16819 /* FIX(2.053119869) */ -#define FIX_2_562915447 20995 /* FIX(2.562915447) */ -#define FIX_3_072711026 25172 /* FIX(3.072711026) */ - - - -/* Multiply an long variable by an long constant to yield an long result. -* For 8-bit samples with the recommended scaling, all the variable -* and constant values involved are no more than 16 bits wide, so a -* 16x16->32 bit multiply can be used instead of a full 32x32 multiply. -* For 12-bit samples, a full 32-bit multiplication will be needed. -*/ -#define MULTIPLY16(var,const) (((short) (var)) * ((short) (const))) - - -/* Dequantize a coefficient by multiplying it by the multiplier-table -* entry; produce an int result. In this module, both inputs and result -* are 16 bits or less, so either int or short multiply will work. -*/ -/* #define DEQUANTIZE(coef,quantval) (((int) (coef)) * (quantval)) */ -#define DEQUANTIZE MULTIPLY16 - -/* Descale and correctly round an int value that's scaled by N bits. -* We assume RIGHT_SHIFT rounds towards minus infinity, so adding -* the fudge factor is correct for either sign of X. -*/ -#define DESCALE(x,n) (((x) + (1l << ((n)-1))) >> (n)) - - - -/* -* Perform dequantization and inverse DCT on one block of coefficients, -* producing a reduced-size 1x1 output block. -*/ -void idct1x1(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) -{ - (void)skip_line; /* unused */ - *p_byte = range_limit(inptr[0] * quantptr[0] >> 3); -} - - - -/* -* Perform dequantization and inverse DCT on one block of coefficients, -* producing a reduced-size 2x2 output block. -*/ -void idct2x2(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) -{ - int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; - unsigned char* outptr; - - /* Pass 1: process columns from input, store into work array. */ - - /* Column 0 */ - tmp4 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); - tmp5 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); - - tmp0 = tmp4 + tmp5; - tmp2 = tmp4 - tmp5; - - /* Column 1 */ - tmp4 = DEQUANTIZE(inptr[8*0+1], quantptr[8*0+1]); - tmp5 = DEQUANTIZE(inptr[8*1+1], quantptr[8*1+1]); - - tmp1 = tmp4 + tmp5; - tmp3 = tmp4 - tmp5; - - /* Pass 2: process 2 rows, store into output array. */ - - /* Row 0 */ - outptr = p_byte; - - outptr[0] = range_limit((int) DESCALE(tmp0 + tmp1, 3)); - outptr[1] = range_limit((int) DESCALE(tmp0 - tmp1, 3)); - - /* Row 1 */ - outptr = p_byte + skip_line; - - outptr[0] = range_limit((int) DESCALE(tmp2 + tmp3, 3)); - outptr[1] = range_limit((int) DESCALE(tmp2 - tmp3, 3)); -} - - - -/* -* Perform dequantization and inverse DCT on one block of coefficients, -* producing a reduced-size 4x4 output block. -*/ -void idct4x4(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) -{ - int tmp0, tmp2, tmp10, tmp12; - int z1, z2, z3; - int * wsptr; - unsigned char* outptr; - int ctr; - int workspace[4*4]; /* buffers data between passes */ - - /* Pass 1: process columns from input, store into work array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 4; ctr++, inptr++, quantptr++, wsptr++) - { - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); - tmp2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); - - tmp10 = (tmp0 + tmp2) << PASS1_BITS; - tmp12 = (tmp0 - tmp2) << PASS1_BITS; - - /* Odd part */ - /* Same rotation as in the even part of the 8x8 LL&M IDCT */ - - z2 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); - z3 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); - - z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); - tmp0 = DESCALE(z1 + MULTIPLY16(z3, - FIX_1_847759065), CONST_BITS-PASS1_BITS); - tmp2 = DESCALE(z1 + MULTIPLY16(z2, FIX_0_765366865), CONST_BITS-PASS1_BITS); - - /* Final output stage */ - - wsptr[4*0] = (int) (tmp10 + tmp2); - wsptr[4*3] = (int) (tmp10 - tmp2); - wsptr[4*1] = (int) (tmp12 + tmp0); - wsptr[4*2] = (int) (tmp12 - tmp0); - } - - /* Pass 2: process 4 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 4; ctr++) - { - outptr = p_byte + (ctr*skip_line); - /* Even part */ - - tmp0 = (int) wsptr[0]; - tmp2 = (int) wsptr[2]; - - tmp10 = (tmp0 + tmp2) << CONST_BITS; - tmp12 = (tmp0 - tmp2) << CONST_BITS; - - /* Odd part */ - /* Same rotation as in the even part of the 8x8 LL&M IDCT */ - - z2 = (int) wsptr[1]; - z3 = (int) wsptr[3]; - - z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); - tmp0 = z1 + MULTIPLY16(z3, - FIX_1_847759065); - tmp2 = z1 + MULTIPLY16(z2, FIX_0_765366865); - - /* Final output stage */ - - outptr[0] = range_limit((int) DESCALE(tmp10 + tmp2, - CONST_BITS+PASS1_BITS+3)); - outptr[3] = range_limit((int) DESCALE(tmp10 - tmp2, - CONST_BITS+PASS1_BITS+3)); - outptr[1] = range_limit((int) DESCALE(tmp12 + tmp0, - CONST_BITS+PASS1_BITS+3)); - outptr[2] = range_limit((int) DESCALE(tmp12 - tmp0, - CONST_BITS+PASS1_BITS+3)); - - wsptr += 4; /* advance pointer to next row */ - } -} - - - -/* -* Perform dequantization and inverse DCT on one block of coefficients. -*/ -void idct8x8(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) -{ - long tmp0, tmp1, tmp2, tmp3; - long tmp10, tmp11, tmp12, tmp13; - long z1, z2, z3, z4, z5; - int * wsptr; - unsigned char* outptr; - int ctr; - int workspace[64]; /* buffers data between passes */ - - /* Pass 1: process columns from input, store into work array. */ - /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ - /* furthermore, we scale the results by 2**PASS1_BITS. */ - - wsptr = workspace; - for (ctr = 8; ctr > 0; ctr--) - { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if ((inptr[8*1] | inptr[8*2] | inptr[8*3] - | inptr[8*4] | inptr[8*5] | inptr[8*6] | inptr[8*7]) == 0) - { - /* AC terms all zero */ - int dcval = DEQUANTIZE(inptr[8*0], quantptr[8*0]) << PASS1_BITS; - - wsptr[8*0] = wsptr[8*1] = wsptr[8*2] = wsptr[8*3] = wsptr[8*4] - = wsptr[8*5] = wsptr[8*6] = wsptr[8*7] = dcval; - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - continue; - } - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - z2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); - z3 = DEQUANTIZE(inptr[8*6], quantptr[8*6]); - - z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); - tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); - tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); - - z2 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); - z3 = DEQUANTIZE(inptr[8*4], quantptr[8*4]); - - tmp0 = (z2 + z3) << CONST_BITS; - tmp1 = (z2 - z3) << CONST_BITS; - - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ - - tmp0 = DEQUANTIZE(inptr[8*7], quantptr[8*7]); - tmp1 = DEQUANTIZE(inptr[8*5], quantptr[8*5]); - tmp2 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); - tmp3 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - z4 = tmp1 + tmp3; - z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ - - tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - wsptr[8*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - } - - /* Pass 2: process rows from work array, store into output array. */ - /* Note that we must descale the results by a factor of 8 == 2**3, */ - /* and also undo the PASS1_BITS scaling. */ - - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++) - { - outptr = p_byte + (ctr*skip_line); - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * On machines with very fast multiplication, it's possible that the - * test takes more time than it's worth. In that case this section - * may be commented out. - */ - -#ifndef NO_ZERO_ROW_TEST - if ((wsptr[1] | wsptr[2] | wsptr[3] - | wsptr[4] | wsptr[5] | wsptr[6] | wsptr[7]) == 0) - { - /* AC terms all zero */ - unsigned char dcval = range_limit((int) DESCALE((long) wsptr[0], - PASS1_BITS+3)); - - outptr[0] = dcval; - outptr[1] = dcval; - outptr[2] = dcval; - outptr[3] = dcval; - outptr[4] = dcval; - outptr[5] = dcval; - outptr[6] = dcval; - outptr[7] = dcval; - - wsptr += 8; /* advance pointer to next row */ - continue; - } -#endif - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - z2 = (long) wsptr[2]; - z3 = (long) wsptr[6]; - - z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); - tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); - tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); - - tmp0 = ((long) wsptr[0] + (long) wsptr[4]) << CONST_BITS; - tmp1 = ((long) wsptr[0] - (long) wsptr[4]) << CONST_BITS; - - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ - - tmp0 = (long) wsptr[7]; - tmp1 = (long) wsptr[5]; - tmp2 = (long) wsptr[3]; - tmp3 = (long) wsptr[1]; - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - z4 = tmp1 + tmp3; - z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ - - tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - outptr[0] = range_limit((int) DESCALE(tmp10 + tmp3, - CONST_BITS+PASS1_BITS+3)); - outptr[7] = range_limit((int) DESCALE(tmp10 - tmp3, - CONST_BITS+PASS1_BITS+3)); - outptr[1] = range_limit((int) DESCALE(tmp11 + tmp2, - CONST_BITS+PASS1_BITS+3)); - outptr[6] = range_limit((int) DESCALE(tmp11 - tmp2, - CONST_BITS+PASS1_BITS+3)); - outptr[2] = range_limit((int) DESCALE(tmp12 + tmp1, - CONST_BITS+PASS1_BITS+3)); - outptr[5] = range_limit((int) DESCALE(tmp12 - tmp1, - CONST_BITS+PASS1_BITS+3)); - outptr[3] = range_limit((int) DESCALE(tmp13 + tmp0, - CONST_BITS+PASS1_BITS+3)); - outptr[4] = range_limit((int) DESCALE(tmp13 - tmp0, - CONST_BITS+PASS1_BITS+3)); - - wsptr += 8; /* advance pointer to next row */ - } -} - - - -/* JPEG decoder implementation */ - - -#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ - -struct derived_tbl -{ - /* Basic tables: (element [0] of each array is unused) */ - long mincode[17]; /* smallest code of length k */ - long maxcode[18]; /* largest code of length k (-1 if none) */ - /* (maxcode[17] is a sentinel to ensure huff_DECODE terminates) */ - int valptr[17]; /* huffval[] index of 1st symbol of length k */ - - /* Back link to public Huffman table (needed only in slow_DECODE) */ - int* pub; - - /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of - the input data stream. If the next Huffman code is no more - than HUFF_LOOKAHEAD bits long, we can obtain its length and - the corresponding symbol directly from these tables. */ - int look_nbits[1<<HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */ - unsigned char look_sym[1<<HUFF_LOOKAHEAD]; /* symbol, or unused */ -}; - -#define QUANT_TABLE_LENGTH 64 - -/* for type of Huffman table */ -#define DC_LEN 28 -#define AC_LEN 178 - -struct huffman_table -{ /* length and code according to JFIF format */ - int huffmancodes_dc[DC_LEN]; - int huffmancodes_ac[AC_LEN]; -}; - -struct frame_component -{ - int ID; - int horizontal_sampling; - int vertical_sampling; - int quanttable_select; -}; - -struct scan_component -{ - int ID; - int DC_select; - int AC_select; -}; - -struct bitstream -{ - unsigned long get_buffer; /* current bit-extraction buffer */ - int bits_left; /* # of unused bits in it */ - unsigned char* next_input_byte; - unsigned char* input_end; /* upper limit +1 */ -}; - -struct jpeg -{ - int x_size, y_size; /* size of image (can be less than block boundary) */ - int x_phys, y_phys; /* physical size, block aligned */ - int x_mbl; /* x dimension of MBL */ - int y_mbl; /* y dimension of MBL */ - int blocks; /* blocks per MB */ - int restart_interval; /* number of MCUs between RSTm markers */ - int store_pos[4]; /* for Y block ordering */ - - unsigned char* p_entropy_data; - unsigned char* p_entropy_end; - - int quanttable[4][QUANT_TABLE_LENGTH]; /* raw quantization tables 0-3 */ - int qt_idct[2][QUANT_TABLE_LENGTH]; /* quantization tables for IDCT */ - - struct huffman_table hufftable[2]; /* Huffman tables */ - struct derived_tbl dc_derived_tbls[2]; /* Huffman-LUTs */ - struct derived_tbl ac_derived_tbls[2]; - - struct frame_component frameheader[3]; /* Component descriptor */ - struct scan_component scanheader[3]; /* currently not used */ - - int mcu_membership[6]; /* info per block */ - int tab_membership[6]; - int subsample_x[3]; /* info per component */ - int subsample_y[3]; -}; - - -/* possible return flags for process_markers() */ -#define HUFFTAB 0x0001 /* with huffman table */ -#define QUANTTAB 0x0002 /* with quantization table */ -#define APP0_JFIF 0x0004 /* with APP0 segment following JFIF standard */ -#define FILL_FF 0x0008 /* with 0xFF padding bytes at begin/end */ -#define SOF0 0x0010 /* with SOF0-Segment */ -#define DHT 0x0020 /* with Definition of huffman tables */ -#define SOS 0x0040 /* with Start-of-Scan segment */ -#define DQT 0x0080 /* with definition of quantization table */ - -/* Preprocess the JPEG JFIF file */ -int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) -{ - unsigned char* p_bytes = p_src; - int marker_size; /* variable length of marker segment */ - int i, j, n; - int ret = 0; /* returned flags */ - - p_jpeg->p_entropy_end = p_src + size; - - while (p_src < p_bytes + size) - { - if (*p_src++ != 0xFF) /* no marker? */ - { - p_src--; /* it's image data, put it back */ - p_jpeg->p_entropy_data = p_src; - break; /* exit marker processing */ - } - - switch (*p_src++) - { - case 0xFF: /* Fill byte */ - ret |= FILL_FF; - case 0x00: /* Zero stuffed byte - entropy data */ - p_src--; /* put it back */ - continue; - - case 0xC0: /* SOF Huff - Baseline DCT */ - { - ret |= SOF0; - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - n = *p_src++; /* sample precision (= 8 or 12) */ - if (n != 8) - { - return(-1); /* Unsupported sample precision */ - } - p_jpeg->y_size = *p_src++ << 8; /* Highbyte */ - p_jpeg->y_size |= *p_src++; /* Lowbyte */ - p_jpeg->x_size = *p_src++ << 8; /* Highbyte */ - p_jpeg->x_size |= *p_src++; /* Lowbyte */ - - n = (marker_size-2-6)/3; - if (*p_src++ != n || (n != 1 && n != 3)) - { - return(-2); /* Unsupported SOF0 component specification */ - } - for (i=0; i<n; i++) - { - p_jpeg->frameheader[i].ID = *p_src++; /* Component info */ - p_jpeg->frameheader[i].horizontal_sampling = *p_src >> 4; - p_jpeg->frameheader[i].vertical_sampling = *p_src++ & 0x0F; - p_jpeg->frameheader[i].quanttable_select = *p_src++; - if (p_jpeg->frameheader[i].horizontal_sampling > 2 - || p_jpeg->frameheader[i].vertical_sampling > 2) - return -3; /* Unsupported SOF0 subsampling */ - } - p_jpeg->blocks = n; - } - break; - - case 0xC1: /* SOF Huff - Extended sequential DCT*/ - case 0xC2: /* SOF Huff - Progressive DCT*/ - case 0xC3: /* SOF Huff - Spatial (sequential) lossless*/ - case 0xC5: /* SOF Huff - Differential sequential DCT*/ - case 0xC6: /* SOF Huff - Differential progressive DCT*/ - case 0xC7: /* SOF Huff - Differential spatial*/ - case 0xC8: /* SOF Arith - Reserved for JPEG extensions*/ - case 0xC9: /* SOF Arith - Extended sequential DCT*/ - case 0xCA: /* SOF Arith - Progressive DCT*/ - case 0xCB: /* SOF Arith - Spatial (sequential) lossless*/ - case 0xCD: /* SOF Arith - Differential sequential DCT*/ - case 0xCE: /* SOF Arith - Differential progressive DCT*/ - case 0xCF: /* SOF Arith - Differential spatial*/ - { - return (-4); /* other DCT model than baseline not implemented */ - } - - case 0xC4: /* Define Huffman Table(s) */ - { - unsigned char* p_temp; - - ret |= DHT; - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - - p_temp = p_src; - while (p_src < p_temp+marker_size-2-17) /* another table */ - { - int sum = 0; - i = *p_src & 0x0F; /* table index */ - if (i > 1) - { - return (-5); /* Huffman table index out of range */ - } - else if (*p_src++ & 0xF0) /* AC table */ - { - for (j=0; j<16; j++) - { - sum += *p_src; - p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; - } - if(16 + sum > AC_LEN) - return -10; /* longer than allowed */ - - for (; j < 16 + sum; j++) - p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; - } - else /* DC table */ - { - for (j=0; j<16; j++) - { - sum += *p_src; - p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; - } - if(16 + sum > DC_LEN) - return -11; /* longer than allowed */ - - for (; j < 16 + sum; j++) - p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; - } - } /* while */ - p_src = p_temp+marker_size - 2; /* skip possible residue */ - } - break; - - case 0xCC: /* Define Arithmetic coding conditioning(s) */ - return(-6); /* Arithmetic coding not supported */ - - case 0xD8: /* Start of Image */ - case 0xD9: /* End of Image */ - case 0x01: /* for temp private use arith code */ - break; /* skip parameterless marker */ - - - case 0xDA: /* Start of Scan */ - { - ret |= SOS; - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - - n = (marker_size-2-1-3)/2; - if (*p_src++ != n || (n != 1 && n != 3)) - { - return (-7); /* Unsupported SOS component specification */ - } - for (i=0; i<n; i++) - { - p_jpeg->scanheader[i].ID = *p_src++; - p_jpeg->scanheader[i].DC_select = *p_src >> 4; - p_jpeg->scanheader[i].AC_select = *p_src++ & 0x0F; - } - p_src += 3; /* skip spectral information */ - } - break; - - case 0xDB: /* Define quantization Table(s) */ - { - ret |= DQT; - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - n = (marker_size-2)/(QUANT_TABLE_LENGTH+1); /* # of tables */ - for (i=0; i<n; i++) - { - int id = *p_src++; /* ID */ - if (id >= 4) - { - return (-8); /* Unsupported quantization table */ - } - /* Read Quantisation table: */ - for (j=0; j<QUANT_TABLE_LENGTH; j++) - p_jpeg->quanttable[id][j] = *p_src++; - } - } - break; - - case 0xDD: /* Define Restart Interval */ - { - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - p_jpeg->restart_interval = *p_src++ << 8; /* Highbyte */ - p_jpeg->restart_interval |= *p_src++; /* Lowbyte */ - p_src += marker_size-4; /* skip segment */ - } - break; - - case 0xDC: /* Define Number of Lines */ - case 0xDE: /* Define Hierarchical progression */ - case 0xDF: /* Expand Reference Component(s) */ - case 0xE0: /* Application Field 0*/ - case 0xE1: /* Application Field 1*/ - case 0xE2: /* Application Field 2*/ - case 0xE3: /* Application Field 3*/ - case 0xE4: /* Application Field 4*/ - case 0xE5: /* Application Field 5*/ - case 0xE6: /* Application Field 6*/ - case 0xE7: /* Application Field 7*/ - case 0xE8: /* Application Field 8*/ - case 0xE9: /* Application Field 9*/ - case 0xEA: /* Application Field 10*/ - case 0xEB: /* Application Field 11*/ - case 0xEC: /* Application Field 12*/ - case 0xED: /* Application Field 13*/ - case 0xEE: /* Application Field 14*/ - case 0xEF: /* Application Field 15*/ - case 0xFE: /* Comment */ - { - marker_size = *p_src++ << 8; /* Highbyte */ - marker_size |= *p_src++; /* Lowbyte */ - p_src += marker_size-2; /* skip segment */ - } - break; - - case 0xF0: /* Reserved for JPEG extensions */ - case 0xF1: /* Reserved for JPEG extensions */ - case 0xF2: /* Reserved for JPEG extensions */ - case 0xF3: /* Reserved for JPEG extensions */ - case 0xF4: /* Reserved for JPEG extensions */ - case 0xF5: /* Reserved for JPEG extensions */ - case 0xF6: /* Reserved for JPEG extensions */ - case 0xF7: /* Reserved for JPEG extensions */ - case 0xF8: /* Reserved for JPEG extensions */ - case 0xF9: /* Reserved for JPEG extensions */ - case 0xFA: /* Reserved for JPEG extensions */ - case 0xFB: /* Reserved for JPEG extensions */ - case 0xFC: /* Reserved for JPEG extensions */ - case 0xFD: /* Reserved for JPEG extensions */ - case 0x02: /* Reserved */ - default: - return (-9); /* Unknown marker */ - } /* switch */ - } /* while */ - - return (ret); /* return flags with seen markers */ -} - - -void default_huff_tbl(struct jpeg* p_jpeg) -{ - static const struct huffman_table luma_table = - { - { - 0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B - }, - { - 0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D, - 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, - 0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0, - 0x24,0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28, - 0x29,0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49, - 0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69, - 0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89, - 0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, - 0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5, - 0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2, - 0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, - 0xF9,0xFA - } - }; - - static const struct huffman_table chroma_table = - { - { - 0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B - }, - { - 0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77, - 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, - 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0, - 0x15,0x62,0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26, - 0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48, - 0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68, - 0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87, - 0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5, - 0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3, - 0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA, - 0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, - 0xF9,0xFA - } - }; - - MEMCPY(&p_jpeg->hufftable[0], &luma_table, sizeof(luma_table)); - MEMCPY(&p_jpeg->hufftable[1], &chroma_table, sizeof(chroma_table)); - - return; -} - -/* Compute the derived values for a Huffman table */ -void fix_huff_tbl(int* htbl, struct derived_tbl* dtbl) -{ - int p, i, l, si; - int lookbits, ctr; - char huffsize[257]; - unsigned int huffcode[257]; - unsigned int code; - - dtbl->pub = htbl; /* fill in back link */ - - /* Figure C.1: make table of Huffman code length for each symbol */ - /* Note that this is in code-length order. */ - - p = 0; - for (l = 1; l <= 16; l++) - { /* all possible code length */ - for (i = 1; i <= (int) htbl[l-1]; i++) /* all codes per length */ - huffsize[p++] = (char) l; - } - huffsize[p] = 0; - - /* Figure C.2: generate the codes themselves */ - /* Note that this is in code-length order. */ - - code = 0; - si = huffsize[0]; - p = 0; - while (huffsize[p]) - { - while (((int) huffsize[p]) == si) - { - huffcode[p++] = code; - code++; - } - code <<= 1; - si++; - } - - /* Figure F.15: generate decoding tables for bit-sequential decoding */ - - p = 0; - for (l = 1; l <= 16; l++) - { - if (htbl[l-1]) - { - dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ - dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ - p += htbl[l-1]; - dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ - } - else - { - dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ - } - } - dtbl->maxcode[17] = 0xFFFFFL; /* ensures huff_DECODE terminates */ - - /* Compute lookahead tables to speed up decoding. - * First we set all the table entries to 0, indicating "too long"; - * then we iterate through the Huffman codes that are short enough and - * fill in all the entries that correspond to bit sequences starting - * with that code. - */ - - MEMSET(dtbl->look_nbits, 0, sizeof(dtbl->look_nbits)); - - p = 0; - for (l = 1; l <= HUFF_LOOKAHEAD; l++) - { - for (i = 1; i <= (int) htbl[l-1]; i++, p++) - { - /* l = current code's length, p = its index in huffcode[] & huffval[]. */ - /* Generate left-justified code followed by all possible bit sequences */ - lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); - for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) - { - dtbl->look_nbits[lookbits] = l; - dtbl->look_sym[lookbits] = htbl[16+p]; - lookbits++; - } - } - } -} - - -/* zag[i] is the natural-order position of the i'th element of zigzag order. - * If the incoming data is corrupted, decode_mcu could attempt to - * reference values beyond the end of the array. To avoid a wild store, - * we put some extra zeroes after the real entries. - */ -static const int zag[] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - 0, 0, 0, 0, 0, 0, 0, 0, /* extra entries in case k>63 below */ - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -void build_lut(struct jpeg* p_jpeg) -{ - int i; - fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_dc, - &p_jpeg->dc_derived_tbls[0]); - fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_ac, - &p_jpeg->ac_derived_tbls[0]); - fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_dc, - &p_jpeg->dc_derived_tbls[1]); - fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_ac, - &p_jpeg->ac_derived_tbls[1]); - - /* build the dequantization tables for the IDCT (De-ZiZagged) */ - for (i=0; i<64; i++) - { - p_jpeg->qt_idct[0][zag[i]] = p_jpeg->quanttable[0][i]; - p_jpeg->qt_idct[1][zag[i]] = p_jpeg->quanttable[1][i]; - } - - for (i=0; i<4; i++) - p_jpeg->store_pos[i] = i; /* default ordering */ - - /* assignments for the decoding of blocks */ - if (p_jpeg->frameheader[0].horizontal_sampling == 2 - && p_jpeg->frameheader[0].vertical_sampling == 1) - { /* 4:2:2 */ - p_jpeg->blocks = 4; - p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; - p_jpeg->x_phys = p_jpeg->x_mbl * 16; - p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; - p_jpeg->y_phys = p_jpeg->y_mbl * 8; - p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ - p_jpeg->mcu_membership[1] = 0; - p_jpeg->mcu_membership[2] = 1; - p_jpeg->mcu_membership[3] = 2; - p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ - p_jpeg->tab_membership[1] = 0; - p_jpeg->tab_membership[2] = 1; - p_jpeg->tab_membership[3] = 1; - p_jpeg->subsample_x[0] = 1; - p_jpeg->subsample_x[1] = 2; - p_jpeg->subsample_x[2] = 2; - p_jpeg->subsample_y[0] = 1; - p_jpeg->subsample_y[1] = 1; - p_jpeg->subsample_y[2] = 1; - } - if (p_jpeg->frameheader[0].horizontal_sampling == 1 - && p_jpeg->frameheader[0].vertical_sampling == 2) - { /* 4:2:2 vertically subsampled */ - p_jpeg->store_pos[1] = 2; /* block positions are mirrored */ - p_jpeg->store_pos[2] = 1; - p_jpeg->blocks = 4; - p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; - p_jpeg->x_phys = p_jpeg->x_mbl * 8; - p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; - p_jpeg->y_phys = p_jpeg->y_mbl * 16; - p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ - p_jpeg->mcu_membership[1] = 0; - p_jpeg->mcu_membership[2] = 1; - p_jpeg->mcu_membership[3] = 2; - p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ - p_jpeg->tab_membership[1] = 0; - p_jpeg->tab_membership[2] = 1; - p_jpeg->tab_membership[3] = 1; - p_jpeg->subsample_x[0] = 1; - p_jpeg->subsample_x[1] = 1; - p_jpeg->subsample_x[2] = 1; - p_jpeg->subsample_y[0] = 1; - p_jpeg->subsample_y[1] = 2; - p_jpeg->subsample_y[2] = 2; - } - else if (p_jpeg->frameheader[0].horizontal_sampling == 2 - && p_jpeg->frameheader[0].vertical_sampling == 2) - { /* 4:2:0 */ - p_jpeg->blocks = 6; - p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; - p_jpeg->x_phys = p_jpeg->x_mbl * 16; - p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; - p_jpeg->y_phys = p_jpeg->y_mbl * 16; - p_jpeg->mcu_membership[0] = 0; - p_jpeg->mcu_membership[1] = 0; - p_jpeg->mcu_membership[2] = 0; - p_jpeg->mcu_membership[3] = 0; - p_jpeg->mcu_membership[4] = 1; - p_jpeg->mcu_membership[5] = 2; - p_jpeg->tab_membership[0] = 0; - p_jpeg->tab_membership[1] = 0; - p_jpeg->tab_membership[2] = 0; - p_jpeg->tab_membership[3] = 0; - p_jpeg->tab_membership[4] = 1; - p_jpeg->tab_membership[5] = 1; - p_jpeg->subsample_x[0] = 1; - p_jpeg->subsample_x[1] = 2; - p_jpeg->subsample_x[2] = 2; - p_jpeg->subsample_y[0] = 1; - p_jpeg->subsample_y[1] = 2; - p_jpeg->subsample_y[2] = 2; - } - else if (p_jpeg->frameheader[0].horizontal_sampling == 1 - && p_jpeg->frameheader[0].vertical_sampling == 1) - { /* 4:4:4 */ - /* don't overwrite p_jpeg->blocks */ - p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; - p_jpeg->x_phys = p_jpeg->x_mbl * 8; - p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; - p_jpeg->y_phys = p_jpeg->y_mbl * 8; - p_jpeg->mcu_membership[0] = 0; - p_jpeg->mcu_membership[1] = 1; - p_jpeg->mcu_membership[2] = 2; - p_jpeg->tab_membership[0] = 0; - p_jpeg->tab_membership[1] = 1; - p_jpeg->tab_membership[2] = 1; - p_jpeg->subsample_x[0] = 1; - p_jpeg->subsample_x[1] = 1; - p_jpeg->subsample_x[2] = 1; - p_jpeg->subsample_y[0] = 1; - p_jpeg->subsample_y[1] = 1; - p_jpeg->subsample_y[2] = 1; - } - else - { - /* error */ - } - -} - - -/* -* These functions/macros provide the in-line portion of bit fetching. -* Use check_bit_buffer to ensure there are N bits in get_buffer -* before using get_bits, peek_bits, or drop_bits. -* check_bit_buffer(state,n,action); -* Ensure there are N bits in get_buffer; if suspend, take action. -* val = get_bits(n); -* Fetch next N bits. -* val = peek_bits(n); -* Fetch next N bits without removing them from the buffer. -* drop_bits(n); -* Discard next N bits. -* The value N should be a simple variable, not an expression, because it -* is evaluated multiple times. -*/ - -INLINE void check_bit_buffer(struct bitstream* pb, int nbits) -{ - if (pb->bits_left < nbits) - { /* nbits is <= 16, so I can always refill 2 bytes in this case */ - unsigned char byte; - - byte = *pb->next_input_byte++; - if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ - { /* simplification: just skip the (one-byte) marker code */ - pb->next_input_byte++; - } - pb->get_buffer = (pb->get_buffer << 8) | byte; - - byte = *pb->next_input_byte++; - if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ - { /* simplification: just skip the (one-byte) marker code */ - pb->next_input_byte++; - } - pb->get_buffer = (pb->get_buffer << 8) | byte; - - pb->bits_left += 16; - } -} - -INLINE int get_bits(struct bitstream* pb, int nbits) -{ - return ((int) (pb->get_buffer >> (pb->bits_left -= nbits))) & ((1<<nbits)-1); -} - -INLINE int peek_bits(struct bitstream* pb, int nbits) -{ - return ((int) (pb->get_buffer >> (pb->bits_left - nbits))) & ((1<<nbits)-1); -} - -INLINE void drop_bits(struct bitstream* pb, int nbits) -{ - pb->bits_left -= nbits; -} - -/* re-synchronize to entropy data (skip restart marker) */ -void search_restart(struct bitstream* pb) -{ - pb->next_input_byte--; /* we may have overread it, taking 2 bytes */ - /* search for a non-byte-padding marker, has to be RSTm or EOS */ - while (pb->next_input_byte < pb->input_end && - (pb->next_input_byte[-2] != 0xFF || pb->next_input_byte[-1] == 0x00)) - { - pb->next_input_byte++; - } - pb->bits_left = 0; -} - -/* Figure F.12: extend sign bit. */ -#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) - -static const int extend_test[16] = /* entry n is 2**(n-1) */ -{ - 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, - 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 -}; - -static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ -{ - 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, - ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, - ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, - ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 -}; - -/* Decode a single value */ -INLINE int huff_decode_dc(struct bitstream* bs, struct derived_tbl* tbl) -{ - int nb, look, s, r; - - check_bit_buffer(bs, HUFF_LOOKAHEAD); - look = peek_bits(bs, HUFF_LOOKAHEAD); - if ((nb = tbl->look_nbits[look]) != 0) - { - drop_bits(bs, nb); - s = tbl->look_sym[look]; - check_bit_buffer(bs, s); - r = get_bits(bs, s); - s = HUFF_EXTEND(r, s); - } - else - { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ - long code; - nb=HUFF_LOOKAHEAD+1; - check_bit_buffer(bs, nb); - code = get_bits(bs, nb); - while (code > tbl->maxcode[nb]) - { - code <<= 1; - check_bit_buffer(bs, 1); - code |= get_bits(bs, 1); - nb++; - } - if (nb > 16) /* error in Huffman */ - { - s=0; /* fake a zero, this is most safe */ - } - else - { - s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; - check_bit_buffer(bs, s); - r = get_bits(bs, s); - s = HUFF_EXTEND(r, s); - } - } /* end slow decode */ - return s; -} - -INLINE int huff_decode_ac(struct bitstream* bs, struct derived_tbl* tbl) -{ - int nb, look, s; - - check_bit_buffer(bs, HUFF_LOOKAHEAD); - look = peek_bits(bs, HUFF_LOOKAHEAD); - if ((nb = tbl->look_nbits[look]) != 0) - { - drop_bits(bs, nb); - s = tbl->look_sym[look]; - } - else - { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ - long code; - nb=HUFF_LOOKAHEAD+1; - check_bit_buffer(bs, nb); - code = get_bits(bs, nb); - while (code > tbl->maxcode[nb]) - { - code <<= 1; - check_bit_buffer(bs, 1); - code |= get_bits(bs, 1); - nb++; - } - if (nb > 16) /* error in Huffman */ - { - s=0; /* fake a zero, this is most safe */ - } - else - { - s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; - } - } /* end slow decode */ - return s; -} - - -#ifdef HAVE_LCD_COLOR - -/* JPEG decoder variant for YUV decoding, into 3 different planes */ -/* Note: it keeps the original color subsampling, even if resized. */ -int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[3], - int downscale, void (*pf_progress)(int current, int total)) -{ - struct bitstream bs; /* bitstream "object" */ - int block[64]; /* decoded DCT coefficients */ - - int width, height; - int skip_line[3]; /* bytes from one line to the next (skip_line) */ - int skip_strip[3], skip_mcu[3]; /* bytes to next DCT row / column */ - - int i, x, y; /* loop counter */ - - unsigned char* p_line[3] = {p_pixel[0], p_pixel[1], p_pixel[2]}; - unsigned char* p_byte[3]; /* bitmap pointer */ - - void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ - int k_need; /* AC coefficients needed up to here */ - int zero_need; /* init the block with this many zeros */ - - int last_dc_val[3] = {0, 0, 0}; /* or 128 for chroma? */ - int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ - int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ - - /* pick the IDCT we want, determine how to work with coefs */ - if (downscale == 1) - { - pf_idct = idct8x8; - k_need = 64; /* all */ - zero_need = 63; /* all */ - } - else if (downscale == 2) - { - pf_idct = idct4x4; - k_need = 25; /* this far in zig-zag to cover 4*4 */ - zero_need = 27; /* clear this far in linear order */ - } - else if (downscale == 4) - { - pf_idct = idct2x2; - k_need = 5; /* this far in zig-zag to cover 2*2 */ - zero_need = 9; /* clear this far in linear order */ - } - else if (downscale == 8) - { - pf_idct = idct1x1; - k_need = 0; /* no AC, not needed */ - zero_need = 0; /* no AC, not needed */ - } - else return -1; /* not supported */ - - /* init bitstream, fake a restart to make it start */ - bs.next_input_byte = p_jpeg->p_entropy_data; - bs.bits_left = 0; - bs.input_end = p_jpeg->p_entropy_end; - - width = p_jpeg->x_phys / downscale; - height = p_jpeg->y_phys / downscale; - for (i=0; i<3; i++) /* calculate some strides */ - { - skip_line[i] = width / p_jpeg->subsample_x[i]; - skip_strip[i] = skip_line[i] - * (height / p_jpeg->y_mbl) / p_jpeg->subsample_y[i]; - skip_mcu[i] = width/p_jpeg->x_mbl / p_jpeg->subsample_x[i]; - } - - /* prepare offsets about where to store the different blocks */ - store_offs[p_jpeg->store_pos[0]] = 0; - store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ - store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ - store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ - - for(y=0; y<p_jpeg->y_mbl && bs.next_input_byte <= bs.input_end; y++) - { - for (i=0; i<3; i++) /* scan line init */ - { - p_byte[i] = p_line[i]; - p_line[i] += skip_strip[i]; - } - for (x=0; x<p_jpeg->x_mbl; x++) - { - int blkn; - - /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < p_jpeg->blocks; blkn++) - { /* Decode a single block's worth of coefficients */ - int k = 1; /* coefficient index */ - int s, r; /* huffman values */ - int ci = p_jpeg->mcu_membership[blkn]; /* component index */ - int ti = p_jpeg->tab_membership[blkn]; /* table index */ - struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; - struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; - - /* Section F.2.2.1: decode the DC coefficient difference */ - s = huff_decode_dc(&bs, dctbl); - - last_dc_val[ci] += s; - block[0] = last_dc_val[ci]; /* output it (assumes zag[0] = 0) */ - - /* coefficient buffer must be cleared */ - MEMSET(block+1, 0, zero_need*sizeof(block[0])); - - /* Section F.2.2.2: decode the AC coefficients */ - for (; k < k_need; k++) - { - s = huff_decode_ac(&bs, actbl); - r = s >> 4; - s &= 15; - - if (s) - { - k += r; - check_bit_buffer(&bs, s); - r = get_bits(&bs, s); - block[zag[k]] = HUFF_EXTEND(r, s); - } - else - { - if (r != 15) - { - k = 64; - break; - } - k += r; - } - } /* for k */ - /* In this path we just discard the values */ - for (; k < 64; k++) - { - s = huff_decode_ac(&bs, actbl); - r = s >> 4; - s &= 15; - - if (s) - { - k += r; - check_bit_buffer(&bs, s); - drop_bits(&bs, s); - } - else - { - if (r != 15) - break; - k += r; - } - } /* for k */ - - if (ci == 0) - { /* Y component needs to bother about block store */ - pf_idct(p_byte[0]+store_offs[blkn], block, - p_jpeg->qt_idct[ti], skip_line[0]); - } - else - { /* chroma */ - pf_idct(p_byte[ci], block, p_jpeg->qt_idct[ti], - skip_line[ci]); - } - } /* for blkn */ - p_byte[0] += skip_mcu[0]; /* unrolled for (i=0; i<3; i++) loop */ - p_byte[1] += skip_mcu[1]; - p_byte[2] += skip_mcu[2]; - if (p_jpeg->restart_interval && --restart == 0) - { /* if a restart marker is due: */ - restart = p_jpeg->restart_interval; /* count again */ - search_restart(&bs); /* align the bitstream */ - last_dc_val[0] = last_dc_val[1] = - last_dc_val[2] = 0; /* reset decoder */ - } - } /* for x */ - if (pf_progress != NULL) - pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ - } /* for y */ - - return 0; /* success */ -} -#else /* !HAVE_LCD_COLOR */ - -/* a JPEG decoder specialized in decoding only the luminance (b&w) */ -int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, - void (*pf_progress)(int current, int total)) -{ - struct bitstream bs; /* bitstream "object" */ - int block[64]; /* decoded DCT coefficients */ - - int width, height; - int skip_line; /* bytes from one line to the next (skip_line) */ - int skip_strip, skip_mcu; /* bytes to next DCT row / column */ - - int x, y; /* loop counter */ - - unsigned char* p_line = p_pixel[0]; - unsigned char* p_byte; /* bitmap pointer */ - - void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ - int k_need; /* AC coefficients needed up to here */ - int zero_need; /* init the block with this many zeros */ - - int last_dc_val = 0; - int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ - int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ - - /* pick the IDCT we want, determine how to work with coefs */ - if (downscale == 1) - { - pf_idct = idct8x8; - k_need = 64; /* all */ - zero_need = 63; /* all */ - } - else if (downscale == 2) - { - pf_idct = idct4x4; - k_need = 25; /* this far in zig-zag to cover 4*4 */ - zero_need = 27; /* clear this far in linear order */ - } - else if (downscale == 4) - { - pf_idct = idct2x2; - k_need = 5; /* this far in zig-zag to cover 2*2 */ - zero_need = 9; /* clear this far in linear order */ - } - else if (downscale == 8) - { - pf_idct = idct1x1; - k_need = 0; /* no AC, not needed */ - zero_need = 0; /* no AC, not needed */ - } - else return -1; /* not supported */ - - /* init bitstream, fake a restart to make it start */ - bs.next_input_byte = p_jpeg->p_entropy_data; - bs.bits_left = 0; - bs.input_end = p_jpeg->p_entropy_end; - - width = p_jpeg->x_phys / downscale; - height = p_jpeg->y_phys / downscale; - skip_line = width; - skip_strip = skip_line * (height / p_jpeg->y_mbl); - skip_mcu = (width/p_jpeg->x_mbl); - - /* prepare offsets about where to store the different blocks */ - store_offs[p_jpeg->store_pos[0]] = 0; - store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ - store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ - store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ - - for(y=0; y<p_jpeg->y_mbl && bs.next_input_byte <= bs.input_end; y++) - { - p_byte = p_line; - p_line += skip_strip; - for (x=0; x<p_jpeg->x_mbl; x++) - { - int blkn; - - /* Outer loop handles each block in the MCU */ - for (blkn = 0; blkn < p_jpeg->blocks; blkn++) - { /* Decode a single block's worth of coefficients */ - int k = 1; /* coefficient index */ - int s, r; /* huffman values */ - int ci = p_jpeg->mcu_membership[blkn]; /* component index */ - int ti = p_jpeg->tab_membership[blkn]; /* table index */ - struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; - struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; - - /* Section F.2.2.1: decode the DC coefficient difference */ - s = huff_decode_dc(&bs, dctbl); - - if (ci == 0) /* only for Y component */ - { - last_dc_val += s; - block[0] = last_dc_val; /* output it (assumes zag[0] = 0) */ - - /* coefficient buffer must be cleared */ - MEMSET(block+1, 0, zero_need*sizeof(block[0])); - - /* Section F.2.2.2: decode the AC coefficients */ - for (; k < k_need; k++) - { - s = huff_decode_ac(&bs, actbl); - r = s >> 4; - s &= 15; - - if (s) - { - k += r; - check_bit_buffer(&bs, s); - r = get_bits(&bs, s); - block[zag[k]] = HUFF_EXTEND(r, s); - } - else - { - if (r != 15) - { - k = 64; - break; - } - k += r; - } - } /* for k */ - } - /* In this path we just discard the values */ - for (; k < 64; k++) - { - s = huff_decode_ac(&bs, actbl); - r = s >> 4; - s &= 15; - - if (s) - { - k += r; - check_bit_buffer(&bs, s); - drop_bits(&bs, s); - } - else - { - if (r != 15) - break; - k += r; - } - } /* for k */ - - if (ci == 0) - { /* only for Y component */ - pf_idct(p_byte+store_offs[blkn], block, p_jpeg->qt_idct[ti], - skip_line); - } - } /* for blkn */ - p_byte += skip_mcu; - if (p_jpeg->restart_interval && --restart == 0) - { /* if a restart marker is due: */ - restart = p_jpeg->restart_interval; /* count again */ - search_restart(&bs); /* align the bitstream */ - last_dc_val = 0; /* reset decoder */ - } - } /* for x */ - if (pf_progress != NULL) - pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ - } /* for y */ - - return 0; /* success */ -} -#endif /* !HAVE_LCD_COLOR */ - -/**************** end JPEG code ********************/ - - - -/**************** begin Application ********************/ - - -/************************* Types ***************************/ - -struct t_disp -{ -#ifdef HAVE_LCD_COLOR - unsigned char* bitmap[3]; /* Y, Cr, Cb */ - int csub_x, csub_y; -#else - unsigned char* bitmap[1]; /* Y only */ -#endif - int width; - int height; - int stride; - int x, y; -}; - -/************************* Globals ***************************/ - -/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ -struct t_disp disp[9]; - -/* my memory pool (from the mp3 buffer) */ -char print[32]; /* use a common snprintf() buffer */ -unsigned char* buf; /* up to here currently used by image(s) */ - -/* the remaining free part of the buffer for compressed+uncompressed images */ -unsigned char* buf_images; - -ssize_t buf_size, buf_images_size; -/* the root of the images, hereafter are decompresed ones */ -unsigned char* buf_root; -int root_size; - -int ds, ds_min, ds_max; /* downscaling and limits */ -static struct jpeg jpg; /* too large for stack */ - -static struct tree_context *tree; - -/* the current full file name */ -static char np_file[MAX_PATH]; -int curfile = 0, direction = DIR_NONE, entries = 0; - -/* list of the jpeg files */ -char **file_pt; -/* are we using the plugin buffer or the audio buffer? */ -bool plug_buf = false; - - -/************************* Implementation ***************************/ - -#ifdef HAVE_LCD_COLOR -/* - * 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(int x) -{ - if ((unsigned)x > YUV_WHITE) - x = x < 0 ? 0 : YUV_WHITE; - return x; -} - -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]; - -fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when - DITHER_DIFFUSION is set */ - -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, - }, -}; - -/** - * 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) -{ - fb_data *dst, *dst_end; - fb_data (*pixel_func)(void); - struct rgb_pixel px; - - 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 = rb->lcd_framebuffer + LCD_WIDTH * y + x; - dst_end = dst + LCD_WIDTH * height; - - if (jpeg_settings.colour_mode == COLOURMODE_GRAY) - csub_y = 0; /* Ignore Cb, Cr */ - - pixel_func = pixel_funcs[jpeg_settings.colour_mode] - [jpeg_settings.dither_mode]; - - if (jpeg_settings.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 (jpeg_settings.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_row = dst; - row_end = dst_row + width; - px.col = src_x; - } - else - { - /* Scan is R->L */ - row_end = dst - 1; - dst_row = row_end + width; - 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 += px.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 += px.inc; - } - while (dst_row != row_end); - } - - src_y++; - dst += LCD_WIDTH; - } - while (dst < dst_end); -} - -#endif /* HAVE_LCD_COLOR */ - - -/* support function for qsort() */ -static int compare(const void* p1, const void* p2) -{ - return rb->strcasecmp(*((char **)p1), *((char **)p2)); -} - -bool jpg_ext(const char ext[]) -{ - if(!ext) - return false; - if(!rb->strcasecmp(ext,".jpg") || - !rb->strcasecmp(ext,".jpe") || - !rb->strcasecmp(ext,".jpeg")) - return true; - else - return false; -} - -/*Read directory contents for scrolling. */ -void get_pic_list(void) -{ - int i; - long int str_len = 0; - char *pname; - tree = rb->tree_get_context(); - -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - file_pt = rb->plugin_get_buffer((size_t *)&buf_size); -#else - file_pt = rb->plugin_get_audio_buffer((size_t *)&buf_size); -#endif - - for(i = 0; i < tree->filesindir; i++) - { - if(jpg_ext(rb->strrchr(&tree->name_buffer[str_len],'.'))) - file_pt[entries++] = &tree->name_buffer[str_len]; - - str_len += rb->strlen(&tree->name_buffer[str_len]) + 1; - } - - rb->qsort(file_pt, entries, sizeof(char**), compare); - - /* Remove path and leave only the name.*/ - pname = rb->strrchr(np_file,'/'); - pname++; - - /* Find Selected File. */ - for(i = 0; i < entries; i++) - if(!rb->strcmp(file_pt[i], pname)) - curfile = i; -} - -int change_filename(int direct) -{ - int count = 0; - direction = direct; - - if(direct == DIR_PREV) - { - do - { - count++; - if(curfile == 0) - curfile = entries - 1; - else - curfile--; - }while(file_pt[curfile] == '\0' && count < entries); - /* we "erase" the file name if we encounter - * a non-supported file, so skip it now */ - } - else /* DIR_NEXT/DIR_NONE */ - { - do - { - count++; - if(curfile == entries - 1) - curfile = 0; - else - curfile++; - }while(file_pt[curfile] == '\0' && count < entries); - } - - if(count == entries && file_pt[curfile] == '\0') - { - rb->splash(HZ, "No supported files"); - return PLUGIN_ERROR; - } - if(rb->strlen(tree->currdir) > 1) - { - rb->strcpy(np_file, tree->currdir); - rb->strcat(np_file, "/"); - } - else - rb->strcpy(np_file, tree->currdir); - - rb->strcat(np_file, file_pt[curfile]); - - return PLUGIN_OTHER; -} - -/* switch off overlay, for handling SYS_ events */ -void cleanup(void *parameter) -{ - (void)parameter; -#ifdef USEGSLIB - grey_show(false); -#endif -} - -#define VSCROLL (LCD_HEIGHT/8) -#define HSCROLL (LCD_WIDTH/10) - -#define ZOOM_IN 100 /* return codes for below function */ -#define ZOOM_OUT 101 - -#ifdef HAVE_LCD_COLOR -bool set_option_grayscale(void) -{ - bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY; - rb->set_bool("Grayscale", &gray); - jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR; - return false; -} - -bool set_option_dithering(void) -{ - static const struct opt_items dithering[DITHER_NUM_MODES] = { - [DITHER_NONE] = { "Off", -1 }, - [DITHER_ORDERED] = { "Ordered", -1 }, - [DITHER_DIFFUSION] = { "Diffusion", -1 }, - }; - - rb->set_option("Dithering", &jpeg_settings.dither_mode, INT, - dithering, DITHER_NUM_MODES, NULL); - return false; -} - -static void display_options(void) -{ - static const struct menu_item items[] = { - { "Grayscale", set_option_grayscale }, - { "Dithering", set_option_dithering }, - }; - - int m = menu_init(rb, items, ARRAYLEN(items), - NULL, NULL, NULL, NULL); - menu_run(m); - menu_exit(m); -} -#endif /* HAVE_LCD_COLOR */ - -int show_menu(void) /* return 1 to quit */ -{ -#if LCD_DEPTH > 1 - rb->lcd_set_backdrop(old_backdrop); -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(rb->global_settings->fg_color); - rb->lcd_set_background(rb->global_settings->bg_color); -#else - rb->lcd_set_foreground(LCD_BLACK); - rb->lcd_set_background(LCD_WHITE); -#endif -#endif - int m; - int result; - - enum menu_id - { - MIID_QUIT = 0, - MIID_TOGGLE_SS_MODE, - MIID_CHANGE_SS_MODE, -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - MIID_SHOW_PLAYBACK_MENU, -#endif -#ifdef HAVE_LCD_COLOR - MIID_DISPLAY_OPTIONS, -#endif - MIID_RETURN, - }; - - static const struct menu_item items[] = { - [MIID_QUIT] = - { "Quit", NULL }, - [MIID_TOGGLE_SS_MODE] = - { "Toggle Slideshow Mode", NULL }, - [MIID_CHANGE_SS_MODE] = - { "Change Slideshow Time", NULL }, -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - [MIID_SHOW_PLAYBACK_MENU] = - { "Show Playback Menu", NULL }, -#endif -#ifdef HAVE_LCD_COLOR - [MIID_DISPLAY_OPTIONS] = - { "Display Options", NULL }, -#endif - [MIID_RETURN] = - { "Return", NULL }, - }; - - static const struct opt_items slideshow[2] = { - { "Disable", -1 }, - { "Enable", -1 }, - }; - - m = menu_init(rb, items, sizeof(items) / sizeof(*items), - NULL, NULL, NULL, NULL); - result=menu_show(m); - - switch (result) - { - case MIID_QUIT: - menu_exit(m); - return 1; - break; - case MIID_TOGGLE_SS_MODE: - rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, - slideshow , 2, NULL); - break; - case MIID_CHANGE_SS_MODE: - rb->set_int("Slideshow Time", "s", UNIT_SEC, - &jpeg_settings.ss_timeout, NULL, 1, - SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL); - break; - -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - case MIID_SHOW_PLAYBACK_MENU: - if (plug_buf) - { - playback_control(rb, NULL); - } - else - { - rb->splash(HZ, "Cannot restart playback"); - } - break; -#endif -#ifdef HAVE_LCD_COLOR - case MIID_DISPLAY_OPTIONS: - display_options(); - break; -#endif - case MIID_RETURN: - break; - } - -#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) - /* change ata spindown time based on slideshow time setting */ - immediate_ata_off = false; - rb->ata_spindown(rb->global_settings->disk_spindown); - - if (slideshow_enabled) - { - if(jpeg_settings.ss_timeout < 10) - { - /* slideshow times < 10s keep disk spinning */ - rb->ata_spindown(0); - } - else if (!rb->mp3_is_playing()) - { - /* slideshow times > 10s and not playing: ata_off after load */ - immediate_ata_off = true; - } - } -#endif -#if LCD_DEPTH > 1 - rb->lcd_set_backdrop(NULL); - rb->lcd_set_foreground(LCD_WHITE); - rb->lcd_set_background(LCD_BLACK); -#endif - rb->lcd_clear_display(); - menu_exit(m); - return 0; -} -/* interactively scroll around the image */ -int scroll_bmp(struct t_disp* pdisp) -{ - int lastbutton = 0; - - while (true) - { - int button; - int move; - - if (slideshow_enabled) - button = rb->button_get_w_tmo(jpeg_settings.ss_timeout * HZ); - else button = rb->button_get(true); - - running_slideshow = false; - - switch(button) - { - case JPEG_LEFT: - if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE) - return change_filename(DIR_PREV); - case JPEG_LEFT | BUTTON_REPEAT: - move = MIN(HSCROLL, pdisp->x); - if (move > 0) - { - MYXLCD(scroll_right)(move); /* scroll right */ - pdisp->x -= move; -#ifdef HAVE_LCD_COLOR - yuv_bitmap_part( - pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, - pdisp->x, pdisp->y, pdisp->stride, - 0, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ - move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ -#else - MYXLCD(gray_bitmap_part)( - pdisp->bitmap[0], pdisp->x, pdisp->y, pdisp->stride, - 0, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ - move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ -#endif - MYLCD_UPDATE(); - } - break; - - case JPEG_RIGHT: - if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE) - return change_filename(DIR_NEXT); - case JPEG_RIGHT | BUTTON_REPEAT: - move = MIN(HSCROLL, pdisp->width - pdisp->x - LCD_WIDTH); - if (move > 0) - { - MYXLCD(scroll_left)(move); /* scroll left */ - pdisp->x += move; -#ifdef HAVE_LCD_COLOR - yuv_bitmap_part( - pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, - pdisp->x + LCD_WIDTH - move, pdisp->y, pdisp->stride, - LCD_WIDTH - move, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ - move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ -#else - MYXLCD(gray_bitmap_part)( - pdisp->bitmap[0], pdisp->x + LCD_WIDTH - move, - pdisp->y, pdisp->stride, - LCD_WIDTH - move, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ - move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ -#endif - MYLCD_UPDATE(); - } - break; - - case JPEG_UP: - case JPEG_UP | BUTTON_REPEAT: - move = MIN(VSCROLL, pdisp->y); - if (move > 0) - { - MYXLCD(scroll_down)(move); /* scroll down */ - pdisp->y -= move; -#ifdef HAVE_LCD_COLOR - if (jpeg_settings.dither_mode == DITHER_DIFFUSION) - { - /* Draw over the band at the top of the last update - caused by lack of error history on line zero. */ - move = MIN(move + 1, pdisp->y + pdisp->height); - } - - yuv_bitmap_part( - pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, - pdisp->x, pdisp->y, pdisp->stride, - MAX(0, (LCD_WIDTH-pdisp->width)/2), 0, /* x, y */ - MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ -#else - MYXLCD(gray_bitmap_part)( - pdisp->bitmap[0], pdisp->x, pdisp->y, pdisp->stride, - MAX(0, (LCD_WIDTH-pdisp->width)/2), 0, /* x, y */ - MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ -#endif - MYLCD_UPDATE(); - } - break; - - case JPEG_DOWN: - case JPEG_DOWN | BUTTON_REPEAT: - move = MIN(VSCROLL, pdisp->height - pdisp->y - LCD_HEIGHT); - if (move > 0) - { - MYXLCD(scroll_up)(move); /* scroll up */ - pdisp->y += move; -#ifdef HAVE_LCD_COLOR - if (jpeg_settings.dither_mode == DITHER_DIFFUSION) - { - /* Save the line that was on the last line of the display - and draw one extra line above then recover the line with - image data that had an error history when it was drawn. - */ - move++, pdisp->y--; - MEMCPY(rgb_linebuf, - rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, - LCD_WIDTH*sizeof (fb_data)); - } - - yuv_bitmap_part( - pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, pdisp->x, - pdisp->y + LCD_HEIGHT - move, pdisp->stride, - MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */ - MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ - - if (jpeg_settings.dither_mode == DITHER_DIFFUSION) - { - /* Cover the first row drawn with previous image data. */ - MEMCPY(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, - rgb_linebuf, - LCD_WIDTH*sizeof (fb_data)); - pdisp->y++; - } -#else - MYXLCD(gray_bitmap_part)( - pdisp->bitmap[0], pdisp->x, - pdisp->y + LCD_HEIGHT - move, pdisp->stride, - MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */ - MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ -#endif - MYLCD_UPDATE(); - } - break; - case BUTTON_NONE: - if (!slideshow_enabled) - break; - running_slideshow = true; - if (entries > 0) - return change_filename(DIR_NEXT); - break; - -#ifdef JPEG_SLIDE_SHOW - case JPEG_SLIDE_SHOW: - slideshow_enabled = !slideshow_enabled; - running_slideshow = slideshow_enabled; - break; -#endif - -#ifdef JPEG_NEXT_REPEAT - case JPEG_NEXT_REPEAT: -#endif - case JPEG_NEXT: - if (entries > 0) - return change_filename(DIR_NEXT); - break; - -#ifdef JPEG_PREVIOUS_REPEAT - case JPEG_PREVIOUS_REPEAT: -#endif - case JPEG_PREVIOUS: - if (entries > 0) - return change_filename(DIR_PREV); - break; - - case JPEG_ZOOM_IN: -#ifdef JPEG_ZOOM_PRE - if (lastbutton != JPEG_ZOOM_PRE) - break; -#endif - return ZOOM_IN; - break; - - case JPEG_ZOOM_OUT: -#ifdef JPEG_ZOOM_PRE - if (lastbutton != JPEG_ZOOM_PRE) - break; -#endif - return ZOOM_OUT; - break; -#ifdef JPEG_RC_MENU - case JPEG_RC_MENU: -#endif - case JPEG_MENU: -#ifdef USEGSLIB - grey_show(false); /* switch off greyscale overlay */ -#endif - if (show_menu() == 1) - return PLUGIN_OK; - -#ifdef USEGSLIB - grey_show(true); /* switch on greyscale overlay */ -#else - yuv_bitmap_part( - pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, - pdisp->x, pdisp->y, pdisp->stride, - MAX(0, (LCD_WIDTH - pdisp->width) / 2), - MAX(0, (LCD_HEIGHT - pdisp->height) / 2), - MIN(LCD_WIDTH, pdisp->width), - MIN(LCD_HEIGHT, pdisp->height)); - MYLCD_UPDATE(); -#endif - break; - default: - if (rb->default_event_handler_ex(button, cleanup, NULL) - == SYS_USB_CONNECTED) - return PLUGIN_USB_CONNECTED; - break; - - } /* switch */ - - if (button != BUTTON_NONE) - lastbutton = button; - } /* while (true) */ -} - -/********************* main function *************************/ - -/* callback updating a progress meter while JPEG decoding */ -void cb_progess(int current, int total) -{ - rb->yield(); /* be nice to the other threads */ - if(!running_slideshow) - { - rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0, - current, HORIZONTAL); - rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); - } -#ifndef USEGSLIB - else - { - /* in slideshow mode, keep gui interference to a minimum */ - rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0, - current, HORIZONTAL); - rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4); - } -#endif -} - -int jpegmem(struct jpeg *p_jpg, int ds) -{ - int size; - - size = (p_jpg->x_phys/ds/p_jpg->subsample_x[0]) - * (p_jpg->y_phys/ds/p_jpg->subsample_y[0]); -#ifdef HAVE_LCD_COLOR - if (p_jpg->blocks > 1) /* colour, add requirements for chroma */ - { - size += (p_jpg->x_phys/ds/p_jpg->subsample_x[1]) - * (p_jpg->y_phys/ds/p_jpg->subsample_y[1]); - size += (p_jpg->x_phys/ds/p_jpg->subsample_x[2]) - * (p_jpg->y_phys/ds/p_jpg->subsample_y[2]); - } -#endif - return size; -} - -/* how far can we zoom in without running out of memory */ -int min_downscale(struct jpeg *p_jpg, int bufsize) -{ - int downscale = 8; - - if (jpegmem(p_jpg, 8) > bufsize) - return 0; /* error, too large, even 1:8 doesn't fit */ - - while (downscale > 1 && jpegmem(p_jpg, downscale/2) <= bufsize) - downscale /= 2; - - return downscale; -} - - -/* how far can we zoom out, to fit image into the LCD */ -int max_downscale(struct jpeg *p_jpg) -{ - int downscale = 1; - - while (downscale < 8 && (p_jpg->x_size > LCD_WIDTH*downscale - || p_jpg->y_size > LCD_HEIGHT*downscale)) - { - downscale *= 2; - } - - return downscale; -} - - -/* return decoded or cached image */ -struct t_disp* get_image(struct jpeg* p_jpg, int ds) -{ - int w, h; /* used to center output */ - int size; /* decompressed image size */ - long time; /* measured ticks */ - int status; - - struct t_disp* p_disp = &disp[ds]; /* short cut */ - - if (p_disp->bitmap[0] != NULL) - { - return p_disp; /* we still have it */ - } - - /* assign image buffer */ - - /* physical size needed for decoding */ - size = jpegmem(p_jpg, ds); - if (buf_size <= size) - { /* have to discard the current */ - int i; - for (i=1; i<=8; i++) - disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */ - buf = buf_root; /* start again from the beginning of the buffer */ - buf_size = root_size; - } - -#ifdef HAVE_LCD_COLOR - if (p_jpg->blocks > 1) /* colour jpeg */ - { - int i; - - for (i = 1; i < 3; i++) - { - size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i]) - * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]); - p_disp->bitmap[i] = buf; - buf += size; - buf_size -= size; - } - p_disp->csub_x = p_jpg->subsample_x[1]; - p_disp->csub_y = p_jpg->subsample_y[1]; - } - else - { - p_disp->csub_x = p_disp->csub_y = 0; - p_disp->bitmap[1] = p_disp->bitmap[2] = buf; - } -#endif - /* size may be less when decoded (if height is not block aligned) */ - size = (p_jpg->x_phys/ds) * (p_jpg->y_size / ds); - p_disp->bitmap[0] = buf; - buf += size; - buf_size -= size; - - if(!running_slideshow) - { - rb->snprintf(print, sizeof(print), "decoding %d*%d", - p_jpg->x_size/ds, p_jpg->y_size/ds); - rb->lcd_puts(0, 3, print); - rb->lcd_update(); - } - - /* update image properties */ - p_disp->width = p_jpg->x_size / ds; - p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */ - p_disp->height = p_jpg->y_size / ds; - - /* the actual decoding */ - time = *rb->current_tick; -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - rb->cpu_boost(true); - status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess); - rb->cpu_boost(false); -#else - status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess); -#endif - if (status) - { - rb->splashf(HZ, "decode error %d", status); - file_pt[curfile] = '\0'; - return NULL; - } - time = *rb->current_tick - time; - - if(!running_slideshow) - { - rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); - rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ - rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); - rb->lcd_update(); - } - - return p_disp; -} - - -/* set the view to the given center point, limit if necessary */ -void set_view (struct t_disp* p_disp, int cx, int cy) -{ - int x, y; - - /* plain center to available width/height */ - x = cx - MIN(LCD_WIDTH, p_disp->width) / 2; - y = cy - MIN(LCD_HEIGHT, p_disp->height) / 2; - - /* limit against upper image size */ - x = MIN(p_disp->width - LCD_WIDTH, x); - y = MIN(p_disp->height - LCD_HEIGHT, y); - - /* limit against negative side */ - x = MAX(0, x); - y = MAX(0, y); - - p_disp->x = x; /* set the values */ - p_disp->y = y; -} - - -/* calculate the view center based on the bitmap position */ -void get_view(struct t_disp* p_disp, int* p_cx, int* p_cy) -{ - *p_cx = p_disp->x + MIN(LCD_WIDTH, p_disp->width) / 2; - *p_cy = p_disp->y + MIN(LCD_HEIGHT, p_disp->height) / 2; -} - - -/* load, decode, display the image */ -int load_and_show(char* filename) -{ - int fd; - int filesize; - unsigned char* buf_jpeg; /* compressed JPEG image */ - int status; - struct t_disp* p_disp; /* currenly displayed image */ - int cx, cy; /* view center */ - - fd = rb->open(filename, O_RDONLY); - if (fd < 0) - { - rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd); - rb->splash(HZ, print); - return PLUGIN_ERROR; - } - filesize = rb->filesize(fd); - rb->memset(&disp, 0, sizeof(disp)); - - buf = buf_images + filesize; - buf_size = buf_images_size - filesize; - /* allocate JPEG buffer */ - buf_jpeg = buf_images; - - buf_root = buf; /* we can start the decompressed images behind it */ - root_size = buf_size; - - if (buf_size <= 0) - { -#if PLUGIN_BUFFER_SIZE >= MIN_MEM - if(plug_buf) - { - rb->close(fd); - rb->lcd_setfont(FONT_SYSFIXED); - rb->lcd_clear_display(); - rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1); - rb->lcd_puts(0,0,print); - rb->lcd_puts(0,1,"Not enough plugin memory!"); - rb->lcd_puts(0,2,"Zoom In: Stop playback."); - if(entries>1) - rb->lcd_puts(0,3,"Left/Right: Skip File."); - rb->lcd_puts(0,4,"Off: Quit."); - rb->lcd_update(); - rb->lcd_setfont(FONT_UI); - - rb->button_clear_queue(); - - while (1) - { - int button = rb->button_get(true); - switch(button) - { - case JPEG_ZOOM_IN: - plug_buf = false; - buf_images = rb->plugin_get_audio_buffer( - (size_t *)&buf_images_size); - /*try again this file, now using the audio buffer */ - return PLUGIN_OTHER; -#ifdef JPEG_RC_MENU - case JPEG_RC_MENU: -#endif - case JPEG_MENU: - return PLUGIN_OK; - - case JPEG_LEFT: - if(entries>1) - { - rb->lcd_clear_display(); - return change_filename(DIR_PREV); - } - break; - - case JPEG_RIGHT: - if(entries>1) - { - rb->lcd_clear_display(); - return change_filename(DIR_NEXT); - } - break; - default: - if(rb->default_event_handler_ex(button, cleanup, NULL) - == SYS_USB_CONNECTED) - return PLUGIN_USB_CONNECTED; - - } - } - } - else -#endif - { - rb->splash(HZ, "Out of Memory"); - rb->close(fd); - return PLUGIN_ERROR; - } - } - - if(!running_slideshow) - { -#if LCD_DEPTH > 1 - rb->lcd_set_foreground(LCD_WHITE); - rb->lcd_set_background(LCD_BLACK); - rb->lcd_set_backdrop(NULL); -#endif - - rb->lcd_clear_display(); - rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); - rb->lcd_puts(0, 0, print); - rb->lcd_update(); - - rb->snprintf(print, sizeof(print), "loading %d bytes", filesize); - rb->lcd_puts(0, 1, print); - rb->lcd_update(); - } - - rb->read(fd, buf_jpeg, filesize); - rb->close(fd); - - if(!running_slideshow) - { - rb->snprintf(print, sizeof(print), "decoding markers"); - rb->lcd_puts(0, 2, print); - rb->lcd_update(); - } -#ifndef SIMULATOR - else if(immediate_ata_off) - { - /* running slideshow and time is long enough: power down disk */ - rb->ata_sleep(); - } -#endif - - rb->memset(&jpg, 0, sizeof(jpg)); /* clear info struct */ - /* process markers, unstuffing */ - status = process_markers(buf_jpeg, filesize, &jpg); - - if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0)) - { /* bad format or minimum components not contained */ - rb->splashf(HZ, "unsupported %d", status); - file_pt[curfile] = '\0'; - return change_filename(direction); - } - - if (!(status & DHT)) /* if no Huffman table present: */ - default_huff_tbl(&jpg); /* use default */ - build_lut(&jpg); /* derive Huffman and other lookup-tables */ - - if(!running_slideshow) - { - rb->snprintf(print, sizeof(print), "image %dx%d", jpg.x_size, jpg.y_size); - rb->lcd_puts(0, 2, print); - rb->lcd_update(); - } - ds_max = max_downscale(&jpg); /* check display constraint */ - ds_min = min_downscale(&jpg, buf_size); /* check memory constraint */ - if (ds_min == 0) - { - rb->splash(HZ, "too large"); - file_pt[curfile] = '\0'; - return change_filename(direction); - } - - ds = ds_max; /* initials setting */ - cx = jpg.x_size/ds/2; /* center the view */ - cy = jpg.y_size/ds/2; - - do /* loop the image prepare and decoding when zoomed */ - { - p_disp = get_image(&jpg, ds); /* decode or fetch from cache */ - if (p_disp == NULL) - return change_filename(direction); - - set_view(p_disp, cx, cy); - - if(!running_slideshow) - { - rb->snprintf(print, sizeof(print), "showing %dx%d", - p_disp->width, p_disp->height); - rb->lcd_puts(0, 3, print); - rb->lcd_update(); - } - MYLCD(clear_display)(); -#ifdef HAVE_LCD_COLOR - yuv_bitmap_part( - p_disp->bitmap, p_disp->csub_x, p_disp->csub_y, - p_disp->x, p_disp->y, p_disp->stride, - MAX(0, (LCD_WIDTH - p_disp->width) / 2), - MAX(0, (LCD_HEIGHT - p_disp->height) / 2), - MIN(LCD_WIDTH, p_disp->width), - MIN(LCD_HEIGHT, p_disp->height)); -#else - MYXLCD(gray_bitmap_part)( - p_disp->bitmap[0], p_disp->x, p_disp->y, p_disp->stride, - MAX(0, (LCD_WIDTH - p_disp->width) / 2), - MAX(0, (LCD_HEIGHT - p_disp->height) / 2), - MIN(LCD_WIDTH, p_disp->width), - MIN(LCD_HEIGHT, p_disp->height)); -#endif - MYLCD_UPDATE(); - -#ifdef USEGSLIB - grey_show(true); /* switch on greyscale overlay */ -#endif - - /* drawing is now finished, play around with scrolling - * until you press OFF or connect USB - */ - while (1) - { - status = scroll_bmp(p_disp); - if (status == ZOOM_IN) - { - if (ds > ds_min) - { - ds /= 2; /* reduce downscaling to zoom in */ - get_view(p_disp, &cx, &cy); - cx *= 2; /* prepare the position in the new image */ - cy *= 2; - } - else - continue; - } - - if (status == ZOOM_OUT) - { - if (ds < ds_max) - { - ds *= 2; /* increase downscaling to zoom out */ - get_view(p_disp, &cx, &cy); - cx /= 2; /* prepare the position in the new image */ - cy /= 2; - } - else - continue; - } - break; - } - -#ifdef USEGSLIB - grey_show(false); /* switch off overlay */ -#endif - rb->lcd_clear_display(); - } - while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED - && status != PLUGIN_OTHER); -#ifdef USEGSLIB - rb->lcd_update(); -#endif - return status; -} - -/******************** Plugin entry point *********************/ - -enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) -{ - rb = api; - - int condition; -#ifdef USEGSLIB - long greysize; /* helper */ -#endif -#if LCD_DEPTH > 1 - old_backdrop = rb->lcd_get_backdrop(); -#endif - - if(!parameter) return PLUGIN_ERROR; - - rb->strcpy(np_file, parameter); - get_pic_list(); - - if(!entries) return PLUGIN_ERROR; - -#if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR) - if(rb->audio_status()) - { - buf = rb->plugin_get_buffer((size_t *)&buf_size) + - (entries * sizeof(char**)); - buf_size -= (entries * sizeof(char**)); - plug_buf = true; - } - else - buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); -#else - buf = rb->plugin_get_audio_buffer(&buf_size) + - (entries * sizeof(char**)); - buf_size -= (entries * sizeof(char**)); -#endif - -#ifdef USEGSLIB - if (!grey_init(rb, buf, buf_size, GREY_ON_COP, - LCD_WIDTH, LCD_HEIGHT, &greysize)) - { - rb->splash(HZ, "grey buf error"); - return PLUGIN_ERROR; - } - buf += greysize; - buf_size -= greysize; -#else - xlcd_init(rb); -#endif - - /* should be ok to just load settings since a parameter is present - here and the drive should be spinning */ - configfile_init(rb); - configfile_load(JPEG_CONFIGFILE, jpeg_config, - ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION); - old_settings = jpeg_settings; - - buf_images = buf; buf_images_size = buf_size; - - /* Turn off backlight timeout */ - backlight_force_on(rb); /* backlight control in lib/helper.c */ - - do - { - condition = load_and_show(np_file); - }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED - && condition != PLUGIN_ERROR); - - if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings))) - { - /* Just in case drive has to spin, keep it from looking locked */ - rb->splash(0, "Saving Settings"); - configfile_save(JPEG_CONFIGFILE, jpeg_config, - ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION); - } - -#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) - /* set back ata spindown time in case we changed it */ - rb->ata_spindown(rb->global_settings->disk_spindown); -#endif - - /* Turn on backlight timeout (revert to settings) */ - backlight_use_settings(rb); /* backlight control in lib/helper.c */ - -#ifdef USEGSLIB - grey_release(); /* deinitialize */ -#endif - - return condition; -} - -#endif /* HAVE_LCD_BITMAP */ diff --git a/apps/plugins/jpeg/Makefile b/apps/plugins/jpeg/Makefile new file mode 100644 index 0000000000..83207258b5 --- /dev/null +++ b/apps/plugins/jpeg/Makefile @@ -0,0 +1,75 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +INCLUDES = -I$(APPSDIR) -I.. -I. $(TARGET_INC) -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ + -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(OUTDIR) -I$(BUILDDIR) \ + -I$(BUILDDIR)/pluginbitmaps -I$(APPSDIR)/plugins/lib +CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \ + -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN + +ifdef APPEXTRA + INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) +endif + +LINKFILE := $(OBJDIR)/link.lds +DEPFILE = $(OBJDIR)/dep-jpeg + +# This sets up 'SRC' based on the files mentioned in SOURCES +include $(TOOLSDIR)/makesrc.inc + +SOURCES = $(SRC) +OBJS := $(SRC:%.c=$(OBJDIR)/%.o) +DIRS = . + +ifndef SIMVER + LDS := ../plugin.lds + OUTPUT = $(OUTDIR)/jpeg.rock +else ## simulators + OUTPUT = $(OUTDIR)/jpeg.rock +endif + +all: $(OUTPUT) + +ifndef SIMVER +$(OBJDIR)/jpeg.elf: $(OBJS) $(LINKFILE) $(BITMAPLIBS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) -o $@ $(OBJS) -L$(BUILDDIR) -lplugin -lgcc \ + $(LINKBITMAPS) -T$(LINKFILE) -Wl,--gc-sections -Wl,-Map,$(OBJDIR)/jpeg.map + +$(OUTPUT): $(OBJDIR)/jpeg.elf + $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ +else +################################################### +# This is the SDL simulator version + +$(OUTPUT): $(OBJS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif + +endif # end of simulator section + + +include $(TOOLSDIR)/make.inc + +# MEMORYSIZE should be passed on to this makefile with the chosen memory size +# given in number of MB +$(LINKFILE): $(LDS) + $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \ + $(DEFINES) -E -P - >$@ + +clean: + $(call PRINTS,cleaning jpeg)rm -rf $(OBJDIR)/jpeg + $(SILENT)rm -f $(OBJDIR)/jpeg.* $(DEPFILE) + +-include $(DEPFILE) diff --git a/apps/plugins/jpeg/SOURCES b/apps/plugins/jpeg/SOURCES new file mode 100644 index 0000000000..c3524001e2 --- /dev/null +++ b/apps/plugins/jpeg/SOURCES @@ -0,0 +1,5 @@ +jpeg.c +jpeg_decoder.c +#ifdef HAVE_LCD_COLOR +yuv2rgb.c +#endif diff --git a/apps/plugins/jpeg/jpeg.c b/apps/plugins/jpeg/jpeg.c new file mode 100644 index 0000000000..d70fca809f --- /dev/null +++ b/apps/plugins/jpeg/jpeg.c @@ -0,0 +1,1235 @@ +/*************************************************************************** +* __________ __ ___. +* 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 <lib/playback_control.h> +#include <lib/oldmenuapi.h> +#include <lib/helper.h> +#include <lib/configfile.h> + +#include <lib/grey.h> +#include <lib/xlcd.h> + +#include "jpeg.h" +#include "jpeg_decoder.h" + +PLUGIN_HEADER + +#ifdef HAVE_LCD_COLOR +#include "yuv2rgb.h" +#endif + +/* different graphics libraries */ +#if LCD_DEPTH < 8 +#define USEGSLIB +GREY_INFO_STRUCT +#define MYLCD(fn) grey_ub_ ## fn +#define MYLCD_UPDATE() +#define MYXLCD(fn) grey_ub_ ## fn +#else +#define MYLCD(fn) rb->lcd_ ## fn +#define MYLCD_UPDATE() rb->lcd_update(); +#define MYXLCD(fn) xlcd_ ## fn +#endif + +#define MAX_X_SIZE LCD_WIDTH*8 + +/* Min memory allowing us to use the plugin buffer + * and thus not stopping the music + * *Very* rough estimation: + * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes + * + 20k code size = 60 000 + * + 50k min for jpeg = 120 000 + */ +#define MIN_MEM 120000 + +/* Headings */ +#define DIR_PREV 1 +#define DIR_NEXT -1 +#define DIR_NONE 0 + +#define PLUGIN_OTHER 10 /* State code for output with return. */ + +/******************************* Globals ***********************************/ + +const struct plugin_api* rb; +MEM_FUNCTION_WRAPPERS(rb); + +static int slideshow_enabled = false; /* run slideshow */ +static int running_slideshow = false; /* loading image because of slideshw */ +#ifndef SIMULATOR +static int immediate_ata_off = false; /* power down disk after loading */ +#endif + +#ifdef HAVE_LCD_COLOR +fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when + DITHER_DIFFUSION is set */ +#endif + + +/* Persistent configuration */ +#define JPEG_CONFIGFILE "jpeg.cfg" +#define JPEG_SETTINGS_MINVERSION 1 +#define JPEG_SETTINGS_VERSION 2 + +/* Slideshow times */ +#define SS_MIN_TIMEOUT 1 +#define SS_MAX_TIMEOUT 20 +#define SS_DEFAULT_TIMEOUT 5 + +struct jpeg_settings +{ +#ifdef HAVE_LCD_COLOR + int colour_mode; + int dither_mode; +#endif + int ss_timeout; +}; + +static struct jpeg_settings jpeg_settings = + { +#ifdef HAVE_LCD_COLOR + COLOURMODE_COLOUR, + DITHER_NONE, +#endif + SS_DEFAULT_TIMEOUT + }; +static struct jpeg_settings old_settings; + +static struct configdata jpeg_config[] = +{ +#ifdef HAVE_LCD_COLOR + { TYPE_ENUM, 0, COLOUR_NUM_MODES, &jpeg_settings.colour_mode, + "Colour Mode", (char *[]){ "Colour", "Grayscale" }, NULL }, + { TYPE_ENUM, 0, DITHER_NUM_MODES, &jpeg_settings.dither_mode, + "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" }, NULL }, +#endif + { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, &jpeg_settings.ss_timeout, + "Slideshow Time", NULL, NULL}, +}; + +#if LCD_DEPTH > 1 +fb_data* old_backdrop; +#endif + +/**************** begin Application ********************/ + + +/************************* Types ***************************/ + +struct t_disp +{ +#ifdef HAVE_LCD_COLOR + unsigned char* bitmap[3]; /* Y, Cr, Cb */ + int csub_x, csub_y; +#else + unsigned char* bitmap[1]; /* Y only */ +#endif + int width; + int height; + int stride; + int x, y; +}; + +/************************* Globals ***************************/ + +/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ +struct t_disp disp[9]; + +/* my memory pool (from the mp3 buffer) */ +char print[32]; /* use a common snprintf() buffer */ +unsigned char* buf; /* up to here currently used by image(s) */ + +/* the remaining free part of the buffer for compressed+uncompressed images */ +unsigned char* buf_images; + +ssize_t buf_size, buf_images_size; +/* the root of the images, hereafter are decompresed ones */ +unsigned char* buf_root; +int root_size; + +int ds, ds_min, ds_max; /* downscaling and limits */ +static struct jpeg jpg; /* too large for stack */ + +static struct tree_context *tree; + +/* the current full file name */ +static char np_file[MAX_PATH]; +int curfile = 0, direction = DIR_NONE, entries = 0; + +/* list of the jpeg files */ +char **file_pt; +/* are we using the plugin buffer or the audio buffer? */ +bool plug_buf = false; + + +/************************* Implementation ***************************/ + +/* support function for qsort() */ +static int compare(const void* p1, const void* p2) +{ + return rb->strcasecmp(*((char **)p1), *((char **)p2)); +} + +bool jpg_ext(const char ext[]) +{ + if(!ext) + return false; + if(!rb->strcasecmp(ext,".jpg") || + !rb->strcasecmp(ext,".jpe") || + !rb->strcasecmp(ext,".jpeg")) + return true; + else + return false; +} + +/*Read directory contents for scrolling. */ +void get_pic_list(void) +{ + int i; + long int str_len = 0; + char *pname; + tree = rb->tree_get_context(); + +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + file_pt = rb->plugin_get_buffer((size_t *)&buf_size); +#else + file_pt = rb->plugin_get_audio_buffer((size_t *)&buf_size); +#endif + + for(i = 0; i < tree->filesindir; i++) + { + if(jpg_ext(rb->strrchr(&tree->name_buffer[str_len],'.'))) + file_pt[entries++] = &tree->name_buffer[str_len]; + + str_len += rb->strlen(&tree->name_buffer[str_len]) + 1; + } + + rb->qsort(file_pt, entries, sizeof(char**), compare); + + /* Remove path and leave only the name.*/ + pname = rb->strrchr(np_file,'/'); + pname++; + + /* Find Selected File. */ + for(i = 0; i < entries; i++) + if(!rb->strcmp(file_pt[i], pname)) + curfile = i; +} + +int change_filename(int direct) +{ + int count = 0; + direction = direct; + + if(direct == DIR_PREV) + { + do + { + count++; + if(curfile == 0) + curfile = entries - 1; + else + curfile--; + }while(file_pt[curfile] == '\0' && count < entries); + /* we "erase" the file name if we encounter + * a non-supported file, so skip it now */ + } + else /* DIR_NEXT/DIR_NONE */ + { + do + { + count++; + if(curfile == entries - 1) + curfile = 0; + else + curfile++; + }while(file_pt[curfile] == '\0' && count < entries); + } + + if(count == entries && file_pt[curfile] == '\0') + { + rb->splash(HZ, "No supported files"); + return PLUGIN_ERROR; + } + if(rb->strlen(tree->currdir) > 1) + { + rb->strcpy(np_file, tree->currdir); + rb->strcat(np_file, "/"); + } + else + rb->strcpy(np_file, tree->currdir); + + rb->strcat(np_file, file_pt[curfile]); + + return PLUGIN_OTHER; +} + +/* switch off overlay, for handling SYS_ events */ +void cleanup(void *parameter) +{ + (void)parameter; +#ifdef USEGSLIB + grey_show(false); +#endif +} + +#define VSCROLL (LCD_HEIGHT/8) +#define HSCROLL (LCD_WIDTH/10) + +#define ZOOM_IN 100 /* return codes for below function */ +#define ZOOM_OUT 101 + +#ifdef HAVE_LCD_COLOR +bool set_option_grayscale(void) +{ + bool gray = jpeg_settings.colour_mode == COLOURMODE_GRAY; + rb->set_bool("Grayscale", &gray); + jpeg_settings.colour_mode = gray ? COLOURMODE_GRAY : COLOURMODE_COLOUR; + return false; +} + +bool set_option_dithering(void) +{ + static const struct opt_items dithering[DITHER_NUM_MODES] = { + [DITHER_NONE] = { "Off", -1 }, + [DITHER_ORDERED] = { "Ordered", -1 }, + [DITHER_DIFFUSION] = { "Diffusion", -1 }, + }; + + rb->set_option("Dithering", &jpeg_settings.dither_mode, INT, + dithering, DITHER_NUM_MODES, NULL); + return false; +} + +static void display_options(void) +{ + static const struct menu_item items[] = { + { "Grayscale", set_option_grayscale }, + { "Dithering", set_option_dithering }, + }; + + int m = menu_init(rb, items, ARRAYLEN(items), + NULL, NULL, NULL, NULL); + menu_run(m); + menu_exit(m); +} +#endif /* HAVE_LCD_COLOR */ + +int show_menu(void) /* return 1 to quit */ +{ +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(old_backdrop); +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(rb->global_settings->fg_color); + rb->lcd_set_background(rb->global_settings->bg_color); +#else + rb->lcd_set_foreground(LCD_BLACK); + rb->lcd_set_background(LCD_WHITE); +#endif +#endif + int m; + int result; + + enum menu_id + { + MIID_QUIT = 0, + MIID_TOGGLE_SS_MODE, + MIID_CHANGE_SS_MODE, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + MIID_SHOW_PLAYBACK_MENU, +#endif +#ifdef HAVE_LCD_COLOR + MIID_DISPLAY_OPTIONS, +#endif + MIID_RETURN, + }; + + static const struct menu_item items[] = { + [MIID_QUIT] = + { "Quit", NULL }, + [MIID_TOGGLE_SS_MODE] = + { "Toggle Slideshow Mode", NULL }, + [MIID_CHANGE_SS_MODE] = + { "Change Slideshow Time", NULL }, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + [MIID_SHOW_PLAYBACK_MENU] = + { "Show Playback Menu", NULL }, +#endif +#ifdef HAVE_LCD_COLOR + [MIID_DISPLAY_OPTIONS] = + { "Display Options", NULL }, +#endif + [MIID_RETURN] = + { "Return", NULL }, + }; + + static const struct opt_items slideshow[2] = { + { "Disable", -1 }, + { "Enable", -1 }, + }; + + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + result=menu_show(m); + + switch (result) + { + case MIID_QUIT: + menu_exit(m); + return 1; + break; + case MIID_TOGGLE_SS_MODE: + rb->set_option("Toggle Slideshow", &slideshow_enabled, INT, + slideshow , 2, NULL); + break; + case MIID_CHANGE_SS_MODE: + rb->set_int("Slideshow Time", "s", UNIT_SEC, + &jpeg_settings.ss_timeout, NULL, 1, + SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL); + break; + +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + case MIID_SHOW_PLAYBACK_MENU: + if (plug_buf) + { + playback_control(rb, NULL); + } + else + { + rb->splash(HZ, "Cannot restart playback"); + } + break; +#endif +#ifdef HAVE_LCD_COLOR + case MIID_DISPLAY_OPTIONS: + display_options(); + break; +#endif + case MIID_RETURN: + break; + } + +#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) + /* change ata spindown time based on slideshow time setting */ + immediate_ata_off = false; + rb->ata_spindown(rb->global_settings->disk_spindown); + + if (slideshow_enabled) + { + if(jpeg_settings.ss_timeout < 10) + { + /* slideshow times < 10s keep disk spinning */ + rb->ata_spindown(0); + } + else if (!rb->mp3_is_playing()) + { + /* slideshow times > 10s and not playing: ata_off after load */ + immediate_ata_off = true; + } + } +#endif +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(NULL); + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); +#endif + rb->lcd_clear_display(); + menu_exit(m); + return 0; +} +/* interactively scroll around the image */ +int scroll_bmp(struct t_disp* pdisp) +{ + int lastbutton = 0; + + while (true) + { + int button; + int move; + + if (slideshow_enabled) + button = rb->button_get_w_tmo(jpeg_settings.ss_timeout * HZ); + else button = rb->button_get(true); + + running_slideshow = false; + + switch(button) + { + case JPEG_LEFT: + if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE) + return change_filename(DIR_PREV); + case JPEG_LEFT | BUTTON_REPEAT: + move = MIN(HSCROLL, pdisp->x); + if (move > 0) + { + MYXLCD(scroll_right)(move); /* scroll right */ + pdisp->x -= move; +#ifdef HAVE_LCD_COLOR + yuv_bitmap_part( + pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, + pdisp->x, pdisp->y, pdisp->stride, + 0, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ + move, MIN(LCD_HEIGHT, pdisp->height), /* w, h */ + jpeg_settings.colour_mode, jpeg_settings.dither_mode); +#else + MYXLCD(gray_bitmap_part)( + pdisp->bitmap[0], pdisp->x, pdisp->y, pdisp->stride, + 0, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ + move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ +#endif + MYLCD_UPDATE(); + } + break; + + case JPEG_RIGHT: + if (!(ds < ds_max) && entries > 0 && jpg.x_size <= MAX_X_SIZE) + return change_filename(DIR_NEXT); + case JPEG_RIGHT | BUTTON_REPEAT: + move = MIN(HSCROLL, pdisp->width - pdisp->x - LCD_WIDTH); + if (move > 0) + { + MYXLCD(scroll_left)(move); /* scroll left */ + pdisp->x += move; +#ifdef HAVE_LCD_COLOR + yuv_bitmap_part( + pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, + pdisp->x + LCD_WIDTH - move, pdisp->y, pdisp->stride, + LCD_WIDTH - move, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ + move, MIN(LCD_HEIGHT, pdisp->height), /* w, h */ + jpeg_settings.colour_mode, jpeg_settings.dither_mode); +#else + MYXLCD(gray_bitmap_part)( + pdisp->bitmap[0], pdisp->x + LCD_WIDTH - move, + pdisp->y, pdisp->stride, + LCD_WIDTH - move, MAX(0, (LCD_HEIGHT-pdisp->height)/2), /* x, y */ + move, MIN(LCD_HEIGHT, pdisp->height)); /* w, h */ +#endif + MYLCD_UPDATE(); + } + break; + + case JPEG_UP: + case JPEG_UP | BUTTON_REPEAT: + move = MIN(VSCROLL, pdisp->y); + if (move > 0) + { + MYXLCD(scroll_down)(move); /* scroll down */ + pdisp->y -= move; +#ifdef HAVE_LCD_COLOR + if (jpeg_settings.dither_mode == DITHER_DIFFUSION) + { + /* Draw over the band at the top of the last update + caused by lack of error history on line zero. */ + move = MIN(move + 1, pdisp->y + pdisp->height); + } + + yuv_bitmap_part( + pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, + pdisp->x, pdisp->y, pdisp->stride, + MAX(0, (LCD_WIDTH-pdisp->width)/2), 0, /* x, y */ + MIN(LCD_WIDTH, pdisp->width), move, /* w, h */ + jpeg_settings.colour_mode, jpeg_settings.dither_mode); +#else + MYXLCD(gray_bitmap_part)( + pdisp->bitmap[0], pdisp->x, pdisp->y, pdisp->stride, + MAX(0, (LCD_WIDTH-pdisp->width)/2), 0, /* x, y */ + MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ +#endif + MYLCD_UPDATE(); + } + break; + + case JPEG_DOWN: + case JPEG_DOWN | BUTTON_REPEAT: + move = MIN(VSCROLL, pdisp->height - pdisp->y - LCD_HEIGHT); + if (move > 0) + { + MYXLCD(scroll_up)(move); /* scroll up */ + pdisp->y += move; +#ifdef HAVE_LCD_COLOR + if (jpeg_settings.dither_mode == DITHER_DIFFUSION) + { + /* Save the line that was on the last line of the display + and draw one extra line above then recover the line with + image data that had an error history when it was drawn. + */ + move++, pdisp->y--; + rb->memcpy(rgb_linebuf, + rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, + LCD_WIDTH*sizeof (fb_data)); + } + + yuv_bitmap_part( + pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, pdisp->x, + pdisp->y + LCD_HEIGHT - move, pdisp->stride, + MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */ + MIN(LCD_WIDTH, pdisp->width), move, /* w, h */ + jpeg_settings.colour_mode, jpeg_settings.dither_mode); + + if (jpeg_settings.dither_mode == DITHER_DIFFUSION) + { + /* Cover the first row drawn with previous image data. */ + rb->memcpy(rb->lcd_framebuffer + (LCD_HEIGHT - move)*LCD_WIDTH, + rgb_linebuf, + LCD_WIDTH*sizeof (fb_data)); + pdisp->y++; + } +#else + MYXLCD(gray_bitmap_part)( + pdisp->bitmap[0], pdisp->x, + pdisp->y + LCD_HEIGHT - move, pdisp->stride, + MAX(0, (LCD_WIDTH-pdisp->width)/2), LCD_HEIGHT - move, /* x, y */ + MIN(LCD_WIDTH, pdisp->width), move); /* w, h */ +#endif + MYLCD_UPDATE(); + } + break; + case BUTTON_NONE: + if (!slideshow_enabled) + break; + running_slideshow = true; + if (entries > 0) + return change_filename(DIR_NEXT); + break; + +#ifdef JPEG_SLIDE_SHOW + case JPEG_SLIDE_SHOW: + slideshow_enabled = !slideshow_enabled; + running_slideshow = slideshow_enabled; + break; +#endif + +#ifdef JPEG_NEXT_REPEAT + case JPEG_NEXT_REPEAT: +#endif + case JPEG_NEXT: + if (entries > 0) + return change_filename(DIR_NEXT); + break; + +#ifdef JPEG_PREVIOUS_REPEAT + case JPEG_PREVIOUS_REPEAT: +#endif + case JPEG_PREVIOUS: + if (entries > 0) + return change_filename(DIR_PREV); + break; + + case JPEG_ZOOM_IN: +#ifdef JPEG_ZOOM_PRE + if (lastbutton != JPEG_ZOOM_PRE) + break; +#endif + return ZOOM_IN; + break; + + case JPEG_ZOOM_OUT: +#ifdef JPEG_ZOOM_PRE + if (lastbutton != JPEG_ZOOM_PRE) + break; +#endif + return ZOOM_OUT; + break; +#ifdef JPEG_RC_MENU + case JPEG_RC_MENU: +#endif + case JPEG_MENU: +#ifdef USEGSLIB + grey_show(false); /* switch off greyscale overlay */ +#endif + if (show_menu() == 1) + return PLUGIN_OK; + +#ifdef USEGSLIB + grey_show(true); /* switch on greyscale overlay */ +#else + yuv_bitmap_part( + pdisp->bitmap, pdisp->csub_x, pdisp->csub_y, + pdisp->x, pdisp->y, pdisp->stride, + MAX(0, (LCD_WIDTH - pdisp->width) / 2), + MAX(0, (LCD_HEIGHT - pdisp->height) / 2), + MIN(LCD_WIDTH, pdisp->width), + MIN(LCD_HEIGHT, pdisp->height), + jpeg_settings.colour_mode, jpeg_settings.dither_mode); + MYLCD_UPDATE(); +#endif + break; + default: + if (rb->default_event_handler_ex(button, cleanup, NULL) + == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + break; + + } /* switch */ + + if (button != BUTTON_NONE) + lastbutton = button; + } /* while (true) */ +} + +/********************* main function *************************/ + +/* callback updating a progress meter while JPEG decoding */ +void cb_progess(int current, int total) +{ + rb->yield(); /* be nice to the other threads */ + if(!running_slideshow) + { + rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0, + current, HORIZONTAL); + rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8); + } +#ifndef USEGSLIB + else + { + /* in slideshow mode, keep gui interference to a minimum */ + rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0, + current, HORIZONTAL); + rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4); + } +#endif +} + +int jpegmem(struct jpeg *p_jpg, int ds) +{ + int size; + + size = (p_jpg->x_phys/ds/p_jpg->subsample_x[0]) + * (p_jpg->y_phys/ds/p_jpg->subsample_y[0]); +#ifdef HAVE_LCD_COLOR + if (p_jpg->blocks > 1) /* colour, add requirements for chroma */ + { + size += (p_jpg->x_phys/ds/p_jpg->subsample_x[1]) + * (p_jpg->y_phys/ds/p_jpg->subsample_y[1]); + size += (p_jpg->x_phys/ds/p_jpg->subsample_x[2]) + * (p_jpg->y_phys/ds/p_jpg->subsample_y[2]); + } +#endif + return size; +} + +/* how far can we zoom in without running out of memory */ +int min_downscale(struct jpeg *p_jpg, int bufsize) +{ + int downscale = 8; + + if (jpegmem(p_jpg, 8) > bufsize) + return 0; /* error, too large, even 1:8 doesn't fit */ + + while (downscale > 1 && jpegmem(p_jpg, downscale/2) <= bufsize) + downscale /= 2; + + return downscale; +} + + +/* how far can we zoom out, to fit image into the LCD */ +int max_downscale(struct jpeg *p_jpg) +{ + int downscale = 1; + + while (downscale < 8 && (p_jpg->x_size > LCD_WIDTH*downscale + || p_jpg->y_size > LCD_HEIGHT*downscale)) + { + downscale *= 2; + } + + return downscale; +} + + +/* return decoded or cached image */ +struct t_disp* get_image(struct jpeg* p_jpg, int ds) +{ + int w, h; /* used to center output */ + int size; /* decompressed image size */ + long time; /* measured ticks */ + int status; + + struct t_disp* p_disp = &disp[ds]; /* short cut */ + + if (p_disp->bitmap[0] != NULL) + { + return p_disp; /* we still have it */ + } + + /* assign image buffer */ + + /* physical size needed for decoding */ + size = jpegmem(p_jpg, ds); + if (buf_size <= size) + { /* have to discard the current */ + int i; + for (i=1; i<=8; i++) + disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */ + buf = buf_root; /* start again from the beginning of the buffer */ + buf_size = root_size; + } + +#ifdef HAVE_LCD_COLOR + if (p_jpg->blocks > 1) /* colour jpeg */ + { + int i; + + for (i = 1; i < 3; i++) + { + size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i]) + * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]); + p_disp->bitmap[i] = buf; + buf += size; + buf_size -= size; + } + p_disp->csub_x = p_jpg->subsample_x[1]; + p_disp->csub_y = p_jpg->subsample_y[1]; + } + else + { + p_disp->csub_x = p_disp->csub_y = 0; + p_disp->bitmap[1] = p_disp->bitmap[2] = buf; + } +#endif + /* size may be less when decoded (if height is not block aligned) */ + size = (p_jpg->x_phys/ds) * (p_jpg->y_size / ds); + p_disp->bitmap[0] = buf; + buf += size; + buf_size -= size; + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "decoding %d*%d", + p_jpg->x_size/ds, p_jpg->y_size/ds); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + + /* update image properties */ + p_disp->width = p_jpg->x_size / ds; + p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */ + p_disp->height = p_jpg->y_size / ds; + + /* the actual decoding */ + time = *rb->current_tick; +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); + status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess); + rb->cpu_boost(false); +#else + status = jpeg_decode(p_jpg, p_disp->bitmap, ds, cb_progess); +#endif + if (status) + { + rb->splashf(HZ, "decode error %d", status); + file_pt[curfile] = '\0'; + return NULL; + } + time = *rb->current_tick - time; + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); + rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ + rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); + rb->lcd_update(); + } + + return p_disp; +} + + +/* set the view to the given center point, limit if necessary */ +void set_view (struct t_disp* p_disp, int cx, int cy) +{ + int x, y; + + /* plain center to available width/height */ + x = cx - MIN(LCD_WIDTH, p_disp->width) / 2; + y = cy - MIN(LCD_HEIGHT, p_disp->height) / 2; + + /* limit against upper image size */ + x = MIN(p_disp->width - LCD_WIDTH, x); + y = MIN(p_disp->height - LCD_HEIGHT, y); + + /* limit against negative side */ + x = MAX(0, x); + y = MAX(0, y); + + p_disp->x = x; /* set the values */ + p_disp->y = y; +} + + +/* calculate the view center based on the bitmap position */ +void get_view(struct t_disp* p_disp, int* p_cx, int* p_cy) +{ + *p_cx = p_disp->x + MIN(LCD_WIDTH, p_disp->width) / 2; + *p_cy = p_disp->y + MIN(LCD_HEIGHT, p_disp->height) / 2; +} + + +/* load, decode, display the image */ +int load_and_show(char* filename) +{ + int fd; + int filesize; + unsigned char* buf_jpeg; /* compressed JPEG image */ + int status; + struct t_disp* p_disp; /* currenly displayed image */ + int cx, cy; /* view center */ + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + { + rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd); + rb->splash(HZ, print); + return PLUGIN_ERROR; + } + filesize = rb->filesize(fd); + rb->memset(&disp, 0, sizeof(disp)); + + buf = buf_images + filesize; + buf_size = buf_images_size - filesize; + /* allocate JPEG buffer */ + buf_jpeg = buf_images; + + buf_root = buf; /* we can start the decompressed images behind it */ + root_size = buf_size; + + if (buf_size <= 0) + { +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + if(plug_buf) + { + rb->close(fd); + rb->lcd_setfont(FONT_SYSFIXED); + rb->lcd_clear_display(); + rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1); + rb->lcd_puts(0,0,print); + rb->lcd_puts(0,1,"Not enough plugin memory!"); + rb->lcd_puts(0,2,"Zoom In: Stop playback."); + if(entries>1) + rb->lcd_puts(0,3,"Left/Right: Skip File."); + rb->lcd_puts(0,4,"Off: Quit."); + rb->lcd_update(); + rb->lcd_setfont(FONT_UI); + + rb->button_clear_queue(); + + while (1) + { + int button = rb->button_get(true); + switch(button) + { + case JPEG_ZOOM_IN: + plug_buf = false; + buf_images = rb->plugin_get_audio_buffer( + (size_t *)&buf_images_size); + /*try again this file, now using the audio buffer */ + return PLUGIN_OTHER; +#ifdef JPEG_RC_MENU + case JPEG_RC_MENU: +#endif + case JPEG_MENU: + return PLUGIN_OK; + + case JPEG_LEFT: + if(entries>1) + { + rb->lcd_clear_display(); + return change_filename(DIR_PREV); + } + break; + + case JPEG_RIGHT: + if(entries>1) + { + rb->lcd_clear_display(); + return change_filename(DIR_NEXT); + } + break; + default: + if(rb->default_event_handler_ex(button, cleanup, NULL) + == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + + } + } + } + else +#endif + { + rb->splash(HZ, "Out of Memory"); + rb->close(fd); + return PLUGIN_ERROR; + } + } + + if(!running_slideshow) + { +#if LCD_DEPTH > 1 + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); + rb->lcd_set_backdrop(NULL); +#endif + + rb->lcd_clear_display(); + rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1); + rb->lcd_puts(0, 0, print); + rb->lcd_update(); + + rb->snprintf(print, sizeof(print), "loading %d bytes", filesize); + rb->lcd_puts(0, 1, print); + rb->lcd_update(); + } + + rb->read(fd, buf_jpeg, filesize); + rb->close(fd); + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "decoding markers"); + rb->lcd_puts(0, 2, print); + rb->lcd_update(); + } +#ifndef SIMULATOR + else if(immediate_ata_off) + { + /* running slideshow and time is long enough: power down disk */ + rb->ata_sleep(); + } +#endif + + rb->memset(&jpg, 0, sizeof(jpg)); /* clear info struct */ + /* process markers, unstuffing */ + status = process_markers(buf_jpeg, filesize, &jpg); + + if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0)) + { /* bad format or minimum components not contained */ + rb->splashf(HZ, "unsupported %d", status); + file_pt[curfile] = '\0'; + return change_filename(direction); + } + + if (!(status & DHT)) /* if no Huffman table present: */ + default_huff_tbl(&jpg); /* use default */ + build_lut(&jpg); /* derive Huffman and other lookup-tables */ + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "image %dx%d", jpg.x_size, jpg.y_size); + rb->lcd_puts(0, 2, print); + rb->lcd_update(); + } + ds_max = max_downscale(&jpg); /* check display constraint */ + ds_min = min_downscale(&jpg, buf_size); /* check memory constraint */ + if (ds_min == 0) + { + rb->splash(HZ, "too large"); + file_pt[curfile] = '\0'; + return change_filename(direction); + } + + ds = ds_max; /* initials setting */ + cx = jpg.x_size/ds/2; /* center the view */ + cy = jpg.y_size/ds/2; + + do /* loop the image prepare and decoding when zoomed */ + { + p_disp = get_image(&jpg, ds); /* decode or fetch from cache */ + if (p_disp == NULL) + return change_filename(direction); + + set_view(p_disp, cx, cy); + + if(!running_slideshow) + { + rb->snprintf(print, sizeof(print), "showing %dx%d", + p_disp->width, p_disp->height); + rb->lcd_puts(0, 3, print); + rb->lcd_update(); + } + MYLCD(clear_display)(); +#ifdef HAVE_LCD_COLOR + yuv_bitmap_part( + p_disp->bitmap, p_disp->csub_x, p_disp->csub_y, + p_disp->x, p_disp->y, p_disp->stride, + MAX(0, (LCD_WIDTH - p_disp->width) / 2), + MAX(0, (LCD_HEIGHT - p_disp->height) / 2), + MIN(LCD_WIDTH, p_disp->width), + MIN(LCD_HEIGHT, p_disp->height), + jpeg_settings.colour_mode, jpeg_settings.dither_mode); +#else + MYXLCD(gray_bitmap_part)( + p_disp->bitmap[0], p_disp->x, p_disp->y, p_disp->stride, + MAX(0, (LCD_WIDTH - p_disp->width) / 2), + MAX(0, (LCD_HEIGHT - p_disp->height) / 2), + MIN(LCD_WIDTH, p_disp->width), + MIN(LCD_HEIGHT, p_disp->height)); +#endif + MYLCD_UPDATE(); + +#ifdef USEGSLIB + grey_show(true); /* switch on greyscale overlay */ +#endif + + /* drawing is now finished, play around with scrolling + * until you press OFF or connect USB + */ + while (1) + { + status = scroll_bmp(p_disp); + if (status == ZOOM_IN) + { + if (ds > ds_min) + { + ds /= 2; /* reduce downscaling to zoom in */ + get_view(p_disp, &cx, &cy); + cx *= 2; /* prepare the position in the new image */ + cy *= 2; + } + else + continue; + } + + if (status == ZOOM_OUT) + { + if (ds < ds_max) + { + ds *= 2; /* increase downscaling to zoom out */ + get_view(p_disp, &cx, &cy); + cx /= 2; /* prepare the position in the new image */ + cy /= 2; + } + else + continue; + } + break; + } + +#ifdef USEGSLIB + grey_show(false); /* switch off overlay */ +#endif + rb->lcd_clear_display(); + } + while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED + && status != PLUGIN_OTHER); +#ifdef USEGSLIB + rb->lcd_update(); +#endif + return status; +} + +/******************** Plugin entry point *********************/ + +enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) +{ + rb = api; + + int condition; +#ifdef USEGSLIB + long greysize; /* helper */ +#endif +#if LCD_DEPTH > 1 + old_backdrop = rb->lcd_get_backdrop(); +#endif + + if(!parameter) return PLUGIN_ERROR; + + rb->strcpy(np_file, parameter); + get_pic_list(); + + if(!entries) return PLUGIN_ERROR; + +#if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR) + if(rb->audio_status()) + { + buf = rb->plugin_get_buffer((size_t *)&buf_size) + + (entries * sizeof(char**)); + buf_size -= (entries * sizeof(char**)); + plug_buf = true; + } + else + buf = rb->plugin_get_audio_buffer((size_t *)&buf_size); +#else + buf = rb->plugin_get_audio_buffer(&buf_size) + + (entries * sizeof(char**)); + buf_size -= (entries * sizeof(char**)); +#endif + +#ifdef USEGSLIB + if (!grey_init(rb, buf, buf_size, GREY_ON_COP, + LCD_WIDTH, LCD_HEIGHT, &greysize)) + { + rb->splash(HZ, "grey buf error"); + return PLUGIN_ERROR; + } + buf += greysize; + buf_size -= greysize; +#else + xlcd_init(rb); +#endif + + /* should be ok to just load settings since a parameter is present + here and the drive should be spinning */ + configfile_init(rb); + configfile_load(JPEG_CONFIGFILE, jpeg_config, + ARRAYLEN(jpeg_config), JPEG_SETTINGS_MINVERSION); + old_settings = jpeg_settings; + + buf_images = buf; buf_images_size = buf_size; + + /* Turn off backlight timeout */ + backlight_force_on(rb); /* backlight control in lib/helper.c */ + + do + { + condition = load_and_show(np_file); + }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED + && condition != PLUGIN_ERROR); + + if (rb->memcmp(&jpeg_settings, &old_settings, sizeof (jpeg_settings))) + { + /* Just in case drive has to spin, keep it from looking locked */ + rb->splash(0, "Saving Settings"); + configfile_save(JPEG_CONFIGFILE, jpeg_config, + ARRAYLEN(jpeg_config), JPEG_SETTINGS_VERSION); + } + +#if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE) + /* set back ata spindown time in case we changed it */ + rb->ata_spindown(rb->global_settings->disk_spindown); +#endif + + /* Turn on backlight timeout (revert to settings) */ + backlight_use_settings(rb); /* backlight control in lib/helper.c */ + +#ifdef USEGSLIB + grey_release(); /* deinitialize */ +#endif + + return condition; +} diff --git a/apps/plugins/jpeg/jpeg.h b/apps/plugins/jpeg/jpeg.h new file mode 100644 index 0000000000..1a24948a19 --- /dev/null +++ b/apps/plugins/jpeg/jpeg.h @@ -0,0 +1,256 @@ +/*************************************************************************** +* __________ __ ___. +* 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. +* +****************************************************************************/ + +#ifndef _JPEG_JPEG_H +#define _JPEG_JPEG_H + +#include "plugin.h" + +/* variable button definitions */ +#if CONFIG_KEYPAD == RECORDER_PAD +#define JPEG_ZOOM_IN BUTTON_PLAY +#define JPEG_ZOOM_OUT BUTTON_ON +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_NEXT BUTTON_F3 +#define JPEG_PREVIOUS BUTTON_F2 +#define JPEG_MENU BUTTON_OFF + +#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD +#define JPEG_ZOOM_IN BUTTON_SELECT +#define JPEG_ZOOM_OUT BUTTON_ON +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_NEXT BUTTON_F3 +#define JPEG_PREVIOUS BUTTON_F2 +#define JPEG_MENU BUTTON_OFF + +#elif CONFIG_KEYPAD == ONDIO_PAD +#define JPEG_ZOOM_PRE BUTTON_MENU +#define JPEG_ZOOM_IN (BUTTON_MENU | BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_MENU | BUTTON_DOWN) +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_NEXT (BUTTON_MENU | BUTTON_RIGHT) +#define JPEG_PREVIOUS (BUTTON_MENU | BUTTON_LEFT) +#define JPEG_MENU BUTTON_OFF + +#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ + (CONFIG_KEYPAD == IRIVER_H300_PAD) +#define JPEG_ZOOM_IN BUTTON_SELECT +#define JPEG_ZOOM_OUT BUTTON_MODE +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) +#define JPEG_NEXT BUTTON_ON +#define JPEG_PREVIOUS BUTTON_REC +#else +#define JPEG_NEXT BUTTON_REC +#define JPEG_PREVIOUS BUTTON_ON +#endif +#define JPEG_MENU BUTTON_OFF +#define JPEG_RC_MENU BUTTON_RC_STOP + +#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ + (CONFIG_KEYPAD == IPOD_1G2G_PAD) +#define JPEG_ZOOM_IN BUTTON_SCROLL_FWD +#define JPEG_ZOOM_OUT BUTTON_SCROLL_BACK +#define JPEG_UP BUTTON_MENU +#define JPEG_DOWN BUTTON_PLAY +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU (BUTTON_SELECT | BUTTON_MENU) +#define JPEG_NEXT (BUTTON_SELECT | BUTTON_RIGHT) +#define JPEG_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD +#define JPEG_ZOOM_PRE BUTTON_SELECT +#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_POWER +#define JPEG_NEXT BUTTON_PLAY +#define JPEG_PREVIOUS BUTTON_REC + +#elif CONFIG_KEYPAD == GIGABEAT_PAD +#define JPEG_ZOOM_IN BUTTON_VOL_UP +#define JPEG_ZOOM_OUT BUTTON_VOL_DOWN +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_MENU +#define JPEG_NEXT (BUTTON_A | BUTTON_RIGHT) +#define JPEG_PREVIOUS (BUTTON_A | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == SANSA_E200_PAD +#define JPEG_ZOOM_PRE BUTTON_SELECT +#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_POWER +#define JPEG_SLIDE_SHOW BUTTON_REC +#define JPEG_NEXT BUTTON_SCROLL_FWD +#define JPEG_NEXT_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT) +#define JPEG_PREVIOUS BUTTON_SCROLL_BACK +#define JPEG_PREVIOUS_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == SANSA_C200_PAD +#define JPEG_ZOOM_PRE BUTTON_SELECT +#define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_POWER +#define JPEG_SLIDE_SHOW BUTTON_REC +#define JPEG_NEXT BUTTON_VOL_UP +#define JPEG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) +#define JPEG_PREVIOUS BUTTON_VOL_DOWN +#define JPEG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) + +#elif CONFIG_KEYPAD == IRIVER_H10_PAD +#define JPEG_ZOOM_PRE BUTTON_PLAY +#define JPEG_ZOOM_IN (BUTTON_PLAY | BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_PLAY | BUTTON_REPEAT) +#define JPEG_UP BUTTON_SCROLL_UP +#define JPEG_DOWN BUTTON_SCROLL_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_POWER +#define JPEG_NEXT BUTTON_FF +#define JPEG_PREVIOUS BUTTON_REW + +#elif CONFIG_KEYPAD == MROBE500_PAD +#define JPEG_ZOOM_IN BUTTON_RC_VOL_UP +#define JPEG_ZOOM_OUT BUTTON_RC_VOL_DOWN +#define JPEG_UP BUTTON_RC_PLAY +#define JPEG_DOWN BUTTON_RC_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_POWER +#define JPEG_NEXT BUTTON_RC_HEART +#define JPEG_PREVIOUS BUTTON_RC_MODE + +#elif CONFIG_KEYPAD == GIGABEAT_S_PAD +#define JPEG_ZOOM_IN BUTTON_VOL_UP +#define JPEG_ZOOM_OUT BUTTON_VOL_DOWN +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_MENU +#define JPEG_NEXT BUTTON_NEXT +#define JPEG_PREVIOUS BUTTON_PREV + +#elif CONFIG_KEYPAD == MROBE100_PAD +#define JPEG_ZOOM_IN BUTTON_SELECT +#define JPEG_ZOOM_OUT BUTTON_PLAY +#define JPEG_UP BUTTON_UP +#define JPEG_DOWN BUTTON_DOWN +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_MENU +#define JPEG_NEXT (BUTTON_DISPLAY | BUTTON_RIGHT) +#define JPEG_PREVIOUS (BUTTON_DISPLAY | BUTTON_LEFT) + +#elif CONFIG_KEYPAD == IAUDIO_M3_PAD +#define JPEG_ZOOM_PRE BUTTON_RC_PLAY +#define JPEG_ZOOM_IN (BUTTON_RC_PLAY|BUTTON_REL) +#define JPEG_ZOOM_OUT (BUTTON_RC_PLAY|BUTTON_REPEAT) +#define JPEG_UP BUTTON_RC_VOL_UP +#define JPEG_DOWN BUTTON_RC_VOL_DOWN +#define JPEG_LEFT BUTTON_RC_REW +#define JPEG_RIGHT BUTTON_RC_FF +#define JPEG_MENU BUTTON_RC_REC +#define JPEG_NEXT BUTTON_RC_MODE +#define JPEG_PREVIOUS BUTTON_RC_MENU + +#elif CONFIG_KEYPAD == COWOND2_PAD + +#elif CONFIG_KEYPAD == IAUDIO67_PAD +#define JPEG_ZOOM_IN BUTTON_VOLUP +#define JPEG_ZOOM_OUT BUTTON_VOLDOWN +#define JPEG_UP BUTTON_STOP +#define JPEG_DOWN BUTTON_PLAY +#define JPEG_LEFT BUTTON_LEFT +#define JPEG_RIGHT BUTTON_RIGHT +#define JPEG_MENU BUTTON_MENU +#define JPEG_NEXT (BUTTON_PLAY|BUTTON_VOLUP) +#define JPEG_PREVIOUS (BUTTON_PLAY|BUTTON_VOLDOWN) + +#else +#error No keymap defined! +#endif + +#ifdef HAVE_TOUCHSCREEN +#ifndef JPEG_UP +#define JPEG_UP BUTTON_TOPMIDDLE +#endif +#ifndef JPEG_DOWN +#define JPEG_DOWN BUTTON_BOTTOMMIDDLE +#endif +#ifndef JPEG_LEFT +#define JPEG_LEFT BUTTON_MIDLEFT +#endif +#ifndef JPEG_RIGHT +#define JPEG_RIGHT BUTTON_MIDRIGHT +#endif +#ifndef JPEG_ZOOM_IN +#define JPEG_ZOOM_IN BUTTON_TOPRIGHT +#endif +#ifndef JPEG_ZOOM_OUT +#define JPEG_ZOOM_OUT BUTTON_TOPLEFT +#endif +#ifndef JPEG_MENU +#define JPEG_MENU (BUTTON_CENTER|BUTTON_REL) +#endif +#ifndef JPEG_NEXT +#define JPEG_NEXT BUTTON_BOTTOMRIGHT +#endif +#ifndef JPEG_PREVIOUS +#define JPEG_PREVIOUS BUTTON_BOTTOMLEFT +#endif +#endif + + +#endif /* _JPEG_JPEG_H */ diff --git a/apps/plugins/jpeg/jpeg_decoder.c b/apps/plugins/jpeg/jpeg_decoder.c new file mode 100644 index 0000000000..ffd71a1320 --- /dev/null +++ b/apps/plugins/jpeg/jpeg_decoder.c @@ -0,0 +1,1540 @@ +/*************************************************************************** +* __________ __ ___. +* 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 "jpeg_decoder.h" + +extern const struct plugin_api* rb; + +/* for portability of below JPEG code */ +#define MEMSET(p,v,c) rb->memset(p,v,c) +#define MEMCPY(d,s,c) rb->memcpy(d,s,c) +#define INLINE static inline +#define ENDIAN_SWAP16(n) n /* only for poor little endian machines */ + +/**************** begin JPEG code ********************/ + +INLINE unsigned range_limit(int value) +{ +#if CONFIG_CPU == SH7034 + unsigned tmp; + asm ( /* Note: Uses knowledge that only low byte of result is used */ + "mov #-128,%[t] \n" + "sub %[t],%[v] \n" /* value -= -128; equals value += 128; */ + "extu.b %[v],%[t] \n" + "cmp/eq %[v],%[t] \n" /* low byte == whole number ? */ + "bt 1f \n" /* yes: no overflow */ + "cmp/pz %[v] \n" /* overflow: positive? */ + "subc %[v],%[v] \n" /* %[r] now either 0 or 0xffffffff */ + "1: \n" + : /* outputs */ + [v]"+r"(value), + [t]"=&r"(tmp) + ); + return value; +#elif defined(CPU_COLDFIRE) + asm ( /* Note: Uses knowledge that only the low byte of the result is used */ + "add.l #128,%[v] \n" /* value += 128; */ + "cmp.l #255,%[v] \n" /* overflow? */ + "bls.b 1f \n" /* no: return value */ + "spl.b %[v] \n" /* yes: set low byte to appropriate boundary */ + "1: \n" + : /* outputs */ + [v]"+d"(value) + ); + return value; +#elif defined(CPU_ARM) + asm ( /* Note: Uses knowledge that only the low byte of the result is used */ + "add %[v], %[v], #128 \n" /* value += 128 */ + "cmp %[v], #255 \n" /* out of range 0..255? */ + "mvnhi %[v], %[v], asr #31 \n" /* yes: set all bits to ~(sign_bit) */ + : /* outputs */ + [v]"+r"(value) + ); + return value; +#else + value += 128; + + if ((unsigned)value <= 255) + return value; + + if (value < 0) + return 0; + + return 255; +#endif +} + +/* IDCT implementation */ + + +#define CONST_BITS 13 +#define PASS1_BITS 2 + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus +* causing a lot of useless floating-point operations at run time. +* To get around this we use the following pre-calculated constants. +* If you change CONST_BITS you may want to add appropriate values. +* (With a reasonable C compiler, you can just rely on the FIX() macro...) +*/ +#define FIX_0_298631336 2446 /* FIX(0.298631336) */ +#define FIX_0_390180644 3196 /* FIX(0.390180644) */ +#define FIX_0_541196100 4433 /* FIX(0.541196100) */ +#define FIX_0_765366865 6270 /* FIX(0.765366865) */ +#define FIX_0_899976223 7373 /* FIX(0.899976223) */ +#define FIX_1_175875602 9633 /* FIX(1.175875602) */ +#define FIX_1_501321110 12299 /* FIX(1.501321110) */ +#define FIX_1_847759065 15137 /* FIX(1.847759065) */ +#define FIX_1_961570560 16069 /* FIX(1.961570560) */ +#define FIX_2_053119869 16819 /* FIX(2.053119869) */ +#define FIX_2_562915447 20995 /* FIX(2.562915447) */ +#define FIX_3_072711026 25172 /* FIX(3.072711026) */ + + + +/* Multiply an long variable by an long constant to yield an long result. +* For 8-bit samples with the recommended scaling, all the variable +* and constant values involved are no more than 16 bits wide, so a +* 16x16->32 bit multiply can be used instead of a full 32x32 multiply. +* For 12-bit samples, a full 32-bit multiplication will be needed. +*/ +#define MULTIPLY16(var,const) (((short) (var)) * ((short) (const))) + + +/* Dequantize a coefficient by multiplying it by the multiplier-table +* entry; produce an int result. In this module, both inputs and result +* are 16 bits or less, so either int or short multiply will work. +*/ +/* #define DEQUANTIZE(coef,quantval) (((int) (coef)) * (quantval)) */ +#define DEQUANTIZE MULTIPLY16 + +/* Descale and correctly round an int value that's scaled by N bits. +* We assume RIGHT_SHIFT rounds towards minus infinity, so adding +* the fudge factor is correct for either sign of X. +*/ +#define DESCALE(x,n) (((x) + (1l << ((n)-1))) >> (n)) + + + +/* +* Perform dequantization and inverse DCT on one block of coefficients, +* producing a reduced-size 1x1 output block. +*/ +void idct1x1(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) +{ + (void)skip_line; /* unused */ + *p_byte = range_limit(inptr[0] * quantptr[0] >> 3); +} + + + +/* +* Perform dequantization and inverse DCT on one block of coefficients, +* producing a reduced-size 2x2 output block. +*/ +void idct2x2(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) +{ + int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + unsigned char* outptr; + + /* Pass 1: process columns from input, store into work array. */ + + /* Column 0 */ + tmp4 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); + tmp5 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); + + tmp0 = tmp4 + tmp5; + tmp2 = tmp4 - tmp5; + + /* Column 1 */ + tmp4 = DEQUANTIZE(inptr[8*0+1], quantptr[8*0+1]); + tmp5 = DEQUANTIZE(inptr[8*1+1], quantptr[8*1+1]); + + tmp1 = tmp4 + tmp5; + tmp3 = tmp4 - tmp5; + + /* Pass 2: process 2 rows, store into output array. */ + + /* Row 0 */ + outptr = p_byte; + + outptr[0] = range_limit((int) DESCALE(tmp0 + tmp1, 3)); + outptr[1] = range_limit((int) DESCALE(tmp0 - tmp1, 3)); + + /* Row 1 */ + outptr = p_byte + skip_line; + + outptr[0] = range_limit((int) DESCALE(tmp2 + tmp3, 3)); + outptr[1] = range_limit((int) DESCALE(tmp2 - tmp3, 3)); +} + + + +/* +* Perform dequantization and inverse DCT on one block of coefficients, +* producing a reduced-size 4x4 output block. +*/ +void idct4x4(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) +{ + int tmp0, tmp2, tmp10, tmp12; + int z1, z2, z3; + int * wsptr; + unsigned char* outptr; + int ctr; + int workspace[4*4]; /* buffers data between passes */ + + /* Pass 1: process columns from input, store into work array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++, inptr++, quantptr++, wsptr++) + { + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); + tmp2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); + + tmp10 = (tmp0 + tmp2) << PASS1_BITS; + tmp12 = (tmp0 - tmp2) << PASS1_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); + z3 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); + + z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); + tmp0 = DESCALE(z1 + MULTIPLY16(z3, - FIX_1_847759065), CONST_BITS-PASS1_BITS); + tmp2 = DESCALE(z1 + MULTIPLY16(z2, FIX_0_765366865), CONST_BITS-PASS1_BITS); + + /* Final output stage */ + + wsptr[4*0] = (int) (tmp10 + tmp2); + wsptr[4*3] = (int) (tmp10 - tmp2); + wsptr[4*1] = (int) (tmp12 + tmp0); + wsptr[4*2] = (int) (tmp12 - tmp0); + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) + { + outptr = p_byte + (ctr*skip_line); + /* Even part */ + + tmp0 = (int) wsptr[0]; + tmp2 = (int) wsptr[2]; + + tmp10 = (tmp0 + tmp2) << CONST_BITS; + tmp12 = (tmp0 - tmp2) << CONST_BITS; + + /* Odd part */ + /* Same rotation as in the even part of the 8x8 LL&M IDCT */ + + z2 = (int) wsptr[1]; + z3 = (int) wsptr[3]; + + z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); + tmp0 = z1 + MULTIPLY16(z3, - FIX_1_847759065); + tmp2 = z1 + MULTIPLY16(z2, FIX_0_765366865); + + /* Final output stage */ + + outptr[0] = range_limit((int) DESCALE(tmp10 + tmp2, + CONST_BITS+PASS1_BITS+3)); + outptr[3] = range_limit((int) DESCALE(tmp10 - tmp2, + CONST_BITS+PASS1_BITS+3)); + outptr[1] = range_limit((int) DESCALE(tmp12 + tmp0, + CONST_BITS+PASS1_BITS+3)); + outptr[2] = range_limit((int) DESCALE(tmp12 - tmp0, + CONST_BITS+PASS1_BITS+3)); + + wsptr += 4; /* advance pointer to next row */ + } +} + + + +/* +* Perform dequantization and inverse DCT on one block of coefficients. +*/ +void idct8x8(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) +{ + long tmp0, tmp1, tmp2, tmp3; + long tmp10, tmp11, tmp12, tmp13; + long z1, z2, z3, z4, z5; + int * wsptr; + unsigned char* outptr; + int ctr; + int workspace[64]; /* buffers data between passes */ + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + wsptr = workspace; + for (ctr = 8; ctr > 0; ctr--) + { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if ((inptr[8*1] | inptr[8*2] | inptr[8*3] + | inptr[8*4] | inptr[8*5] | inptr[8*6] | inptr[8*7]) == 0) + { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[8*0], quantptr[8*0]) << PASS1_BITS; + + wsptr[8*0] = wsptr[8*1] = wsptr[8*2] = wsptr[8*3] = wsptr[8*4] + = wsptr[8*5] = wsptr[8*6] = wsptr[8*7] = dcval; + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); + z3 = DEQUANTIZE(inptr[8*6], quantptr[8*6]); + + z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); + + z2 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); + z3 = DEQUANTIZE(inptr[8*4], quantptr[8*4]); + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ + + tmp0 = DEQUANTIZE(inptr[8*7], quantptr[8*7]); + tmp1 = DEQUANTIZE(inptr[8*5], quantptr[8*5]); + tmp2 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); + tmp3 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[8*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[8*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[8*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[8*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[8*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++) + { + outptr = p_byte + (ctr*skip_line); + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if ((wsptr[1] | wsptr[2] | wsptr[3] + | wsptr[4] | wsptr[5] | wsptr[6] | wsptr[7]) == 0) + { + /* AC terms all zero */ + unsigned char dcval = range_limit((int) DESCALE((long) wsptr[0], + PASS1_BITS+3)); + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += 8; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (long) wsptr[2]; + z3 = (long) wsptr[6]; + + z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); + + tmp0 = ((long) wsptr[0] + (long) wsptr[4]) << CONST_BITS; + tmp1 = ((long) wsptr[0] - (long) wsptr[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ + + tmp0 = (long) wsptr[7]; + tmp1 = (long) wsptr[5]; + tmp2 = (long) wsptr[3]; + tmp3 = (long) wsptr[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit((int) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3)); + outptr[7] = range_limit((int) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3)); + outptr[1] = range_limit((int) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3)); + outptr[6] = range_limit((int) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3)); + outptr[2] = range_limit((int) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3)); + outptr[5] = range_limit((int) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3)); + outptr[3] = range_limit((int) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3)); + outptr[4] = range_limit((int) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3)); + + wsptr += 8; /* advance pointer to next row */ + } +} + + + +/* JPEG decoder implementation */ + +/* Preprocess the JPEG JFIF file */ +int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) +{ + unsigned char* p_bytes = p_src; + int marker_size; /* variable length of marker segment */ + int i, j, n; + int ret = 0; /* returned flags */ + + p_jpeg->p_entropy_end = p_src + size; + + while (p_src < p_bytes + size) + { + if (*p_src++ != 0xFF) /* no marker? */ + { + p_src--; /* it's image data, put it back */ + p_jpeg->p_entropy_data = p_src; + break; /* exit marker processing */ + } + + switch (*p_src++) + { + case 0xFF: /* Fill byte */ + ret |= FILL_FF; + case 0x00: /* Zero stuffed byte - entropy data */ + p_src--; /* put it back */ + continue; + + case 0xC0: /* SOF Huff - Baseline DCT */ + { + ret |= SOF0; + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + n = *p_src++; /* sample precision (= 8 or 12) */ + if (n != 8) + { + return(-1); /* Unsupported sample precision */ + } + p_jpeg->y_size = *p_src++ << 8; /* Highbyte */ + p_jpeg->y_size |= *p_src++; /* Lowbyte */ + p_jpeg->x_size = *p_src++ << 8; /* Highbyte */ + p_jpeg->x_size |= *p_src++; /* Lowbyte */ + + n = (marker_size-2-6)/3; + if (*p_src++ != n || (n != 1 && n != 3)) + { + return(-2); /* Unsupported SOF0 component specification */ + } + for (i=0; i<n; i++) + { + p_jpeg->frameheader[i].ID = *p_src++; /* Component info */ + p_jpeg->frameheader[i].horizontal_sampling = *p_src >> 4; + p_jpeg->frameheader[i].vertical_sampling = *p_src++ & 0x0F; + p_jpeg->frameheader[i].quanttable_select = *p_src++; + if (p_jpeg->frameheader[i].horizontal_sampling > 2 + || p_jpeg->frameheader[i].vertical_sampling > 2) + return -3; /* Unsupported SOF0 subsampling */ + } + p_jpeg->blocks = n; + } + break; + + case 0xC1: /* SOF Huff - Extended sequential DCT*/ + case 0xC2: /* SOF Huff - Progressive DCT*/ + case 0xC3: /* SOF Huff - Spatial (sequential) lossless*/ + case 0xC5: /* SOF Huff - Differential sequential DCT*/ + case 0xC6: /* SOF Huff - Differential progressive DCT*/ + case 0xC7: /* SOF Huff - Differential spatial*/ + case 0xC8: /* SOF Arith - Reserved for JPEG extensions*/ + case 0xC9: /* SOF Arith - Extended sequential DCT*/ + case 0xCA: /* SOF Arith - Progressive DCT*/ + case 0xCB: /* SOF Arith - Spatial (sequential) lossless*/ + case 0xCD: /* SOF Arith - Differential sequential DCT*/ + case 0xCE: /* SOF Arith - Differential progressive DCT*/ + case 0xCF: /* SOF Arith - Differential spatial*/ + { + return (-4); /* other DCT model than baseline not implemented */ + } + + case 0xC4: /* Define Huffman Table(s) */ + { + unsigned char* p_temp; + + ret |= DHT; + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + + p_temp = p_src; + while (p_src < p_temp+marker_size-2-17) /* another table */ + { + int sum = 0; + i = *p_src & 0x0F; /* table index */ + if (i > 1) + { + return (-5); /* Huffman table index out of range */ + } + else if (*p_src++ & 0xF0) /* AC table */ + { + for (j=0; j<16; j++) + { + sum += *p_src; + p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; + } + if(16 + sum > AC_LEN) + return -10; /* longer than allowed */ + + for (; j < 16 + sum; j++) + p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; + } + else /* DC table */ + { + for (j=0; j<16; j++) + { + sum += *p_src; + p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; + } + if(16 + sum > DC_LEN) + return -11; /* longer than allowed */ + + for (; j < 16 + sum; j++) + p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; + } + } /* while */ + p_src = p_temp+marker_size - 2; /* skip possible residue */ + } + break; + + case 0xCC: /* Define Arithmetic coding conditioning(s) */ + return(-6); /* Arithmetic coding not supported */ + + case 0xD8: /* Start of Image */ + case 0xD9: /* End of Image */ + case 0x01: /* for temp private use arith code */ + break; /* skip parameterless marker */ + + + case 0xDA: /* Start of Scan */ + { + ret |= SOS; + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + + n = (marker_size-2-1-3)/2; + if (*p_src++ != n || (n != 1 && n != 3)) + { + return (-7); /* Unsupported SOS component specification */ + } + for (i=0; i<n; i++) + { + p_jpeg->scanheader[i].ID = *p_src++; + p_jpeg->scanheader[i].DC_select = *p_src >> 4; + p_jpeg->scanheader[i].AC_select = *p_src++ & 0x0F; + } + p_src += 3; /* skip spectral information */ + } + break; + + case 0xDB: /* Define quantization Table(s) */ + { + ret |= DQT; + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + n = (marker_size-2)/(QUANT_TABLE_LENGTH+1); /* # of tables */ + for (i=0; i<n; i++) + { + int id = *p_src++; /* ID */ + if (id >= 4) + { + return (-8); /* Unsupported quantization table */ + } + /* Read Quantisation table: */ + for (j=0; j<QUANT_TABLE_LENGTH; j++) + p_jpeg->quanttable[id][j] = *p_src++; + } + } + break; + + case 0xDD: /* Define Restart Interval */ + { + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + p_jpeg->restart_interval = *p_src++ << 8; /* Highbyte */ + p_jpeg->restart_interval |= *p_src++; /* Lowbyte */ + p_src += marker_size-4; /* skip segment */ + } + break; + + case 0xDC: /* Define Number of Lines */ + case 0xDE: /* Define Hierarchical progression */ + case 0xDF: /* Expand Reference Component(s) */ + case 0xE0: /* Application Field 0*/ + case 0xE1: /* Application Field 1*/ + case 0xE2: /* Application Field 2*/ + case 0xE3: /* Application Field 3*/ + case 0xE4: /* Application Field 4*/ + case 0xE5: /* Application Field 5*/ + case 0xE6: /* Application Field 6*/ + case 0xE7: /* Application Field 7*/ + case 0xE8: /* Application Field 8*/ + case 0xE9: /* Application Field 9*/ + case 0xEA: /* Application Field 10*/ + case 0xEB: /* Application Field 11*/ + case 0xEC: /* Application Field 12*/ + case 0xED: /* Application Field 13*/ + case 0xEE: /* Application Field 14*/ + case 0xEF: /* Application Field 15*/ + case 0xFE: /* Comment */ + { + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + p_src += marker_size-2; /* skip segment */ + } + break; + + case 0xF0: /* Reserved for JPEG extensions */ + case 0xF1: /* Reserved for JPEG extensions */ + case 0xF2: /* Reserved for JPEG extensions */ + case 0xF3: /* Reserved for JPEG extensions */ + case 0xF4: /* Reserved for JPEG extensions */ + case 0xF5: /* Reserved for JPEG extensions */ + case 0xF6: /* Reserved for JPEG extensions */ + case 0xF7: /* Reserved for JPEG extensions */ + case 0xF8: /* Reserved for JPEG extensions */ + case 0xF9: /* Reserved for JPEG extensions */ + case 0xFA: /* Reserved for JPEG extensions */ + case 0xFB: /* Reserved for JPEG extensions */ + case 0xFC: /* Reserved for JPEG extensions */ + case 0xFD: /* Reserved for JPEG extensions */ + case 0x02: /* Reserved */ + default: + return (-9); /* Unknown marker */ + } /* switch */ + } /* while */ + + return (ret); /* return flags with seen markers */ +} + + +void default_huff_tbl(struct jpeg* p_jpeg) +{ + static const struct huffman_table luma_table = + { + { + 0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B + }, + { + 0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D, + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, + 0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0, + 0x24,0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28, + 0x29,0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49, + 0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69, + 0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, + 0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5, + 0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2, + 0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, + 0xF9,0xFA + } + }; + + static const struct huffman_table chroma_table = + { + { + 0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B + }, + { + 0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77, + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, + 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0, + 0x15,0x62,0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26, + 0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48, + 0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68, + 0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5, + 0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3, + 0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA, + 0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, + 0xF9,0xFA + } + }; + + MEMCPY(&p_jpeg->hufftable[0], &luma_table, sizeof(luma_table)); + MEMCPY(&p_jpeg->hufftable[1], &chroma_table, sizeof(chroma_table)); + + return; +} + +/* Compute the derived values for a Huffman table */ +void fix_huff_tbl(int* htbl, struct derived_tbl* dtbl) +{ + int p, i, l, si; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + /* Note that this is in code-length order. */ + + p = 0; + for (l = 1; l <= 16; l++) + { /* all possible code length */ + for (i = 1; i <= (int) htbl[l-1]; i++) /* all codes per length */ + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + + /* Figure C.2: generate the codes themselves */ + /* Note that this is in code-length order. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) + { + while (((int) huffsize[p]) == si) + { + huffcode[p++] = code; + code++; + } + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) + { + if (htbl[l-1]) + { + dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ + dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ + p += htbl[l-1]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } + else + { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures huff_DECODE terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMSET(dtbl->look_nbits, 0, sizeof(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) + { + for (i = 1; i <= (int) htbl[l-1]; i++, p++) + { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) + { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl[16+p]; + lookbits++; + } + } + } +} + + +/* zag[i] is the natural-order position of the i'th element of zigzag order. + * If the incoming data is corrupted, decode_mcu could attempt to + * reference values beyond the end of the array. To avoid a wild store, + * we put some extra zeroes after the real entries. + */ +static const int zag[] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 0, 0, 0, 0, 0, 0, 0, 0, /* extra entries in case k>63 below */ + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void build_lut(struct jpeg* p_jpeg) +{ + int i; + fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_dc, + &p_jpeg->dc_derived_tbls[0]); + fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_ac, + &p_jpeg->ac_derived_tbls[0]); + fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_dc, + &p_jpeg->dc_derived_tbls[1]); + fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_ac, + &p_jpeg->ac_derived_tbls[1]); + + /* build the dequantization tables for the IDCT (De-ZiZagged) */ + for (i=0; i<64; i++) + { + p_jpeg->qt_idct[0][zag[i]] = p_jpeg->quanttable[0][i]; + p_jpeg->qt_idct[1][zag[i]] = p_jpeg->quanttable[1][i]; + } + + for (i=0; i<4; i++) + p_jpeg->store_pos[i] = i; /* default ordering */ + + /* assignments for the decoding of blocks */ + if (p_jpeg->frameheader[0].horizontal_sampling == 2 + && p_jpeg->frameheader[0].vertical_sampling == 1) + { /* 4:2:2 */ + p_jpeg->blocks = 4; + p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; + p_jpeg->x_phys = p_jpeg->x_mbl * 16; + p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; + p_jpeg->y_phys = p_jpeg->y_mbl * 8; + p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ + p_jpeg->mcu_membership[1] = 0; + p_jpeg->mcu_membership[2] = 1; + p_jpeg->mcu_membership[3] = 2; + p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ + p_jpeg->tab_membership[1] = 0; + p_jpeg->tab_membership[2] = 1; + p_jpeg->tab_membership[3] = 1; + p_jpeg->subsample_x[0] = 1; + p_jpeg->subsample_x[1] = 2; + p_jpeg->subsample_x[2] = 2; + p_jpeg->subsample_y[0] = 1; + p_jpeg->subsample_y[1] = 1; + p_jpeg->subsample_y[2] = 1; + } + if (p_jpeg->frameheader[0].horizontal_sampling == 1 + && p_jpeg->frameheader[0].vertical_sampling == 2) + { /* 4:2:2 vertically subsampled */ + p_jpeg->store_pos[1] = 2; /* block positions are mirrored */ + p_jpeg->store_pos[2] = 1; + p_jpeg->blocks = 4; + p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; + p_jpeg->x_phys = p_jpeg->x_mbl * 8; + p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; + p_jpeg->y_phys = p_jpeg->y_mbl * 16; + p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ + p_jpeg->mcu_membership[1] = 0; + p_jpeg->mcu_membership[2] = 1; + p_jpeg->mcu_membership[3] = 2; + p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ + p_jpeg->tab_membership[1] = 0; + p_jpeg->tab_membership[2] = 1; + p_jpeg->tab_membership[3] = 1; + p_jpeg->subsample_x[0] = 1; + p_jpeg->subsample_x[1] = 1; + p_jpeg->subsample_x[2] = 1; + p_jpeg->subsample_y[0] = 1; + p_jpeg->subsample_y[1] = 2; + p_jpeg->subsample_y[2] = 2; + } + else if (p_jpeg->frameheader[0].horizontal_sampling == 2 + && p_jpeg->frameheader[0].vertical_sampling == 2) + { /* 4:2:0 */ + p_jpeg->blocks = 6; + p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; + p_jpeg->x_phys = p_jpeg->x_mbl * 16; + p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; + p_jpeg->y_phys = p_jpeg->y_mbl * 16; + p_jpeg->mcu_membership[0] = 0; + p_jpeg->mcu_membership[1] = 0; + p_jpeg->mcu_membership[2] = 0; + p_jpeg->mcu_membership[3] = 0; + p_jpeg->mcu_membership[4] = 1; + p_jpeg->mcu_membership[5] = 2; + p_jpeg->tab_membership[0] = 0; + p_jpeg->tab_membership[1] = 0; + p_jpeg->tab_membership[2] = 0; + p_jpeg->tab_membership[3] = 0; + p_jpeg->tab_membership[4] = 1; + p_jpeg->tab_membership[5] = 1; + p_jpeg->subsample_x[0] = 1; + p_jpeg->subsample_x[1] = 2; + p_jpeg->subsample_x[2] = 2; + p_jpeg->subsample_y[0] = 1; + p_jpeg->subsample_y[1] = 2; + p_jpeg->subsample_y[2] = 2; + } + else if (p_jpeg->frameheader[0].horizontal_sampling == 1 + && p_jpeg->frameheader[0].vertical_sampling == 1) + { /* 4:4:4 */ + /* don't overwrite p_jpeg->blocks */ + p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; + p_jpeg->x_phys = p_jpeg->x_mbl * 8; + p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; + p_jpeg->y_phys = p_jpeg->y_mbl * 8; + p_jpeg->mcu_membership[0] = 0; + p_jpeg->mcu_membership[1] = 1; + p_jpeg->mcu_membership[2] = 2; + p_jpeg->tab_membership[0] = 0; + p_jpeg->tab_membership[1] = 1; + p_jpeg->tab_membership[2] = 1; + p_jpeg->subsample_x[0] = 1; + p_jpeg->subsample_x[1] = 1; + p_jpeg->subsample_x[2] = 1; + p_jpeg->subsample_y[0] = 1; + p_jpeg->subsample_y[1] = 1; + p_jpeg->subsample_y[2] = 1; + } + else + { + /* error */ + } + +} + + +/* +* These functions/macros provide the in-line portion of bit fetching. +* Use check_bit_buffer to ensure there are N bits in get_buffer +* before using get_bits, peek_bits, or drop_bits. +* check_bit_buffer(state,n,action); +* Ensure there are N bits in get_buffer; if suspend, take action. +* val = get_bits(n); +* Fetch next N bits. +* val = peek_bits(n); +* Fetch next N bits without removing them from the buffer. +* drop_bits(n); +* Discard next N bits. +* The value N should be a simple variable, not an expression, because it +* is evaluated multiple times. +*/ + +INLINE void check_bit_buffer(struct bitstream* pb, int nbits) +{ + if (pb->bits_left < nbits) + { /* nbits is <= 16, so I can always refill 2 bytes in this case */ + unsigned char byte; + + byte = *pb->next_input_byte++; + if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ + { /* simplification: just skip the (one-byte) marker code */ + pb->next_input_byte++; + } + pb->get_buffer = (pb->get_buffer << 8) | byte; + + byte = *pb->next_input_byte++; + if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ + { /* simplification: just skip the (one-byte) marker code */ + pb->next_input_byte++; + } + pb->get_buffer = (pb->get_buffer << 8) | byte; + + pb->bits_left += 16; + } +} + +INLINE int get_bits(struct bitstream* pb, int nbits) +{ + return ((int) (pb->get_buffer >> (pb->bits_left -= nbits))) & ((1<<nbits)-1); +} + +INLINE int peek_bits(struct bitstream* pb, int nbits) +{ + return ((int) (pb->get_buffer >> (pb->bits_left - nbits))) & ((1<<nbits)-1); +} + +INLINE void drop_bits(struct bitstream* pb, int nbits) +{ + pb->bits_left -= nbits; +} + +/* re-synchronize to entropy data (skip restart marker) */ +void search_restart(struct bitstream* pb) +{ + pb->next_input_byte--; /* we may have overread it, taking 2 bytes */ + /* search for a non-byte-padding marker, has to be RSTm or EOS */ + while (pb->next_input_byte < pb->input_end && + (pb->next_input_byte[-2] != 0xFF || pb->next_input_byte[-1] == 0x00)) + { + pb->next_input_byte++; + } + pb->bits_left = 0; +} + +/* Figure F.12: extend sign bit. */ +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ +{ + 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 +}; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ +{ + 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 +}; + +/* Decode a single value */ +INLINE int huff_decode_dc(struct bitstream* bs, struct derived_tbl* tbl) +{ + int nb, look, s, r; + + check_bit_buffer(bs, HUFF_LOOKAHEAD); + look = peek_bits(bs, HUFF_LOOKAHEAD); + if ((nb = tbl->look_nbits[look]) != 0) + { + drop_bits(bs, nb); + s = tbl->look_sym[look]; + check_bit_buffer(bs, s); + r = get_bits(bs, s); + s = HUFF_EXTEND(r, s); + } + else + { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ + long code; + nb=HUFF_LOOKAHEAD+1; + check_bit_buffer(bs, nb); + code = get_bits(bs, nb); + while (code > tbl->maxcode[nb]) + { + code <<= 1; + check_bit_buffer(bs, 1); + code |= get_bits(bs, 1); + nb++; + } + if (nb > 16) /* error in Huffman */ + { + s=0; /* fake a zero, this is most safe */ + } + else + { + s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; + check_bit_buffer(bs, s); + r = get_bits(bs, s); + s = HUFF_EXTEND(r, s); + } + } /* end slow decode */ + return s; +} + +INLINE int huff_decode_ac(struct bitstream* bs, struct derived_tbl* tbl) +{ + int nb, look, s; + + check_bit_buffer(bs, HUFF_LOOKAHEAD); + look = peek_bits(bs, HUFF_LOOKAHEAD); + if ((nb = tbl->look_nbits[look]) != 0) + { + drop_bits(bs, nb); + s = tbl->look_sym[look]; + } + else + { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ + long code; + nb=HUFF_LOOKAHEAD+1; + check_bit_buffer(bs, nb); + code = get_bits(bs, nb); + while (code > tbl->maxcode[nb]) + { + code <<= 1; + check_bit_buffer(bs, 1); + code |= get_bits(bs, 1); + nb++; + } + if (nb > 16) /* error in Huffman */ + { + s=0; /* fake a zero, this is most safe */ + } + else + { + s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; + } + } /* end slow decode */ + return s; +} + + +#ifdef HAVE_LCD_COLOR + +/* JPEG decoder variant for YUV decoding, into 3 different planes */ +/* Note: it keeps the original color subsampling, even if resized. */ +int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[3], + int downscale, void (*pf_progress)(int current, int total)) +{ + struct bitstream bs; /* bitstream "object" */ + int block[64]; /* decoded DCT coefficients */ + + int width, height; + int skip_line[3]; /* bytes from one line to the next (skip_line) */ + int skip_strip[3], skip_mcu[3]; /* bytes to next DCT row / column */ + + int i, x, y; /* loop counter */ + + unsigned char* p_line[3] = {p_pixel[0], p_pixel[1], p_pixel[2]}; + unsigned char* p_byte[3]; /* bitmap pointer */ + + void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ + int k_need; /* AC coefficients needed up to here */ + int zero_need; /* init the block with this many zeros */ + + int last_dc_val[3] = {0, 0, 0}; /* or 128 for chroma? */ + int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ + int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ + + /* pick the IDCT we want, determine how to work with coefs */ + if (downscale == 1) + { + pf_idct = idct8x8; + k_need = 64; /* all */ + zero_need = 63; /* all */ + } + else if (downscale == 2) + { + pf_idct = idct4x4; + k_need = 25; /* this far in zig-zag to cover 4*4 */ + zero_need = 27; /* clear this far in linear order */ + } + else if (downscale == 4) + { + pf_idct = idct2x2; + k_need = 5; /* this far in zig-zag to cover 2*2 */ + zero_need = 9; /* clear this far in linear order */ + } + else if (downscale == 8) + { + pf_idct = idct1x1; + k_need = 0; /* no AC, not needed */ + zero_need = 0; /* no AC, not needed */ + } + else return -1; /* not supported */ + + /* init bitstream, fake a restart to make it start */ + bs.next_input_byte = p_jpeg->p_entropy_data; + bs.bits_left = 0; + bs.input_end = p_jpeg->p_entropy_end; + + width = p_jpeg->x_phys / downscale; + height = p_jpeg->y_phys / downscale; + for (i=0; i<3; i++) /* calculate some strides */ + { + skip_line[i] = width / p_jpeg->subsample_x[i]; + skip_strip[i] = skip_line[i] + * (height / p_jpeg->y_mbl) / p_jpeg->subsample_y[i]; + skip_mcu[i] = width/p_jpeg->x_mbl / p_jpeg->subsample_x[i]; + } + + /* prepare offsets about where to store the different blocks */ + store_offs[p_jpeg->store_pos[0]] = 0; + store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ + store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ + store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ + + for(y=0; y<p_jpeg->y_mbl && bs.next_input_byte <= bs.input_end; y++) + { + for (i=0; i<3; i++) /* scan line init */ + { + p_byte[i] = p_line[i]; + p_line[i] += skip_strip[i]; + } + for (x=0; x<p_jpeg->x_mbl; x++) + { + int blkn; + + /* Outer loop handles each block in the MCU */ + for (blkn = 0; blkn < p_jpeg->blocks; blkn++) + { /* Decode a single block's worth of coefficients */ + int k = 1; /* coefficient index */ + int s, r; /* huffman values */ + int ci = p_jpeg->mcu_membership[blkn]; /* component index */ + int ti = p_jpeg->tab_membership[blkn]; /* table index */ + struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; + struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; + + /* Section F.2.2.1: decode the DC coefficient difference */ + s = huff_decode_dc(&bs, dctbl); + + last_dc_val[ci] += s; + block[0] = last_dc_val[ci]; /* output it (assumes zag[0] = 0) */ + + /* coefficient buffer must be cleared */ + MEMSET(block+1, 0, zero_need*sizeof(block[0])); + + /* Section F.2.2.2: decode the AC coefficients */ + for (; k < k_need; k++) + { + s = huff_decode_ac(&bs, actbl); + r = s >> 4; + s &= 15; + + if (s) + { + k += r; + check_bit_buffer(&bs, s); + r = get_bits(&bs, s); + block[zag[k]] = HUFF_EXTEND(r, s); + } + else + { + if (r != 15) + { + k = 64; + break; + } + k += r; + } + } /* for k */ + /* In this path we just discard the values */ + for (; k < 64; k++) + { + s = huff_decode_ac(&bs, actbl); + r = s >> 4; + s &= 15; + + if (s) + { + k += r; + check_bit_buffer(&bs, s); + drop_bits(&bs, s); + } + else + { + if (r != 15) + break; + k += r; + } + } /* for k */ + + if (ci == 0) + { /* Y component needs to bother about block store */ + pf_idct(p_byte[0]+store_offs[blkn], block, + p_jpeg->qt_idct[ti], skip_line[0]); + } + else + { /* chroma */ + pf_idct(p_byte[ci], block, p_jpeg->qt_idct[ti], + skip_line[ci]); + } + } /* for blkn */ + p_byte[0] += skip_mcu[0]; /* unrolled for (i=0; i<3; i++) loop */ + p_byte[1] += skip_mcu[1]; + p_byte[2] += skip_mcu[2]; + if (p_jpeg->restart_interval && --restart == 0) + { /* if a restart marker is due: */ + restart = p_jpeg->restart_interval; /* count again */ + search_restart(&bs); /* align the bitstream */ + last_dc_val[0] = last_dc_val[1] = + last_dc_val[2] = 0; /* reset decoder */ + } + } /* for x */ + if (pf_progress != NULL) + pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ + } /* for y */ + + return 0; /* success */ +} +#else /* !HAVE_LCD_COLOR */ + +/* a JPEG decoder specialized in decoding only the luminance (b&w) */ +int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, + void (*pf_progress)(int current, int total)) +{ + struct bitstream bs; /* bitstream "object" */ + int block[64]; /* decoded DCT coefficients */ + + int width, height; + int skip_line; /* bytes from one line to the next (skip_line) */ + int skip_strip, skip_mcu; /* bytes to next DCT row / column */ + + int x, y; /* loop counter */ + + unsigned char* p_line = p_pixel[0]; + unsigned char* p_byte; /* bitmap pointer */ + + void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ + int k_need; /* AC coefficients needed up to here */ + int zero_need; /* init the block with this many zeros */ + + int last_dc_val = 0; + int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ + int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ + + /* pick the IDCT we want, determine how to work with coefs */ + if (downscale == 1) + { + pf_idct = idct8x8; + k_need = 64; /* all */ + zero_need = 63; /* all */ + } + else if (downscale == 2) + { + pf_idct = idct4x4; + k_need = 25; /* this far in zig-zag to cover 4*4 */ + zero_need = 27; /* clear this far in linear order */ + } + else if (downscale == 4) + { + pf_idct = idct2x2; + k_need = 5; /* this far in zig-zag to cover 2*2 */ + zero_need = 9; /* clear this far in linear order */ + } + else if (downscale == 8) + { + pf_idct = idct1x1; + k_need = 0; /* no AC, not needed */ + zero_need = 0; /* no AC, not needed */ + } + else return -1; /* not supported */ + + /* init bitstream, fake a restart to make it start */ + bs.next_input_byte = p_jpeg->p_entropy_data; + bs.bits_left = 0; + bs.input_end = p_jpeg->p_entropy_end; + + width = p_jpeg->x_phys / downscale; + height = p_jpeg->y_phys / downscale; + skip_line = width; + skip_strip = skip_line * (height / p_jpeg->y_mbl); + skip_mcu = (width/p_jpeg->x_mbl); + + /* prepare offsets about where to store the different blocks */ + store_offs[p_jpeg->store_pos[0]] = 0; + store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ + store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ + store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ + + for(y=0; y<p_jpeg->y_mbl && bs.next_input_byte <= bs.input_end; y++) + { + p_byte = p_line; + p_line += skip_strip; + for (x=0; x<p_jpeg->x_mbl; x++) + { + int blkn; + + /* Outer loop handles each block in the MCU */ + for (blkn = 0; blkn < p_jpeg->blocks; blkn++) + { /* Decode a single block's worth of coefficients */ + int k = 1; /* coefficient index */ + int s, r; /* huffman values */ + int ci = p_jpeg->mcu_membership[blkn]; /* component index */ + int ti = p_jpeg->tab_membership[blkn]; /* table index */ + struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; + struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; + + /* Section F.2.2.1: decode the DC coefficient difference */ + s = huff_decode_dc(&bs, dctbl); + + if (ci == 0) /* only for Y component */ + { + last_dc_val += s; + block[0] = last_dc_val; /* output it (assumes zag[0] = 0) */ + + /* coefficient buffer must be cleared */ + MEMSET(block+1, 0, zero_need*sizeof(block[0])); + + /* Section F.2.2.2: decode the AC coefficients */ + for (; k < k_need; k++) + { + s = huff_decode_ac(&bs, actbl); + r = s >> 4; + s &= 15; + + if (s) + { + k += r; + check_bit_buffer(&bs, s); + r = get_bits(&bs, s); + block[zag[k]] = HUFF_EXTEND(r, s); + } + else + { + if (r != 15) + { + k = 64; + break; + } + k += r; + } + } /* for k */ + } + /* In this path we just discard the values */ + for (; k < 64; k++) + { + s = huff_decode_ac(&bs, actbl); + r = s >> 4; + s &= 15; + + if (s) + { + k += r; + check_bit_buffer(&bs, s); + drop_bits(&bs, s); + } + else + { + if (r != 15) + break; + k += r; + } + } /* for k */ + + if (ci == 0) + { /* only for Y component */ + pf_idct(p_byte+store_offs[blkn], block, p_jpeg->qt_idct[ti], + skip_line); + } + } /* for blkn */ + p_byte += skip_mcu; + if (p_jpeg->restart_interval && --restart == 0) + { /* if a restart marker is due: */ + restart = p_jpeg->restart_interval; /* count again */ + search_restart(&bs); /* align the bitstream */ + last_dc_val = 0; /* reset decoder */ + } + } /* for x */ + if (pf_progress != NULL) + pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ + } /* for y */ + + return 0; /* success */ +} +#endif /* !HAVE_LCD_COLOR */ + +/**************** end JPEG code ********************/ diff --git a/apps/plugins/jpeg/jpeg_decoder.h b/apps/plugins/jpeg/jpeg_decoder.h new file mode 100644 index 0000000000..f4dbeaa147 --- /dev/null +++ b/apps/plugins/jpeg/jpeg_decoder.h @@ -0,0 +1,142 @@ +/*************************************************************************** +* __________ __ ___. +* 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. +* +****************************************************************************/ + +#ifndef _JPEG_JPEG_DECODER_H +#define _JPEG_JPEG_DECODER_H + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +struct derived_tbl +{ + /* Basic tables: (element [0] of each array is unused) */ + long mincode[17]; /* smallest code of length k */ + long maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure huff_DECODE terminates) */ + int valptr[17]; /* huffval[] index of 1st symbol of length k */ + + /* Back link to public Huffman table (needed only in slow_DECODE) */ + int* pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + the input data stream. If the next Huffman code is no more + than HUFF_LOOKAHEAD bits long, we can obtain its length and + the corresponding symbol directly from these tables. */ + int look_nbits[1<<HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */ + unsigned char look_sym[1<<HUFF_LOOKAHEAD]; /* symbol, or unused */ +}; + +#define QUANT_TABLE_LENGTH 64 + +/* for type of Huffman table */ +#define DC_LEN 28 +#define AC_LEN 178 + +struct huffman_table +{ /* length and code according to JFIF format */ + int huffmancodes_dc[DC_LEN]; + int huffmancodes_ac[AC_LEN]; +}; + +struct frame_component +{ + int ID; + int horizontal_sampling; + int vertical_sampling; + int quanttable_select; +}; + +struct scan_component +{ + int ID; + int DC_select; + int AC_select; +}; + +struct bitstream +{ + unsigned long get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + unsigned char* next_input_byte; + unsigned char* input_end; /* upper limit +1 */ +}; + +struct jpeg +{ + int x_size, y_size; /* size of image (can be less than block boundary) */ + int x_phys, y_phys; /* physical size, block aligned */ + int x_mbl; /* x dimension of MBL */ + int y_mbl; /* y dimension of MBL */ + int blocks; /* blocks per MB */ + int restart_interval; /* number of MCUs between RSTm markers */ + int store_pos[4]; /* for Y block ordering */ + + unsigned char* p_entropy_data; + unsigned char* p_entropy_end; + + int quanttable[4][QUANT_TABLE_LENGTH]; /* raw quantization tables 0-3 */ + int qt_idct[2][QUANT_TABLE_LENGTH]; /* quantization tables for IDCT */ + + struct huffman_table hufftable[2]; /* Huffman tables */ + struct derived_tbl dc_derived_tbls[2]; /* Huffman-LUTs */ + struct derived_tbl ac_derived_tbls[2]; + + struct frame_component frameheader[3]; /* Component descriptor */ + struct scan_component scanheader[3]; /* currently not used */ + + int mcu_membership[6]; /* info per block */ + int tab_membership[6]; + int subsample_x[3]; /* info per component */ + int subsample_y[3]; +}; + + +/* possible return flags for process_markers() */ +#define HUFFTAB 0x0001 /* with huffman table */ +#define QUANTTAB 0x0002 /* with quantization table */ +#define APP0_JFIF 0x0004 /* with APP0 segment following JFIF standard */ +#define FILL_FF 0x0008 /* with 0xFF padding bytes at begin/end */ +#define SOF0 0x0010 /* with SOF0-Segment */ +#define DHT 0x0020 /* with Definition of huffman tables */ +#define SOS 0x0040 /* with Start-of-Scan segment */ +#define DQT 0x0080 /* with definition of quantization table */ + +/* various helper functions */ +void default_huff_tbl(struct jpeg* p_jpeg); +void build_lut(struct jpeg* p_jpeg); +int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg); + +/* the main decode function */ +#ifdef HAVE_LCD_COLOR +int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[3], + int downscale, void (*pf_progress)(int current, int total)); +#else +int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, + void (*pf_progress)(int current, int total)); +#endif + + +#endif /* _JPEG_JPEG_DECODER_H */ diff --git a/apps/plugins/jpeg/yuv2rgb.c b/apps/plugins/jpeg/yuv2rgb.c new file mode 100644 index 0000000000..1130f038af --- /dev/null +++ b/apps/plugins/jpeg/yuv2rgb.c @@ -0,0 +1,401 @@ +/*************************************************************************** +* __________ __ ___. +* 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" + +/* Needed for memset and rb->lcd_framebuffer */ +extern const struct plugin_api* rb; + +/* + * 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(int x) +{ + if ((unsigned)x > YUV_WHITE) + x = x < 0 ? 0 : YUV_WHITE; + return x; +} + +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, + }, +}; + +/** + * 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; + + 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 = rb->lcd_framebuffer + LCD_WIDTH * y + x; + dst_end = dst + LCD_WIDTH * 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_row = dst; + row_end = dst_row + width; + px.col = src_x; + } + else + { + /* Scan is R->L */ + row_end = dst - 1; + dst_row = row_end + width; + 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 += px.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 += px.inc; + } + while (dst_row != row_end); + } + + src_y++; + dst += LCD_WIDTH; + } + while (dst < dst_end); +} diff --git a/apps/plugins/jpeg/yuv2rgb.h b/apps/plugins/jpeg/yuv2rgb.h new file mode 100644 index 0000000000..d10a944f38 --- /dev/null +++ b/apps/plugins/jpeg/yuv2rgb.h @@ -0,0 +1,51 @@ +/*************************************************************************** +* __________ __ ___. +* 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. +* +****************************************************************************/ + +#ifndef _JPEG_YUV2RGB_H +#define _JPEG_YUV2RGB_H + +enum color_modes +{ + COLOURMODE_COLOUR = 0, + COLOURMODE_GRAY, + COLOUR_NUM_MODES +}; + +enum dither_modes +{ + DITHER_NONE = 0, /* No dithering */ + DITHER_ORDERED, /* Bayer ordered */ + DITHER_DIFFUSION, /* Floyd/Steinberg error diffusion */ + DITHER_NUM_MODES +}; + +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); + +#endif |