mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-10 08:03:04 -04:00
jpegp converted every image from YCbCr, so RGB JPEGs showed scrambled colours. That affects progressive RGB files, and now also baseline RGB files the jpeg decoder rejects and hands on to jpegp, such as RGB with the R component sampled 2x2. Record the JFIF and Adobe APP14 markers, decide the colour space with the same rule as the other decoders, and skip the YUV conversion for RGB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Change-Id: If023eeb612b7f8a891d21fbe0070ab43c5a08f17
298 lines
8.8 KiB
C
298 lines
8.8 KiB
C
#include "jpeg81.h"
|
|
#include "idct.h"
|
|
#include "GETC.h"
|
|
#include "rb_glue.h"
|
|
|
|
#include "../imageviewer.h"
|
|
#include "jpeg_common.h"
|
|
|
|
|
|
/**************** begin Application ********************/
|
|
|
|
/************************* Types ***************************/
|
|
|
|
struct t_disp
|
|
{
|
|
unsigned char* bitmap;
|
|
};
|
|
|
|
/************************* Globals ***************************/
|
|
|
|
/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
|
|
static struct t_disp disp[9];
|
|
|
|
static struct JPEGD 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
|
|
rb->lcd_bitmap_part(
|
|
(fb_data*)pdisp->bitmap, info->x + x, info->y + y,
|
|
STRIDE(SCREEN_MAIN, info->width, info->height),
|
|
x + MAX(0, (LCD_WIDTH-info->width)/2),
|
|
y + MAX(0, (LCD_HEIGHT-info->height)/2),
|
|
width, height);
|
|
#else
|
|
mylcd_ub_gray_bitmap_part(
|
|
pdisp->bitmap, info->x + x, info->y + y, info->width,
|
|
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)
|
|
{
|
|
struct JPEGD* j = &jpg;
|
|
return j->Y/ds * j->X/ds*sizeof(fb_data);
|
|
}
|
|
|
|
/* my memory pool (from the mp3 buffer) */
|
|
static char print[32]; /* use a common snprintf() buffer */
|
|
|
|
static void scaled_dequantization_and_idct(void)
|
|
{
|
|
struct JPEGD* j = &jpg;
|
|
// The following code is based on RAINBOW lib jpeg2bmp example:
|
|
// https://github.com/Halicery/vc_rainbow/blob/605c045a564dad8e2df84e48914eac3d2d8d4a9b/jpeg2bmp.c
|
|
|
|
printf("Scaled de-quantization and IDCT.. ");
|
|
int c, i, n;
|
|
|
|
// Pre-scale quant-tables
|
|
int SQ[4][64];
|
|
for (c=0; c<4 && j->QT[c][0]; c++)
|
|
{
|
|
int *q= j->QT[c], *sq= SQ[c];
|
|
for (i=0; i<64; i++) sq[i]= q[i] * SCALEM[zigzag[i]];
|
|
}
|
|
|
|
// DEQUANT + IDCT
|
|
for (c=0; c<j->Nf; c++)
|
|
{
|
|
struct COMP *C= j->Components+c;
|
|
//int *q= j->QT[C->Qi];
|
|
int *sq= SQ[C->Qi];
|
|
|
|
for (n=0; n < C->du_size; n++)
|
|
{
|
|
/*
|
|
// <--- scaled idct
|
|
int k, t[64];
|
|
TCOEF *coef= du[x];
|
|
t[0]= coef[0] * q[0] + 1024; // dequant DC and level-shift (8-bit)
|
|
for (k=1; k<64; k++) t[zigzag[k]] = coef[k] * q[k]; // dequant AC (+zigzag)
|
|
idct_s(t, coef);
|
|
*/
|
|
|
|
// <--- scaled idct with dequant
|
|
idct_sq( C->du[ (n / C->du_w) * C->du_width + n % C->du_w ], sq );
|
|
}
|
|
}
|
|
printf("done\n");
|
|
}
|
|
|
|
static int load_image(char *filename, struct image_info *info,
|
|
unsigned char *buf, ssize_t *buf_size,
|
|
int offset, int filesize)
|
|
{
|
|
(void)filesize;
|
|
int status;
|
|
struct JPEGD *p_jpg = &jpg;
|
|
|
|
memset(&disp, 0, sizeof(disp));
|
|
memset(&jpg, 0, sizeof(jpg));
|
|
|
|
init_mem_pool(buf, *buf_size);
|
|
|
|
if (!OPEN(filename))
|
|
{
|
|
return PLUGIN_ERROR;
|
|
}
|
|
if (offset)
|
|
{
|
|
POS(offset);
|
|
}
|
|
|
|
if (!iv->settings->hide_info &&
|
|
!iv->running_slideshow)
|
|
{
|
|
rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1);
|
|
rb->lcd_puts(0, 2, "decoding...");
|
|
rb->lcd_update();
|
|
}
|
|
long time; /* measured ticks */
|
|
|
|
/* the actual decoding */
|
|
time = *rb->current_tick;
|
|
status = JPEGDecode(p_jpg);
|
|
time = *rb->current_tick - time;
|
|
|
|
CLOSE();
|
|
|
|
if (status < 0)
|
|
{ /* bad format or minimum components not contained */
|
|
if (status == JPEGENUMERR_MALLOC)
|
|
{
|
|
return PLUGIN_OUTOFMEM;
|
|
}
|
|
rb->splashf(HZ, "unsupported %d", status);
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
if (!iv->settings->hide_info &&
|
|
!iv->running_slideshow)
|
|
{
|
|
rb->lcd_putsf(0, 2, "image %dx%d", p_jpg->X, p_jpg->Y);
|
|
int w, h; /* used to center output */
|
|
rb->snprintf(print, sizeof(print), "jpegp %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();
|
|
//rb->sleep(100);
|
|
}
|
|
|
|
info->x_size = p_jpg->X;
|
|
info->y_size = p_jpg->Y;
|
|
|
|
#ifdef DISK_SPINDOWN
|
|
if (iv->running_slideshow && iv->immediate_ata_off)
|
|
{
|
|
/* running slideshow and time is long enough: power down disk */
|
|
rb->storage_sleep();
|
|
}
|
|
#endif
|
|
|
|
if ( 3 != p_jpg->Nf )
|
|
return PLUGIN_ERROR;
|
|
|
|
scaled_dequantization_and_idct();
|
|
|
|
*buf_size = freeze_mem_pool();
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
static int get_image(struct image_info *info, int frame, int ds)
|
|
{
|
|
(void)frame;
|
|
struct JPEGD* p_jpg = &jpg;
|
|
struct t_disp* p_disp = &disp[ds]; /* short cut */
|
|
|
|
info->width = p_jpg->X / ds;
|
|
info->height = p_jpg->Y / ds;
|
|
info->data = p_disp;
|
|
|
|
if (p_disp->bitmap != NULL)
|
|
{
|
|
/* we still have it */
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
struct JPEGD* j = p_jpg;
|
|
int mem = img_mem(ds);
|
|
|
|
p_disp->bitmap = malloc(mem);
|
|
|
|
if (!p_disp->bitmap)
|
|
{
|
|
clear_mem_pool();
|
|
memset(&disp, 0, sizeof(disp));
|
|
p_disp->bitmap = malloc(mem);
|
|
if (!p_disp->bitmap)
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
fb_data *bmp = (fb_data *)p_disp->bitmap;
|
|
|
|
// The following code is based on RAINBOW lib jpeg2bmp example:
|
|
// https://github.com/Halicery/vc_rainbow/blob/605c045a564dad8e2df84e48914eac3d2d8d4a9b/jpeg2bmp.c
|
|
// Primitive yuv-rgb converter for all sub-sampling types, 24-bit BMP only
|
|
printf("YUV-to-RGB conversion.. ");
|
|
int h0 = j->Hmax / j->Components[0].Hi;
|
|
int v0 = j->Vmax / j->Components[0].Vi;
|
|
int h1 = j->Hmax / j->Components[1].Hi;
|
|
int v1 = j->Vmax / j->Components[1].Vi;
|
|
int h2 = j->Hmax / j->Components[2].Hi;
|
|
int v2 = j->Vmax / j->Components[2].Vi;
|
|
bool rgb = jpeg_is_rgb(j->jfif, j->adobe, j->Components[0].Ci,
|
|
j->Components[1].Ci, j->Components[2].Ci);
|
|
|
|
int x, y;
|
|
int max_y = info->height * ds;
|
|
int max_x = info->width * ds;
|
|
for (y = 0; y < max_y; y += ds)
|
|
{
|
|
TCOEF *C0 =
|
|
j->Components[0].du[j->Components[0].du_width * ((y / v0) / 8)] + 8 * ((y / v0) & 7);
|
|
TCOEF *C1 =
|
|
j->Components[1].du[j->Components[1].du_width * ((y / v1) / 8)] + 8 * ((y / v1) & 7);
|
|
TCOEF *C2 =
|
|
j->Components[2].du[j->Components[2].du_width * ((y / v2) / 8)] + 8 * ((y / v2) & 7);
|
|
for (x = 0; x < max_x; x += ds)
|
|
{
|
|
int c0, c1, c2;
|
|
if (ds == 1)
|
|
{
|
|
c0 = C0[(x / h0 / 8) * 64 + ((x / h0) & 7)];
|
|
c1 = C1[(x / h1 / 8) * 64 + ((x / h1) & 7)];
|
|
c2 = C2[(x / h2 / 8) * 64 + ((x / h2) & 7)];
|
|
}
|
|
else
|
|
{
|
|
/* Box filter downsampling (area averaging):
|
|
Average a ds*ds block of Y/U/V samples per output pixel
|
|
for simple anti-aliasing without extra buffers */
|
|
int sumY = 0, sumU = 0, sumV = 0;
|
|
int dx, dy;
|
|
int area = ds * ds;
|
|
for (dy = 0; dy < ds; dy++)
|
|
{
|
|
TCOEF *C0_dy = j->Components[0].du[j->Components[0].du_width * (((y + dy) / v0) / 8)] + 8 * (((y + dy) / v0) & 7);
|
|
TCOEF *C1_dy = j->Components[1].du[j->Components[1].du_width * (((y + dy) / v1) / 8)] + 8 * (((y + dy) / v1) & 7);
|
|
TCOEF *C2_dy = j->Components[2].du[j->Components[2].du_width * (((y + dy) / v2) / 8)] + 8 * (((y + dy) / v2) & 7);
|
|
for (dx = 0; dx < ds; dx++)
|
|
{
|
|
int sx = x + dx;
|
|
sumY += C0_dy[((sx / h0 / 8) * 64) + ((sx / h0) & 7)];
|
|
sumU += C1_dy[((sx / h1 / 8) * 64) + ((sx / h1) & 7)];
|
|
sumV += C2_dy[((sx / h2 / 8) * 64) + ((sx / h2) & 7)];
|
|
}
|
|
}
|
|
c0 = (sumY + area/2) / area;
|
|
c1 = (sumU + area/2) / area;
|
|
c2 = (sumV + area/2) / area;
|
|
}
|
|
|
|
if (rgb) // components are R, G, B: no conversion
|
|
*bmp++= FB_RGBPACK(c0, c1, c2);
|
|
else
|
|
// ITU BT.601 full-range YUV-to-RGB integer approximation
|
|
{
|
|
int y = (c0 << 5) + 16;
|
|
int u = c1 - 128;
|
|
int v = c2 - 128;
|
|
|
|
int b = CLIP[(y + 57 * u)>>5]; // B;
|
|
int g = CLIP[(y - 11 * u - 23 * v)>>5]; // G
|
|
int r = CLIP[(y + 45 * v)>>5]; // R;
|
|
*bmp++= FB_RGBPACK(r,g,b);
|
|
}
|
|
}
|
|
}
|
|
printf("done\n");
|
|
return 0;
|
|
}
|
|
|
|
const struct image_decoder image_decoder = {
|
|
false,
|
|
img_mem,
|
|
load_image,
|
|
get_image,
|
|
draw_image_rect,
|
|
};
|
|
|
|
IMGDEC_HEADER
|