commit a7f852f14c9223f1034d06d0cd1d9f88d51bb887
parent fd71fd8fd418e13dc702656a1221e4a9db3da638
Author: luajitos <bbhbb2094@gmail.com>
Date: Mon, 1 Dec 2025 22:32:30 +0000
Added JPEG support
Diffstat:
15 files changed, 1711 insertions(+), 53 deletions(-)
diff --git a/build.sh b/build.sh
@@ -173,6 +173,9 @@ ${CC} ${CFLAGS} ${LUAJIT_INCLUDE} -c decoder_BMP.c -o build/decoder_BMP.o
echo "Step 4f: Compiling decoder_PNG.c..."
${CC} ${CFLAGS} ${LUAJIT_INCLUDE} -c decoder_PNG.c -o build/decoder_PNG.o
+echo "Step 4f1: Compiling decoder_JPEG.c..."
+${CC} ${CFLAGS} ${LUAJIT_INCLUDE} -c decoder_JPEG.c -o build/decoder_JPEG.o
+
echo "Step 4f2: Compiling splash.c..."
${CC} ${CFLAGS} ${LUAJIT_INCLUDE} -c splash.c -o build/splash.o
@@ -287,7 +290,7 @@ if [ -f "$LIBGCC_PATH" ]; then
echo "Using libgcc: $LIBGCC"
${LD} ${LDFLAGS} -T linker.ld -o build/kernel.bin \
build/boot.o build/syscall.o build/exceptions.o build/libc.o build/paging.o build/graphics.o build/vesa.o build/mouse.o build/usb_uhci.o build/keyboard.o build/ata.o build/fde.o build/diskfs.o build/partition.o build/fat16.o \
- build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/splash.o \
+ build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/decoder_JPEG.o build/splash.o \
build/compression.o build/deflate_impl.o build/Deflate.o build/LZMA.o build/GZip.o build/zlib.o \
build/ramdisk.o build/luajit_init.o build/kernel.o \
build/crypto_baremetal.o build/CSPRNG.o build/Ed25519.o build/X25519.o build/Curve25519.o \
@@ -305,7 +308,7 @@ else
echo "Warning: 32-bit libgcc not found, linking without it"
${LD} ${LDFLAGS} -T linker.ld -o build/kernel.bin \
build/boot.o build/syscall.o build/exceptions.o build/libc.o build/paging.o build/graphics.o build/vesa.o build/mouse.o build/usb_uhci.o build/keyboard.o build/ata.o build/fde.o build/diskfs.o build/partition.o build/fat16.o \
- build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/splash.o \
+ build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/decoder_JPEG.o build/splash.o \
build/compression.o build/deflate_impl.o build/Deflate.o build/LZMA.o build/GZip.o build/zlib.o \
build/ramdisk.o build/luajit_init.o build/kernel.o \
build/crypto_baremetal.o build/CSPRNG.o build/Ed25519.o build/X25519.o build/Curve25519.o \
diff --git a/decoder_JPEG.c b/decoder_JPEG.c
@@ -0,0 +1,1246 @@
+/*
+ * JPEG Decoder Implementation
+ * Supports: Baseline DCT, Extended Sequential DCT, Progressive DCT
+ * Chroma subsampling: 4:4:4, 4:2:2, 4:2:0, 4:1:1
+ *
+ * References:
+ * - ITU-T T.81 (JPEG Standard)
+ * - ISO/IEC 10918-1
+ */
+
+#include "decoder_JPEG.h"
+#include <stdlib.h>
+#include <string.h>
+#include <lauxlib.h>
+
+/* Zigzag ordering table */
+const uint8_t jpeg_zigzag[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
+};
+
+/* Inverse zigzag (zigzag to normal order) */
+const uint8_t jpeg_unzigzag[64] = {
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+
+/* IDCT constants - scaled by 2^12 for fixed-point math */
+#define IDCT_SCALE 4096
+#define IDCT_ROUND 2048
+
+/* Cosine values for IDCT, scaled by 2^14 */
+#define C1 16069 /* cos(1*pi/16) * 2^14 */
+#define C2 15137 /* cos(2*pi/16) * 2^14 */
+#define C3 13623 /* cos(3*pi/16) * 2^14 */
+#define C4 11585 /* cos(4*pi/16) * 2^14 = sqrt(2) * 2^13 */
+#define C5 9102 /* cos(5*pi/16) * 2^14 */
+#define C6 6270 /* cos(6*pi/16) * 2^14 */
+#define C7 3196 /* cos(7*pi/16) * 2^14 */
+
+/* Clip value to 0-255 range */
+static inline uint8_t clip_uint8(int x) {
+ if (x < 0) return 0;
+ if (x > 255) return 255;
+ return (uint8_t)x;
+}
+
+/* Read 16-bit big-endian value */
+static inline uint16_t read_be16(const uint8_t* data) {
+ return (uint16_t)((data[0] << 8) | data[1]);
+}
+
+/* ============================================================
+ * Memory Management
+ * ============================================================ */
+
+jpeg_decoder_t* jpeg_decoder_create(void) {
+ jpeg_decoder_t* dec = (jpeg_decoder_t*)calloc(1, sizeof(jpeg_decoder_t));
+ if (!dec) return NULL;
+
+ /* Initialize all table validity flags to 0 */
+ memset(dec->dc_table_valid, 0, sizeof(dec->dc_table_valid));
+ memset(dec->ac_table_valid, 0, sizeof(dec->ac_table_valid));
+ memset(dec->quant_table_valid, 0, sizeof(dec->quant_table_valid));
+
+ return dec;
+}
+
+void jpeg_decoder_destroy(jpeg_decoder_t* dec) {
+ if (!dec) return;
+
+ /* Free component data */
+ for (int i = 0; i < JPEG_MAX_COMPONENTS; i++) {
+ if (dec->components[i].data) {
+ free(dec->components[i].data);
+ }
+ }
+
+ /* Free progressive coefficient blocks */
+ if (dec->coef_blocks) {
+ for (int i = 0; i < dec->coef_blocks_count; i++) {
+ if (dec->coef_blocks[i]) {
+ free(dec->coef_blocks[i]);
+ }
+ }
+ free(dec->coef_blocks);
+ }
+
+ free(dec);
+}
+
+/* ============================================================
+ * Bit Reading
+ * ============================================================ */
+
+/* Fill bit buffer from stream */
+static int jpeg_fill_bits(jpeg_decoder_t* dec) {
+ while (dec->bits_count < 24 && dec->pos < dec->data_size) {
+ uint8_t byte = dec->data[dec->pos++];
+
+ /* Handle byte stuffing (0xFF followed by 0x00) */
+ if (byte == 0xFF) {
+ if (dec->pos >= dec->data_size) {
+ dec->error = 1;
+ return -1;
+ }
+ uint8_t next = dec->data[dec->pos];
+ if (next == 0x00) {
+ /* Stuffed byte, skip the 0x00 */
+ dec->pos++;
+ } else if (next >= 0xD0 && next <= 0xD7) {
+ /* Restart marker - ignore and continue */
+ dec->pos++;
+ continue;
+ } else {
+ /* Other marker - we've hit the end of scan data */
+ dec->pos--;
+ break;
+ }
+ }
+
+ dec->bits_buffer = (dec->bits_buffer << 8) | byte;
+ dec->bits_count += 8;
+ }
+ return 0;
+}
+
+int jpeg_get_bits(jpeg_decoder_t* dec, int nbits) {
+ if (nbits == 0) return 0;
+
+ while (dec->bits_count < nbits) {
+ if (jpeg_fill_bits(dec) < 0) return -1;
+ if (dec->bits_count < nbits) {
+ dec->error = 1;
+ return -1;
+ }
+ }
+
+ dec->bits_count -= nbits;
+ return (dec->bits_buffer >> dec->bits_count) & ((1 << nbits) - 1);
+}
+
+int jpeg_peek_bits(jpeg_decoder_t* dec, int nbits) {
+ if (nbits == 0) return 0;
+
+ while (dec->bits_count < nbits) {
+ if (jpeg_fill_bits(dec) < 0) return -1;
+ if (dec->bits_count < nbits) {
+ dec->error = 1;
+ return -1;
+ }
+ }
+
+ return (dec->bits_buffer >> (dec->bits_count - nbits)) & ((1 << nbits) - 1);
+}
+
+void jpeg_skip_bits(jpeg_decoder_t* dec, int nbits) {
+ dec->bits_count -= nbits;
+}
+
+void jpeg_align_bits(jpeg_decoder_t* dec) {
+ dec->bits_count &= ~7; /* Align to byte boundary */
+}
+
+int jpeg_next_marker(jpeg_decoder_t* dec) {
+ /* Align to byte boundary */
+ jpeg_align_bits(dec);
+ dec->bits_count = 0;
+ dec->bits_buffer = 0;
+
+ /* Find next marker */
+ while (dec->pos < dec->data_size - 1) {
+ if (dec->data[dec->pos] == 0xFF) {
+ uint8_t marker = dec->data[dec->pos + 1];
+ if (marker != 0x00 && marker != 0xFF) {
+ dec->pos += 2;
+ return 0xFF00 | marker;
+ }
+ }
+ dec->pos++;
+ }
+
+ return -1;
+}
+
+/* ============================================================
+ * Huffman Table Handling
+ * ============================================================ */
+
+int jpeg_build_huffman_table(jpeg_huffman_table_t* table) {
+ int code = 0;
+ int si = 1;
+ int p = 0;
+
+ /* Generate huffcode and huffsize arrays */
+ for (int i = 1; i <= 16; i++) {
+ for (int j = 0; j < table->bits[i]; j++) {
+ table->huffcode[p] = code;
+ table->huffsize[p] = i;
+ p++;
+ code++;
+ }
+ code <<= 1;
+ }
+ table->num_symbols = p;
+
+ /* Generate mincode, maxcode, and valptr */
+ p = 0;
+ for (int i = 1; i <= 16; i++) {
+ if (table->bits[i]) {
+ table->valptr[i] = p;
+ table->mincode[i] = table->huffcode[p];
+ p += table->bits[i];
+ table->maxcode[i] = table->huffcode[p - 1];
+ } else {
+ table->maxcode[i] = -1;
+ table->mincode[i] = 0;
+ table->valptr[i] = 0;
+ }
+ }
+ table->maxcode[17] = 0xFFFFFF; /* Sentinel */
+
+ return 0;
+}
+
+int jpeg_decode_huffman(jpeg_decoder_t* dec, jpeg_huffman_table_t* table) {
+ int code = 0;
+ int size = 1;
+
+ /* Read bits one at a time until we find a valid code */
+ while (size <= 16) {
+ int bit = jpeg_get_bits(dec, 1);
+ if (bit < 0) return -1;
+
+ code = (code << 1) | bit;
+
+ if (code <= table->maxcode[size]) {
+ int index = table->valptr[size] + code - table->mincode[size];
+ return table->huffval[index];
+ }
+ size++;
+ }
+
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Invalid Huffman code");
+ return -1;
+}
+
+/* Extend signed value from Huffman decoding */
+int jpeg_receive_extend(jpeg_decoder_t* dec, int nbits) {
+ if (nbits == 0) return 0;
+
+ int value = jpeg_get_bits(dec, nbits);
+ if (value < 0) return 0;
+
+ /* Sign extension */
+ if (value < (1 << (nbits - 1))) {
+ value = value - (1 << nbits) + 1;
+ }
+
+ return value;
+}
+
+/* ============================================================
+ * Marker Parsing
+ * ============================================================ */
+
+static int jpeg_parse_dqt(jpeg_decoder_t* dec) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ int remaining = length - 2;
+
+ while (remaining > 0) {
+ uint8_t info = dec->data[dec->pos++];
+ remaining--;
+
+ int precision = (info >> 4) & 0x0F; /* 0 = 8-bit, 1 = 16-bit */
+ int table_id = info & 0x0F;
+
+ if (table_id >= JPEG_MAX_QUANT_TABLES) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Invalid quantization table ID: %d", table_id);
+ return -1;
+ }
+
+ jpeg_quant_table_t* qt = &dec->quant_tables[table_id];
+ qt->precision = precision;
+
+ if (precision == 0) {
+ /* 8-bit values */
+ for (int i = 0; i < 64; i++) {
+ qt->table[jpeg_zigzag[i]] = dec->data[dec->pos++];
+ }
+ remaining -= 64;
+ } else {
+ /* 16-bit values */
+ for (int i = 0; i < 64; i++) {
+ qt->table[jpeg_zigzag[i]] = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ }
+ remaining -= 128;
+ }
+
+ dec->quant_table_valid[table_id] = 1;
+ }
+
+ return 0;
+}
+
+static int jpeg_parse_dht(jpeg_decoder_t* dec) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ int remaining = length - 2;
+
+ while (remaining > 0) {
+ uint8_t info = dec->data[dec->pos++];
+ remaining--;
+
+ int table_class = (info >> 4) & 0x0F; /* 0 = DC, 1 = AC */
+ int table_id = info & 0x0F;
+
+ if (table_id >= JPEG_MAX_HUFFMAN_TABLES) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Invalid Huffman table ID: %d", table_id);
+ return -1;
+ }
+
+ jpeg_huffman_table_t* ht;
+ if (table_class == 0) {
+ ht = &dec->dc_tables[table_id];
+ dec->dc_table_valid[table_id] = 1;
+ } else {
+ ht = &dec->ac_tables[table_id];
+ dec->ac_table_valid[table_id] = 1;
+ }
+
+ /* Read bits counts (BITS) */
+ int total_symbols = 0;
+ ht->bits[0] = 0;
+ for (int i = 1; i <= 16; i++) {
+ ht->bits[i] = dec->data[dec->pos++];
+ total_symbols += ht->bits[i];
+ remaining--;
+ }
+
+ /* Read symbols (HUFFVAL) */
+ for (int i = 0; i < total_symbols; i++) {
+ ht->huffval[i] = dec->data[dec->pos++];
+ remaining--;
+ }
+
+ /* Build derived tables */
+ jpeg_build_huffman_table(ht);
+ }
+
+ return 0;
+}
+
+static int jpeg_parse_sof(jpeg_decoder_t* dec, uint8_t marker) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ (void)length;
+
+ dec->frame_type = marker;
+ dec->is_baseline = (marker == 0xC0);
+ dec->is_progressive = (marker == 0xC2 || marker == 0xCA);
+
+ dec->precision = dec->data[dec->pos++];
+ dec->height = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ dec->width = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ dec->num_components = dec->data[dec->pos++];
+
+ if (dec->num_components > JPEG_MAX_COMPONENTS) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Too many components: %d", dec->num_components);
+ return -1;
+ }
+
+ if (dec->precision != 8 && dec->precision != 12) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Unsupported precision: %d", dec->precision);
+ return -1;
+ }
+
+ dec->max_h_samp = 1;
+ dec->max_v_samp = 1;
+
+ for (int i = 0; i < dec->num_components; i++) {
+ jpeg_component_t* comp = &dec->components[i];
+ comp->id = dec->data[dec->pos++];
+ uint8_t sampling = dec->data[dec->pos++];
+ comp->h_samp = (sampling >> 4) & 0x0F;
+ comp->v_samp = sampling & 0x0F;
+ comp->quant_table_id = dec->data[dec->pos++];
+ comp->dc_pred = 0;
+
+ if (comp->h_samp > dec->max_h_samp) dec->max_h_samp = comp->h_samp;
+ if (comp->v_samp > dec->max_v_samp) dec->max_v_samp = comp->v_samp;
+
+ if (comp->quant_table_id >= JPEG_MAX_QUANT_TABLES) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Invalid quantization table ID: %d", comp->quant_table_id);
+ return -1;
+ }
+ }
+
+ /* Calculate MCU dimensions */
+ dec->mcu_width = dec->max_h_samp * 8;
+ dec->mcu_height = dec->max_v_samp * 8;
+ dec->mcus_per_row = (dec->width + dec->mcu_width - 1) / dec->mcu_width;
+ dec->mcu_rows = (dec->height + dec->mcu_height - 1) / dec->mcu_height;
+
+ /* Allocate component buffers */
+ for (int i = 0; i < dec->num_components; i++) {
+ jpeg_component_t* comp = &dec->components[i];
+
+ /* Calculate component dimensions */
+ comp->width = dec->mcus_per_row * comp->h_samp * 8;
+ comp->height = dec->mcu_rows * comp->v_samp * 8;
+ comp->stride = comp->width;
+
+ comp->data = (uint8_t*)calloc(comp->width * comp->height, 1);
+ if (!comp->data) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Out of memory");
+ return -1;
+ }
+ }
+
+ /* Allocate coefficient blocks for progressive JPEG */
+ if (dec->is_progressive) {
+ int total_blocks = 0;
+ for (int i = 0; i < dec->num_components; i++) {
+ jpeg_component_t* comp = &dec->components[i];
+ int blocks_x = comp->width / 8;
+ int blocks_y = comp->height / 8;
+ total_blocks += blocks_x * blocks_y;
+ }
+
+ dec->coef_blocks = (int16_t**)calloc(total_blocks, sizeof(int16_t*));
+ if (!dec->coef_blocks) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Out of memory");
+ return -1;
+ }
+
+ for (int i = 0; i < total_blocks; i++) {
+ dec->coef_blocks[i] = (int16_t*)calloc(64, sizeof(int16_t));
+ if (!dec->coef_blocks[i]) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Out of memory");
+ return -1;
+ }
+ }
+ dec->coef_blocks_count = total_blocks;
+ }
+
+ return 0;
+}
+
+static int jpeg_parse_dri(jpeg_decoder_t* dec) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ (void)length;
+
+ dec->restart_interval = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+
+ return 0;
+}
+
+static int jpeg_parse_sos(jpeg_decoder_t* dec, jpeg_scan_t* scan) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += 2;
+ (void)length;
+
+ scan->num_components = dec->data[dec->pos++];
+
+ for (int i = 0; i < scan->num_components; i++) {
+ uint8_t comp_id = dec->data[dec->pos++];
+ uint8_t table_ids = dec->data[dec->pos++];
+
+ scan->component_ids[i] = comp_id;
+
+ /* Find matching component */
+ for (int j = 0; j < dec->num_components; j++) {
+ if (dec->components[j].id == comp_id) {
+ dec->components[j].dc_table_id = (table_ids >> 4) & 0x0F;
+ dec->components[j].ac_table_id = table_ids & 0x0F;
+ break;
+ }
+ }
+ }
+
+ scan->ss = dec->data[dec->pos++]; /* Start of spectral selection */
+ scan->se = dec->data[dec->pos++]; /* End of spectral selection */
+ uint8_t approx = dec->data[dec->pos++];
+ scan->ah = (approx >> 4) & 0x0F; /* Successive approximation high */
+ scan->al = approx & 0x0F; /* Successive approximation low */
+
+ return 0;
+}
+
+int jpeg_parse_markers(jpeg_decoder_t* dec, const uint8_t* data, uint32_t size) {
+ dec->data = data;
+ dec->data_size = size;
+ dec->pos = 0;
+
+ /* Check SOI marker */
+ if (size < 2 || data[0] != 0xFF || data[1] != 0xD8) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Invalid JPEG: missing SOI marker");
+ return -1;
+ }
+ dec->pos = 2;
+
+ /* Parse markers */
+ while (dec->pos < size - 1) {
+ /* Find marker */
+ if (dec->data[dec->pos] != 0xFF) {
+ dec->pos++;
+ continue;
+ }
+
+ uint8_t marker = dec->data[dec->pos + 1];
+ dec->pos += 2;
+
+ /* Skip padding bytes */
+ if (marker == 0xFF || marker == 0x00) {
+ continue;
+ }
+
+ switch (marker) {
+ case 0xD8: /* SOI - already handled */
+ break;
+
+ case 0xD9: /* EOI */
+ return 0;
+
+ case 0xDB: /* DQT */
+ if (jpeg_parse_dqt(dec) < 0) return -1;
+ break;
+
+ case 0xC4: /* DHT */
+ if (jpeg_parse_dht(dec) < 0) return -1;
+ break;
+
+ case 0xC0: /* SOF0 - Baseline DCT */
+ case 0xC1: /* SOF1 - Extended Sequential DCT */
+ case 0xC2: /* SOF2 - Progressive DCT */
+ if (jpeg_parse_sof(dec, marker) < 0) return -1;
+ break;
+
+ case 0xC3: /* SOF3 - Lossless */
+ case 0xC5: /* SOF5 */
+ case 0xC6: /* SOF6 */
+ case 0xC7: /* SOF7 */
+ case 0xC9: /* SOF9 */
+ case 0xCA: /* SOF10 */
+ case 0xCB: /* SOF11 */
+ case 0xCD: /* SOF13 */
+ case 0xCE: /* SOF14 */
+ case 0xCF: /* SOF15 */
+ if (marker == 0xCA) {
+ /* Progressive with arithmetic coding */
+ if (jpeg_parse_sof(dec, marker) < 0) return -1;
+ } else {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg),
+ "Unsupported JPEG type: SOF%d", marker - 0xC0);
+ return -1;
+ }
+ break;
+
+ case 0xDD: /* DRI */
+ if (jpeg_parse_dri(dec) < 0) return -1;
+ break;
+
+ case 0xDA: /* SOS - Start of Scan */
+ return 0; /* Stop parsing, scan data follows */
+
+ case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+ case 0xE4: case 0xE5: case 0xE6: case 0xE7:
+ case 0xE8: case 0xE9: case 0xEA: case 0xEB:
+ case 0xEC: case 0xED: case 0xEE: case 0xEF:
+ case 0xFE: /* APP and COM markers - skip */
+ {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += length;
+ break;
+ }
+
+ case 0xD0: case 0xD1: case 0xD2: case 0xD3:
+ case 0xD4: case 0xD5: case 0xD6: case 0xD7:
+ /* RST markers - no length */
+ break;
+
+ default:
+ /* Unknown marker - try to skip */
+ if (dec->pos + 2 <= size) {
+ uint16_t length = read_be16(dec->data + dec->pos);
+ dec->pos += length;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* ============================================================
+ * IDCT Implementation (AAN algorithm)
+ * ============================================================ */
+
+void jpeg_idct_block(int16_t* block, uint8_t* output, int stride) {
+ int tmp[64];
+ int* tmpptr;
+ int16_t* blkptr;
+ int i;
+
+ /* Pass 1: Process columns */
+ blkptr = block;
+ tmpptr = tmp;
+ for (i = 0; i < 8; i++) {
+ int s0 = blkptr[0*8];
+ int s1 = blkptr[1*8];
+ int s2 = blkptr[2*8];
+ int s3 = blkptr[3*8];
+ int s4 = blkptr[4*8];
+ int s5 = blkptr[5*8];
+ int s6 = blkptr[6*8];
+ int s7 = blkptr[7*8];
+
+ /* Check for all-zero AC coefficients */
+ if ((s1 | s2 | s3 | s4 | s5 | s6 | s7) == 0) {
+ int dc = s0 << 2;
+ tmpptr[0*8] = dc;
+ tmpptr[1*8] = dc;
+ tmpptr[2*8] = dc;
+ tmpptr[3*8] = dc;
+ tmpptr[4*8] = dc;
+ tmpptr[5*8] = dc;
+ tmpptr[6*8] = dc;
+ tmpptr[7*8] = dc;
+ } else {
+ /* Even part */
+ int t0 = s0 + s4;
+ int t1 = s0 - s4;
+ int t2 = (s2 * C6 - s6 * C2) >> 14;
+ int t3 = (s2 * C2 + s6 * C6) >> 14;
+
+ int e0 = t0 + t3;
+ int e1 = t1 + t2;
+ int e2 = t1 - t2;
+ int e3 = t0 - t3;
+
+ /* Odd part */
+ int t4 = (s1 * C7 - s7 * C1) >> 14;
+ int t5 = (s5 * C3 - s3 * C5) >> 14;
+ int t6 = (s5 * C5 + s3 * C3) >> 14;
+ int t7 = (s1 * C1 + s7 * C7) >> 14;
+
+ int o0 = t4 + t5;
+ int o1 = t7 - t6;
+ int o2 = ((t4 - t5 + t7 + t6) * C4) >> 14;
+ int o3 = t7 + t6;
+
+ int o4 = o2 - o0;
+ int o5 = o1;
+
+ /* Final output */
+ tmpptr[0*8] = (e0 + o3) << 2;
+ tmpptr[7*8] = (e0 - o3) << 2;
+ tmpptr[1*8] = (e1 + o4) << 2;
+ tmpptr[6*8] = (e1 - o4) << 2;
+ tmpptr[2*8] = (e2 + o5) << 2;
+ tmpptr[5*8] = (e2 - o5) << 2;
+ tmpptr[3*8] = (e3 + o0 - o2 + o1) << 2;
+ tmpptr[4*8] = (e3 - o0 + o2 - o1) << 2;
+ }
+
+ blkptr++;
+ tmpptr++;
+ }
+
+ /* Pass 2: Process rows */
+ tmpptr = tmp;
+ for (i = 0; i < 8; i++) {
+ int s0 = tmpptr[0];
+ int s1 = tmpptr[1];
+ int s2 = tmpptr[2];
+ int s3 = tmpptr[3];
+ int s4 = tmpptr[4];
+ int s5 = tmpptr[5];
+ int s6 = tmpptr[6];
+ int s7 = tmpptr[7];
+
+ /* Even part */
+ int t0 = s0 + s4;
+ int t1 = s0 - s4;
+ int t2 = (s2 * C6 - s6 * C2) >> 14;
+ int t3 = (s2 * C2 + s6 * C6) >> 14;
+
+ int e0 = t0 + t3;
+ int e1 = t1 + t2;
+ int e2 = t1 - t2;
+ int e3 = t0 - t3;
+
+ /* Odd part */
+ int t4 = (s1 * C7 - s7 * C1) >> 14;
+ int t5 = (s5 * C3 - s3 * C5) >> 14;
+ int t6 = (s5 * C5 + s3 * C3) >> 14;
+ int t7 = (s1 * C1 + s7 * C7) >> 14;
+
+ int o0 = t4 + t5;
+ int o1 = t7 - t6;
+ int o2 = ((t4 - t5 + t7 + t6) * C4) >> 14;
+ int o3 = t7 + t6;
+
+ int o4 = o2 - o0;
+ int o5 = o1;
+
+ /* Final output with level shift (add 128) and clamp */
+ output[0] = clip_uint8(((e0 + o3) >> 5) + 128);
+ output[7] = clip_uint8(((e0 - o3) >> 5) + 128);
+ output[1] = clip_uint8(((e1 + o4) >> 5) + 128);
+ output[6] = clip_uint8(((e1 - o4) >> 5) + 128);
+ output[2] = clip_uint8(((e2 + o5) >> 5) + 128);
+ output[5] = clip_uint8(((e2 - o5) >> 5) + 128);
+ output[3] = clip_uint8(((e3 + o0 - o2 + o1) >> 5) + 128);
+ output[4] = clip_uint8(((e3 - o0 + o2 - o1) >> 5) + 128);
+
+ tmpptr += 8;
+ output += stride;
+ }
+}
+
+/* ============================================================
+ * Block Decoding
+ * ============================================================ */
+
+static int jpeg_decode_block(jpeg_decoder_t* dec, int16_t* block,
+ jpeg_component_t* comp, int is_dc_only) {
+ jpeg_huffman_table_t* dc_table = &dec->dc_tables[comp->dc_table_id];
+ jpeg_huffman_table_t* ac_table = &dec->ac_tables[comp->ac_table_id];
+ jpeg_quant_table_t* qt = &dec->quant_tables[comp->quant_table_id];
+
+ memset(block, 0, 64 * sizeof(int16_t));
+
+ /* Decode DC coefficient */
+ int dc_size = jpeg_decode_huffman(dec, dc_table);
+ if (dc_size < 0) return -1;
+
+ int dc_diff = jpeg_receive_extend(dec, dc_size);
+ comp->dc_pred += dc_diff;
+ block[0] = comp->dc_pred * qt->table[0];
+
+ if (is_dc_only) return 0;
+
+ /* Decode AC coefficients */
+ int k = 1;
+ while (k < 64) {
+ int rs = jpeg_decode_huffman(dec, ac_table);
+ if (rs < 0) return -1;
+
+ int r = (rs >> 4) & 0x0F; /* Run length */
+ int s = rs & 0x0F; /* Size */
+
+ if (s == 0) {
+ if (r == 0) {
+ /* EOB - End of Block */
+ break;
+ } else if (r == 15) {
+ /* ZRL - Zero Run Length (16 zeros) */
+ k += 16;
+ } else {
+ break;
+ }
+ } else {
+ k += r;
+ if (k >= 64) break;
+
+ int ac = jpeg_receive_extend(dec, s);
+ int zz_index = jpeg_zigzag[k];
+ block[zz_index] = ac * qt->table[zz_index];
+ k++;
+ }
+ }
+
+ return 0;
+}
+
+/* ============================================================
+ * Progressive JPEG Decoding
+ * ============================================================ */
+
+int jpeg_decode_dc_first(jpeg_decoder_t* dec, jpeg_component_t* comp, int16_t* block) {
+ jpeg_huffman_table_t* dc_table = &dec->dc_tables[comp->dc_table_id];
+
+ int dc_size = jpeg_decode_huffman(dec, dc_table);
+ if (dc_size < 0) return -1;
+
+ int dc_diff = jpeg_receive_extend(dec, dc_size);
+ comp->dc_pred += dc_diff;
+ block[0] = comp->dc_pred;
+
+ return 0;
+}
+
+int jpeg_decode_dc_refine(jpeg_decoder_t* dec, int16_t* block, int al) {
+ int bit = jpeg_get_bits(dec, 1);
+ if (bit < 0) return -1;
+
+ block[0] |= (bit << al);
+ return 0;
+}
+
+int jpeg_decode_ac_first(jpeg_decoder_t* dec, jpeg_component_t* comp,
+ int16_t* block, int ss, int se) {
+ jpeg_huffman_table_t* ac_table = &dec->ac_tables[comp->ac_table_id];
+
+ int k = ss;
+ while (k <= se) {
+ int rs = jpeg_decode_huffman(dec, ac_table);
+ if (rs < 0) return -1;
+
+ int r = (rs >> 4) & 0x0F;
+ int s = rs & 0x0F;
+
+ if (s == 0) {
+ if (r == 15) {
+ k += 16;
+ } else {
+ /* EOB run */
+ break;
+ }
+ } else {
+ k += r;
+ if (k > se) break;
+
+ int ac = jpeg_receive_extend(dec, s);
+ block[jpeg_zigzag[k]] = ac;
+ k++;
+ }
+ }
+
+ return 0;
+}
+
+int jpeg_decode_ac_refine(jpeg_decoder_t* dec, int16_t* block,
+ int ss, int se, int al) {
+ int k = ss;
+
+ while (k <= se) {
+ int bit = jpeg_get_bits(dec, 1);
+ if (bit < 0) return -1;
+
+ int zz = jpeg_zigzag[k];
+ if (block[zz] != 0) {
+ block[zz] |= (bit << al);
+ }
+ k++;
+ }
+
+ return 0;
+}
+
+/* ============================================================
+ * YCbCr to RGB Conversion
+ * ============================================================ */
+
+void jpeg_ycbcr_to_rgb(jpeg_decoder_t* dec, image_t* img) {
+ if (dec->num_components == 1) {
+ /* Grayscale */
+ jpeg_component_t* y_comp = &dec->components[0];
+
+ for (uint32_t py = 0; py < img->height; py++) {
+ for (uint32_t px = 0; px < img->width; px++) {
+ int y_x = px * y_comp->h_samp / dec->max_h_samp;
+ int y_y = py * y_comp->v_samp / dec->max_v_samp;
+
+ uint8_t y = y_comp->data[y_y * y_comp->stride + y_x];
+
+ image_set_pixel(img, px, py, y, y, y, 255);
+ }
+ }
+ } else if (dec->num_components >= 3) {
+ /* YCbCr to RGB */
+ jpeg_component_t* y_comp = &dec->components[0];
+ jpeg_component_t* cb_comp = &dec->components[1];
+ jpeg_component_t* cr_comp = &dec->components[2];
+
+ for (uint32_t py = 0; py < img->height; py++) {
+ for (uint32_t px = 0; px < img->width; px++) {
+ /* Calculate sample positions for each component */
+ int y_x = px * y_comp->h_samp / dec->max_h_samp;
+ int y_y = py * y_comp->v_samp / dec->max_v_samp;
+
+ int cb_x = px * cb_comp->h_samp / dec->max_h_samp;
+ int cb_y = py * cb_comp->v_samp / dec->max_v_samp;
+
+ int cr_x = px * cr_comp->h_samp / dec->max_h_samp;
+ int cr_y = py * cr_comp->v_samp / dec->max_v_samp;
+
+ /* Clamp to valid range */
+ if (y_x >= y_comp->width) y_x = y_comp->width - 1;
+ if (y_y >= y_comp->height) y_y = y_comp->height - 1;
+ if (cb_x >= cb_comp->width) cb_x = cb_comp->width - 1;
+ if (cb_y >= cb_comp->height) cb_y = cb_comp->height - 1;
+ if (cr_x >= cr_comp->width) cr_x = cr_comp->width - 1;
+ if (cr_y >= cr_comp->height) cr_y = cr_comp->height - 1;
+
+ int y = y_comp->data[y_y * y_comp->stride + y_x];
+ int cb = cb_comp->data[cb_y * cb_comp->stride + cb_x] - 128;
+ int cr = cr_comp->data[cr_y * cr_comp->stride + cr_x] - 128;
+
+ /* YCbCr to RGB conversion (ITU-R BT.601) */
+ /* R = Y + 1.402 * Cr */
+ /* G = Y - 0.344136 * Cb - 0.714136 * Cr */
+ /* B = Y + 1.772 * Cb */
+
+ /* Using fixed-point arithmetic (scaled by 2^16) */
+ int r = y + ((91881 * cr) >> 16);
+ int g = y - ((22554 * cb + 46802 * cr) >> 16);
+ int b = y + ((116130 * cb) >> 16);
+
+ image_set_pixel(img, px, py,
+ clip_uint8(r),
+ clip_uint8(g),
+ clip_uint8(b), 255);
+ }
+ }
+ }
+}
+
+/* ============================================================
+ * Scan Decoding
+ * ============================================================ */
+
+int jpeg_decode_scan(jpeg_decoder_t* dec) {
+ jpeg_scan_t scan;
+
+ if (jpeg_parse_sos(dec, &scan) < 0) {
+ return -1;
+ }
+
+ /* Initialize bit reader */
+ dec->bits_buffer = 0;
+ dec->bits_count = 0;
+
+ /* Reset DC predictors */
+ for (int i = 0; i < dec->num_components; i++) {
+ dec->components[i].dc_pred = 0;
+ }
+
+ int mcu_count = 0;
+ int restart_count = dec->restart_interval;
+
+ /* Non-interleaved scan (single component) */
+ if (scan.num_components == 1) {
+ /* Find component */
+ jpeg_component_t* comp = NULL;
+ int comp_idx = 0;
+ for (int i = 0; i < dec->num_components; i++) {
+ if (dec->components[i].id == scan.component_ids[0]) {
+ comp = &dec->components[i];
+ comp_idx = i;
+ break;
+ }
+ }
+
+ if (!comp) {
+ dec->error = 1;
+ snprintf(dec->error_msg, sizeof(dec->error_msg), "Component not found");
+ return -1;
+ }
+
+ int blocks_x = comp->width / 8;
+ int blocks_y = comp->height / 8;
+
+ for (int by = 0; by < blocks_y; by++) {
+ for (int bx = 0; bx < blocks_x; bx++) {
+ int16_t block[64];
+
+ if (dec->is_progressive) {
+ int block_idx = 0;
+ for (int c = 0; c < comp_idx; c++) {
+ block_idx += (dec->components[c].width / 8) *
+ (dec->components[c].height / 8);
+ }
+ block_idx += by * blocks_x + bx;
+ int16_t* coef_block = dec->coef_blocks[block_idx];
+
+ if (scan.ss == 0 && scan.se == 0) {
+ /* DC scan */
+ if (scan.ah == 0) {
+ if (jpeg_decode_dc_first(dec, comp, coef_block) < 0) return -1;
+ coef_block[0] <<= scan.al;
+ } else {
+ if (jpeg_decode_dc_refine(dec, coef_block, scan.al) < 0) return -1;
+ }
+ } else {
+ /* AC scan */
+ if (scan.ah == 0) {
+ if (jpeg_decode_ac_first(dec, comp, coef_block, scan.ss, scan.se) < 0) return -1;
+ /* Shift all AC coefficients */
+ for (int k = scan.ss; k <= scan.se; k++) {
+ coef_block[jpeg_zigzag[k]] <<= scan.al;
+ }
+ } else {
+ if (jpeg_decode_ac_refine(dec, coef_block, scan.ss, scan.se, scan.al) < 0) return -1;
+ }
+ }
+ } else {
+ /* Baseline/Sequential */
+ if (jpeg_decode_block(dec, block, comp, 0) < 0) return -1;
+
+ /* IDCT and store */
+ uint8_t* out = comp->data + by * 8 * comp->stride + bx * 8;
+ jpeg_idct_block(block, out, comp->stride);
+ }
+
+ /* Handle restart */
+ if (dec->restart_interval > 0) {
+ mcu_count++;
+ if (mcu_count >= restart_count && (by < blocks_y - 1 || bx < blocks_x - 1)) {
+ jpeg_align_bits(dec);
+ jpeg_next_marker(dec);
+ dec->bits_buffer = 0;
+ dec->bits_count = 0;
+ comp->dc_pred = 0;
+ mcu_count = 0;
+ }
+ }
+ }
+ }
+ } else {
+ /* Interleaved scan */
+ for (int mcu_y = 0; mcu_y < dec->mcu_rows; mcu_y++) {
+ for (int mcu_x = 0; mcu_x < dec->mcus_per_row; mcu_x++) {
+ /* Process each component in the MCU */
+ for (int c = 0; c < scan.num_components; c++) {
+ /* Find component */
+ jpeg_component_t* comp = NULL;
+ for (int i = 0; i < dec->num_components; i++) {
+ if (dec->components[i].id == scan.component_ids[c]) {
+ comp = &dec->components[i];
+ break;
+ }
+ }
+
+ if (!comp) continue;
+
+ /* Process blocks for this component in the MCU */
+ for (int v = 0; v < comp->v_samp; v++) {
+ for (int h = 0; h < comp->h_samp; h++) {
+ int16_t block[64];
+
+ int bx = mcu_x * comp->h_samp + h;
+ int by = mcu_y * comp->v_samp + v;
+
+ if (bx * 8 >= comp->width || by * 8 >= comp->height) {
+ /* Skip blocks outside component dimensions */
+ if (jpeg_decode_block(dec, block, comp, 0) < 0) return -1;
+ continue;
+ }
+
+ if (jpeg_decode_block(dec, block, comp, 0) < 0) return -1;
+
+ /* IDCT and store */
+ uint8_t* out = comp->data + by * 8 * comp->stride + bx * 8;
+ jpeg_idct_block(block, out, comp->stride);
+ }
+ }
+ }
+
+ /* Handle restart */
+ if (dec->restart_interval > 0) {
+ mcu_count++;
+ if (mcu_count >= (int)dec->restart_interval &&
+ (mcu_y < dec->mcu_rows - 1 || mcu_x < dec->mcus_per_row - 1)) {
+ jpeg_align_bits(dec);
+ jpeg_next_marker(dec);
+ dec->bits_buffer = 0;
+ dec->bits_count = 0;
+
+ /* Reset DC predictors */
+ for (int i = 0; i < dec->num_components; i++) {
+ dec->components[i].dc_pred = 0;
+ }
+ mcu_count = 0;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* ============================================================
+ * Progressive Final Processing
+ * ============================================================ */
+
+static void jpeg_progressive_finish(jpeg_decoder_t* dec) {
+ int block_idx = 0;
+
+ for (int c = 0; c < dec->num_components; c++) {
+ jpeg_component_t* comp = &dec->components[c];
+ jpeg_quant_table_t* qt = &dec->quant_tables[comp->quant_table_id];
+
+ int blocks_x = comp->width / 8;
+ int blocks_y = comp->height / 8;
+
+ for (int by = 0; by < blocks_y; by++) {
+ for (int bx = 0; bx < blocks_x; bx++) {
+ int16_t* coef_block = dec->coef_blocks[block_idx++];
+ int16_t block[64];
+
+ /* Dequantize */
+ for (int k = 0; k < 64; k++) {
+ block[k] = coef_block[k] * qt->table[k];
+ }
+
+ /* IDCT and store */
+ uint8_t* out = comp->data + by * 8 * comp->stride + bx * 8;
+ jpeg_idct_block(block, out, comp->stride);
+ }
+ }
+ }
+}
+
+/* ============================================================
+ * Main Decode Function
+ * ============================================================ */
+
+image_t* jpeg_decode(const uint8_t* data, uint32_t data_size) {
+ if (!data || data_size < 2) return NULL;
+
+ jpeg_decoder_t* dec = jpeg_decoder_create();
+ if (!dec) return NULL;
+
+ /* Parse markers up to first SOS */
+ if (jpeg_parse_markers(dec, data, data_size) < 0) {
+ jpeg_decoder_destroy(dec);
+ return NULL;
+ }
+
+ /* Validate we have what we need */
+ if (dec->width == 0 || dec->height == 0) {
+ jpeg_decoder_destroy(dec);
+ return NULL;
+ }
+
+ /* Decode scans */
+ if (dec->is_progressive) {
+ /* Progressive: may have multiple scans */
+ while (dec->pos < data_size - 1) {
+ /* Find SOS marker */
+ if (dec->data[dec->pos - 2] == 0xFF && dec->data[dec->pos - 1] == 0xDA) {
+ if (jpeg_decode_scan(dec) < 0) {
+ jpeg_decoder_destroy(dec);
+ return NULL;
+ }
+ }
+
+ /* Find next marker */
+ int marker = jpeg_next_marker(dec);
+ if (marker < 0 || marker == JPEG_EOI) break;
+
+ if (marker == JPEG_DHT) {
+ dec->pos -= 2;
+ jpeg_parse_dht(dec);
+ } else if (marker == JPEG_SOS) {
+ dec->pos -= 2;
+ }
+ }
+
+ /* Final IDCT pass */
+ jpeg_progressive_finish(dec);
+ } else {
+ /* Baseline/Sequential: single scan */
+ if (jpeg_decode_scan(dec) < 0) {
+ jpeg_decoder_destroy(dec);
+ return NULL;
+ }
+ }
+
+ /* Create output image */
+ image_t* img = image_create(dec->width, dec->height, 24);
+ if (!img) {
+ jpeg_decoder_destroy(dec);
+ return NULL;
+ }
+
+ /* Convert to RGB */
+ jpeg_ycbcr_to_rgb(dec, img);
+
+ jpeg_decoder_destroy(dec);
+ return img;
+}
+
+image_t* jpeg_load_file(const char* filename) {
+ (void)filename;
+ /* TODO: Implement file loading once filesystem is available */
+ return NULL;
+}
+
+/* ============================================================
+ * Lua Bindings
+ * ============================================================ */
+
+int lua_jpeg_load(lua_State* L) {
+ size_t data_size;
+ const uint8_t* data = (const uint8_t*)luaL_checklstring(L, 1, &data_size);
+
+ image_t* img = jpeg_decode(data, (uint32_t)data_size);
+
+ if (img) {
+ lua_pushlightuserdata(L, img);
+ return 1;
+ } else {
+ lua_pushnil(L);
+ lua_pushstring(L, "Failed to decode JPEG");
+ return 2;
+ }
+}
diff --git a/decoder_JPEG.h b/decoder_JPEG.h
@@ -0,0 +1,190 @@
+#ifndef DECODER_JPEG_H
+#define DECODER_JPEG_H
+
+#include "decoder.h"
+#include <stdint.h>
+#include <lua.h>
+
+/* JPEG Markers */
+#define JPEG_SOI 0xFFD8 /* Start of Image */
+#define JPEG_EOI 0xFFD9 /* End of Image */
+#define JPEG_SOS 0xFFDA /* Start of Scan */
+#define JPEG_DQT 0xFFDB /* Define Quantization Table */
+#define JPEG_DNL 0xFFDC /* Define Number of Lines */
+#define JPEG_DRI 0xFFDD /* Define Restart Interval */
+#define JPEG_DHP 0xFFDE /* Define Hierarchical Progression */
+#define JPEG_EXP 0xFFDF /* Expand Reference Component */
+#define JPEG_DHT 0xFFC4 /* Define Huffman Table */
+#define JPEG_DAC 0xFFCC /* Define Arithmetic Coding */
+#define JPEG_COM 0xFFFE /* Comment */
+
+/* Frame markers - Start of Frame */
+#define JPEG_SOF0 0xFFC0 /* Baseline DCT */
+#define JPEG_SOF1 0xFFC1 /* Extended Sequential DCT */
+#define JPEG_SOF2 0xFFC2 /* Progressive DCT */
+#define JPEG_SOF3 0xFFC3 /* Lossless (sequential) */
+#define JPEG_SOF5 0xFFC5 /* Differential Sequential DCT */
+#define JPEG_SOF6 0xFFC6 /* Differential Progressive DCT */
+#define JPEG_SOF7 0xFFC7 /* Differential Lossless (sequential) */
+#define JPEG_SOF9 0xFFC9 /* Extended Sequential DCT, Arithmetic coding */
+#define JPEG_SOF10 0xFFCA /* Progressive DCT, Arithmetic coding */
+#define JPEG_SOF11 0xFFCB /* Lossless (sequential), Arithmetic coding */
+#define JPEG_SOF13 0xFFCD /* Differential Sequential DCT, Arithmetic coding */
+#define JPEG_SOF14 0xFFCE /* Differential Progressive DCT, Arithmetic coding */
+#define JPEG_SOF15 0xFFCF /* Differential Lossless, Arithmetic coding */
+
+/* Application markers */
+#define JPEG_APP0 0xFFE0 /* JFIF marker */
+#define JPEG_APP1 0xFFE1 /* EXIF marker */
+#define JPEG_APP2 0xFFE2 /* ICC Profile */
+#define JPEG_APP14 0xFFEE /* Adobe marker */
+
+/* Restart markers */
+#define JPEG_RST0 0xFFD0
+#define JPEG_RST7 0xFFD7
+
+/* Maximum values */
+#define JPEG_MAX_COMPONENTS 4
+#define JPEG_MAX_HUFFMAN_TABLES 4
+#define JPEG_MAX_QUANT_TABLES 4
+#define JPEG_BLOCK_SIZE 64 /* 8x8 DCT block */
+
+/* Huffman table structure */
+typedef struct {
+ uint8_t bits[17]; /* Number of codes of each length (1-16) */
+ uint8_t huffval[256]; /* Symbol values */
+ /* Derived values for fast decoding */
+ uint16_t huffcode[256]; /* Huffman codes */
+ uint8_t huffsize[256]; /* Code sizes */
+ int maxcode[18]; /* Maximum code of length k */
+ int mincode[17]; /* Minimum code of length k */
+ int valptr[17]; /* Index into huffval for code of length k */
+ int num_symbols; /* Total number of symbols */
+} jpeg_huffman_table_t;
+
+/* Quantization table structure */
+typedef struct {
+ uint16_t table[64]; /* Quantization values in zigzag order */
+ int precision; /* 0 = 8-bit, 1 = 16-bit */
+} jpeg_quant_table_t;
+
+/* Component structure */
+typedef struct {
+ uint8_t id; /* Component identifier */
+ uint8_t h_samp; /* Horizontal sampling factor */
+ uint8_t v_samp; /* Vertical sampling factor */
+ uint8_t quant_table_id; /* Quantization table index */
+ uint8_t dc_table_id; /* DC Huffman table index */
+ uint8_t ac_table_id; /* AC Huffman table index */
+ int16_t dc_pred; /* DC predictor for this component */
+ int width; /* Component width in samples */
+ int height; /* Component height in samples */
+ int stride; /* Row stride in bytes */
+ uint8_t* data; /* Decoded component data */
+} jpeg_component_t;
+
+/* Scan structure for progressive JPEG */
+typedef struct {
+ uint8_t component_ids[JPEG_MAX_COMPONENTS];
+ uint8_t num_components;
+ uint8_t ss; /* Start of spectral selection */
+ uint8_t se; /* End of spectral selection */
+ uint8_t ah; /* Successive approximation high */
+ uint8_t al; /* Successive approximation low */
+} jpeg_scan_t;
+
+/* Main JPEG decoder context */
+typedef struct {
+ /* Image dimensions */
+ uint16_t width;
+ uint16_t height;
+ uint8_t num_components;
+ uint8_t precision; /* Sample precision (usually 8) */
+
+ /* Frame type */
+ uint8_t frame_type; /* SOF marker type */
+ int is_progressive;
+ int is_baseline;
+
+ /* Sampling factors */
+ uint8_t max_h_samp;
+ uint8_t max_v_samp;
+
+ /* MCU dimensions */
+ int mcu_width; /* MCU width in pixels */
+ int mcu_height; /* MCU height in pixels */
+ int mcus_per_row;
+ int mcu_rows;
+
+ /* Restart interval */
+ uint16_t restart_interval;
+
+ /* Tables */
+ jpeg_huffman_table_t dc_tables[JPEG_MAX_HUFFMAN_TABLES];
+ jpeg_huffman_table_t ac_tables[JPEG_MAX_HUFFMAN_TABLES];
+ jpeg_quant_table_t quant_tables[JPEG_MAX_QUANT_TABLES];
+ int dc_table_valid[JPEG_MAX_HUFFMAN_TABLES];
+ int ac_table_valid[JPEG_MAX_HUFFMAN_TABLES];
+ int quant_table_valid[JPEG_MAX_QUANT_TABLES];
+
+ /* Components */
+ jpeg_component_t components[JPEG_MAX_COMPONENTS];
+
+ /* Bit reader state */
+ const uint8_t* data;
+ uint32_t data_size;
+ uint32_t pos;
+ uint32_t bits_buffer;
+ int bits_count;
+
+ /* Progressive decoding */
+ int16_t** coef_blocks; /* DCT coefficient blocks for progressive */
+ int coef_blocks_count;
+
+ /* Error state */
+ int error;
+ char error_msg[256];
+} jpeg_decoder_t;
+
+/* Zigzag ordering for DCT coefficients */
+extern const uint8_t jpeg_zigzag[64];
+extern const uint8_t jpeg_unzigzag[64];
+
+/* IDCT constants */
+extern const int16_t jpeg_idct_scale[64];
+
+/* JPEG decoding functions */
+image_t* jpeg_decode(const uint8_t* data, uint32_t data_size);
+image_t* jpeg_load_file(const char* filename);
+
+/* Internal decoder functions */
+jpeg_decoder_t* jpeg_decoder_create(void);
+void jpeg_decoder_destroy(jpeg_decoder_t* dec);
+int jpeg_parse_markers(jpeg_decoder_t* dec, const uint8_t* data, uint32_t size);
+int jpeg_decode_scan(jpeg_decoder_t* dec);
+int jpeg_decode_mcu(jpeg_decoder_t* dec, int mcu_x, int mcu_y);
+void jpeg_idct_block(int16_t* block, uint8_t* output, int stride);
+void jpeg_ycbcr_to_rgb(jpeg_decoder_t* dec, image_t* img);
+
+/* Huffman decoding */
+int jpeg_build_huffman_table(jpeg_huffman_table_t* table);
+int jpeg_decode_huffman(jpeg_decoder_t* dec, jpeg_huffman_table_t* table);
+int jpeg_receive_extend(jpeg_decoder_t* dec, int nbits);
+
+/* Bit reading */
+int jpeg_get_bits(jpeg_decoder_t* dec, int nbits);
+int jpeg_peek_bits(jpeg_decoder_t* dec, int nbits);
+void jpeg_skip_bits(jpeg_decoder_t* dec, int nbits);
+int jpeg_next_marker(jpeg_decoder_t* dec);
+void jpeg_align_bits(jpeg_decoder_t* dec);
+
+/* Progressive decoding */
+int jpeg_decode_dc_first(jpeg_decoder_t* dec, jpeg_component_t* comp, int16_t* block);
+int jpeg_decode_dc_refine(jpeg_decoder_t* dec, int16_t* block, int al);
+int jpeg_decode_ac_first(jpeg_decoder_t* dec, jpeg_component_t* comp, int16_t* block, int ss, int se);
+int jpeg_decode_ac_refine(jpeg_decoder_t* dec, int16_t* block, int ss, int se, int al);
+
+/* Lua bindings */
+int lua_jpeg_load(lua_State* L);
+
+#endif /* DECODER_JPEG_H */
diff --git a/iso_includes/apps/com.luajitos.lensviewer/src/lens.lua b/iso_includes/apps/com.luajitos.lensviewer/src/lens.lua
@@ -1,5 +1,5 @@
-- Lens - LuajitOS Image Viewer
--- Displays BMP and PNG images with a toolbar
+-- Displays BMP, PNG, and JPEG images with a toolbar
if osprint then
osprint("Lens: Starting...\n")
@@ -116,13 +116,32 @@ local function loadImage(path)
if osprint then
osprint("Lens: PNGLoad available: " .. tostring(PNGLoad ~= nil) .. "\n")
osprint("Lens: BMPLoad available: " .. tostring(BMPLoad ~= nil) .. "\n")
+ osprint("Lens: JPEGLoad available: " .. tostring(JPEGLoad ~= nil) .. "\n")
end
- -- Try to decode as PNG first, then BMP
+ -- Detect file type from magic bytes
+ local fileType = nil
+ if #imageData >= 4 then
+ local b1, b2, b3, b4 = string.byte(imageData, 1, 4)
+ if b1 == 0x89 and b2 == 0x50 and b3 == 0x4E and b4 == 0x47 then
+ fileType = "PNG"
+ elseif b1 == 0x42 and b2 == 0x4D then
+ fileType = "BMP"
+ elseif b1 == 0xFF and b2 == 0xD8 then
+ fileType = "JPEG"
+ end
+ end
+
+ if osprint then
+ osprint("Lens: Detected file type: " .. tostring(fileType) .. "\n")
+ end
+
+ -- Try to decode based on detected type first, then try others
local image = nil
local decoder_used = nil
- if PNGLoad then
+ -- Try detected type first
+ if fileType == "PNG" and PNGLoad then
if osprint then
osprint("Lens: Trying PNG decoder...\n")
end
@@ -133,21 +152,10 @@ local function loadImage(path)
if osprint then
osprint("Lens: Successfully decoded as PNG\n")
end
- else
- if osprint then
- osprint("Lens: PNG decoder returned nil\n")
- if err then
- osprint("Lens: PNG error: " .. tostring(err) .. "\n")
- end
- end
- end
- else
- if osprint then
- osprint("Lens: PNG decoder not available\n")
+ elseif osprint then
+ osprint("Lens: PNG decoder failed: " .. tostring(err) .. "\n")
end
- end
-
- if not image and BMPLoad then
+ elseif fileType == "BMP" and BMPLoad then
if osprint then
osprint("Lens: Trying BMP decoder...\n")
end
@@ -158,13 +166,47 @@ local function loadImage(path)
if osprint then
osprint("Lens: Successfully decoded as BMP\n")
end
- else
+ elseif osprint then
+ osprint("Lens: BMP decoder failed: " .. tostring(err) .. "\n")
+ end
+ elseif fileType == "JPEG" and JPEGLoad then
+ if osprint then
+ osprint("Lens: Trying JPEG decoder...\n")
+ end
+ local result, err = JPEGLoad(imageData)
+ if result then
+ image = result
+ decoder_used = "JPEG"
if osprint then
- osprint("Lens: BMP decoder returned nil\n")
- if err then
- osprint("Lens: BMP error: " .. tostring(err) .. "\n")
- end
+ osprint("Lens: Successfully decoded as JPEG\n")
end
+ elseif osprint then
+ osprint("Lens: JPEG decoder failed: " .. tostring(err) .. "\n")
+ end
+ end
+
+ -- If detected type failed or unknown, try all decoders
+ if not image and PNGLoad then
+ local result, err = PNGLoad(imageData)
+ if result then
+ image = result
+ decoder_used = "PNG"
+ end
+ end
+
+ if not image and JPEGLoad then
+ local result, err = JPEGLoad(imageData)
+ if result then
+ image = result
+ decoder_used = "JPEG"
+ end
+ end
+
+ if not image and BMPLoad then
+ local result, err = BMPLoad(imageData)
+ if result then
+ image = result
+ decoder_used = "BMP"
end
end
@@ -172,7 +214,7 @@ local function loadImage(path)
if osprint then
osprint("Lens: ERROR - Failed to decode image with any decoder\n")
end
- return false, "Failed to decode image (not a valid BMP or PNG)"
+ return false, "Failed to decode image (not a valid BMP, PNG, or JPEG)"
end
-- Get image info
diff --git a/iso_includes/apps/com.luajitos.moonbrowser/src/browser.lua b/iso_includes/apps/com.luajitos.moonbrowser/src/browser.lua
@@ -30,17 +30,46 @@ if not fs then
return
end
--- Check if file exists
+-- Check if file exists - if not, show a "file not found" page
+local htmlContent
if not fs:exists(htmlPath) then
- print("ERROR: File not found: " .. htmlPath)
- return
-end
-
--- Read the HTML file
-local htmlContent, err = fs:read(htmlPath)
-if not htmlContent then
- print("ERROR: Failed to read file: " .. tostring(err))
- return
+ print("Moonbrowser: File not found, showing error page: " .. htmlPath)
+ htmlContent = [[
+<!DOCTYPE html>
+<html>
+<head>
+ <title>File Not Found</title>
+</head>
+<body style="background-color: #2d2d2d; color: #ffffff; font-family: sans-serif; padding: 20px;">
+ <h1 style="color: #ff6b6b;">File Not Found</h1>
+ <p>The requested file could not be found:</p>
+ <p style="color: #aaaaaa; font-family: monospace; background-color: #1a1a1a; padding: 10px;">]] .. htmlPath .. [[</p>
+ <p style="margin-top: 20px;">Please check the path and try again.</p>
+ <p style="color: #888888; font-size: 12px; margin-top: 30px;">Tip: Use the file dialog in Explorer to open HTML files, or pass a path as an argument.</p>
+</body>
+</html>
+]]
+else
+ -- Read the HTML file
+ local err
+ htmlContent, err = fs:read(htmlPath)
+ if not htmlContent then
+ print("Moonbrowser: Failed to read file, showing error page: " .. tostring(err))
+ htmlContent = [[
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Read Error</title>
+</head>
+<body style="background-color: #2d2d2d; color: #ffffff; font-family: sans-serif; padding: 20px;">
+ <h1 style="color: #ff6b6b;">Failed to Read File</h1>
+ <p>Could not read the file:</p>
+ <p style="color: #aaaaaa; font-family: monospace; background-color: #1a1a1a; padding: 10px;">]] .. htmlPath .. [[</p>
+ <p style="color: #ff9999;">Error: ]] .. tostring(err) .. [[</p>
+</body>
+</html>
+]]
+ end
end
print("Moonbrowser: Read " .. #htmlContent .. " bytes")
@@ -90,18 +119,27 @@ for content, name in htmlContent:gmatch('<meta[^>]+content%s*=%s*["\']([^"\']+)[
end
end
+-- Toolbar settings
+local toolbarHeight = 30
+local statusBarHeight = 20
+
print("Moonbrowser: Window size " .. windowWidth .. "x" .. windowHeight)
+-- Adjust window height to include toolbar
+local totalWindowHeight = windowHeight + toolbarHeight
+
-- Create window
-local window = app:newWindow("Moonbrowser - " .. htmlPath, windowWidth, windowHeight, true)
+local window = app:newWindow("Moonbrowser - " .. htmlPath, windowWidth, totalWindowHeight, true)
if not window then
print("ERROR: Failed to create window")
return
end
--- Create renderer once
-local renderer = Renderer.new(nil, windowWidth, windowHeight)
+-- Create renderer with content area (excluding toolbar and status bar)
+local contentHeight = windowHeight - statusBarHeight
+local renderer = Renderer.new(nil, windowWidth, contentHeight)
+renderer.offsetY = toolbarHeight -- Set offset for toolbar
-- Store DOM in renderer for query() support
renderer:setDOM(dom)
@@ -235,15 +273,44 @@ end
-- Store executeInSandbox for use in click handler
renderer.executeScript = executeInSandbox
+-- Toolbar button definitions
+local toolbarButtons = {
+ { x = 5, y = 5, width = 50, height = 20, label = "Open", action = "open" },
+ { x = 60, y = 5, width = 50, height = 20, label = "Back", action = "back" },
+ { x = 115, y = 5, width = 60, height = 20, label = "Refresh", action = "refresh" },
+}
+
+-- History for back button
+local history = {}
+
-- Draw callback
window.onDraw = function(gfx)
- -- Render DOM to window
+ -- Draw toolbar background
+ gfx:fillRect(0, 0, windowWidth, toolbarHeight, 0x404040)
+
+ -- Draw toolbar buttons
+ for _, btn in ipairs(toolbarButtons) do
+ -- Button background
+ gfx:fillRect(btn.x, btn.y, btn.width, btn.height, 0x606060)
+ -- Button border
+ gfx:drawRect(btn.x, btn.y, btn.width, btn.height, 0x808080)
+ -- Button label
+ local textX = btn.x + (btn.width - #btn.label * 6) / 2
+ local textY = btn.y + 6
+ gfx:drawText(textX, textY, btn.label, 0xFFFFFF)
+ end
+
+ -- Draw separator line below toolbar
+ gfx:fillRect(0, toolbarHeight - 1, windowWidth, 1, 0x303030)
+
+ -- Render DOM to content area (renderer handles its own offset)
renderer:render(dom, gfx)
- -- Draw status bar
- gfx:fillRect(0, windowHeight - 20, windowWidth, 20, 0x333333)
- gfx:drawText(5, windowHeight - 16, htmlPath, 0xFFFFFF)
- gfx:drawText(windowWidth - 80, windowHeight - 16, "Arrows: Scroll Q: Quit", 0xAAAAAA)
+ -- Draw status bar at bottom
+ local statusY = totalWindowHeight - statusBarHeight
+ gfx:fillRect(0, statusY, windowWidth, statusBarHeight, 0x333333)
+ gfx:drawText(5, statusY + 4, htmlPath, 0xFFFFFF)
+ gfx:drawText(windowWidth - 150, statusY + 4, "Arrows: Scroll Q: Quit", 0xAAAAAA)
end
-- Input callback
@@ -338,9 +405,84 @@ local function navigateTo(newPath)
return true
end
+-- Function to open file dialog
+local function openFileDialog()
+ if not Dialog or not Dialog.fileOpen then
+ print("Moonbrowser: Dialog.fileOpen not available")
+ return
+ end
+
+ local dialog = Dialog.fileOpen("/home", {
+ app = app,
+ fs = fs,
+ title = "Open HTML File"
+ })
+
+ dialog:openDialog(function(selectedPath)
+ if selectedPath then
+ -- Check if it's an HTML file
+ if selectedPath:match("%.html?$") then
+ -- Save current path to history
+ table.insert(history, htmlPath)
+ -- Navigate to selected file
+ if navigateTo(selectedPath) then
+ window.title = "Moonbrowser - " .. selectedPath
+ window:markDirty()
+ end
+ else
+ print("Moonbrowser: Not an HTML file: " .. selectedPath)
+ if Dialog.alert then
+ Dialog.alert("Please select an HTML file (.html or .htm)", { app = app })
+ end
+ end
+ end
+ end)
+end
+
+-- Function to go back in history
+local function goBack()
+ if #history > 0 then
+ local prevPath = table.remove(history)
+ if navigateTo(prevPath) then
+ window.title = "Moonbrowser - " .. prevPath
+ window:markDirty()
+ end
+ end
+end
+
+-- Function to refresh current page
+local function refreshPage()
+ if navigateTo(htmlPath) then
+ window:markDirty()
+ end
+end
+
-- Click callback
window.onClick = function(x, y, button)
- local result = renderer:handleClick(x, y)
+ -- Check if click is in toolbar area
+ if y < toolbarHeight then
+ -- Check toolbar buttons
+ for _, btn in ipairs(toolbarButtons) do
+ if x >= btn.x and x < btn.x + btn.width and
+ y >= btn.y and y < btn.y + btn.height then
+ print("Moonbrowser: Toolbar button clicked: " .. btn.action)
+ if btn.action == "open" then
+ openFileDialog()
+ elseif btn.action == "back" then
+ goBack()
+ elseif btn.action == "refresh" then
+ refreshPage()
+ end
+ return
+ end
+ end
+ return
+ end
+
+ -- Adjust click position for content area
+ local contentY = y - toolbarHeight
+
+ local result = renderer:handleClick(x, contentY)
if result then
if result.type == "button" then
print("Button clicked: " .. (result.text or "(no text)"))
@@ -361,6 +503,8 @@ window.onClick = function(x, y, button)
-- javascript: URL
executeInSandbox(jsCode)
elseif newPath then
+ -- Save current path to history before navigating
+ table.insert(history, htmlPath)
-- Navigate to new page
navigateTo(newPath)
end
diff --git a/iso_includes/apps/com.luajitos.moonbrowser/src/render.lua b/iso_includes/apps/com.luajitos.moonbrowser/src/render.lua
@@ -84,6 +84,7 @@ function Renderer.new(app, width, height)
self.width = width or 800
self.height = height or 600
self.scrollY = 0
+ self.offsetY = 0 -- Vertical offset for toolbar etc.
self.contentHeight = 0
self.interactiveElements = {} -- Store buttons/inputs for click handling
self.inputValues = {} -- Store input field values
@@ -110,11 +111,11 @@ function Renderer:render(dom, gfx)
-- Clear interactive elements for this frame
self.interactiveElements = {}
- -- Clear background to white
- gfx:fillRect(0, 0, self.width, self.height, 0xFFFFFF)
+ -- Clear background to white (with offset)
+ gfx:fillRect(0, self.offsetY, self.width, self.height, 0xFFFFFF)
- -- Start rendering from root
- local y = 10 - self.scrollY
+ -- Start rendering from root (with offset)
+ local y = 10 - self.scrollY + self.offsetY
y = self:renderElement(dom, gfx, 10, y, self.width - 20, 16)
-- Store content height for scrolling
diff --git a/iso_includes/apps/com.luajitos.taskbar/src/init.lua b/iso_includes/apps/com.luajitos.taskbar/src/init.lua
@@ -224,9 +224,11 @@ local function loadAppIcon(appId)
return appIconCache[appId]
end
- -- Try PNG first, then BMP
+ -- Try PNG first, then JPEG, then BMP
local iconPaths = {
"/apps/" .. appId .. "/icon.png",
+ "/apps/" .. appId .. "/icon.jpg",
+ "/apps/" .. appId .. "/icon.jpeg",
"/apps/" .. appId .. "/icon.bmp"
}
local defaultIconPath = "/os/res/default.bmp"
@@ -243,6 +245,8 @@ local function loadAppIcon(appId)
local img = nil
if iconPath:match("%.png$") and PNGLoad then
img = PNGLoad(iconData)
+ elseif (iconPath:match("%.jpg$") or iconPath:match("%.jpeg$")) and JPEGLoad then
+ img = JPEGLoad(iconData)
elseif iconPath:match("%.bmp$") and BMPLoad then
img = BMPLoad(iconData)
end
diff --git a/iso_includes/home/Pictures/Samples/wolf2.bmp b/iso_includes/home/Pictures/Samples/wolf2.bmp
Binary files differ.
diff --git a/iso_includes/home/Pictures/Samples/wolf2.png b/iso_includes/home/Pictures/Samples/wolf2.png
Binary files differ.
diff --git a/iso_includes/home/Pictures/Samples/wolf4.jpeg b/iso_includes/home/Pictures/Samples/wolf4.jpeg
Binary files differ.
diff --git a/iso_includes/home/Pictures/Samples/wolf4.png b/iso_includes/home/Pictures/Samples/wolf4.png
Binary files differ.
diff --git a/iso_includes/os/libs/Run.lua b/iso_includes/os/libs/Run.lua
@@ -854,9 +854,11 @@ function run.execute(app_name, fsRoot)
local iconLoaded = false
local defaultIconPath = "/os/res/default.bmp"
- -- Try loading app-specific icon (PNG first, then BMP)
+ -- Try loading app-specific icon (PNG first, then JPEG, then BMP)
local iconPaths = {
app_dir .. "/icon.png",
+ app_dir .. "/icon.jpg",
+ app_dir .. "/icon.jpeg",
app_dir .. "/icon.bmp"
}
@@ -871,6 +873,8 @@ function run.execute(app_name, fsRoot)
local img = nil
if iconPath:match("%.png$") and PNGLoad then
img = PNGLoad(iconData)
+ elseif (iconPath:match("%.jpg$") or iconPath:match("%.jpeg$")) and JPEGLoad then
+ img = JPEGLoad(iconData)
elseif iconPath:match("%.bmp$") and BMPLoad then
img = BMPLoad(iconData)
end
@@ -1649,6 +1653,7 @@ function run.execute(app_name, fsRoot)
-- Add image loading/drawing functions
sandbox_env.PNGLoad = _G.PNGLoad
sandbox_env.BMPLoad = _G.BMPLoad
+ sandbox_env.JPEGLoad = _G.JPEGLoad
sandbox_env.ImageDraw = _G.ImageDraw
sandbox_env.ImageDrawScaled = _G.ImageDrawScaled
sandbox_env.ImageGetInfo = _G.ImageGetInfo
@@ -1664,6 +1669,8 @@ function run.execute(app_name, fsRoot)
osprint("Draw permission granted - graphics functions available\n")
osprint(" BMPLoad in _G: " .. tostring(_G.BMPLoad ~= nil) .. "\n")
osprint(" BMPLoad in sandbox: " .. tostring(sandbox_env.BMPLoad ~= nil) .. "\n")
+ osprint(" JPEGLoad in _G: " .. tostring(_G.JPEGLoad ~= nil) .. "\n")
+ osprint(" JPEGLoad in sandbox: " .. tostring(sandbox_env.JPEGLoad ~= nil) .. "\n")
osprint(" ImageGetWidth in _G: " .. tostring(_G.ImageGetWidth ~= nil) .. "\n")
osprint(" ImageGetHeight in _G: " .. tostring(_G.ImageGetHeight ~= nil) .. "\n")
end
@@ -2087,11 +2094,14 @@ function run.execute(app_name, fsRoot)
for k, _ in pairs(sandbox_env) do
allowed_keys[k] = true
end
- -- Also allow keys that were explicitly set to nil
+ -- Also allow keys that were explicitly set to nil or may be nil
allowed_keys.fs = true
allowed_keys.crypto = true
allowed_keys.sys = true
allowed_keys.window = true
+ allowed_keys.JPEGLoad = true
+ allowed_keys.PNGLoad = true
+ allowed_keys.BMPLoad = true
setmetatable(sandbox_env, {
__index = function(t, k)
diff --git a/kernel.c b/kernel.c
@@ -19,6 +19,7 @@
#include "decoder.h"
#include "decoder_BMP.h"
#include "decoder_PNG.h"
+#include "decoder_JPEG.h"
#include "screen_config.h"
#include "ata.h"
#include "fde.h"
@@ -1016,6 +1017,9 @@ void usermode_function(void) {
lua_pushcfunction(L, lua_png_load);
lua_setglobal(L, "PNGLoad");
+ lua_pushcfunction(L, lua_jpeg_load);
+ lua_setglobal(L, "JPEGLoad");
+
// lua_pushcfunction(L, lua_png_save);
// lua_setglobal(L, "PNGSave");
diff --git a/luajit_init.c b/luajit_init.c
@@ -17,6 +17,7 @@
#include "decoder.h"
#include "decoder_BMP.h"
#include "decoder_PNG.h"
+#include "decoder_JPEG.h"
#include "compression/compression.h"
#include "ramdisk.h"
@@ -1160,6 +1161,19 @@ __attribute__((naked)) void usermode_main(void) {
lua_pushcfunction(L, lua_png_load);
lua_setglobal(L, "PNGLoad");
+ lua_pushcfunction(L, lua_jpeg_load);
+ lua_setglobal(L, "JPEGLoad");
+ terminal_writestring("Registered JPEGLoad function\n");
+
+ /* Verify JPEGLoad is in global */
+ lua_getglobal(L, "JPEGLoad");
+ if (lua_isfunction(L, -1)) {
+ terminal_writestring("JPEGLoad verification: SUCCESS\n");
+ } else {
+ terminal_writestring("JPEGLoad verification: FAILED\n");
+ }
+ lua_pop(L, 1);
+
// lua_pushcfunction(L, lua_png_save);
// lua_setglobal(L, "PNGSave");
diff --git a/repack_lua.sh b/repack_lua.sh
@@ -112,13 +112,13 @@ fi
# Link all objects together
${LD} ${LDFLAGS} -T linker.ld -o build/kernel.bin \
build/boot.o build/syscall.o build/exceptions.o build/libc.o build/paging.o build/graphics.o build/vesa.o build/mouse.o build/usb_uhci.o build/keyboard.o build/ata.o build/fde.o build/diskfs.o build/partition.o build/fat16.o \
- build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/splash.o \
+ build/decoder.o build/decoder_BMP.o build/decoder_PNG.o build/decoder_JPEG.o build/splash.o \
build/compression.o build/deflate_impl.o build/Deflate.o build/LZMA.o build/GZip.o build/zlib.o \
build/ramdisk.o build/luajit_init.o build/kernel.o \
build/crypto_baremetal.o build/CSPRNG.o build/Ed25519.o build/X25519.o build/Curve25519.o \
build/AES-256-GCM.o build/AES-128-GCM.o build/ChaCha20-Poly1305.o build/XChaCha20-Poly1305.o \
build/Serpent-256-GCM.o build/Twofish-256-GCM.o build/Salsa20-Poly1305.o \
- build/PBKDF2.o build/Argon2.o build/stubs.o build/RSA.o build/P256.o \
+ build/PBKDF2.o build/Argon2.o build/HKDF.o build/stubs.o build/RSA.o build/P256.o \
build/Kyber.o build/Dilithium.o \
build/hash.o build/MD5.o build/SHA1.o build/SHA256.o build/SHA3.o build/BLAKE2.o \
build/CSPRNG_Lua.o build/Ed25519_Lua.o build/X25519_Lua.o build/Hash_Lua.o \