mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
Most of the churn here occurs because 'filesize' is one of the redefined filesystem functions, but some of the structs used by the native FS code also include a 'filesize' member variable which gets renamed by the macro in some but not all source files. It's easier to rename 'filesize()' to 'ffilesize()' rather than try to clean up the macro mess or renaming the struct members. There is weirdness with root_realpath() which now breaks on native builds because it was assumed to be unprefixed there. dir_get_info() was unprefixed everywhere but this just seems inconsistent; make it follow the FS_PREFIX() convention too. Change-Id: Ic3700c6234ea45f32679c1a8429d70fdb8f4088a
314 lines
8.8 KiB
C
314 lines
8.8 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
*
|
|
* 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 offset, int filesize)
|
|
{
|
|
int fd;
|
|
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;
|
|
}
|
|
|
|
if (offset)
|
|
{
|
|
rb->lseek(fd, offset, SEEK_SET);
|
|
}
|
|
else
|
|
{
|
|
filesize = rb->ffilesize(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->settings->hide_info &&
|
|
!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->settings->hide_info &&
|
|
!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->settings->hide_info &&
|
|
!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->settings->hide_info &&
|
|
!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->settings->hide_info &&
|
|
!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
|