297e0504da
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17261 a1c6a512-1295-4272-9138-f99709370657
3410 lines
109 KiB
C
3410 lines
109 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* 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
|
|
|
|
#else
|
|
#error No keymap defined!
|
|
#endif
|
|
|
|
#ifdef HAVE_TOUCHPAD
|
|
#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 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:
|
|
playback_control(rb, NULL);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_LCD_COLOR
|
|
case MIID_DISPLAY_OPTIONS:
|
|
display_options();
|
|
break;
|
|
#endif
|
|
case MIID_RETURN:
|
|
break;
|
|
}
|
|
|
|
#if !defined(SIMULATOR) && !defined(HAVE_FLASH_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->splash(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->splash(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(struct plugin_api* api, 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_FLASH_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 */
|