This library provides lightweight JPEG encoder and decoder suitable for embedded systems. It supports multiple input/output formats and optional SIMD optimizations.
int jpeg_encode_rgb(const uint8_t *rgb_data, int width, int height, int quality,
uint8_t **out_data, size_t *out_size);Encodes RGB image data to JPEG format.
Parameters:
rgb_data: Input RGB data in interleaved format (R,G,B,R,G,B,...)width: Image width in pixelsheight: Image height in pixelsquality: Quality factor (1-100, where 100 is best quality)out_data: Pointer to receive allocated output bufferout_size: Pointer to receive output size in bytes
Returns:
0on success-1on error (invalid parameters)
Memory:
- Output buffer is dynamically allocated
- Caller must free using
jpeg_free()
Example:
uint8_t *rgb = ...; // width * height * 3 bytes
uint8_t *jpeg_data = NULL;
size_t jpeg_size = 0;
if (jpeg_encode_rgb(rgb, 640, 480, 85, &jpeg_data, &jpeg_size) == 0) {
// Use jpeg_data
jpeg_free(jpeg_data);
}int jpeg_encode_yuv420(const uint8_t *y_plane, const uint8_t *u_plane,
const uint8_t *v_plane, int width, int height,
int quality, uint8_t **out_data, size_t *out_size);Encodes YUV420 planar image to JPEG format.
Parameters:
y_plane: Y (luminance) plane, size = width × heightu_plane: U (Cb) plane, size = (width/2) × (height/2)v_plane: V (Cr) plane, size = (width/2) × (height/2)width: Image width in pixels (should be even)height: Image height in pixels (should be even)quality: Quality factor (1-100)out_data: Pointer to receive allocated output bufferout_size: Pointer to receive output size in bytes
Returns:
0on success-1on error
Notes:
- More efficient than RGB encoding for video pipelines
- Best results with width/height as multiples of 16
Example:
uint8_t *y = ...; // width * height
uint8_t *u = ...; // (width/2) * (height/2)
uint8_t *v = ...; // (width/2) * (height/2)
uint8_t *jpeg_data = NULL;
size_t jpeg_size = 0;
if (jpeg_encode_yuv420(y, u, v, 640, 480, 85, &jpeg_data, &jpeg_size) == 0) {
jpeg_free(jpeg_data);
}int jpeg_encode_uyvy(const uint8_t *uyvy_data, int width, int height,
int quality, uint8_t **out_data, size_t *out_size);Encodes UYVY (YUV422) packed image to JPEG format.
Parameters:
uyvy_data: Input UYVY data (U,Y0,V,Y1,U,Y2,V,Y3,...)width: Image width in pixels (must be even)height: Image height in pixelsquality: Quality factor (1-100)out_data: Pointer to receive allocated output bufferout_size: Pointer to receive output size in bytes
Returns:
0on success-1on error
Notes:
- Common format for cameras and capture devices
- Width must be even
- Input size = width × height × 2 bytes
Example:
uint8_t *uyvy = ...; // width * height * 2 bytes
uint8_t *jpeg_data = NULL;
size_t jpeg_size = 0;
if (jpeg_encode_uyvy(uyvy, 640, 480, 85, &jpeg_data, &jpeg_size) == 0) {
jpeg_free(jpeg_data);
}void jpeg_free(uint8_t *data);Frees memory allocated by encoding functions.
Parameters:
data: Pointer returned byjpeg_encode_*functions
Example:
uint8_t *jpeg_data = NULL;
size_t jpeg_size = 0;
jpeg_encode_rgb(rgb, w, h, q, &jpeg_data, &jpeg_size);
// Use jpeg_data...
jpeg_free(jpeg_data);The quality parameter (1-100) controls the trade-off between file size and image quality:
- 1-50: Low quality, high compression, small files
- 50-75: Medium quality, good for web images
- 75-90: High quality, suitable for most applications
- 90-100: Very high quality, large files
Recommended values:
- Web thumbnails: 60-70
- Web images: 75-85
- Print quality: 90-95
- Archival: 95-100
All encoding functions return:
0on success-1on error
Common error conditions:
- NULL input pointers
- Invalid dimensions (width/height ≤ 0)
- Invalid quality (< 1 or > 100)
- Memory allocation failure
Example:
int result = jpeg_encode_rgb(rgb, width, height, quality, &out, &size);
if (result != 0) {
fprintf(stderr, "Encoding failed\n");
return -1;
}The encoder is not thread-safe. Each thread should use separate encoder instances or external synchronization.
- All output buffers are allocated with
malloc() - Initial buffer size: 64KB (configurable via
JPEG_BUFFER_INITIAL_CAPACITY) - Buffer grows automatically as needed (doubles each time)
- Always call
jpeg_free()to release memory
- Use YUV formats when available (avoids color conversion)
- Enable SIMD by compiling with appropriate flags
- Align dimensions to multiples of 16 for best performance
- Reuse buffers in video encoding scenarios
- Choose appropriate quality (85 is often optimal)
Enable SIMD optimizations at compile time:
# ARM NEON
make ARCH=arm
# AArch64
make ARCH=aarch64
# MIPS MSA
make ARCH=mips
# x86 SSE2
make ARCH=x86
# x86 AVX2
make ARCH=x86_avxSIMD provides 2-4x speedup for:
- RGB to YCbCr conversion
- DCT transform
- Baseline JPEG only (no progressive)
- Fixed 4:2:0 chroma subsampling
- Standard Huffman tables (not optimized)
- No restart markers
- No EXIF metadata support
int jpeg_decode_rgb565(const uint8_t *jpeg_data, size_t jpeg_size,
uint16_t **out_data, int *width, int *height);Decodes JPEG image to RGB565 format.
Parameters:
jpeg_data: Input JPEG data bufferjpeg_size: Size of JPEG data in bytesout_data: Pointer to receive allocated RGB565 output bufferwidth: Pointer to receive image widthheight: Pointer to receive image height
Returns:
0on success-1on error
Memory:
- Output buffer is dynamically allocated
- Caller must free using
jpeg_decoder_free()
RGB565 Format:
- 16 bits per pixel:
[RRRRR GGGGGG BBBBB] - 5 bits for Red, 6 bits for Green, 5 bits for Blue
- Little-endian byte order
- Output size = width × height × 2 bytes
Example:
#include "jpeg_decoder.h"
uint8_t *jpeg_data = ...; // JPEG file data
size_t jpeg_size = ...; // JPEG file size
uint16_t *rgb565 = NULL;
int width, height;
if (jpeg_decode_rgb565(jpeg_data, jpeg_size, &rgb565, &width, &height) == 0) {
// Use RGB565 data for display
// Format: pixel = (R>>3)<<11 | (G>>2)<<5 | (B>>3)
jpeg_decoder_free(rgb565);
}void jpeg_decoder_free(void *data);Frees memory allocated by decoder.
Parameters:
data: Pointer returned byjpeg_decode_rgb565()
Example:
uint16_t *rgb565 = NULL;
int width, height;
jpeg_decode_rgb565(jpeg_data, jpeg_size, &rgb565, &width, &height);
// Use rgb565...
jpeg_decoder_free(rgb565);- ✅ Baseline JPEG (ISO/IEC 10918-1)
- ✅ 4:2:0 chroma subsampling
- ✅ Standard quantization tables
- ✅ Standard Huffman tables
- ✅ SIMD optimization (NEON, SSE2)
- Baseline JPEG only (no progressive)
- No restart markers support
- No EXIF parsing
- Fixed RGB565 output format
The decoder uses SIMD instructions for:
- YCbCr to RGB conversion - Vectorized color space conversion
- RGB565 packing - Parallel bit operations
Performance:
- SIMD provides 2-3× speedup
- Processes 8 pixels in parallel
- Uses fixed-point arithmetic
Enable SIMD:
# ARM NEON
make ARCH=arm
# x86 SSE2
make ARCH=x86#include "jpeg_encoder.h"
#include "jpeg_decoder.h"
// Step 1: Encode RGB to JPEG
uint8_t *rgb_input = ...; // width * height * 3
uint8_t *jpeg_data = NULL;
size_t jpeg_size = 0;
jpeg_encode_rgb(rgb_input, 640, 480, 85, &jpeg_data, &jpeg_size);
// Step 2: Decode JPEG to RGB565
uint16_t *rgb565_output = NULL;
int width, height;
jpeg_decode_rgb565(jpeg_data, jpeg_size, &rgb565_output, &width, &height);
// Step 3: Use RGB565 for display
for (int i = 0; i < width * height; i++) {
uint16_t pixel = rgb565_output[i];
uint8_t r = (pixel >> 11) & 0x1F; // 5 bits
uint8_t g = (pixel >> 5) & 0x3F; // 6 bits
uint8_t b = pixel & 0x1F; // 5 bits
// Display pixel...
}
// Step 4: Cleanup
jpeg_decoder_free(rgb565_output);
jpeg_free(jpeg_data);Bit: 15 14 13 12 11 | 10 9 8 7 6 | 5 4 3 2 1 0
R R R R R | G G G G G G | B B B B B
RGB888 to RGB565:
uint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);RGB565 to RGB888:
uint8_t r = ((rgb565 >> 11) & 0x1F) << 3;
uint8_t g = ((rgb565 >> 5) & 0x3F) << 2;
uint8_t b = (rgb565 & 0x1F) << 3;- Embedded displays - Many LCD controllers use RGB565
- Framebuffers - Reduced memory footprint (2 bytes vs 3)
- Real-time video - Lower bandwidth requirements
- Memory-constrained systems - 33% less memory than RGB888
Common error conditions:
- Invalid JPEG format
- Corrupted data
- Unsupported JPEG features
- Memory allocation failure
Example:
int result = jpeg_decode_rgb565(jpeg_data, jpeg_size, &out, &w, &h);
if (result != 0) {
fprintf(stderr, "Decoding failed\n");
// Check JPEG file validity
// Verify sufficient memory
return -1;
}| Resolution | Quality | Output Size | Time (scalar) | Time (SIMD) |
|---|---|---|---|---|
| 640×480 | 85 | ~50-100KB | ~50ms | ~15ms |
| 1280×720 | 85 | ~150-250KB | ~150ms | ~45ms |
| 1920×1080 | 85 | ~300-500KB | ~350ms | ~100ms |
| Resolution | Input Size | Time (scalar) | Time (SIMD) |
|---|---|---|---|
| 640×480 | ~50KB | ~30ms | ~12ms |
| 1280×720 | ~150KB | ~90ms | ~35ms |
| 1920×1080 | ~300KB | ~200ms | ~75ms |
Note: Times are approximate and vary by platform