# MozJPEG MozJPEG is Mozilla's JPEG encoder project that improves JPEG compression efficiency, achieving smaller file sizes while maintaining compatibility with the JPEG standard and all web browsers. Built on libjpeg-turbo, it provides enhanced encoding features including progressive encoding with jpegrescan optimization, trellis quantization, and new quantization table presets designed for high-resolution displays. The library offers two main APIs: the traditional libjpeg API for full control over compression/decompression parameters, and the TurboJPEG API which provides a simpler interface for common operations. MozJPEG supports 8-bit, 12-bit, and 16-bit data precision, various chrominance subsampling options (4:4:4, 4:2:2, 4:2:0, grayscale), and multiple pixel formats including RGB, BGR, RGBA, BGRA, and CMYK. ## Command-Line Tools ### cjpeg - Compress Images to JPEG The cjpeg utility compresses image files (PPM, PGM, BMP) into JPEG format with configurable quality and encoding options. ```bash # Basic compression with default quality (75) cjpeg input.ppm > output.jpg # High quality compression (95%) cjpeg -quality 95 input.bmp > output.jpg # Progressive JPEG with optimized Huffman tables cjpeg -progressive -optimize input.ppm > output.jpg # Grayscale conversion cjpeg -grayscale input.ppm > output.jpg # Control chrominance subsampling (4:4:4 for best quality) cjpeg -sample 1x1 -quality 90 input.ppm > output.jpg # 12-bit data precision (requires 12-bit PPM input) cjpeg -precision 12 input.ppm > output.jpg # Lossless JPEG compression cjpeg -lossless 1 -precision 16 input.ppm > output.jpg # Embed ICC color profile cjpeg -icc profile.icc input.ppm > output.jpg # Add restart markers for fault tolerance cjpeg -restart 1 input.ppm > output.jpg ``` ### djpeg - Decompress JPEG Images The djpeg utility decompresses JPEG files to various image formats with optional scaling and cropping. ```bash # Basic decompression to PPM djpeg input.jpg > output.ppm # Output to BMP format djpeg -bmp input.jpg > output.bmp # Scale output to 1/2 size djpeg -scale 1/2 input.jpg > output.ppm # Scale output to 1/8 size (fastest) djpeg -scale 1/8 input.jpg > output.ppm # Force grayscale output djpeg -grayscale input.jpg > output.pgm # Crop region during decompression djpeg -crop 100x100+50+50 input.jpg > output.ppm # Skip rows during decompression djpeg -skip 100,200 input.jpg > output.ppm # Extract ICC profile djpeg -icc profile.icc input.jpg > output.ppm # Limit scan count for security djpeg -maxscans 100 input.jpg > output.ppm ``` ### jpegtran - Lossless JPEG Transformations The jpegtran utility performs lossless transformations on JPEG images without recompression quality loss. ```bash # Rotate 90 degrees clockwise jpegtran -rotate 90 input.jpg > output.jpg # Rotate 180 degrees jpegtran -rotate 180 input.jpg > output.jpg # Flip horizontally jpegtran -flip horizontal input.jpg > output.jpg # Flip vertically jpegtran -flip vertical input.jpg > output.jpg # Transpose image jpegtran -transpose input.jpg > output.jpg # Convert to progressive JPEG jpegtran -progressive input.jpg > output.jpg # Optimize Huffman tables jpegtran -optimize input.jpg > output.jpg # Convert to arithmetic coding jpegtran -arithmetic input.jpg > output.jpg # Lossless crop jpegtran -crop 200x150+10+10 input.jpg > output.jpg # Convert to grayscale (discard chroma) jpegtran -grayscale input.jpg > output.jpg # Perfect transform only (fail if not possible) jpegtran -rotate 90 -perfect input.jpg > output.jpg # Trim edge blocks for clean rotation jpegtran -rotate 90 -trim input.jpg > output.jpg # Copy no metadata jpegtran -copy none input.jpg > output.jpg # Copy all metadata including EXIF jpegtran -copy all input.jpg > output.jpg # Drop another image into position jpegtran -drop +100+50 overlay.jpg input.jpg > output.jpg # Wipe (gray out) a region jpegtran -wipe 50x50+100+100 input.jpg > output.jpg ``` ## TurboJPEG API ### tj3Init - Initialize TurboJPEG Instance Creates a new TurboJPEG instance for compression, decompression, or lossless transformation operations. ```c #include // Initialize for compression tjhandle compressor = tj3Init(TJINIT_COMPRESS); if (compressor == NULL) { printf("Error: %s\n", tj3GetErrorStr(NULL)); return -1; } // Initialize for decompression tjhandle decompressor = tj3Init(TJINIT_DECOMPRESS); if (decompressor == NULL) { printf("Error: %s\n", tj3GetErrorStr(NULL)); return -1; } // Initialize for lossless transformation tjhandle transformer = tj3Init(TJINIT_TRANSFORM); if (transformer == NULL) { printf("Error: %s\n", tj3GetErrorStr(NULL)); return -1; } // Clean up when done tj3Destroy(compressor); tj3Destroy(decompressor); tj3Destroy(transformer); ``` ### tj3Set / tj3Get - Configure Parameters Sets or retrieves compression/decompression parameters for a TurboJPEG instance. ```c #include tjhandle handle = tj3Init(TJINIT_COMPRESS); // Set JPEG quality (1-100) tj3Set(handle, TJPARAM_QUALITY, 85); // Set chrominance subsampling tj3Set(handle, TJPARAM_SUBSAMP, TJSAMP_422); // 4:2:2 subsampling // Options: TJSAMP_444, TJSAMP_422, TJSAMP_420, TJSAMP_GRAY // Enable progressive JPEG tj3Set(handle, TJPARAM_PROGRESSIVE, 1); // Enable Huffman table optimization tj3Set(handle, TJPARAM_OPTIMIZE, 1); // Use fastest DCT algorithm tj3Set(handle, TJPARAM_FASTDCT, 1); // Enable arithmetic coding tj3Set(handle, TJPARAM_ARITHMETIC, 1); // Set row order (0 = top-down, 1 = bottom-up) tj3Set(handle, TJPARAM_BOTTOMUP, 0); // Enable lossless compression tj3Set(handle, TJPARAM_LOSSLESS, 1); tj3Set(handle, TJPARAM_LOSSLESSPSV, 1); // Predictor selection value (1-7) // Get current parameter values int quality = tj3Get(handle, TJPARAM_QUALITY); int subsamp = tj3Get(handle, TJPARAM_SUBSAMP); tj3Destroy(handle); ``` ### tj3Compress8 - Compress RGB to JPEG Compresses an 8-bit-per-sample packed-pixel image into a JPEG image. ```c #include #include int compress_image(unsigned char *rgbBuffer, int width, int height, unsigned char **jpegBuf, size_t *jpegSize) { tjhandle handle = tj3Init(TJINIT_COMPRESS); if (handle == NULL) return -1; // Configure compression parameters tj3Set(handle, TJPARAM_QUALITY, 90); tj3Set(handle, TJPARAM_SUBSAMP, TJSAMP_420); tj3Set(handle, TJPARAM_PROGRESSIVE, 1); // Let TurboJPEG allocate the output buffer *jpegBuf = NULL; *jpegSize = 0; // Compress the image // pitch = 0 means tightly packed rows (width * pixel_size) if (tj3Compress8(handle, rgbBuffer, width, 0, height, TJPF_RGB, jpegBuf, jpegSize) < 0) { printf("Compression error: %s\n", tj3GetErrorStr(handle)); tj3Destroy(handle); return -1; } printf("Compressed %dx%d image to %zu bytes\n", width, height, *jpegSize); tj3Destroy(handle); return 0; } // Usage example int main() { int width = 1920, height = 1080; unsigned char *rgb = malloc(width * height * 3); unsigned char *jpeg = NULL; size_t jpegSize = 0; // Fill rgb buffer with image data... compress_image(rgb, width, height, &jpeg, &jpegSize); // Write JPEG to file FILE *f = fopen("output.jpg", "wb"); fwrite(jpeg, 1, jpegSize, f); fclose(f); tj3Free(jpeg); free(rgb); return 0; } ``` ### tj3Decompress8 - Decompress JPEG to RGB Decompresses a JPEG image into an 8-bit-per-sample packed-pixel RGB image. ```c #include #include int decompress_image(unsigned char *jpegBuf, size_t jpegSize, unsigned char **rgbBuf, int *width, int *height) { tjhandle handle = tj3Init(TJINIT_DECOMPRESS); if (handle == NULL) return -1; // Read JPEG header to get image dimensions if (tj3DecompressHeader(handle, jpegBuf, jpegSize) < 0) { printf("Header error: %s\n", tj3GetErrorStr(handle)); tj3Destroy(handle); return -1; } // Get image properties *width = tj3Get(handle, TJPARAM_JPEGWIDTH); *height = tj3Get(handle, TJPARAM_JPEGHEIGHT); int subsamp = tj3Get(handle, TJPARAM_SUBSAMP); int colorspace = tj3Get(handle, TJPARAM_COLORSPACE); int precision = tj3Get(handle, TJPARAM_PRECISION); printf("JPEG: %dx%d, %d-bit, subsamp=%d, colorspace=%d\n", *width, *height, precision, subsamp, colorspace); // Allocate output buffer *rgbBuf = (unsigned char *)malloc(*width * *height * tjPixelSize[TJPF_RGB]); if (*rgbBuf == NULL) { tj3Destroy(handle); return -1; } // Decompress to RGB if (tj3Decompress8(handle, jpegBuf, jpegSize, *rgbBuf, 0, TJPF_RGB) < 0) { printf("Decompress error: %s\n", tj3GetErrorStr(handle)); free(*rgbBuf); tj3Destroy(handle); return -1; } tj3Destroy(handle); return 0; } // Usage with scaling int decompress_scaled(unsigned char *jpegBuf, size_t jpegSize, int scaleFactor) { tjhandle handle = tj3Init(TJINIT_DECOMPRESS); tj3DecompressHeader(handle, jpegBuf, jpegSize); int origWidth = tj3Get(handle, TJPARAM_JPEGWIDTH); int origHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT); // Get available scaling factors int numFactors; tjscalingfactor *factors = tj3GetScalingFactors(&numFactors); // Set scaling to 1/2 tjscalingfactor scale = {1, 2}; tj3SetScalingFactor(handle, scale); // Calculate scaled dimensions int scaledWidth = TJSCALED(origWidth, scale); int scaledHeight = TJSCALED(origHeight, scale); unsigned char *rgb = malloc(scaledWidth * scaledHeight * 3); tj3Decompress8(handle, jpegBuf, jpegSize, rgb, 0, TJPF_RGB); printf("Scaled from %dx%d to %dx%d\n", origWidth, origHeight, scaledWidth, scaledHeight); free(rgb); tj3Destroy(handle); return 0; } ``` ### tj3Transform - Lossless JPEG Transformation Performs lossless transformations (rotate, flip, crop) on JPEG images without recompression. ```c #include #include int transform_jpeg(unsigned char *srcBuf, size_t srcSize, unsigned char **dstBuf, size_t *dstSize, int operation) { tjhandle handle = tj3Init(TJINIT_TRANSFORM); if (handle == NULL) return -1; // Configure the transformation tjtransform xform; memset(&xform, 0, sizeof(xform)); xform.op = operation; // TJXOP_ROT90, TJXOP_ROT180, etc. xform.options = TJXOPT_TRIM; // Trim edge blocks if needed // Optionally add cropping // xform.r.x = 0; // xform.r.y = 0; // xform.r.w = 100; // xform.r.h = 100; // xform.options |= TJXOPT_CROP; // Let TurboJPEG allocate output buffer *dstBuf = NULL; *dstSize = 0; if (tj3Transform(handle, srcBuf, srcSize, 1, dstBuf, dstSize, &xform) < 0) { printf("Transform error: %s\n", tj3GetErrorStr(handle)); tj3Destroy(handle); return -1; } tj3Destroy(handle); return 0; } // Usage examples int main() { // Load source JPEG FILE *f = fopen("input.jpg", "rb"); fseek(f, 0, SEEK_END); size_t srcSize = ftell(f); fseek(f, 0, SEEK_SET); unsigned char *srcBuf = tj3Alloc(srcSize); fread(srcBuf, 1, srcSize, f); fclose(f); unsigned char *dstBuf; size_t dstSize; // Rotate 90 degrees clockwise transform_jpeg(srcBuf, srcSize, &dstBuf, &dstSize, TJXOP_ROT90); // Other operations: // TJXOP_NONE - No transformation // TJXOP_HFLIP - Horizontal flip // TJXOP_VFLIP - Vertical flip // TJXOP_TRANSPOSE - Transpose // TJXOP_TRANSVERSE - Transverse transpose // TJXOP_ROT90 - Rotate 90 degrees clockwise // TJXOP_ROT180 - Rotate 180 degrees // TJXOP_ROT270 - Rotate 270 degrees clockwise // Save result f = fopen("rotated.jpg", "wb"); fwrite(dstBuf, 1, dstSize, f); fclose(f); tj3Free(srcBuf); tj3Free(dstBuf); return 0; } ``` ### tj3EncodeYUV8 / tj3DecodeYUV8 - YUV Conversion Converts between packed-pixel RGB and planar YUV formats for video processing workflows. ```c #include // Encode RGB to planar YUV int rgb_to_yuv(unsigned char *rgb, int width, int height, unsigned char **yuv, int subsamp) { tjhandle handle = tj3Init(TJINIT_COMPRESS); tj3Set(handle, TJPARAM_SUBSAMP, subsamp); // Calculate YUV buffer size size_t yuvSize = tj3YUVBufSize(width, 1, height, subsamp); *yuv = (unsigned char *)malloc(yuvSize); // Encode RGB to YUV if (tj3EncodeYUV8(handle, rgb, width, 0, height, TJPF_RGB, *yuv, 1) < 0) { printf("Encode error: %s\n", tj3GetErrorStr(handle)); free(*yuv); tj3Destroy(handle); return -1; } tj3Destroy(handle); return 0; } // Decode planar YUV to RGB int yuv_to_rgb(unsigned char *yuv, int width, int height, unsigned char **rgb, int subsamp) { tjhandle handle = tj3Init(TJINIT_DECOMPRESS); tj3Set(handle, TJPARAM_SUBSAMP, subsamp); *rgb = (unsigned char *)malloc(width * height * 3); // Decode YUV to RGB if (tj3DecodeYUV8(handle, yuv, 1, *rgb, width, 0, height, TJPF_RGB) < 0) { printf("Decode error: %s\n", tj3GetErrorStr(handle)); free(*rgb); tj3Destroy(handle); return -1; } tj3Destroy(handle); return 0; } // Work with separate YUV planes int encode_yuv_planes(unsigned char *rgb, int width, int height, int subsamp) { tjhandle handle = tj3Init(TJINIT_COMPRESS); tj3Set(handle, TJPARAM_SUBSAMP, subsamp); // Calculate plane sizes int yPlaneSize = tj3YUVPlaneSize(0, width, 0, height, subsamp); int uPlaneSize = tj3YUVPlaneSize(1, width, 0, height, subsamp); int vPlaneSize = tj3YUVPlaneSize(2, width, 0, height, subsamp); unsigned char *yPlane = malloc(yPlaneSize); unsigned char *uPlane = malloc(uPlaneSize); unsigned char *vPlane = malloc(vPlaneSize); unsigned char *planes[3] = {yPlane, uPlane, vPlane}; // Encode to separate planes tj3EncodeYUVPlanes8(handle, rgb, width, 0, height, TJPF_RGB, planes, NULL); // Get plane dimensions int yWidth = tj3YUVPlaneWidth(0, width, subsamp); int yHeight = tj3YUVPlaneHeight(0, height, subsamp); int uvWidth = tj3YUVPlaneWidth(1, width, subsamp); int uvHeight = tj3YUVPlaneHeight(1, height, subsamp); printf("Y plane: %dx%d, UV planes: %dx%d\n", yWidth, yHeight, uvWidth, uvHeight); free(yPlane); free(uPlane); free(vPlane); tj3Destroy(handle); return 0; } ``` ### tj3LoadImage8 / tj3SaveImage8 - Image File I/O Load and save images in BMP and PPM/PGM formats for easy file handling. ```c #include // Load image from disk int load_and_compress(const char *inputFile, const char *outputFile) { tjhandle handle = tj3Init(TJINIT_COMPRESS); int width, height; int pixelFormat = TJPF_UNKNOWN; // Auto-detect format // Load BMP or PPM/PGM file unsigned char *imgBuf = tj3LoadImage8(handle, inputFile, &width, 1, &height, &pixelFormat); if (imgBuf == NULL) { printf("Load error: %s\n", tj3GetErrorStr(handle)); tj3Destroy(handle); return -1; } printf("Loaded %dx%d image, format=%d\n", width, height, pixelFormat); // Configure and compress tj3Set(handle, TJPARAM_QUALITY, 85); tj3Set(handle, TJPARAM_SUBSAMP, TJSAMP_420); unsigned char *jpegBuf = NULL; size_t jpegSize = 0; if (tj3Compress8(handle, imgBuf, width, 0, height, pixelFormat, &jpegBuf, &jpegSize) < 0) { printf("Compress error: %s\n", tj3GetErrorStr(handle)); tj3Free(imgBuf); tj3Destroy(handle); return -1; } // Write JPEG file FILE *f = fopen(outputFile, "wb"); fwrite(jpegBuf, 1, jpegSize, f); fclose(f); tj3Free(imgBuf); tj3Free(jpegBuf); tj3Destroy(handle); return 0; } // Decompress and save to disk int decompress_and_save(const char *inputFile, const char *outputFile) { tjhandle handle = tj3Init(TJINIT_DECOMPRESS); // Load JPEG file FILE *f = fopen(inputFile, "rb"); fseek(f, 0, SEEK_END); size_t jpegSize = ftell(f); fseek(f, 0, SEEK_SET); unsigned char *jpegBuf = tj3Alloc(jpegSize); fread(jpegBuf, 1, jpegSize, f); fclose(f); // Read header tj3DecompressHeader(handle, jpegBuf, jpegSize); int width = tj3Get(handle, TJPARAM_JPEGWIDTH); int height = tj3Get(handle, TJPARAM_JPEGHEIGHT); // Decompress unsigned char *imgBuf = malloc(width * height * tjPixelSize[TJPF_RGB]); tj3Decompress8(handle, jpegBuf, jpegSize, imgBuf, 0, TJPF_RGB); // Save to BMP or PPM (determined by extension) if (tj3SaveImage8(handle, outputFile, imgBuf, width, 0, height, TJPF_RGB) < 0) { printf("Save error: %s\n", tj3GetErrorStr(handle)); } free(imgBuf); tj3Free(jpegBuf); tj3Destroy(handle); return 0; } ``` ## libjpeg API ### jpeg_create_compress - Initialize Compression Creates and initializes a JPEG compression object for encoding images. ```c #include #include #include void compress_to_jpeg(unsigned char *rgb, int width, int height, const char *filename, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE *outfile; JSAMPROW row_pointer[1]; int row_stride; // Initialize error handler cinfo.err = jpeg_std_error(&jerr); // Create compression object jpeg_create_compress(&cinfo); // Open output file outfile = fopen(filename, "wb"); if (outfile == NULL) { fprintf(stderr, "Cannot open %s\n", filename); return; } jpeg_stdio_dest(&cinfo, outfile); // Set image parameters cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; // Set default parameters jpeg_set_defaults(&cinfo); // Set quality (0-100) jpeg_set_quality(&cinfo, quality, TRUE); // Enable progressive JPEG jpeg_simple_progression(&cinfo); // Use 4:4:4 subsampling for best quality cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; // Start compression jpeg_start_compress(&cinfo, TRUE); // Write scanlines row_stride = width * 3; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &rgb[cinfo.next_scanline * row_stride]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } // Finish compression jpeg_finish_compress(&cinfo); fclose(outfile); // Clean up jpeg_destroy_compress(&cinfo); } ``` ### jpeg_create_decompress - Initialize Decompression Creates and initializes a JPEG decompression object for decoding images with custom error handling. ```c #include #include #include #include // Custom error handler for graceful error recovery struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr *my_error_ptr; void my_error_exit(j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr)cinfo->err; (*cinfo->err->output_message)(cinfo); longjmp(myerr->setjmp_buffer, 1); } unsigned char *decompress_jpeg(const char *filename, int *width, int *height) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; JSAMPARRAY buffer; int row_stride; unsigned char *output = NULL; // Open file infile = fopen(filename, "rb"); if (infile == NULL) { fprintf(stderr, "Cannot open %s\n", filename); return NULL; } // Set up error handler cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; // Establish setjmp return point for error recovery if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(infile); if (output) free(output); return NULL; } // Create decompression object jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); // Read header jpeg_read_header(&cinfo, TRUE); // Configure output color space cinfo.out_color_space = JCS_RGB; // Optional: scale down for faster decoding // cinfo.scale_num = 1; // cinfo.scale_denom = 2; // Scale to 1/2 size // Start decompression jpeg_start_decompress(&cinfo); *width = cinfo.output_width; *height = cinfo.output_height; row_stride = cinfo.output_width * cinfo.output_components; // Allocate output buffer output = (unsigned char *)malloc(row_stride * cinfo.output_height); // Allocate scanline buffer buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); // Read scanlines while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(&output[(cinfo.output_scanline - 1) * row_stride], buffer[0], row_stride); } // Finish decompression jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return output; } ``` ### Memory-Based Compression/Decompression Compress and decompress JPEG images directly to/from memory buffers without file I/O. ```c #include #include #include // Compress to memory buffer int compress_to_memory(unsigned char *rgb, int width, int height, unsigned char **jpegBuf, unsigned long *jpegSize, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW row_pointer[1]; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); // Initialize memory destination *jpegBuf = NULL; *jpegSize = 0; jpeg_mem_dest(&cinfo, jpegBuf, jpegSize); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &rgb[cinfo.next_scanline * width * 3]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); return 0; } // Decompress from memory buffer unsigned char *decompress_from_memory(unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; unsigned char *output; JSAMPARRAY buffer; int row_stride; cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); // Initialize memory source jpeg_mem_src(&cinfo, jpegBuf, jpegSize); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); *width = cinfo.output_width; *height = cinfo.output_height; row_stride = cinfo.output_width * cinfo.output_components; output = malloc(row_stride * cinfo.output_height); buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(&output[(cinfo.output_scanline - 1) * row_stride], buffer[0], row_stride); } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return output; } ``` ## Building MozJPEG ### CMake Build (Recommended) Build MozJPEG from source using CMake for optimal platform compatibility. ```bash # Clone or download MozJPEG source # Out-of-tree build (recommended) mkdir build && cd build # Basic build cmake -G"Unix Makefiles" .. make # Build with SIMD optimizations cmake -G"Unix Makefiles" -DWITH_SIMD=1 .. make # Build shared library cmake -G"Unix Makefiles" -DENABLE_SHARED=1 .. make # Build static library only cmake -G"Unix Makefiles" -DENABLE_STATIC=1 -DENABLE_SHARED=0 .. make # Build with 12-bit and 16-bit support cmake -G"Unix Makefiles" -DWITH_12BIT=1 .. make # Install sudo make install # macOS with Homebrew NASM cmake -G"Unix Makefiles" -DCMAKE_ASM_NASM_COMPILER=/usr/local/bin/nasm .. make # Windows with Visual Studio cmake -G"Visual Studio 17 2022" -A x64 .. cmake --build . --config Release # Cross-compile for ARM cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain-arm.cmake .. make ``` ## Summary MozJPEG provides a comprehensive JPEG encoding and decoding solution optimized for web image delivery, offering 5-10% better compression than standard JPEG encoders while maintaining full compatibility. The TurboJPEG API simplifies common operations like compression, decompression, and lossless transformations with straightforward function calls, while the traditional libjpeg API provides fine-grained control for advanced use cases including custom error handling, memory-based I/O, and progressive encoding. Integration patterns include using the command-line tools (cjpeg, djpeg, jpegtran) for batch processing and shell scripts, the TurboJPEG API for application development requiring simple high-performance JPEG handling, and the libjpeg API for applications needing precise control over encoding parameters. MozJPEG is particularly well-suited for web servers optimizing image delivery, content management systems, image processing pipelines, and any application where reducing JPEG file sizes while maintaining visual quality is important.