mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
feat(lib): baseline JPEG decoder -- the browser shows photos
Adds a from-scratch baseline JPEG decoder (ITU-T T.81, SOF0 sequential DCT), so the Web app now displays the web's dominant photo format alongside PNG and BMP. image_decode dispatches on the JPEG magic, so the browser needs no change beyond that. lib/jpeg.c handles grayscale and YCbCr, arbitrary chroma subsampling (4:4:4/4:2:2/4:2:0), restart markers, and multiple DQT/DHT tables. Huffman uses canonical tables (counts + symbols, like the inflate decoder) to avoid a large fast-table; the transform is the standard fixed-point integer row/column IDCT; colour is integer YCbCr->RGB. All heap-allocated, no floating point, so it is safe in the freestanding kernel. Progressive (SOF2) and arithmetic-coded JPEGs are rejected rather than mis-decoded. Verified on the host against PIL-encoded references before going in the kernel, the same way the crypto and PNG decoders were: grayscale decodes at mean absolute error 0.9/channel, 4:4:4 at 1.7 (pure IDCT rounding vs libjpeg), and 4:2:0 at 4.3 (nearest-neighbour chroma upsampling softens colour edges -- a known, visually-fine artifact). In QEMU the Web app fetched a 400x260 JPEG over HTTP and rendered it correctly -- gradient, an antialiased red circle, a green box, a yellow diagonal. Builds clean on x86_64, aarch64 and armv8i. Limits: baseline only (no progressive), and chroma upsampling is nearest-neighbour rather than the smoother interpolation libjpeg uses. The browser still shows images by URL, not inline in a page. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
1 parent
fe33ddc79b
commit
db962de248
4 files changed
+486
-1
No files matched your search
@@ -129,7 +129,7 @@ CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/prin
|
|||||||
kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c kernel/time/ktime.c \
|
kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c kernel/time/ktime.c \
|
||||||
kernel/elf.c kernel/exec.c \
|
kernel/elf.c kernel/exec.c \
|
||||||
lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \
|
lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \
|
||||||
lib/inflate.c lib/image.c \
|
lib/inflate.c lib/image.c lib/jpeg.c \
|
||||||
init/cmdline.c init/service.c init/bootlogo.c init/logo_data.c usr/initramfs.c block/block.c block/elevator.c \
|
init/cmdline.c init/service.c init/bootlogo.c init/logo_data.c usr/initramfs.c block/block.c block/elevator.c \
|
||||||
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
|
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
|
||||||
crypto/sha256.c crypto/md5.c crypto/crc32.c crypto/prng.c \
|
crypto/sha256.c crypto/md5.c crypto/crc32.c crypto/prng.c \
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _LIB_JPEG_H
|
||||||
|
#define _LIB_JPEG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <lib/image.h>
|
||||||
|
|
||||||
|
// Baseline JPEG (SOF0) decoder. Fills img with a heap-allocated 0xAARRGGBB
|
||||||
|
// buffer on success (return 0); progressive/arithmetic JPEGs are rejected.
|
||||||
|
int jpeg_decode(const uint8_t* data, unsigned len, image_t* img);
|
||||||
|
|
||||||
|
#endif // _LIB_JPEG_H
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <lib/image.h>
|
#include <lib/image.h>
|
||||||
#include <lib/inflate.h>
|
#include <lib/inflate.h>
|
||||||
|
#include <lib/jpeg.h>
|
||||||
#include <lib/string.h>
|
#include <lib/string.h>
|
||||||
#include <mm/kmalloc.h>
|
#include <mm/kmalloc.h>
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ static const uint8_t png_sig[8] = { 0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a
|
|||||||
|
|
||||||
bool image_sniff(const uint8_t* d, unsigned len) {
|
bool image_sniff(const uint8_t* d, unsigned len) {
|
||||||
if (len >= 8 && memcmp(d, png_sig, 8) == 0) return true;
|
if (len >= 8 && memcmp(d, png_sig, 8) == 0) return true;
|
||||||
|
if (len >= 3 && d[0] == 0xFF && d[1] == 0xD8 && d[2] == 0xFF) return true; // JPEG
|
||||||
if (len >= 2 && d[0] == 'B' && d[1] == 'M') return true;
|
if (len >= 2 && d[0] == 'B' && d[1] == 'M') return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -267,6 +269,8 @@ int image_decode(const uint8_t* data, unsigned len, image_t* img) {
|
|||||||
img->pixels = NULL;
|
img->pixels = NULL;
|
||||||
|
|
||||||
if (len >= 8 && memcmp(data, png_sig, 8) == 0) return decode_png(data, len, img);
|
if (len >= 8 && memcmp(data, png_sig, 8) == 0) return decode_png(data, len, img);
|
||||||
|
if (len >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF)
|
||||||
|
return jpeg_decode(data, len, img);
|
||||||
if (len >= 2 && data[0] == 'B' && data[1] == 'M') return decode_bmp(data, len, img);
|
if (len >= 2 && data[0] == 'B' && data[1] == 'M') return decode_bmp(data, len, img);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
+470
@@ -0,0 +1,470 @@
|
|||||||
|
/*
|
||||||
|
* Baseline JPEG decoder (ITU-T T.81, SOF0 sequential DCT).
|
||||||
|
*
|
||||||
|
* The web's photo format. Supports grayscale and YCbCr, arbitrary chroma
|
||||||
|
* subsampling, restart markers, and the standard integer IDCT -- all integer
|
||||||
|
* arithmetic, safe in the freestanding kernel. Progressive JPEG (SOF2) and
|
||||||
|
* arithmetic coding are rejected. Output is a flat 0xAARRGGBB buffer, so it
|
||||||
|
* plugs into the same image path as PNG and BMP.
|
||||||
|
*
|
||||||
|
* The Huffman decode uses canonical tables (counts + symbols), the way the
|
||||||
|
* inflate decoder does, to avoid a large fast-table. The integer row/column
|
||||||
|
* IDCT is the well-known fixed-point transform from the JPEG reference.
|
||||||
|
*/
|
||||||
|
#include <lib/jpeg.h>
|
||||||
|
#include <lib/string.h>
|
||||||
|
#include <mm/kmalloc.h>
|
||||||
|
|
||||||
|
#define JPEG_MAX_PIXELS (4 * 1024 * 1024)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t counts[17]; // number of codes of each length 1..16
|
||||||
|
uint8_t symbols[256];
|
||||||
|
int first_code[17]; // first canonical code of each length
|
||||||
|
int sym_index[17]; // index into symbols[] for each length
|
||||||
|
int maxlen;
|
||||||
|
} huff_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int cid;
|
||||||
|
int ssx, ssy; // sampling factors
|
||||||
|
int qtsel;
|
||||||
|
int dctab, actab;
|
||||||
|
int dcpred;
|
||||||
|
int width, height; // this component's plane size (upsampled to full)
|
||||||
|
int bx, by; // blocks per MCU (== ssx, ssy)
|
||||||
|
uint8_t* pixels; // decoded, upsampled to image size
|
||||||
|
uint8_t* plane; // native-resolution samples
|
||||||
|
int pstride; // stride of plane
|
||||||
|
int pw, ph; // plane width/height (block-aligned)
|
||||||
|
} comp_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t* data;
|
||||||
|
int size;
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int ncomp;
|
||||||
|
comp_t comp[3];
|
||||||
|
|
||||||
|
uint16_t qtab[4][64];
|
||||||
|
huff_t huff[8]; // 0..3 DC, 4..7 AC
|
||||||
|
|
||||||
|
int restart_interval;
|
||||||
|
|
||||||
|
// entropy bit reader
|
||||||
|
int bitbuf;
|
||||||
|
int bitcnt;
|
||||||
|
int marker; // non-zero: a marker byte was hit in the stream
|
||||||
|
|
||||||
|
int mcu_w, mcu_h; // MCU size in pixels
|
||||||
|
int mcu_cols, mcu_rows;
|
||||||
|
} jpeg_t;
|
||||||
|
|
||||||
|
static const uint8_t zz[64] = {
|
||||||
|
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
|
||||||
|
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
|
||||||
|
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
|
||||||
|
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int clip8(int x) { return x < 0 ? 0 : (x > 255 ? 255 : x); }
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Integer IDCT (JPEG reference fixed-point)
|
||||||
|
// ===========================================================================
|
||||||
|
#define W1 2841
|
||||||
|
#define W2 2676
|
||||||
|
#define W3 2408
|
||||||
|
#define W5 1609
|
||||||
|
#define W6 1108
|
||||||
|
#define W7 565
|
||||||
|
|
||||||
|
static void row_idct(int* blk) {
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
|
||||||
|
if (!((x1 = blk[4] << 11) | (x2 = blk[6]) | (x3 = blk[2]) |
|
||||||
|
(x4 = blk[1]) | (x5 = blk[7]) | (x6 = blk[5]) | (x7 = blk[3]))) {
|
||||||
|
blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x0 = (blk[0] << 11) + 128;
|
||||||
|
x8 = W7 * (x4 + x5);
|
||||||
|
x4 = x8 + (W1 - W7) * x4;
|
||||||
|
x5 = x8 - (W1 + W7) * x5;
|
||||||
|
x8 = W3 * (x6 + x7);
|
||||||
|
x6 = x8 - (W3 - W5) * x6;
|
||||||
|
x7 = x8 - (W3 + W5) * x7;
|
||||||
|
x8 = x0 + x1; x0 -= x1;
|
||||||
|
x1 = W6 * (x3 + x2);
|
||||||
|
x2 = x1 - (W2 + W6) * x2;
|
||||||
|
x3 = x1 + (W2 - W6) * x3;
|
||||||
|
x1 = x4 + x6; x4 -= x6;
|
||||||
|
x6 = x5 + x7; x5 -= x7;
|
||||||
|
x7 = x8 + x3; x8 -= x3;
|
||||||
|
x3 = x0 + x2; x0 -= x2;
|
||||||
|
x2 = (181 * (x4 + x5) + 128) >> 8;
|
||||||
|
x4 = (181 * (x4 - x5) + 128) >> 8;
|
||||||
|
blk[0] = (x7 + x1) >> 8;
|
||||||
|
blk[1] = (x3 + x2) >> 8;
|
||||||
|
blk[2] = (x0 + x4) >> 8;
|
||||||
|
blk[3] = (x8 + x6) >> 8;
|
||||||
|
blk[4] = (x8 - x6) >> 8;
|
||||||
|
blk[5] = (x0 - x4) >> 8;
|
||||||
|
blk[6] = (x3 - x2) >> 8;
|
||||||
|
blk[7] = (x7 - x1) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void col_idct(const int* blk, uint8_t* out, int stride) {
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8;
|
||||||
|
if (!((x1 = blk[8 * 4] << 8) | (x2 = blk[8 * 6]) | (x3 = blk[8 * 2]) |
|
||||||
|
(x4 = blk[8 * 1]) | (x5 = blk[8 * 7]) | (x6 = blk[8 * 5]) | (x7 = blk[8 * 3]))) {
|
||||||
|
x1 = clip8(((blk[0] + 32) >> 6) + 128);
|
||||||
|
for (x0 = 0; x0 < 8; x0++) out[x0 * stride] = (uint8_t)x1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x0 = (blk[0] << 8) + 8192;
|
||||||
|
x8 = W7 * (x4 + x5) + 4;
|
||||||
|
x4 = (x8 + (W1 - W7) * x4) >> 3;
|
||||||
|
x5 = (x8 - (W1 + W7) * x5) >> 3;
|
||||||
|
x8 = W3 * (x6 + x7) + 4;
|
||||||
|
x6 = (x8 - (W3 - W5) * x6) >> 3;
|
||||||
|
x7 = (x8 - (W3 + W5) * x7) >> 3;
|
||||||
|
x8 = x0 + x1; x0 -= x1;
|
||||||
|
x1 = W6 * (x3 + x2) + 4;
|
||||||
|
x2 = (x1 - (W2 + W6) * x2) >> 3;
|
||||||
|
x3 = (x1 + (W2 - W6) * x3) >> 3;
|
||||||
|
x1 = x4 + x6; x4 -= x6;
|
||||||
|
x6 = x5 + x7; x5 -= x7;
|
||||||
|
x7 = x8 + x3; x8 -= x3;
|
||||||
|
x3 = x0 + x2; x0 -= x2;
|
||||||
|
x2 = (181 * (x4 + x5) + 128) >> 8;
|
||||||
|
x4 = (181 * (x4 - x5) + 128) >> 8;
|
||||||
|
out[0 * stride] = (uint8_t)clip8(((x7 + x1) >> 14) + 128);
|
||||||
|
out[1 * stride] = (uint8_t)clip8(((x3 + x2) >> 14) + 128);
|
||||||
|
out[2 * stride] = (uint8_t)clip8(((x0 + x4) >> 14) + 128);
|
||||||
|
out[3 * stride] = (uint8_t)clip8(((x8 + x6) >> 14) + 128);
|
||||||
|
out[4 * stride] = (uint8_t)clip8(((x8 - x6) >> 14) + 128);
|
||||||
|
out[5 * stride] = (uint8_t)clip8(((x0 - x4) >> 14) + 128);
|
||||||
|
out[6 * stride] = (uint8_t)clip8(((x3 - x2) >> 14) + 128);
|
||||||
|
out[7 * stride] = (uint8_t)clip8(((x7 - x1) >> 14) + 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Marker / segment parsing
|
||||||
|
// ===========================================================================
|
||||||
|
static int rd16(jpeg_t* j) {
|
||||||
|
if (j->pos + 2 > j->size) return -1;
|
||||||
|
int v = (j->data[j->pos] << 8) | j->data[j->pos + 1];
|
||||||
|
j->pos += 2;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_huff(huff_t* h) {
|
||||||
|
int code = 0, k = 0;
|
||||||
|
h->maxlen = 0;
|
||||||
|
for (int l = 1; l <= 16; l++) {
|
||||||
|
h->first_code[l] = code;
|
||||||
|
h->sym_index[l] = k;
|
||||||
|
if (h->counts[l]) h->maxlen = l;
|
||||||
|
code = (code + h->counts[l]) << 1;
|
||||||
|
k += h->counts[l];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Entropy bit reader
|
||||||
|
// ===========================================================================
|
||||||
|
static int getbit(jpeg_t* j) {
|
||||||
|
if (j->bitcnt == 0) {
|
||||||
|
if (j->marker) return 0; // past a marker: feed zeros
|
||||||
|
if (j->pos >= j->size) { j->marker = 0xD9; return 0; }
|
||||||
|
int b = j->data[j->pos++];
|
||||||
|
if (b == 0xFF) {
|
||||||
|
int b2 = (j->pos < j->size) ? j->data[j->pos] : 0xD9;
|
||||||
|
if (b2 == 0) {
|
||||||
|
j->pos++; // stuffed 0xFF00 -> literal 0xFF
|
||||||
|
} else {
|
||||||
|
j->marker = b2; // a real marker: stop consuming
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j->bitbuf = b;
|
||||||
|
j->bitcnt = 8;
|
||||||
|
}
|
||||||
|
j->bitcnt--;
|
||||||
|
return (j->bitbuf >> j->bitcnt) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getbits(jpeg_t* j, int n) {
|
||||||
|
int v = 0;
|
||||||
|
for (int i = 0; i < n; i++) v = (v << 1) | getbit(j);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int receive_extend(jpeg_t* j, int s) {
|
||||||
|
if (s == 0) return 0;
|
||||||
|
int v = getbits(j, s);
|
||||||
|
if (v < (1 << (s - 1))) v -= (1 << s) - 1; // sign-extend (avoids UB shift)
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int huff_decode(jpeg_t* j, const huff_t* h) {
|
||||||
|
int code = 0;
|
||||||
|
for (int l = 1; l <= 16; l++) {
|
||||||
|
code = (code << 1) | getbit(j);
|
||||||
|
if (h->counts[l] && (code - h->first_code[l]) < h->counts[l]) {
|
||||||
|
return h->symbols[h->sym_index[l] + (code - h->first_code[l])];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode one 8x8 block, dequantize, IDCT into `out` (native plane).
|
||||||
|
static int decode_block(jpeg_t* j, comp_t* c, uint8_t* out, int stride) {
|
||||||
|
int blk[64];
|
||||||
|
memset(blk, 0, sizeof(blk));
|
||||||
|
|
||||||
|
// DC
|
||||||
|
int t = huff_decode(j, &j->huff[c->dctab]);
|
||||||
|
if (t < 0) return -1;
|
||||||
|
int diff = receive_extend(j, t);
|
||||||
|
c->dcpred += diff;
|
||||||
|
blk[0] = c->dcpred * j->qtab[c->qtsel][0];
|
||||||
|
|
||||||
|
// AC
|
||||||
|
int k = 1;
|
||||||
|
while (k < 64) {
|
||||||
|
int rs = huff_decode(j, &j->huff[4 + c->actab]);
|
||||||
|
if (rs < 0) return -1;
|
||||||
|
int r = rs >> 4, s = rs & 15;
|
||||||
|
if (s == 0) {
|
||||||
|
if (r != 15) break; // EOB
|
||||||
|
k += 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
k += r;
|
||||||
|
if (k >= 64) break;
|
||||||
|
int coeff = receive_extend(j, s) * j->qtab[c->qtsel][k];
|
||||||
|
blk[zz[k]] = coeff;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) row_idct(&blk[i * 8]);
|
||||||
|
for (int i = 0; i < 8; i++) col_idct(&blk[i], out + i, stride);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Scan
|
||||||
|
// ===========================================================================
|
||||||
|
static int decode_scan(jpeg_t* j) {
|
||||||
|
j->bitcnt = 0; j->marker = 0;
|
||||||
|
int rst_left = j->restart_interval;
|
||||||
|
|
||||||
|
for (int my = 0; my < j->mcu_rows; my++) {
|
||||||
|
for (int mx = 0; mx < j->mcu_cols; mx++) {
|
||||||
|
for (int ci = 0; ci < j->ncomp; ci++) {
|
||||||
|
comp_t* c = &j->comp[ci];
|
||||||
|
for (int sy = 0; sy < c->ssy; sy++) {
|
||||||
|
for (int sx = 0; sx < c->ssx; sx++) {
|
||||||
|
int px = (mx * c->ssx + sx) * 8;
|
||||||
|
int py = (my * c->ssy + sy) * 8;
|
||||||
|
uint8_t* out = c->plane + py * c->pstride + px;
|
||||||
|
if (decode_block(j, c, out, c->pstride) < 0) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j->restart_interval && --rst_left == 0 &&
|
||||||
|
!(my == j->mcu_rows - 1 && mx == j->mcu_cols - 1)) {
|
||||||
|
// Align to the next byte and consume the RSTn marker.
|
||||||
|
j->bitcnt = 0;
|
||||||
|
if (!j->marker) {
|
||||||
|
// find the marker in the stream
|
||||||
|
while (j->pos + 1 < j->size &&
|
||||||
|
!(j->data[j->pos] == 0xFF && j->data[j->pos + 1] >= 0xD0 &&
|
||||||
|
j->data[j->pos + 1] <= 0xD7)) {
|
||||||
|
j->pos++;
|
||||||
|
}
|
||||||
|
if (j->pos + 1 < j->size) j->marker = j->data[j->pos + 1];
|
||||||
|
}
|
||||||
|
if (j->marker >= 0xD0 && j->marker <= 0xD7) {
|
||||||
|
j->pos += 2; // skip FF Dn
|
||||||
|
j->marker = 0;
|
||||||
|
}
|
||||||
|
for (int ci = 0; ci < j->ncomp; ci++) j->comp[ci].dcpred = 0;
|
||||||
|
rst_left = j->restart_interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Upsample + colour convert
|
||||||
|
// ===========================================================================
|
||||||
|
static int finish_image(jpeg_t* j, image_t* img) {
|
||||||
|
int W = j->width, H = j->height;
|
||||||
|
uint32_t* out = (uint32_t*)kmalloc((unsigned)W * H * 4);
|
||||||
|
if (!out) return -1;
|
||||||
|
|
||||||
|
// Max sampling, to map component planes to full resolution.
|
||||||
|
int hmax = 1, vmax = 1;
|
||||||
|
for (int ci = 0; ci < j->ncomp; ci++) {
|
||||||
|
if (j->comp[ci].ssx > hmax) hmax = j->comp[ci].ssx;
|
||||||
|
if (j->comp[ci].ssy > vmax) vmax = j->comp[ci].ssy;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < H; y++) {
|
||||||
|
for (int x = 0; x < W; x++) {
|
||||||
|
int Y, Cb = 128, Cr = 128;
|
||||||
|
{
|
||||||
|
comp_t* c = &j->comp[0];
|
||||||
|
int cx = x * c->ssx / hmax;
|
||||||
|
int cy = y * c->ssy / vmax;
|
||||||
|
Y = c->plane[cy * c->pstride + cx];
|
||||||
|
}
|
||||||
|
if (j->ncomp == 3) {
|
||||||
|
comp_t* cb = &j->comp[1];
|
||||||
|
comp_t* cr = &j->comp[2];
|
||||||
|
int bx = x * cb->ssx / hmax, by = y * cb->ssy / vmax;
|
||||||
|
int rx = x * cr->ssx / hmax, ry = y * cr->ssy / vmax;
|
||||||
|
Cb = cb->plane[by * cb->pstride + bx];
|
||||||
|
Cr = cr->plane[ry * cr->pstride + rx];
|
||||||
|
}
|
||||||
|
|
||||||
|
int r, g, b;
|
||||||
|
if (j->ncomp == 1) {
|
||||||
|
r = g = b = Y;
|
||||||
|
} else {
|
||||||
|
int cb = Cb - 128, cr = Cr - 128;
|
||||||
|
r = clip8(Y + ((91881 * cr) >> 16));
|
||||||
|
g = clip8(Y - ((22554 * cb + 46802 * cr) >> 16));
|
||||||
|
b = clip8(Y + ((116130 * cb) >> 16));
|
||||||
|
}
|
||||||
|
out[y * W + x] = 0xFF000000u | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img->width = W;
|
||||||
|
img->height = H;
|
||||||
|
img->pixels = out;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Top level
|
||||||
|
// ===========================================================================
|
||||||
|
int jpeg_decode(const uint8_t* data, unsigned len, image_t* img) {
|
||||||
|
if (len < 4 || data[0] != 0xFF || data[1] != 0xD8) return -1; // SOI
|
||||||
|
|
||||||
|
jpeg_t* j = (jpeg_t*)kzalloc(sizeof(jpeg_t));
|
||||||
|
if (!j) return -1;
|
||||||
|
j->data = data; j->size = (int)len; j->pos = 2;
|
||||||
|
|
||||||
|
int rc = -1;
|
||||||
|
for (;;) {
|
||||||
|
if (j->pos + 2 > j->size) goto done;
|
||||||
|
if (j->data[j->pos] != 0xFF) { j->pos++; continue; }
|
||||||
|
int marker = j->data[j->pos + 1];
|
||||||
|
j->pos += 2;
|
||||||
|
if (marker == 0xD9) break; // EOI
|
||||||
|
if (marker == 0x01 || (marker >= 0xD0 && marker <= 0xD7)) continue;
|
||||||
|
|
||||||
|
int seg_len = rd16(j);
|
||||||
|
if (seg_len < 2) goto done;
|
||||||
|
int seg_start = j->pos;
|
||||||
|
int seg_end = seg_start + seg_len - 2;
|
||||||
|
if (seg_end > j->size) goto done;
|
||||||
|
|
||||||
|
if (marker == 0xC0) { // SOF0 baseline
|
||||||
|
int prec = j->data[j->pos];
|
||||||
|
j->height = (j->data[j->pos + 1] << 8) | j->data[j->pos + 2];
|
||||||
|
j->width = (j->data[j->pos + 3] << 8) | j->data[j->pos + 4];
|
||||||
|
j->ncomp = j->data[j->pos + 5];
|
||||||
|
if (prec != 8 || (j->ncomp != 1 && j->ncomp != 3)) goto done;
|
||||||
|
if (j->width <= 0 || j->height <= 0) goto done;
|
||||||
|
if ((long)j->width * j->height > JPEG_MAX_PIXELS) goto done;
|
||||||
|
const uint8_t* p = j->data + j->pos + 6;
|
||||||
|
int hmax = 1, vmax = 1;
|
||||||
|
for (int i = 0; i < j->ncomp; i++) {
|
||||||
|
j->comp[i].cid = p[0];
|
||||||
|
j->comp[i].ssx = p[1] >> 4;
|
||||||
|
j->comp[i].ssy = p[1] & 15;
|
||||||
|
j->comp[i].qtsel = p[2];
|
||||||
|
if (j->comp[i].ssx < 1 || j->comp[i].ssy < 1) goto done;
|
||||||
|
if (j->comp[i].ssx > hmax) hmax = j->comp[i].ssx;
|
||||||
|
if (j->comp[i].ssy > vmax) vmax = j->comp[i].ssy;
|
||||||
|
p += 3;
|
||||||
|
}
|
||||||
|
j->mcu_w = hmax * 8;
|
||||||
|
j->mcu_h = vmax * 8;
|
||||||
|
j->mcu_cols = (j->width + j->mcu_w - 1) / j->mcu_w;
|
||||||
|
j->mcu_rows = (j->height + j->mcu_h - 1) / j->mcu_h;
|
||||||
|
for (int i = 0; i < j->ncomp; i++) {
|
||||||
|
comp_t* c = &j->comp[i];
|
||||||
|
c->pw = j->mcu_cols * c->ssx * 8;
|
||||||
|
c->ph = j->mcu_rows * c->ssy * 8;
|
||||||
|
c->pstride = c->pw;
|
||||||
|
c->plane = (uint8_t*)kmalloc((unsigned)c->pw * c->ph);
|
||||||
|
if (!c->plane) goto done;
|
||||||
|
}
|
||||||
|
} else if (marker == 0xC2) { // SOF2 progressive
|
||||||
|
goto done; // unsupported
|
||||||
|
} else if (marker == 0xC4) { // DHT
|
||||||
|
const uint8_t* p = j->data + j->pos;
|
||||||
|
const uint8_t* pe = j->data + seg_end;
|
||||||
|
while (p < pe) {
|
||||||
|
int tc = p[0] >> 4, th = p[0] & 15;
|
||||||
|
if (th > 3) goto done;
|
||||||
|
int idx = (tc ? 4 : 0) + th;
|
||||||
|
huff_t* h = &j->huff[idx];
|
||||||
|
p++;
|
||||||
|
int total = 0;
|
||||||
|
for (int l = 1; l <= 16; l++) { h->counts[l] = p[l - 1]; total += h->counts[l]; }
|
||||||
|
p += 16;
|
||||||
|
if (total > 256 || p + total > pe) goto done;
|
||||||
|
for (int i = 0; i < total; i++) h->symbols[i] = p[i];
|
||||||
|
p += total;
|
||||||
|
build_huff(h);
|
||||||
|
}
|
||||||
|
} else if (marker == 0xDB) { // DQT
|
||||||
|
const uint8_t* p = j->data + j->pos;
|
||||||
|
const uint8_t* pe = j->data + seg_end;
|
||||||
|
while (p < pe) {
|
||||||
|
int pq = p[0] >> 4, tq = p[0] & 15;
|
||||||
|
if (tq > 3) goto done;
|
||||||
|
p++;
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
if (pq) { j->qtab[tq][i] = (p[0] << 8) | p[1]; p += 2; }
|
||||||
|
else { j->qtab[tq][i] = p[0]; p += 1; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (marker == 0xDD) { // DRI
|
||||||
|
j->restart_interval = (j->data[j->pos] << 8) | j->data[j->pos + 1];
|
||||||
|
} else if (marker == 0xDA) { // SOS
|
||||||
|
const uint8_t* p = j->data + j->pos;
|
||||||
|
int ns = p[0]; p++;
|
||||||
|
if (ns != j->ncomp) goto done;
|
||||||
|
for (int i = 0; i < ns; i++) {
|
||||||
|
int cid = p[0], td = p[1] >> 4, ta = p[1] & 15;
|
||||||
|
p += 2;
|
||||||
|
for (int k = 0; k < j->ncomp; k++) {
|
||||||
|
if (j->comp[k].cid == cid) { j->comp[k].dctab = td; j->comp[k].actab = ta; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// p now at Ss, Se, Ah/Al (3 bytes) -- ignored for baseline.
|
||||||
|
j->pos = seg_end; // entropy data follows
|
||||||
|
if (decode_scan(j) < 0) goto done;
|
||||||
|
rc = finish_image(j, img);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
j->pos = seg_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
for (int i = 0; i < 3; i++) if (j->comp[i].plane) kfree(j->comp[i].plane);
|
||||||
|
kfree(j);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user