rockbox/apps/plugins/imageviewer/jpegp/jpegp.c
Michael Giacomelli 9936e65e6d imageviewer/jpegp: decode RGB images
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
2026-10-02 08:24:03 -04:00

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