From d384b2b902b55f8de9fdbb826175711f4f61547f Mon Sep 17 00:00:00 2001
From: Daniel Eklöf <daniel@ekloef.se>
Date: Sun, 20 Sep 2020 12:20:46 +0200
Subject: jpeg: initial support for JPEG images, using libjpeg

---
 jpg.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 jpg.c

(limited to 'jpg.c')

diff --git a/jpg.c b/jpg.c
new file mode 100644
index 0000000..d4784e8
--- /dev/null
+++ b/jpg.c
@@ -0,0 +1,119 @@
+#include "jpg.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <setjmp.h>
+
+#include <jpeglib.h>
+
+#define LOG_MODULE "jpg"
+#define LOG_ENABLE_DBG 1
+#include "log.h"
+#include "stride.h"
+
+struct my_error_mgr {
+    struct jpeg_error_mgr mgr;
+    jmp_buf setjmp_buffer;
+};
+
+static void
+error_exit(j_common_ptr cinfo)
+{
+    struct my_error_mgr *err_handler = (struct my_error_mgr *)cinfo->err;
+    longjmp(err_handler->setjmp_buffer, 1);
+}
+
+pixman_image_t *
+jpg_load(const char *path)
+{
+    struct jpeg_decompress_struct cinfo = {0};
+    struct my_error_mgr err_handler;
+
+    uint8_t *image_data = NULL;
+    pixman_image_t *pix = NULL;
+
+    FILE *fp = fopen(path, "rb");
+    if (fp == NULL)
+        goto err;
+
+    cinfo.err = jpeg_std_error(&err_handler.mgr);
+    err_handler.mgr.error_exit = &error_exit;
+    if (setjmp(err_handler.setjmp_buffer)) {
+        char err_string[JMSG_LENGTH_MAX];
+        cinfo.err->format_message((j_common_ptr)&cinfo, err_string);
+        LOG_ERR("%s: %s", path, err_string);
+        goto err;
+    }
+
+    jpeg_create_decompress(&cinfo);
+    jpeg_stdio_src(&cinfo, fp);
+    jpeg_read_header(&cinfo, true);
+    jpeg_calc_output_dimensions(&cinfo);
+
+    if (cinfo.output_components != 1 && cinfo.output_components != 3) {
+        LOG_ERR("%s: unsupported number of color components: %d",
+                path, cinfo.output_components);
+        goto err;
+    }
+
+    pixman_format_code_t format = PIXMAN_b8g8r8;
+    int width = cinfo.output_width;
+    int height = cinfo.output_height;
+    int stride = stride_for_format_and_width(format, width);
+
+    LOG_DBG("width=%d, height=%d, stride=%d, components=%d",
+            width, height, stride, cinfo.output_components);
+
+    image_data = malloc(height * width * stride);
+    if (image_data == NULL)
+        goto err;
+
+    jpeg_start_decompress(&cinfo);
+
+    for (int row_no = 0; cinfo.output_scanline < height; row_no++) {
+        uint8_t *row = &image_data[row_no * stride];
+
+        if (cinfo.output_components == 3) {
+            /* Read directly info our to-be pixman image buffer */
+            jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&row, 1);
+        }
+
+        else {
+            /* Read into temporary buffer, then expand to rgb */
+            assert(cinfo.output_components == 1);
+
+            uint8_t buf[cinfo.output_width];
+            uint8_t *scanline = &buf[0];
+            jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&scanline, 1);
+
+            for (size_t i = 0; i < cinfo.output_width; i++) {
+                row[i * 3 + 0] = buf[i];
+                row[i * 3 + 1] = buf[i];
+                row[i * 3 + 2] = buf[i];
+            }
+        }
+    }
+
+    jpeg_finish_decompress(&cinfo);
+    jpeg_destroy_decompress(&cinfo);
+    fclose(fp);
+
+    pix = pixman_image_create_bits_no_clear(
+        format, width, height, (uint32_t *)image_data, stride);
+
+    if (pix == NULL) {
+        LOG_ERR("%s: failed to instantiate pixman image", path);
+        goto err;
+    }
+
+    return pix;
+
+err:
+    if (pix != NULL)
+        pixman_image_unref(pix);
+    free(image_data);
+    jpeg_destroy_decompress(&cinfo);
+    if (fp != NULL)
+        fclose(fp);
+    return NULL;
+}
-- 
cgit v1.2.3