luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit a7f852f14c9223f1034d06d0cd1d9f88d51bb887
parent fd71fd8fd418e13dc702656a1221e4a9db3da638
Author: luajitos <bbhbb2094@gmail.com>
Date:   Mon,  1 Dec 2025 22:32:30 +0000

Added JPEG support

Diffstat:
Mbuild.sh | 7+++++--
Adecoder_JPEG.c | 1246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adecoder_JPEG.h | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Miso_includes/apps/com.luajitos.lensviewer/src/lens.lua | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Miso_includes/apps/com.luajitos.moonbrowser/src/browser.lua | 182++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Miso_includes/apps/com.luajitos.moonbrowser/src/render.lua | 9+++++----
Miso_includes/apps/com.luajitos.taskbar/src/init.lua | 6+++++-
Aiso_includes/home/Pictures/Samples/wolf2.bmp | 0
Diso_includes/home/Pictures/Samples/wolf2.png | 0
Aiso_includes/home/Pictures/Samples/wolf4.jpeg | 0
Aiso_includes/home/Pictures/Samples/wolf4.png | 0
Miso_includes/os/libs/Run.lua | 14++++++++++++--
Mkernel.c | 4++++
Mluajit_init.c | 14++++++++++++++
Mrepack_lua.sh | 4++--
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 \