Skip to content

Commit 2d85c45

Browse files
committed
Add JPEG image loader
A preliminary JPEG image loader is implemented, capable of converting an arbitrary JPEG file into an ARGB32 pixmap. The exception handling should be improved to deliver corresponding error codes rather than just dumping the message to the console.
1 parent 83fb1ee commit 2d85c45

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# FIXME: make these entries configurable
22
CONFIG_BACKEND_SDL := y
3+
CONFIG_LOADER_JPEG := y
34
CONFIG_LOADER_PNG := y
45

56
# Rules
@@ -51,6 +52,12 @@ libtwin.a_includes-y := include
5152

5253
# Image loaders
5354

55+
ifeq ($(CONFIG_LOADER_JPEG), y)
56+
libtwin.a_files-y += src/image-jpeg.c
57+
libtwin.a_cflags-y += $(shell pkg-config --cflags libjpeg)
58+
TARGET_LIBS += $(shell pkg-config --libs libjpeg)
59+
endif
60+
5461
ifeq ($(CONFIG_LOADER_PNG), y)
5562
libtwin.a_files-y += src/image-png.c
5663
libtwin.a_cflags-y += $(shell pkg-config --cflags libpng)

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ benefiting the entire application stack.
6262
`Mado` is built with a minimalist design in mind. However, its verification
6363
relies on certain third-party packages for full functionality and access to all
6464
its features. To ensure proper operation, the development environment should
65-
have the [SDL2 library](https://www.libsdl.org/) and [libpng](https://github.com/pnggroup/libpng) installed.
66-
* macOS: `brew install sdl2 libpng`
67-
* Ubuntu Linux / Debian: `sudo apt install libsdl2-dev libpng-dev`
65+
have the [SDL2 library](https://www.libsdl.org/), [libjpeg](https://www.ijg.org/), and [libpng](https://github.com/pnggroup/libpng) installed.
66+
* macOS: `brew install sdl2 jpeg libpng`
67+
* Ubuntu Linux / Debian: `sudo apt install libsdl2-dev libjpeg-dev libpng-dev`
6868

6969
Build the library and demo program.
7070
```shell

include/twin.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,12 @@ void twin_icon_draw(twin_pixmap_t *pixmap,
680680
twin_icon_t icon,
681681
twin_matrix_t matrix);
682682

683+
/*
684+
* image-jpeg.c
685+
*/
686+
687+
twin_pixmap_t *twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt);
688+
683689
/*
684690
* image-png.c
685691
*/

src/image-jpeg.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Twin - A Tiny Window System
3+
* Copyright (c) 2024 National Cheng Kung University, Taiwan
4+
* All rights reserved.
5+
*/
6+
7+
#include <setjmp.h>
8+
#include <stdio.h>
9+
#include <stdint.h>
10+
11+
#include <jpeglib.h>
12+
13+
#include "twin_private.h"
14+
15+
/* colorspace definitions matching libjpeg */
16+
typedef enum {
17+
TWIN_JCS_UNKNOWN, /* error/unspecified */
18+
TWIN_JCS_GRAYSCALE, /* monochrome */
19+
TWIN_JCS_RGB, /* red/green/blue */
20+
TWIN_JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */
21+
TWIN_JCS_CMYK, /* C/M/Y/K */
22+
TWIN_JCS_YCCK /* Y/Cb/Cr/K */
23+
} twin_jpeg_cspace_t;
24+
25+
#if BITS_IN_JSAMPLE != 8
26+
#error This implementation supports only libjpeg with 8 bits per sample.
27+
#endif
28+
29+
struct twin_jpeg_err_mgr {
30+
struct jpeg_error_mgr mgr;
31+
jmp_buf jbuf;
32+
};
33+
34+
static void twin_jpeg_error_exit(j_common_ptr cinfo)
35+
{
36+
struct twin_jpeg_err_mgr *jerr = (struct twin_jpeg_err_mgr *) cinfo->err;
37+
38+
/* Optionally display the error message */
39+
(*cinfo->err->output_message)(cinfo);
40+
41+
longjmp(jerr->jbuf, 1);
42+
}
43+
44+
twin_pixmap_t *twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt)
45+
{
46+
twin_pixmap_t *pix = NULL;
47+
48+
/* Current implementation only produces TWIN_ARGB32 and TWIN_A8 */
49+
if (fmt != TWIN_ARGB32 && fmt != TWIN_A8)
50+
return NULL;
51+
52+
FILE *infile = fopen(filepath, "rb");
53+
if (!infile) {
54+
fprintf(stderr, "Failed to open %s\n", filepath);
55+
return NULL;
56+
}
57+
58+
/* Error handling */
59+
struct jpeg_decompress_struct cinfo;
60+
memset(&cinfo, 0, sizeof(cinfo));
61+
struct twin_jpeg_err_mgr jerr = {.mgr.error_exit = twin_jpeg_error_exit};
62+
cinfo.err = jpeg_std_error(&jerr.mgr);
63+
if (setjmp(jerr.jbuf)) {
64+
fprintf(stderr, "Failed to decode %s\n", filepath);
65+
if (pix)
66+
twin_pixmap_destroy(pix);
67+
jpeg_destroy_decompress(&cinfo);
68+
fclose(infile);
69+
return NULL;
70+
}
71+
72+
/* Initialize libjpeg, hook it up to file, and read header */
73+
jpeg_create_decompress(&cinfo);
74+
jpeg_stdio_src(&cinfo, infile);
75+
(void) jpeg_read_header(&cinfo, true);
76+
77+
/* Configure */
78+
twin_coord_t width = cinfo.image_width, height = cinfo.image_height;
79+
if (fmt == TWIN_ARGB32)
80+
cinfo.out_color_space = JCS_RGB;
81+
else
82+
cinfo.out_color_space = JCS_GRAYSCALE;
83+
84+
/* Allocate pixmap */
85+
pix = twin_pixmap_create(fmt, width, height);
86+
if (!pix)
87+
longjmp(jerr.jbuf, 1);
88+
89+
/* Start decompressing */
90+
(void) jpeg_start_decompress(&cinfo);
91+
92+
if ((fmt == TWIN_A8 && cinfo.output_components != 1) ||
93+
(fmt == TWIN_ARGB32 &&
94+
(cinfo.output_components != 3 && cinfo.output_components != 4)))
95+
longjmp(jerr.jbuf, 1);
96+
97+
int rowstride = cinfo.output_width * cinfo.output_components;
98+
99+
JSAMPARRAY rowbuf = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo,
100+
JPOOL_IMAGE, rowstride, 1);
101+
102+
/* Process rows */
103+
while (cinfo.output_scanline < cinfo.output_height) {
104+
twin_pointer_t p = twin_pixmap_pointer(pix, 0, cinfo.output_scanline);
105+
(void) jpeg_read_scanlines(&cinfo, rowbuf, 1);
106+
if (fmt == TWIN_A8 || cinfo.output_components == 4)
107+
memcpy(p.a8, rowbuf, rowstride);
108+
else {
109+
JSAMPLE *s = *rowbuf;
110+
for (int i = 0; i < width; i++) {
111+
uint32_t r = *(s++);
112+
uint32_t g = *(s++);
113+
uint32_t b = *(s++);
114+
*(p.argb32++) = 0xFF000000U | (r << 16) | (g << 8) | b;
115+
}
116+
}
117+
}
118+
119+
/* clean up */
120+
(void) jpeg_finish_decompress(&cinfo);
121+
jpeg_destroy_decompress(&cinfo);
122+
fclose(infile);
123+
124+
return pix;
125+
}

0 commit comments

Comments
 (0)