Image Decoder

Source: src/image_decoder.c · 215 lines · 11 functions.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

hasExtension() · source line 20
static bool hasExtension(const char *name, const char *extension)
  1. Executeconst size_t name_length = strlen(name);
  2. Executeconst size_t extension_length = strlen(extension);
  3. Decisionname_length < extension_length
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  4. Executename += name_length - extension_length;
  5. FOR loopsize_t index = 0; index < extension_length; ++index

    Repeat body while the loop condition permits; then continue below.

      1. Decisiontolower((unsigned char)name[index]) != extension[index]
        True
        1. Exit this functionreturn false;
        False

        Continue after this decision.

  6. Exit this functionreturn true;
Read original function source
static bool hasExtension(const char *name, const char *extension)
{
    const size_t name_length = strlen(name);
    const size_t extension_length = strlen(extension);
    if (name_length < extension_length) return false;
    name += name_length - extension_length;
    for (size_t index = 0; index < extension_length; ++index) {
        if (tolower((unsigned char)name[index]) != extension[index]) return false;
    }
    return true;
}
imageIsSupportedName() · source line 32
bool imageIsSupportedName(const char *name)

Calls into this documentation:

  1. Exit this functionreturn name != NULL && (hasExtension(name, ".jpg") || hasExtension(name, ".jpeg") || hasExtension(name, ".png"));
Read original function source
bool imageIsSupportedName(const char *name)
{
    return name != NULL && (hasExtension(name, ".jpg") || hasExtension(name, ".jpeg") || hasExtension(name, ".png"));
}
jpegInput() · source line 37
static UINT jpegInput(JDEC *decoder, BYTE *buffer, UINT bytes)
  1. ExecuteFILE *file = (FILE *)decoder->device;
  2. Decisionbuffer != NULL
    True
    1. Exit this functionreturn (UINT)fread(buffer, 1, bytes, file);
    False

    Continue after this decision.

  3. Exit this functionreturn fseek(file, (long)bytes, SEEK_CUR) == 0 ? bytes : 0;
Read original function source
static UINT jpegInput(JDEC *decoder, BYTE *buffer, UINT bytes)
{
    FILE *file = (FILE *)decoder->device;
    if (buffer != NULL) return (UINT)fread(buffer, 1, bytes, file);
    return fseek(file, (long)bytes, SEEK_CUR) == 0 ? bytes : 0;
}
jpegOutput() · source line 44
static UINT jpegOutput(JDEC *decoder, void *bitmap, JRECT *rect)
  1. Execute(void)decoder;
  2. Decisionjpeg_target == NULL || rect->right + jpeg_offset_x >= LCD_WIDTH || rect->bottom + jpeg_offset_y >= LCD_HEIGHT
    True
    1. Exit this functionreturn 0;
    False

    Continue after this decision.

  3. Executeconst BYTE *source = bitmap;
  4. Executeconst unsigned width = rect->right - rect->left + 1U;
  5. Executeconst unsigned height = rect->bottom - rect->top + 1U;
  6. FOR loopunsigned row = 0; row < height; ++row

    Repeat body while the loop condition permits; then continue below.

    1. FOR loopunsigned column = 0; column < width; ++column

      Repeat body while the loop condition permits; then continue below.

        1. Executeconst unsigned pixel = row * width + column;
        2. Executejpeg_target[(rect->top + row + jpeg_offset_y) * LCD_WIDTH + rect->left + column + jpeg_offset_x] = RGB565(source[pixel * 3U], source[pixel * 3U + 1U], source[pixel * 3U + 2U]);
  7. Exit this functionreturn 1;
Read original function source
static UINT jpegOutput(JDEC *decoder, void *bitmap, JRECT *rect)
{
    (void)decoder;
    if (jpeg_target == NULL || rect->right + jpeg_offset_x >= LCD_WIDTH || rect->bottom + jpeg_offset_y >= LCD_HEIGHT) return 0;
    const BYTE *source = bitmap;
    const unsigned width = rect->right - rect->left + 1U;
    const unsigned height = rect->bottom - rect->top + 1U;
    for (unsigned row = 0; row < height; ++row) for (unsigned column = 0; column < width; ++column) {
        const unsigned pixel = row * width + column;
        jpeg_target[(rect->top + row + jpeg_offset_y) * LCD_WIDTH + rect->left + column + jpeg_offset_x] =
            RGB565(source[pixel * 3U], source[pixel * 3U + 1U], source[pixel * 3U + 2U]);
    }
    return 1;
}
decodeJpegFile() · source line 59
static bool decodeJpegFile(const char *path, uint16_t *frame, unsigned offset_x, unsigned offset_y, bool clear_target,
                           unsigned requested_scale)

Calls into this documentation:

  1. ExecuteFILE *file = fopen(path, "rb");
  2. Decisionfile == NULL
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  3. ExecuteJDEC decoder;
  4. ExecuteJRESULT result = jd_prepare(&decoder, jpegInput, jpeg_work, sizeof(jpeg_work), file);
  5. Decisionresult != JDR_OK
    True
      1. Executefclose(file);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  6. Executeunsigned scale = requested_scale;
  7. Decisionclear_target
    True
      1. Executescale = 0;
      2. WHILE loopscale < 3U && (((decoder.width + ((1U << scale) - 1U)) >> scale) > LCD_WIDTH || ((decoder.height + ((1U << scale) - 1U)) >> scale) > LCD_HEIGHT)

        Repeat body while the loop condition permits; then continue below.

        1. Execute++scale;
    False

    Continue after this decision.

  8. Decisionscale > 3U
    True
      1. Executefclose(file);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  9. Executeconst unsigned width = (decoder.width + ((1U << scale) - 1U)) >> scale;
  10. Executeconst unsigned height = (decoder.height + ((1U << scale) - 1U)) >> scale;
  11. Decisionwidth > LCD_WIDTH || height > LCD_HEIGHT || offset_x > LCD_WIDTH - width || offset_y > LCD_HEIGHT - height
    True
      1. Executefclose(file);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  12. Decisionclear_target
    True
    1. Executeclear(frame, COLOR_BLACK);
    False

    Continue after this decision.

  13. Executejpeg_offset_x = clear_target ? (LCD_WIDTH - width) / 2U : offset_x;
  14. Executejpeg_offset_y = clear_target ? (LCD_HEIGHT - height) / 2U : offset_y;
  15. Executejpeg_target = frame;
  16. Executeresult = jd_decomp(&decoder, jpegOutput, scale);
  17. Executejpeg_target = NULL;
  18. Executefclose(file);
  19. Exit this functionreturn result == JDR_OK;
Read original function source
static bool decodeJpegFile(const char *path, uint16_t *frame, unsigned offset_x, unsigned offset_y, bool clear_target,
                           unsigned requested_scale)
{
    FILE *file = fopen(path, "rb");
    if (file == NULL) return false;
    JDEC decoder;
    JRESULT result = jd_prepare(&decoder, jpegInput, jpeg_work, sizeof(jpeg_work), file);
    if (result != JDR_OK) {
        fclose(file);
        return false;
    }
    unsigned scale = requested_scale;
    if (clear_target) {
        scale = 0;
        while (scale < 3U && (((decoder.width + ((1U << scale) - 1U)) >> scale) > LCD_WIDTH ||
                              ((decoder.height + ((1U << scale) - 1U)) >> scale) > LCD_HEIGHT)) ++scale;
    }
    if (scale > 3U) { fclose(file); return false; }
    const unsigned width = (decoder.width + ((1U << scale) - 1U)) >> scale;
    const unsigned height = (decoder.height + ((1U << scale) - 1U)) >> scale;
    if (width > LCD_WIDTH || height > LCD_HEIGHT || offset_x > LCD_WIDTH - width || offset_y > LCD_HEIGHT - height) {
        fclose(file);
        return false;
    }
    if (clear_target) clear(frame, COLOR_BLACK);
    jpeg_offset_x = clear_target ? (LCD_WIDTH - width) / 2U : offset_x;
    jpeg_offset_y = clear_target ? (LCD_HEIGHT - height) / 2U : offset_y;
    jpeg_target = frame;
    result = jd_decomp(&decoder, jpegOutput, scale);
    jpeg_target = NULL;
    fclose(file);
    return result == JDR_OK;
}
readBigEndian() · source line 93
static uint32_t readBigEndian(const uint8_t *bytes)
  1. Exit this functionreturn ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) | ((uint32_t)bytes[2] << 8) | bytes[3];
Read original function source
static uint32_t readBigEndian(const uint8_t *bytes)
{
    return ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) | ((uint32_t)bytes[2] << 8) | bytes[3];
}
paeth() · source line 98
static uint8_t paeth(uint8_t left, uint8_t above, uint8_t upper_left)
  1. Executeconst int prediction = (int)left + above - upper_left;
  2. Executeconst int left_distance = prediction > left ? prediction - left : left - prediction;
  3. Executeconst int above_distance = prediction > above ? prediction - above : above - prediction;
  4. Executeconst int upper_left_distance = prediction > upper_left ? prediction - upper_left : upper_left - prediction;
  5. Decisionleft_distance <= above_distance && left_distance <= upper_left_distance
    True
    1. Exit this functionreturn left;
    False

    Continue after this decision.

  6. Exit this functionreturn above_distance <= upper_left_distance ? above : upper_left;
Read original function source
static uint8_t paeth(uint8_t left, uint8_t above, uint8_t upper_left)
{
    const int prediction = (int)left + above - upper_left;
    const int left_distance = prediction > left ? prediction - left : left - prediction;
    const int above_distance = prediction > above ? prediction - above : above - prediction;
    const int upper_left_distance = prediction > upper_left ? prediction - upper_left : upper_left - prediction;
    if (left_distance <= above_distance && left_distance <= upper_left_distance) return left;
    return above_distance <= upper_left_distance ? above : upper_left;
}
decodePngFile() · source line 108
static bool decodePngFile(const char *path, uint16_t *frame)

Calls into this documentation:

  1. Executestatic const uint8_t png_signature[PNG_SIGNATURE_BYTES] = { 137, 80, 78, 71, 13, 10, 26, 10 };
  2. ExecuteFILE *file = fopen(path, "rb");
  3. Executeuint8_t signature[PNG_SIGNATURE_BYTES], header[8], ihdr[PNG_IHDR_BYTES];
  4. Executeuint8_t *compressed = NULL;
  5. Executeuint8_t *pixels = NULL;
  6. Executetinfl_decompressor *decompressor = NULL;
  7. Executesize_t compressed_bytes = 0;
  8. Executebool result = false;
  9. Executeunsigned channels = 0;
  10. Decisionfile == NULL || fread(signature, 1, sizeof(signature), file) != sizeof(signature) || memcmp(signature, png_signature, sizeof(signature)) != 0
    True
    1. Jump to labelgoto done;
    False

    Continue after this decision.

  11. FOR loop;;

    Repeat body while the loop condition permits; then continue below.

      1. Decisionfread(header, 1, sizeof(header), file) != sizeof(header)
        True
        1. Jump to labelgoto done;
        False

        Continue after this decision.

      2. Executeconst uint32_t length = readBigEndian(header);
      3. Decisionlength > PNG_MAX_COMPRESSED_BYTES
        True
        1. Jump to labelgoto done;
        False

        Continue after this decision.

      4. Decisionmemcmp(header + 4, "IHDR", 4) == 0
        True
          1. Decisionlength != PNG_IHDR_BYTES || fread(ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr)
            True
            1. Jump to labelgoto done;
            False

            Continue after this decision.

          2. DecisionreadBigEndian(ihdr) != LCD_WIDTH || readBigEndian(ihdr + 4) != LCD_HEIGHT || ihdr[8] != 8U || ihdr[10] != 0U || ihdr[11] != 0U || ihdr[12] != 0U
            True
            1. Jump to labelgoto done;
            False

            Continue after this decision.

          3. Decisionihdr[9] == 2U
            True
            1. Executechannels = 3U;
            False
            1. Decisionihdr[9] == 6U
              True
              1. Executechannels = 4U;
              False
              1. Jump to labelgoto done;
        False
        1. Decisionmemcmp(header + 4, "IDAT", 4) == 0
          True
            1. Decisionlength > PNG_MAX_COMPRESSED_BYTES - compressed_bytes
              True
              1. Jump to labelgoto done;
              False

              Continue after this decision.

            2. Executeuint8_t *expanded = heap_caps_realloc(compressed, compressed_bytes + length, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
            3. Decisionexpanded == NULL
              True
              1. Jump to labelgoto done;
              False

              Continue after this decision.

            4. Executecompressed = expanded;
            5. Decisionfread(compressed + compressed_bytes, 1, length, file) != length
              True
              1. Jump to labelgoto done;
              False

              Continue after this decision.

            6. Executecompressed_bytes += length;
          False
          1. Decisionfseek(file, (long)length, SEEK_CUR) != 0
            True
            1. Jump to labelgoto done;
            False

            Continue after this decision.

      5. Decisionfseek(file, 4L, SEEK_CUR) != 0
        True
        1. Jump to labelgoto done;
        False

        Continue after this decision.

      6. Decisionmemcmp(header + 4, "IEND", 4) == 0
        True
        1. Exit enclosing loop or switchbreak;
        False

        Continue after this decision.

  12. Decisionchannels == 0U || compressed_bytes == 0U
    True
    1. Jump to labelgoto done;
    False

    Continue after this decision.

  13. Executeconst size_t row_bytes = LCD_WIDTH * channels;
  14. Executeconst size_t decoded_bytes = LCD_HEIGHT * (row_bytes + 1U);
  15. Executepixels = heap_caps_malloc(decoded_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  16. Executedecompressor = calloc(1, sizeof(*decompressor));
  17. Decisionpixels == NULL || decompressor == NULL
    True
    1. Jump to labelgoto done;
    False

    Continue after this decision.

  18. Executetinfl_init(decompressor);
  19. Executesize_t input_position = 0, output_position = 0;
  20. FOR loop;;

    Repeat body while the loop condition permits; then continue below.

      1. Executesize_t input_bytes = compressed_bytes - input_position;
      2. Executesize_t output_bytes = decoded_bytes - output_position;
      3. Executeconst tinfl_status status = tinfl_decompress(decompressor, compressed + input_position, &input_bytes, pixels, pixels + output_position, &output_bytes, TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
      4. Executeinput_position += input_bytes;
      5. Executeoutput_position += output_bytes;
      6. Decisionstatus == TINFL_STATUS_DONE
        True
        1. Exit enclosing loop or switchbreak;
        False

        Continue after this decision.

      7. Decisionstatus < TINFL_STATUS_DONE || (input_bytes == 0U && output_bytes == 0U)
        True
        1. Jump to labelgoto done;
        False

        Continue after this decision.

  21. Decisionoutput_position != decoded_bytes
    True
    1. Jump to labelgoto done;
    False

    Continue after this decision.

  22. FOR loopunsigned row = 0; row < LCD_HEIGHT; ++row

    Repeat body while the loop condition permits; then continue below.

      1. Executeuint8_t *current = pixels + row * (row_bytes + 1U) + 1U;
      2. Executeconst uint8_t *previous = row == 0U ? NULL : current - (row_bytes + 1U);
      3. Executeconst uint8_t filter = current[-1];
      4. Decisionfilter > 4U
        True
        1. Jump to labelgoto done;
        False

        Continue after this decision.

      5. FOR loopsize_t column = 0; column < row_bytes; ++column

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst uint8_t left = column < channels ? 0U : current[column - channels];
          2. Executeconst uint8_t above = previous == NULL ? 0U : previous[column];
          3. Executeconst uint8_t upper_left = previous == NULL || column < channels ? 0U : previous[column - channels];
          4. Decisionfilter == 1U
            True
            1. Executecurrent[column] = (uint8_t)(current[column] + left);
            False
            1. Decisionfilter == 2U
              True
              1. Executecurrent[column] = (uint8_t)(current[column] + above);
              False
              1. Decisionfilter == 3U
                True
                1. Executecurrent[column] = (uint8_t)(current[column] + ((left + above) >> 1));
                False
                1. Decisionfilter == 4U
                  True
                  1. Executecurrent[column] = (uint8_t)(current[column] + paeth(left, above, upper_left));
                  False

                  Continue after this decision.

  23. FOR loopunsigned y = 0; y < LCD_HEIGHT; ++y

    Repeat body while the loop condition permits; then continue below.

    1. FOR loopunsigned x = 0; x < LCD_WIDTH; ++x

      Repeat body while the loop condition permits; then continue below.

        1. Executeconst uint8_t *pixel = pixels + y * (row_bytes + 1U) + 1U + x * channels;
        2. Executeconst uint8_t alpha = channels == 4U ? pixel[3] : 255U;
        3. Executeframe[y * LCD_WIDTH + x] = RGB565((uint8_t)((pixel[0] * alpha) / 255U), (uint8_t)((pixel[1] * alpha) / 255U), (uint8_t)((pixel[2] * alpha) / 255U));
  24. Executeresult = true;
  25. Executedone: free(pixels);
  26. Executefree(compressed);
  27. Executefree(decompressor);
  28. Decisionfile != NULL
    True
    1. Executefclose(file);
    False

    Continue after this decision.

  29. Exit this functionreturn result;
Read original function source
static bool decodePngFile(const char *path, uint16_t *frame)
{
    static const uint8_t png_signature[PNG_SIGNATURE_BYTES] = { 137, 80, 78, 71, 13, 10, 26, 10 };
    FILE *file = fopen(path, "rb");
    uint8_t signature[PNG_SIGNATURE_BYTES], header[8], ihdr[PNG_IHDR_BYTES];
    uint8_t *compressed = NULL;
    uint8_t *pixels = NULL;
    tinfl_decompressor *decompressor = NULL;
    size_t compressed_bytes = 0;
    bool result = false;
    unsigned channels = 0;

    if (file == NULL || fread(signature, 1, sizeof(signature), file) != sizeof(signature) ||
        memcmp(signature, png_signature, sizeof(signature)) != 0) goto done;
    for (;;) {
        if (fread(header, 1, sizeof(header), file) != sizeof(header)) goto done;
        const uint32_t length = readBigEndian(header);
        if (length > PNG_MAX_COMPRESSED_BYTES) goto done;
        if (memcmp(header + 4, "IHDR", 4) == 0) {
            if (length != PNG_IHDR_BYTES || fread(ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr)) goto done;
            if (readBigEndian(ihdr) != LCD_WIDTH || readBigEndian(ihdr + 4) != LCD_HEIGHT ||
                ihdr[8] != 8U || ihdr[10] != 0U || ihdr[11] != 0U || ihdr[12] != 0U) goto done;
            if (ihdr[9] == 2U) channels = 3U;
            else if (ihdr[9] == 6U) channels = 4U;
            else goto done;
        } else if (memcmp(header + 4, "IDAT", 4) == 0) {
            if (length > PNG_MAX_COMPRESSED_BYTES - compressed_bytes) goto done;
            uint8_t *expanded = heap_caps_realloc(compressed, compressed_bytes + length,
                                                  MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
            if (expanded == NULL) goto done;
            compressed = expanded;
            if (fread(compressed + compressed_bytes, 1, length, file) != length) goto done;
            compressed_bytes += length;
        } else if (fseek(file, (long)length, SEEK_CUR) != 0) goto done;
        if (fseek(file, 4L, SEEK_CUR) != 0) goto done;
        if (memcmp(header + 4, "IEND", 4) == 0) break;
    }
    if (channels == 0U || compressed_bytes == 0U) goto done;
    const size_t row_bytes = LCD_WIDTH * channels;
    const size_t decoded_bytes = LCD_HEIGHT * (row_bytes + 1U);
    pixels = heap_caps_malloc(decoded_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    decompressor = calloc(1, sizeof(*decompressor));
    if (pixels == NULL || decompressor == NULL) goto done;
    tinfl_init(decompressor);
    size_t input_position = 0, output_position = 0;
    for (;;) {
        size_t input_bytes = compressed_bytes - input_position;
        size_t output_bytes = decoded_bytes - output_position;
        const tinfl_status status = tinfl_decompress(decompressor, compressed + input_position, &input_bytes,
                                                     pixels, pixels + output_position, &output_bytes,
                                                     TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
        input_position += input_bytes;
        output_position += output_bytes;
        if (status == TINFL_STATUS_DONE) break;
        if (status < TINFL_STATUS_DONE || (input_bytes == 0U && output_bytes == 0U)) goto done;
    }
    if (output_position != decoded_bytes) goto done;
    for (unsigned row = 0; row < LCD_HEIGHT; ++row) {
        uint8_t *current = pixels + row * (row_bytes + 1U) + 1U;
        const uint8_t *previous = row == 0U ? NULL : current - (row_bytes + 1U);
        const uint8_t filter = current[-1];
        if (filter > 4U) goto done;
        for (size_t column = 0; column < row_bytes; ++column) {
            const uint8_t left = column < channels ? 0U : current[column - channels];
            const uint8_t above = previous == NULL ? 0U : previous[column];
            const uint8_t upper_left = previous == NULL || column < channels ? 0U : previous[column - channels];
            if (filter == 1U) current[column] = (uint8_t)(current[column] + left);
            else if (filter == 2U) current[column] = (uint8_t)(current[column] + above);
            else if (filter == 3U) current[column] = (uint8_t)(current[column] + ((left + above) >> 1));
            else if (filter == 4U) current[column] = (uint8_t)(current[column] + paeth(left, above, upper_left));
        }
    }
    for (unsigned y = 0; y < LCD_HEIGHT; ++y) for (unsigned x = 0; x < LCD_WIDTH; ++x) {
        const uint8_t *pixel = pixels + y * (row_bytes + 1U) + 1U + x * channels;
        const uint8_t alpha = channels == 4U ? pixel[3] : 255U;
        frame[y * LCD_WIDTH + x] = RGB565((uint8_t)((pixel[0] * alpha) / 255U),
                                          (uint8_t)((pixel[1] * alpha) / 255U),
                                          (uint8_t)((pixel[2] * alpha) / 255U));
    }
    result = true;

done:
    free(pixels);
    free(compressed);
    free(decompressor);
    if (file != NULL) fclose(file);
    return result;
}
imageDecodeFile() · source line 197
bool imageDecodeFile(const char *path, uint16_t *frame)

Calls into this documentation:

  1. Decisionpath == NULL || frame == NULL
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. DecisionhasExtension(path, ".jpg") || hasExtension(path, ".jpeg")
    True
    1. Exit this functionreturn decodeJpegFile(path, frame, 0U, 0U, true, 0U);
    False

    Continue after this decision.

  3. DecisionhasExtension(path, ".png")
    True
    1. Exit this functionreturn decodePngFile(path, frame);
    False

    Continue after this decision.

  4. Exit this functionreturn false;
Read original function source
bool imageDecodeFile(const char *path, uint16_t *frame)
{
    if (path == NULL || frame == NULL) return false;
    if (hasExtension(path, ".jpg") || hasExtension(path, ".jpeg")) return decodeJpegFile(path, frame, 0U, 0U, true, 0U);
    if (hasExtension(path, ".png")) return decodePngFile(path, frame);
    return false;
}
imageDecodeJpegAt() · source line 205
bool imageDecodeJpegAt(const char *path, uint16_t *frame, unsigned x, unsigned y)

Calls into this documentation:

  1. Decisionpath == NULL || frame == NULL || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Exit this functionreturn decodeJpegFile(path, frame, x, y, false, 0U);
Read original function source
bool imageDecodeJpegAt(const char *path, uint16_t *frame, unsigned x, unsigned y)
{
    if (path == NULL || frame == NULL || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))) return false;
    return decodeJpegFile(path, frame, x, y, false, 0U);
}
imageDecodeJpegScaledAt() · source line 211
bool imageDecodeJpegScaledAt(const char *path, uint16_t *frame, unsigned x, unsigned y, unsigned scale)

Calls into this documentation:

  1. Decisionpath == NULL || frame == NULL || scale > 3U || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Exit this functionreturn decodeJpegFile(path, frame, x, y, false, scale);
Read original function source
bool imageDecodeJpegScaledAt(const char *path, uint16_t *frame, unsigned x, unsigned y, unsigned scale)
{
    if (path == NULL || frame == NULL || scale > 3U || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))) return false;
    return decodeJpegFile(path, frame, x, y, false, scale);
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "image_decoder.h"
#include "game_graphics.h"
#include "esp32s3/rom/tjpgd.h"
#include "esp_heap_caps.h"
#include "miniz.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define JPEG_WORK_BYTES 8192U
#define PNG_SIGNATURE_BYTES 8U
#define PNG_IHDR_BYTES 13U
#define PNG_MAX_COMPRESSED_BYTES (1024U * 1024U)

static uint8_t jpeg_work[JPEG_WORK_BYTES];
static uint16_t *jpeg_target;
static unsigned jpeg_offset_x, jpeg_offset_y;

static bool hasExtension(const char *name, const char *extension)
{
    const size_t name_length = strlen(name);
    const size_t extension_length = strlen(extension);
    if (name_length < extension_length) return false;
    name += name_length - extension_length;
    for (size_t index = 0; index < extension_length; ++index) {
        if (tolower((unsigned char)name[index]) != extension[index]) return false;
    }
    return true;
}

bool imageIsSupportedName(const char *name)
{
    return name != NULL && (hasExtension(name, ".jpg") || hasExtension(name, ".jpeg") || hasExtension(name, ".png"));
}

static UINT jpegInput(JDEC *decoder, BYTE *buffer, UINT bytes)
{
    FILE *file = (FILE *)decoder->device;
    if (buffer != NULL) return (UINT)fread(buffer, 1, bytes, file);
    return fseek(file, (long)bytes, SEEK_CUR) == 0 ? bytes : 0;
}

static UINT jpegOutput(JDEC *decoder, void *bitmap, JRECT *rect)
{
    (void)decoder;
    if (jpeg_target == NULL || rect->right + jpeg_offset_x >= LCD_WIDTH || rect->bottom + jpeg_offset_y >= LCD_HEIGHT) return 0;
    const BYTE *source = bitmap;
    const unsigned width = rect->right - rect->left + 1U;
    const unsigned height = rect->bottom - rect->top + 1U;
    for (unsigned row = 0; row < height; ++row) for (unsigned column = 0; column < width; ++column) {
        const unsigned pixel = row * width + column;
        jpeg_target[(rect->top + row + jpeg_offset_y) * LCD_WIDTH + rect->left + column + jpeg_offset_x] =
            RGB565(source[pixel * 3U], source[pixel * 3U + 1U], source[pixel * 3U + 2U]);
    }
    return 1;
}

static bool decodeJpegFile(const char *path, uint16_t *frame, unsigned offset_x, unsigned offset_y, bool clear_target,
                           unsigned requested_scale)
{
    FILE *file = fopen(path, "rb");
    if (file == NULL) return false;
    JDEC decoder;
    JRESULT result = jd_prepare(&decoder, jpegInput, jpeg_work, sizeof(jpeg_work), file);
    if (result != JDR_OK) {
        fclose(file);
        return false;
    }
    unsigned scale = requested_scale;
    if (clear_target) {
        scale = 0;
        while (scale < 3U && (((decoder.width + ((1U << scale) - 1U)) >> scale) > LCD_WIDTH ||
                              ((decoder.height + ((1U << scale) - 1U)) >> scale) > LCD_HEIGHT)) ++scale;
    }
    if (scale > 3U) { fclose(file); return false; }
    const unsigned width = (decoder.width + ((1U << scale) - 1U)) >> scale;
    const unsigned height = (decoder.height + ((1U << scale) - 1U)) >> scale;
    if (width > LCD_WIDTH || height > LCD_HEIGHT || offset_x > LCD_WIDTH - width || offset_y > LCD_HEIGHT - height) {
        fclose(file);
        return false;
    }
    if (clear_target) clear(frame, COLOR_BLACK);
    jpeg_offset_x = clear_target ? (LCD_WIDTH - width) / 2U : offset_x;
    jpeg_offset_y = clear_target ? (LCD_HEIGHT - height) / 2U : offset_y;
    jpeg_target = frame;
    result = jd_decomp(&decoder, jpegOutput, scale);
    jpeg_target = NULL;
    fclose(file);
    return result == JDR_OK;
}

static uint32_t readBigEndian(const uint8_t *bytes)
{
    return ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) | ((uint32_t)bytes[2] << 8) | bytes[3];
}

static uint8_t paeth(uint8_t left, uint8_t above, uint8_t upper_left)
{
    const int prediction = (int)left + above - upper_left;
    const int left_distance = prediction > left ? prediction - left : left - prediction;
    const int above_distance = prediction > above ? prediction - above : above - prediction;
    const int upper_left_distance = prediction > upper_left ? prediction - upper_left : upper_left - prediction;
    if (left_distance <= above_distance && left_distance <= upper_left_distance) return left;
    return above_distance <= upper_left_distance ? above : upper_left;
}

static bool decodePngFile(const char *path, uint16_t *frame)
{
    static const uint8_t png_signature[PNG_SIGNATURE_BYTES] = { 137, 80, 78, 71, 13, 10, 26, 10 };
    FILE *file = fopen(path, "rb");
    uint8_t signature[PNG_SIGNATURE_BYTES], header[8], ihdr[PNG_IHDR_BYTES];
    uint8_t *compressed = NULL;
    uint8_t *pixels = NULL;
    tinfl_decompressor *decompressor = NULL;
    size_t compressed_bytes = 0;
    bool result = false;
    unsigned channels = 0;

    if (file == NULL || fread(signature, 1, sizeof(signature), file) != sizeof(signature) ||
        memcmp(signature, png_signature, sizeof(signature)) != 0) goto done;
    for (;;) {
        if (fread(header, 1, sizeof(header), file) != sizeof(header)) goto done;
        const uint32_t length = readBigEndian(header);
        if (length > PNG_MAX_COMPRESSED_BYTES) goto done;
        if (memcmp(header + 4, "IHDR", 4) == 0) {
            if (length != PNG_IHDR_BYTES || fread(ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr)) goto done;
            if (readBigEndian(ihdr) != LCD_WIDTH || readBigEndian(ihdr + 4) != LCD_HEIGHT ||
                ihdr[8] != 8U || ihdr[10] != 0U || ihdr[11] != 0U || ihdr[12] != 0U) goto done;
            if (ihdr[9] == 2U) channels = 3U;
            else if (ihdr[9] == 6U) channels = 4U;
            else goto done;
        } else if (memcmp(header + 4, "IDAT", 4) == 0) {
            if (length > PNG_MAX_COMPRESSED_BYTES - compressed_bytes) goto done;
            uint8_t *expanded = heap_caps_realloc(compressed, compressed_bytes + length,
                                                  MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
            if (expanded == NULL) goto done;
            compressed = expanded;
            if (fread(compressed + compressed_bytes, 1, length, file) != length) goto done;
            compressed_bytes += length;
        } else if (fseek(file, (long)length, SEEK_CUR) != 0) goto done;
        if (fseek(file, 4L, SEEK_CUR) != 0) goto done;
        if (memcmp(header + 4, "IEND", 4) == 0) break;
    }
    if (channels == 0U || compressed_bytes == 0U) goto done;
    const size_t row_bytes = LCD_WIDTH * channels;
    const size_t decoded_bytes = LCD_HEIGHT * (row_bytes + 1U);
    pixels = heap_caps_malloc(decoded_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    decompressor = calloc(1, sizeof(*decompressor));
    if (pixels == NULL || decompressor == NULL) goto done;
    tinfl_init(decompressor);
    size_t input_position = 0, output_position = 0;
    for (;;) {
        size_t input_bytes = compressed_bytes - input_position;
        size_t output_bytes = decoded_bytes - output_position;
        const tinfl_status status = tinfl_decompress(decompressor, compressed + input_position, &input_bytes,
                                                     pixels, pixels + output_position, &output_bytes,
                                                     TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
        input_position += input_bytes;
        output_position += output_bytes;
        if (status == TINFL_STATUS_DONE) break;
        if (status < TINFL_STATUS_DONE || (input_bytes == 0U && output_bytes == 0U)) goto done;
    }
    if (output_position != decoded_bytes) goto done;
    for (unsigned row = 0; row < LCD_HEIGHT; ++row) {
        uint8_t *current = pixels + row * (row_bytes + 1U) + 1U;
        const uint8_t *previous = row == 0U ? NULL : current - (row_bytes + 1U);
        const uint8_t filter = current[-1];
        if (filter > 4U) goto done;
        for (size_t column = 0; column < row_bytes; ++column) {
            const uint8_t left = column < channels ? 0U : current[column - channels];
            const uint8_t above = previous == NULL ? 0U : previous[column];
            const uint8_t upper_left = previous == NULL || column < channels ? 0U : previous[column - channels];
            if (filter == 1U) current[column] = (uint8_t)(current[column] + left);
            else if (filter == 2U) current[column] = (uint8_t)(current[column] + above);
            else if (filter == 3U) current[column] = (uint8_t)(current[column] + ((left + above) >> 1));
            else if (filter == 4U) current[column] = (uint8_t)(current[column] + paeth(left, above, upper_left));
        }
    }
    for (unsigned y = 0; y < LCD_HEIGHT; ++y) for (unsigned x = 0; x < LCD_WIDTH; ++x) {
        const uint8_t *pixel = pixels + y * (row_bytes + 1U) + 1U + x * channels;
        const uint8_t alpha = channels == 4U ? pixel[3] : 255U;
        frame[y * LCD_WIDTH + x] = RGB565((uint8_t)((pixel[0] * alpha) / 255U),
                                          (uint8_t)((pixel[1] * alpha) / 255U),
                                          (uint8_t)((pixel[2] * alpha) / 255U));
    }
    result = true;

done:
    free(pixels);
    free(compressed);
    free(decompressor);
    if (file != NULL) fclose(file);
    return result;
}

bool imageDecodeFile(const char *path, uint16_t *frame)
{
    if (path == NULL || frame == NULL) return false;
    if (hasExtension(path, ".jpg") || hasExtension(path, ".jpeg")) return decodeJpegFile(path, frame, 0U, 0U, true, 0U);
    if (hasExtension(path, ".png")) return decodePngFile(path, frame);
    return false;
}

bool imageDecodeJpegAt(const char *path, uint16_t *frame, unsigned x, unsigned y)
{
    if (path == NULL || frame == NULL || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))) return false;
    return decodeJpegFile(path, frame, x, y, false, 0U);
}

bool imageDecodeJpegScaledAt(const char *path, uint16_t *frame, unsigned x, unsigned y, unsigned scale)
{
    if (path == NULL || frame == NULL || scale > 3U || (!hasExtension(path, ".jpg") && !hasExtension(path, ".jpeg"))) return false;
    return decodeJpegFile(path, frame, x, y, false, scale);
}