jpegdec

repository·master·Indexed 20 days ago

https://github.com/bitbank2/jpegdec

A highly optimized, portable JPEG decoder for resource-constrained microcontrollers (MCUs) requiring as little as 20K RAM. It supports SIMD optimizations for ESP32-S3, Arm NEON, and X86 SSE2, fast downscaling, and Floyd-Steinberg dithering for e-paper displays. The library supports data sources from FLASH/RAM, SD cards, or custom media and provides a callback-based design to decouple decoding logic from hardware-specific display and I/O drivers.

Tokens
2K
Snippets
8
Records
12
Agent score
70%

What's inside jpegdec

  1. Overview of JPEGDEC features

    master

    JPEGDEC is a highly optimized, portable JPEG decoder designed for microcontrollers (MCUs) with as little as 20K of RAM (e.g., Cortex-M0+). It is optimized for speed and supports various scaling and dithering options.

    Key Features:

    • Low Memory Footprint: Runs on MCUs with $\ge$ 20K RAM.
    • Speed Optimizations: Includes SIMD optimizations (ESP32-S3, Arm NEON, X86 SSE2) and fast downscaling (1/2, 1/4, 1/8).
    • Flexible Data Sources: Supports JPEG data from memory (FLASH/RAM), SDCard, or custom media.
    • Advanced Output Options:
      • Built-in fast cropping.
      • Embedded Exif thumbnail detection and decoding.
      • Floyd-Steinberg dithering to 1, 2, or 4-bpp grayscale (ideal for e-paper displays).
    • Portability: The core C code has no external dependencies.
  2. Use Floyd-Steinberg dithering for e-paper displays

    master

    JPEGDEC includes an option to apply the Floyd-Steinberg (FS) error diffusion algorithm during decoding. This is particularly useful for generating high-quality grayscale output for high-resolution e-paper displays.

    Supported output modes:

    • 1-bpp grayscale
    • 2-bpp grayscale (4 gray levels)
    • 4-bpp grayscale
  3. How callback functions work in JPEGDEC

    master

    JPEGDEC uses a callback-based design to decouple the decoding logic from your specific hardware (display and file I/O). This allows the library to be portable across different platforms.

    Depending on your data source, you must implement different sets of functions:

    1. Decoding from Memory (RAM or FLASH)

    If your JPEG data is already in memory, you only need to provide one function:

    • A function to draw (or store) each block of image pixels emitted by the library.

    Note for ESP32/ESP8266 (Harvard Architecture): Use openRAM or openFLASH specifically to handle memory correctly. For ARM Cortex-M, these are interchangeable.

    2. Decoding from an external source (e.g., SD Card)

    If you are reading from a file system, you must provide five functions:

    • open
    • close
    • read
    • seek
    • A function to draw (or store) each block of image pixels.
  4. How to provide JPEG data from memory

    master

    To use JPEG images stored in your program's FLASH or RAM, you should convert the binary JPEG files into C arrays. You can use tools like image_to_c or xxd to perform this conversion.

    Important: When defining the array in your code, use const or PROGMEM modifiers to ensure the data is stored in FLASH memory rather than consuming precious RAM.

    // Example of how JPEG data should be declared in your code
    const uint8_t my_jpeg_data[] PROGMEM = {
      0xAB, 0xCD, ...
    };
  5. Use JPEGDEC with M5Stack devices

    master

    To use JPEGDEC on M5Stack hardware, you must use the M5.Lcd.drawBitmap method within your callback function to render the decoded pixels to the screen. The callback should receive the pixel data from the pDraw structure and pass it to the display driver using the image's coordinates and dimensions.

    When decoding, it is recommended to check if the JPEG contains a thumbnail using jpeg.hasThumb(). If a thumbnail exists, use the JPEG_EXIF_THUMBNAIL flag in the decode call to ensure the correct routine is used.

    // Rendering pixels in the callback
    M5.Lcd.drawBitmap((int16_t)pDraw->x, (int16_t)pDraw->y, (int16_t)pDraw->iWidth, (int16_t)pDraw->iHeight, pDraw->pPixels);
    
    // Decoding logic with thumbnail check
    if (jpeg.hasThumb()) {
        jpeg.decode(iCenterX[i], iCenterY[i], JPEG_EXIF_THUMBNAIL | iOption[i]);
    } else {
        jpeg.decode(iCenterX[i], iCenterY[i], iOption[i]);
    }
  6. Center image coordinates using JPEGDISPLAY_CENTER

    master

    When calling loadJPEG methods, you can use the constant JPEGDISPLAY_CENTER (defined as -2) for the x or y parameters to automatically center the image at that coordinate.

    // Center the image on the X axis at Y=0
    display.loadJPEG(&lcd, JPEGDISPLAY_CENTER, 0, "/image.jpg", 0);
  7. Display an image from FLASH memory

    master

    To display a specific image stored in FLASH memory, use the jpeg.openFLASH method. You must provide the pointer to the image data, the size of the data, and the callback function (e.g., JPEGDraw) used for rendering.

    In the example implementation, you select which image to display by uncommenting the corresponding jpeg.openFLASH line and the variable name representing that image.

    // Example of opening a specific image from FLASH
    if (jpeg.openFLASH((uint8_t *)thumb_test, sizeof(thumb_test), JPEGDraw)) {
        // ...
    }
    
    // To switch images, uncomment one and comment others:
    // if (jpeg.openFLASH((uint8_t *)ncc1701, sizeof(ncc1701), JPEGDraw))
    // if (jpeg.openFLASH((uint8_t *)batman, sizeof(batman), JPEGDraw))
  8. Retrieve the last error from JPEGDisplay

    master

    If a loadJPEG or getJPEGInfo operation fails, call getLastError() to retrieve the error code associated with the last operation.

    if (display.loadJPEG(&lcd, 0, 0, "/image.jpg", 0) != 0) {
        int err = display.getLastError();
        // Handle error
    }
  9. Get JPEG dimensions and bit depth with getJPEGInfo

    master

    Use the getJPEGInfo family of methods to retrieve the width, height, and bits per pixel (bpp) of a JPEG image without fully decoding it for display. This is useful for calculating layout or verifying image compatibility.

    JPEGDisplay display;
    int w, h, bpp;
    
    // From a file
    display.getJPEGInfo(&w, &h, &bpp, "/image.jpg");
    
    // From LittleFS
    display.getJPEGInfo_LFS(&w, &h, &bpp, "/image.jpg");
    
    // From a memory buffer
    display.getJPEGInfo(&w, &h, &bpp, pData, iDataSize);
  10. Use JPEGDisplay to load and display JPEG images

    master

    The JPEGDisplay class provides helper methods to decode JPEG files and render them directly onto an LCD screen using a BB_SPI_LCD instance. You can load images from memory buffers, standard file systems (like SD cards), or LittleFS.

    // Example: Loading a JPEG from an SD card file to an LCD
    JPEGDisplay display;
    BB_SPI_LCD lcd;
    
    // Load from SD card
    display.loadJPEG(&lcd, 0, 0, "/image.jpg", 0);
    
    // Load from LittleFS
    display.loadJPEG_LFS(&lcd, 0, 0, "/image.jpg", 0);
    
    // Load from a memory buffer
    // display.loadJPEG(&lcd, x, y, pData, iDataSize, iOptions);