1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/imageviewer/jpeg/jpeg.c
Roman Artiukhin b8238f7b20 imageviewer: Initial support for JPEG progressive images. Add decoder
Added jpeg decoder jpegp.c using RAINBOW lib. Currently enabled only for pictures not supported by old decoder (as old decoder more optimized for low mem targets)

Someone TODO:
 * Old decoder has optimized downscale logic which new decoder doesn't have (it gives big difference in required memory and time for decoding). This logic should be ported/adapted if possible.

 * Add smooth downscaling.

 * Grayscale support

Change-Id: Ie96bc62848b51cc6a3942f8e069ec6ab02dc1c56
2024-10-14 09:19:34 -04:00

302 lines
8.5 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* JPEG image viewer
* (This is a real mess if it has to be coded in one single C file)
*
* File scrolling addition (C) 2005 Alexander Spyridakis
* Copyright (C) 2004 Jörg Hohensohn aka [IDC]Dragon
* Heavily borrowed from the IJG implementation (C) Thomas G. Lane
* Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org
*
* 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 "../imageviewer.h"
#include "jpeg_decoder.h"
#ifdef HAVE_LCD_COLOR
#include "yuv2rgb.h"
#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 stride;
};
/************************* Globals ***************************/
/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
static struct t_disp disp[9];
/* my memory pool (from the mp3 buffer) */
static char print[32]; /* use a common snprintf() buffer */
/* the root of the images, hereafter are decompresed ones */
static unsigned char* buf_root;
static int root_size;
/* up to here currently used by image(s) */
static unsigned char* buf_images;
static ssize_t buf_images_size;
static struct jpeg jpg; /* too large for stack */
/************************* Implementation ***************************/
static void draw_image_rect(struct image_info *info,
int x, int y, int width, int height)
{
struct t_disp* pdisp = (struct t_disp*)info->data;
#ifdef HAVE_LCD_COLOR
yuv_bitmap_part(
pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
info->x + x, info->y + y, pdisp->stride,
x + MAX(0, (LCD_WIDTH - info->width) / 2),
y + MAX(0, (LCD_HEIGHT - info->height) / 2),
width, height,
iv->settings->jpeg_colour_mode, iv->settings->jpeg_dither_mode);
#else
mylcd_ub_gray_bitmap_part(
pdisp->bitmap[0], info->x + x, info->y + y, pdisp->stride,
x + MAX(0, (LCD_WIDTH-info->width)/2),
y + MAX(0, (LCD_HEIGHT-info->height)/2),
width, height);
#endif
}
static int img_mem(int ds)
{
int size;
struct jpeg *p_jpg = &jpg;
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;
}
static int load_image(char *filename, struct image_info *info,
unsigned char *buf, ssize_t *buf_size)
{
int fd;
int filesize;
unsigned char* buf_jpeg; /* compressed JPEG image */
int status;
struct jpeg *p_jpg = &jpg;
rb->memset(&disp, 0, sizeof(disp));
rb->memset(&jpg, 0, sizeof(jpg));
fd = rb->open(filename, O_RDONLY);
if (fd < 0)
{
rb->splashf(HZ, "err opening %s: %d", filename, fd);
return PLUGIN_ERROR;
}
filesize = rb->filesize(fd);
/* allocate JPEG buffer */
buf_jpeg = buf;
/* we can start the decompressed images behind it */
buf_images = buf_root = buf + filesize;
buf_images_size = root_size = *buf_size - filesize;
if (buf_images_size <= 0)
{
rb->close(fd);
return PLUGIN_OUTOFMEM;
}
if(!iv->running_slideshow)
{
rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1);
rb->lcd_putsf(0, 1, "loading %d bytes", filesize);
rb->lcd_update();
}
rb->read(fd, buf_jpeg, filesize);
rb->close(fd);
if(!iv->running_slideshow)
{
rb->lcd_puts(0, 2, "decoding markers");
rb->lcd_update();
}
#ifdef DISK_SPINDOWN
else if(iv->immediate_ata_off)
{
/* running slideshow and time is long enough: power down disk */
rb->storage_sleep();
}
#endif
/* process markers, unstuffing */
status = process_markers(buf_jpeg, filesize, p_jpg);
if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0))
{ /* bad format or minimum components not contained */
#ifndef HAVE_LCD_COLOR
rb->splashf(HZ, "unsupported %d", status);
return PLUGIN_ERROR;
#else
return PLUGIN_JPEG_PROGRESSIVE;
#endif
}
if (!(status & DHT)) /* if no Huffman table present: */
default_huff_tbl(p_jpg); /* use default */
build_lut(p_jpg); /* derive Huffman and other lookup-tables */
if(!iv->running_slideshow)
{
rb->lcd_putsf(0, 2, "image %dx%d", p_jpg->x_size, p_jpg->y_size);
rb->lcd_update();
}
info->x_size = p_jpg->x_size;
info->y_size = p_jpg->y_size;
*buf_size = buf_images_size;
return PLUGIN_OK;
}
static int get_image(struct image_info *info, int frame, int ds)
{
(void)frame;
int w, h; /* used to center output */
int size; /* decompressed image size */
long time; /* measured ticks */
int status;
struct jpeg* p_jpg = &jpg;
struct t_disp* p_disp = &disp[ds]; /* short cut */
info->width = p_jpg->x_size / ds;
info->height = p_jpg->y_size / ds;
info->data = p_disp;
if (p_disp->bitmap[0] != NULL)
{
/* we still have it */
return PLUGIN_OK;
}
/* assign image buffer */
/* physical size needed for decoding */
size = img_mem(ds);
if (buf_images_size <= size)
{ /* have to discard the current */
int i;
for (i=1; i<=8; i++)
disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */
buf_images = buf_root; /* start again from the beginning of the buffer */
buf_images_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_images;
buf_images += size;
buf_images_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_images;
}
#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_images;
buf_images += size;
buf_images_size -= size;
if(!iv->running_slideshow)
{
rb->lcd_putsf(0, 3, "decoding %d*%d", info->width, info->height);
rb->lcd_update();
}
/* update image properties */
p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */
/* 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, iv->cb_progress);
rb->cpu_boost(false);
#else
status = jpeg_decode(p_jpg, p_disp->bitmap, ds, iv->cb_progress);
#endif
if (status)
{
rb->splashf(HZ, "decode error %d", status);
return PLUGIN_ERROR;
}
time = *rb->current_tick - time;
if(!iv->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 PLUGIN_OK;
}
const struct image_decoder image_decoder = {
false,
img_mem,
load_image,
get_image,
draw_image_rect,
};
IMGDEC_HEADER