diff --git a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c index ee4f3bcf62..98566affe8 100644 --- a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c +++ b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c @@ -674,6 +674,16 @@ int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) return (-5); /* Huffman table index out of range */ } } + p_jpeg->rgb = n == 3 && jpeg_is_rgb(p_jpeg->jfif, p_jpeg->adobe, + p_jpeg->frameheader[0].ID, p_jpeg->frameheader[1].ID, + p_jpeg->frameheader[2].ID); + /* RGB needs every component in one block per MCU: the + planes are converted to YCbCr in place (colour) or + combined per block (greyscale) */ + if (p_jpeg->rgb + && (p_jpeg->frameheader[0].horizontal_sampling != 1 + || p_jpeg->frameheader[0].vertical_sampling != 1)) + return -3; /* Unsupported SOF0 subsampling */ p_src += 3; /* skip spectral information */ p_jpeg->p_entropy_data = p_src; p_end = p_src; /* exit while loop */ @@ -713,7 +723,6 @@ int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) 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*/ @@ -727,7 +736,6 @@ int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) 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 */ { @@ -737,6 +745,19 @@ int process_markers(unsigned char* p_src, long size, struct jpeg* p_jpeg) } break; + case 0xE0: /* Application Field 0 (JFIF) */ + case 0xEE: /* Application Field 14 (Adobe) */ + { + int marker = p_src[-1]; + marker_size = *p_src++ << 8; /* Highbyte */ + marker_size |= *p_src++; /* Lowbyte */ + n = MIN(marker_size - 2, p_end - p_src); + jpeg_app_colorspace(marker, p_src, MIN(n, 12), &p_jpeg->jfif, + &p_jpeg->adobe); + 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 */ @@ -1387,6 +1408,20 @@ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[3], pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ } /* for y */ + if (p_jpeg->rgb) + { /* the display expects YCbCr: convert the equal sized R, G and B + planes in place (JFIF equations) */ + unsigned char *pr = p_pixel[0], *pg = p_pixel[1], *pb = p_pixel[2]; + unsigned char *end = pr + width * height; + for (; pr < end; pr++, pg++, pb++) + { + int r = *pr, g = *pg, b = *pb; + *pr = (77 * r + 150 * g + 29 * b + 128) >> 8; + *pg = clamp_component((-43 * r - 85 * g + 128 * b + 32896) >> 8); + *pb = clamp_component((128 * r - 107 * g - 21 * b + 32896) >> 8); + } + } + return 0; /* success */ } #else /* !HAVE_LCD_COLOR */ @@ -1411,7 +1446,8 @@ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, 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 last_dc_val[3] = {0, 0, 0}; + unsigned char rgb_tmp[2][64]; /* R and G of an RGB MCU */ int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ @@ -1482,10 +1518,10 @@ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, /* Section F.2.2.1: decode the DC coefficient difference */ s = huff_decode_dc(&bs, dctbl); - if (ci == 0) /* only for Y component */ + if (ci == 0 || p_jpeg->rgb) /* Y, or all of R, G, B */ { - last_dc_val += s; - block[0] = last_dc_val; /* output it (assumes zag[0] = 0) */ + last_dc_val[ci] += s; + block[0] = last_dc_val[ci]; /* output it (zag[0] = 0) */ /* coefficient buffer must be cleared */ MEMSET(block+1, 0, zero_need*sizeof(block[0])); @@ -1536,18 +1572,35 @@ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, } } /* for k */ - if (ci == 0) + if (ci == 0 && !p_jpeg->rgb) { /* only for Y component */ pf_idct(p_byte+store_offs[blkn], block, p_jpeg->qt_idct[ci], skip_line); } + else if (p_jpeg->rgb && ci < 2) + { /* keep R and G until B is decoded */ + pf_idct(rgb_tmp[ci], block, p_jpeg->qt_idct[ci], 8); + } + else if (p_jpeg->rgb) + { /* luma from R, G and B (JFIF weights) */ + int n = 8 / downscale; + int xi, yi; + unsigned char *p = p_byte; + pf_idct(p, block, p_jpeg->qt_idct[ci], skip_line); + for (yi = 0; yi < n; yi++, p += skip_line) + for (xi = 0; xi < n; xi++) + p[xi] = (77 * rgb_tmp[0][yi * 8 + xi] + + 150 * rgb_tmp[1][yi * 8 + xi] + + 29 * p[xi] + 128) >> 8; + } } /* 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 */ + last_dc_val[0] = last_dc_val[1] = + last_dc_val[2] = 0; /* reset decoder */ } } /* for x */ if (pf_progress != NULL) diff --git a/apps/plugins/imageviewer/jpeg/jpeg_decoder.h b/apps/plugins/imageviewer/jpeg/jpeg_decoder.h index 9262fcf7ff..307b57ff4a 100644 --- a/apps/plugins/imageviewer/jpeg/jpeg_decoder.h +++ b/apps/plugins/imageviewer/jpeg/jpeg_decoder.h @@ -37,6 +37,9 @@ struct jpeg int blocks; /* blocks per MB */ int restart_interval; /* number of MCUs between RSTm markers */ int store_pos[4]; /* for Y block ordering */ + bool jfif; /* saw a JFIF APP0 marker */ + unsigned char adobe; /* Adobe APP14 transform flag + 1, 0 if none */ + bool rgb; /* the components are R, G, B rather than Y, Cb, Cr */ unsigned char* p_entropy_data; unsigned char* p_entropy_end; diff --git a/apps/recorder/jpeg_common.h b/apps/recorder/jpeg_common.h index fb152feb05..eaa439924c 100644 --- a/apps/recorder/jpeg_common.h +++ b/apps/recorder/jpeg_common.h @@ -27,6 +27,7 @@ #ifndef _JPEG_COMMON_H #define _JPEG_COMMON_H +#include #include "bmp.h" #define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ @@ -80,6 +81,40 @@ union uint8_rgbyuv { struct uint8_rgb rgb; }; +/* Record what an APP0 (JFIF) or APP14 (Adobe) segment says about the + * colour space, given its first n bytes (at most 12 are needed). *adobe gets + * the Adobe transform flag + 1. */ +static inline void jpeg_app_colorspace(int marker, const unsigned char *id, + int n, bool *jfif, unsigned char *adobe) +{ + bool is_jfif = marker == 0xE0; + const char *sig = is_jfif ? "JFIF" : "Adobe"; /* 5 bytes with the NUL */ + int i; + + if (n < (is_jfif ? 5 : 12)) + return; + for (i = 0; i < 5; i++) + if (id[i] != (unsigned char)sig[i]) + return; + if (is_jfif) + *jfif = true; + else + *adobe = id[11] + 1; +} + +/* Whether a 3-component image holds RGB rather than YCbCr, decided as + * libjpeg does: a JFIF marker means YCbCr; otherwise an Adobe marker's + * transform flag decides (0 is RGB); otherwise component IDs 'R', 'G', 'B' + * mean RGB. adobe is the transform flag + 1, or 0 without an Adobe marker. */ +static inline bool jpeg_is_rgb(bool jfif, int adobe, int id0, int id1, int id2) +{ + if (jfif) + return false; + if (adobe) + return adobe == 1; + return id0 == 'R' && id1 == 'G' && id2 == 'B'; +} + static inline int clamp_component(int x) { if(x > 255) return 255; diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c index eb10d1e94f..e81aba8ddf 100644 --- a/apps/recorder/jpeg_load.c +++ b/apps/recorder/jpeg_load.c @@ -56,6 +56,13 @@ typedef uint8_t jpeg_pix_t; #endif #define JPEG_IDCT_TRANSPOSE #define JPEG_PIX_SZ (sizeof(jpeg_pix_t)) +/* index into the per-scale arrays: luma or chroma in colour builds; a + greyscale build only decodes chroma for RGB, which is at the luma scale */ +#ifdef HAVE_LCD_COLOR +#define SCALE_IDX(ci) (!!(ci)) +#else +#define SCALE_IDX(ci) 0 +#endif #ifdef HAVE_LCD_COLOR #define COLOR_EXTRA_IDCT_WS 64 #else @@ -106,14 +113,16 @@ struct jpeg int cur_row; /* current row relative to top of image */ int set_rows; int store_pos[4]; /* for Y block ordering */ -#ifdef HAVE_LCD_COLOR + bool jfif; /* saw a JFIF APP0 marker */ + unsigned char adobe; /* Adobe APP14 transform flag + 1, 0 if none */ + bool rgb; /* the components are R, G, B rather than Y, Cb, Cr */ int last_dc_val[3]; +#ifdef HAVE_LCD_COLOR int h_scale[2]; /* horizontal scalefactor = (2**N) / 8 */ int v_scale[2]; /* same as above, for vertical direction */ int k_need[2]; /* per component zig-zag index of last needed coefficient */ int zero_need[2]; /* per compenent number of coefficients to zero */ #else - int last_dc_val; int h_scale[1]; /* horizontal scalefactor = (2**N) / 8 */ int v_scale[1]; /* same as above, for vertical direction */ int k_need[1]; /* per component zig-zag index of last needed coefficient */ @@ -1198,6 +1207,17 @@ static int process_markers(struct jpeg* p_jpeg) return (-5); /* Huffman table index out of range */ } } + p_jpeg->rgb = n == 3 && jpeg_is_rgb(p_jpeg->jfif, p_jpeg->adobe, + p_jpeg->frameheader[0].ID, p_jpeg->frameheader[1].ID, + p_jpeg->frameheader[2].ID); +#ifndef HAVE_LCD_COLOR + /* greyscale RGB is combined per block, so all components + must be one block per MCU (chroma is then 1x1 too) */ + if (p_jpeg->rgb + && (p_jpeg->frameheader[0].horizontal_sampling != 1 + || p_jpeg->frameheader[0].vertical_sampling != 1)) + return -3; /* Unsupported SOF0 subsampling */ +#endif /* skip spectral information */ e_skip_bytes(p_jpeg, marker_size); done = true; @@ -1264,11 +1284,22 @@ static int process_markers(struct jpeg* p_jpeg) case 0xEF: /* Application Field 15*/ case 0xFE: /* Comment */ { + unsigned char id[12]; + int marker = c; marker_size = e_getc(p_jpeg, -1) << 8; /* Highbyte */ marker_size |= e_getc(p_jpeg, -1); /* Lowbyte */ marker_size -= 2; + n = 0; + if (marker == 0xE0 || marker == 0xEE) + { /* JFIF or Adobe: may say which colour space is used */ + n = MIN(marker_size, (int)sizeof(id)); + for (i = 0; i < n; i++) + id[i] = e_getc(p_jpeg, -1); + jpeg_app_colorspace(marker, id, n, &p_jpeg->jfif, + &p_jpeg->adobe); + } JDEBUGF("unhandled marker len %d\n", marker_size); - e_skip_bytes(p_jpeg, marker_size); /* skip segment */ + e_skip_bytes(p_jpeg, marker_size - n); /* skip segment */ } break; @@ -1546,13 +1577,14 @@ INLINE void fix_quant_tables(struct jpeg *p_jpeg) #ifdef HAVE_LCD_COLOR const int nc = p_jpeg->blocks > 1 ? 3 : 1; #else - const int nc = 1; /* chroma is not decoded */ + /* chroma is not decoded; RGB is, at the luma scale */ + const int nc = p_jpeg->rgb ? 3 : 1; #endif for (c = 0; c < nc; c++) { int t = p_jpeg->frameheader[c].quanttable_select; - int shift = idct_tbl[p_jpeg->v_scale[!!c]].scale; + int shift = idct_tbl[p_jpeg->v_scale[SCALE_IDX(c)]].scale; for (f = 0; f < 4; f++) /* a slot already holding t at this scale? */ if (orig[f] == t && want[f] == shift) @@ -1823,6 +1855,9 @@ static struct img_part *store_row_jpeg(void *jpeg_args) store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* decoded DCT coefficients */ int16_t block[IDCT_WS_SIZE] __attribute__((aligned(8))); +#ifndef HAVE_LCD_COLOR + unsigned char rgb_tmp[2][64]; /* R and G of a greyscale RGB MCU */ +#endif for (x = 0; x < p_jpeg->x_mbl; x++) { int blkn; @@ -1830,7 +1865,7 @@ static struct img_part *store_row_jpeg(void *jpeg_args) { int ci = p_jpeg->mcu_membership[blkn]; /* component index */ #ifdef JPEG_IDCT_TRANSPOSE - bool transpose = p_jpeg->v_scale[!!ci] > 2; + bool transpose = p_jpeg->v_scale[SCALE_IDX(ci)] > 2; #endif int k = 1; /* coefficient index */ int s, r; /* huffman values */ @@ -1845,21 +1880,16 @@ static struct img_part *store_row_jpeg(void *jpeg_args) huff_decode_dc(p_jpeg, dctbl, s, r); #ifndef HAVE_LCD_COLOR - if (!ci) + if (!ci || p_jpeg->rgb) #endif { s = HUFF_EXTEND(r, s); -#ifdef HAVE_LCD_COLOR p_jpeg->last_dc_val[ci] += s; /* output it (assumes zag[0] = 0) */ block[0] = MULTIPLY16(p_jpeg->last_dc_val[ci], qt[0]); -#else - p_jpeg->last_dc_val += s; - /* output it (assumes zag[0] = 0) */ - block[0] = MULTIPLY16(p_jpeg->last_dc_val, qt[0]); -#endif /* coefficient buffer must be cleared */ - MEMSET(block+1, 0, p_jpeg->zero_need[!!ci] * sizeof(int)); + MEMSET(block+1, 0, + p_jpeg->zero_need[SCALE_IDX(ci)] * sizeof(int)); /* Section F.2.2.2: decode the AC coefficients */ while(true) { @@ -1870,7 +1900,7 @@ static struct img_part *store_row_jpeg(void *jpeg_args) if (s) { check_bit_buffer(p_jpeg, s); - if (k >= p_jpeg->k_need[!!ci]) + if (k >= p_jpeg->k_need[SCALE_IDX(ci)]) goto skip_rest; r = get_bits(p_jpeg, s); r = HUFF_EXTEND(r, s); @@ -1912,26 +1942,52 @@ skip_rest: } /* for k */ block_end: #ifndef HAVE_LCD_COLOR - if (!ci) + if (!ci || p_jpeg->rgb) #endif { - int idct_cols = BIT_N(MIN(p_jpeg->h_scale[!!ci], 3)); - int idct_rows = BIT_N(p_jpeg->v_scale[!!ci]); - unsigned char *b_out = out + ci + int sc = SCALE_IDX(ci); + int idct_cols = BIT_N(MIN(p_jpeg->h_scale[sc], 3)); + int idct_rows = BIT_N(p_jpeg->v_scale[sc]); + int rowstep = b_width; +#ifdef HAVE_LCD_COLOR + /* Y, Cb, Cr go to bytes 0-2 for the YUV conversion; + R, G, B to their places in struct uint8_rgb */ + unsigned char *b_out = out + (p_jpeg->rgb ? 2 - ci : ci) + store_offs[p_jpeg->mcu_pos[blkn]]; - if (idct_tbl[p_jpeg->v_scale[!!ci]].v_idct) +#else + unsigned char *b_out = out + + store_offs[p_jpeg->mcu_pos[blkn]]; + if (ci < 2 && p_jpeg->rgb) + { /* keep R and G until B is decoded */ + b_out = rgb_tmp[ci]; + rowstep = 8; + } +#endif + if (idct_tbl[p_jpeg->v_scale[sc]].v_idct) #ifdef JPEG_IDCT_TRANSPOSE - idct_tbl[p_jpeg->v_scale[!!ci]].v_idct(block, + idct_tbl[p_jpeg->v_scale[sc]].v_idct(block, transpose ? block + 8 * idct_cols : block + idct_cols); uint16_t * h_block = transpose ? block + 64 : block; - idct_tbl[p_jpeg->h_scale[!!ci]].h_idct(h_block, b_out, - h_block + idct_rows * 8, b_width); + idct_tbl[p_jpeg->h_scale[sc]].h_idct(h_block, b_out, + h_block + idct_rows * 8, rowstep); #else - idct_tbl[p_jpeg->v_scale[!!ci]].v_idct(block, + idct_tbl[p_jpeg->v_scale[sc]].v_idct(block, block + idct_cols); - idct_tbl[p_jpeg->h_scale[!!ci]].h_idct(block, b_out, - block + idct_rows * 8, b_width); + idct_tbl[p_jpeg->h_scale[sc]].h_idct(block, b_out, + block + idct_rows * 8, rowstep); +#endif +#ifndef HAVE_LCD_COLOR + if (ci == 2) + { /* luma from R, G and B (JFIF weights) */ + int w = BIT_N(p_jpeg->h_scale[0]); + int xi, yi; + for (yi = 0; yi < idct_rows; yi++, b_out += b_width) + for (xi = 0; xi < w; xi++) + b_out[xi] = (77 * rgb_tmp[0][yi * 8 + xi] + + 150 * rgb_tmp[1][yi * 8 + xi] + + 29 * b_out[xi] + 128) >> 8; + } #endif } } /* for blkn */ @@ -1958,12 +2014,8 @@ block_end: { /* if a restart marker is due: */ p_jpeg->restart = p_jpeg->restart_interval; /* count again */ search_restart(p_jpeg); /* align the bitstream */ -#ifdef HAVE_LCD_COLOR p_jpeg->last_dc_val[0] = p_jpeg->last_dc_val[1] = p_jpeg->last_dc_val[2] = 0; /* reset decoder */ -#else - p_jpeg->last_dc_val = 0; -#endif } } } /* if !p_jpeg->mcu_row */ @@ -2268,7 +2320,8 @@ int clip_jpeg_fd(int fd, int flags, if (resize) { if (resize_on_load(bm, dither, &src_dim, &rset, buf_start, maxsize, - cformat, IF_PIX_FMT(p_jpeg->blocks == 1 ? 0 : 1,) store_row_jpeg, + cformat, IF_PIX_FMT(p_jpeg->blocks == 1 || p_jpeg->rgb ? 0 : 1,) + store_row_jpeg, p_jpeg)) return bm_size; } else { @@ -2292,7 +2345,7 @@ int clip_jpeg_fd(int fd, int flags, { part = store_row_jpeg(p_jpeg); #ifdef HAVE_LCD_COLOR - if (p_jpeg->blocks > 1) + if (p_jpeg->blocks > 1 && !p_jpeg->rgb) { struct uint8_rgb *qp = part->buf; struct uint8_rgb *end = qp + bm->width;