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