1 /* AirScan (a.k.a. eSCL) backend for SANE
2 *
3 * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4 * See LICENSE for license terms and conditions
5 *
6 * JPEG image decoder
7 */
8
9 #include "airscan.h"
10
11 #include <jpeglib.h>
12 #include <setjmp.h>
13 #include <string.h>
14
15 /* JPEG image decoder
16 */
17 typedef struct {
18 image_decoder decoder; /* Base class */
19 struct jpeg_decompress_struct cinfo; /* libjpeg decoder */
20 struct jpeg_error_mgr jerr; /* libjpeg error manager */
21 jmp_buf jmpb; /* For longjmp from libjpeg */
22 char errbuf[ /* Error buffer */
23 JMSG_LENGTH_MAX + 16];
24 JDIMENSION num_lines; /* Num of lines left to read */
25 } image_decoder_jpeg;
26
27 /* Free JPEG decoder
28 */
29 static void
image_decoder_jpeg_free(image_decoder * decoder)30 image_decoder_jpeg_free (image_decoder *decoder)
31 {
32 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
33
34 jpeg_destroy_decompress(&jpeg->cinfo);
35 mem_free(jpeg);
36 }
37
38 /* Begin JPEG decoding
39 */
40 static error
image_decoder_jpeg_begin(image_decoder * decoder,const void * data,size_t size)41 image_decoder_jpeg_begin (image_decoder *decoder, const void *data,
42 size_t size)
43 {
44 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
45 int rc;
46
47 if (!setjmp(jpeg->jmpb)) {
48 jpeg_mem_src(&jpeg->cinfo, (unsigned char*) data, size);
49
50 rc = jpeg_read_header(&jpeg->cinfo, true);
51 if (rc != JPEG_HEADER_OK) {
52 jpeg_abort((j_common_ptr) &jpeg->cinfo);
53 return ERROR("JPEG: invalid header");
54 }
55
56 if (jpeg->cinfo.num_components != 1) {
57 jpeg->cinfo.out_color_space = JCS_RGB;
58 }
59
60 jpeg_start_decompress(&jpeg->cinfo);
61 jpeg->num_lines = jpeg->cinfo.image_height;
62
63 return NULL;
64 }
65
66 return ERROR(jpeg->errbuf);
67 }
68
69 /* Reset JPEG decoder
70 */
71 static void
image_decoder_jpeg_reset(image_decoder * decoder)72 image_decoder_jpeg_reset (image_decoder *decoder)
73 {
74 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
75
76 jpeg_abort((j_common_ptr) &jpeg->cinfo);
77 }
78
79 /* Get bytes count per pixel
80 */
81 static int
image_decoder_jpeg_get_bytes_per_pixel(image_decoder * decoder)82 image_decoder_jpeg_get_bytes_per_pixel (image_decoder *decoder)
83 {
84 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
85 return jpeg->cinfo.num_components;
86 }
87
88 /* Get image parameters
89 */
90 static void
image_decoder_jpeg_get_params(image_decoder * decoder,SANE_Parameters * params)91 image_decoder_jpeg_get_params (image_decoder *decoder, SANE_Parameters *params)
92 {
93 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
94
95 params->last_frame = SANE_TRUE;
96 params->pixels_per_line = jpeg->cinfo.image_width;
97 params->lines = jpeg->cinfo.image_height;
98 params->depth = 8;
99
100 if (jpeg->cinfo.num_components == 1) {
101 params->format = SANE_FRAME_GRAY;
102 params->bytes_per_line = params->pixels_per_line;
103 } else {
104 params->format = SANE_FRAME_RGB;
105 params->bytes_per_line = params->pixels_per_line * 3;
106 }
107 }
108
109 /* Set clipping window
110 */
111 static error
image_decoder_jpeg_set_window(image_decoder * decoder,image_window * win)112 image_decoder_jpeg_set_window (image_decoder *decoder, image_window *win)
113 {
114 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
115
116 /* Note, image clipping cannot be supported on rather
117 * old libjpeg version (i.e., on Ubuntu 16.04, because
118 * jpeg_crop_scanline() and jpeg_skip_scanlines() functions
119 * are missed. The safe default is to update window to
120 * match the entire image dimensions.
121 */
122
123 #if 1
124 win->x_off = win->y_off = 0;
125 win->wid = jpeg->cinfo.image_width;
126 win->hei = jpeg->cinfo.image_height;
127 return NULL;
128 #else
129 JDIMENSION x_off = win->x_off;
130 JDIMENSION wid = win->wid;
131
132 if (!setjmp(jpeg->jmpb)) {
133 jpeg_crop_scanline(&jpeg->cinfo, &x_off, &wid);
134 if (win->y_off > 0) {
135 jpeg_skip_scanlines(&jpeg->cinfo, win->y_off);
136 }
137
138 jpeg->num_lines = win->hei;
139
140 win->x_off = x_off;
141 win->wid = wid;
142
143 return NULL;
144 }
145
146 return ERROR(jpeg->errbuf);
147 #endif
148 }
149
150 /* Read next line of image
151 */
152 static error
image_decoder_jpeg_read_line(image_decoder * decoder,void * buffer)153 image_decoder_jpeg_read_line (image_decoder *decoder, void *buffer)
154 {
155 image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
156 JSAMPROW lines[1] = {buffer};
157
158 if (!jpeg->num_lines) {
159 return ERROR("JPEG: end of file");
160 }
161
162 if (!setjmp(jpeg->jmpb)) {
163 if (jpeg_read_scanlines(&jpeg->cinfo, lines, 1) == 0) {
164 return ERROR(jpeg->errbuf);
165 }
166
167 jpeg->num_lines --;
168
169 return NULL;
170 }
171
172 return ERROR(jpeg->errbuf);
173 }
174
175 /* "Output error message" callback for JPEG decoder
176 */
177 static void
image_decoder_jpeg_output_message(j_common_ptr cinfo)178 image_decoder_jpeg_output_message (j_common_ptr cinfo)
179 {
180 image_decoder_jpeg *jpeg = OUTER_STRUCT(cinfo, image_decoder_jpeg, cinfo);
181
182 memcpy(jpeg->errbuf, "JPEG: ", 6);
183 (*cinfo->err->format_message)(cinfo, jpeg->errbuf + 6);
184 }
185
186 /* error_exit callback for JPEG decoder. The default callback
187 * terminates a program, which is not good for us
188 */
189 static void
image_decoder_jpeg_error_exit(j_common_ptr cinfo)190 image_decoder_jpeg_error_exit (j_common_ptr cinfo)
191 {
192 image_decoder_jpeg *jpeg = OUTER_STRUCT(cinfo, image_decoder_jpeg, cinfo);
193
194 image_decoder_jpeg_output_message(cinfo);
195
196 jpeg_abort(cinfo);
197 longjmp(jpeg->jmpb, 1);
198 }
199
200 /* Create JPEG image decoder
201 */
202 image_decoder*
image_decoder_jpeg_new(void)203 image_decoder_jpeg_new (void)
204 {
205 image_decoder_jpeg *jpeg = mem_new(image_decoder_jpeg, 1);
206
207 jpeg->decoder.content_type = "image/jpeg";
208 jpeg->decoder.free = image_decoder_jpeg_free;
209 jpeg->decoder.begin = image_decoder_jpeg_begin;
210 jpeg->decoder.reset = image_decoder_jpeg_reset;
211 jpeg->decoder.get_bytes_per_pixel = image_decoder_jpeg_get_bytes_per_pixel;
212 jpeg->decoder.get_params = image_decoder_jpeg_get_params;
213 jpeg->decoder.set_window = image_decoder_jpeg_set_window;
214 jpeg->decoder.read_line = image_decoder_jpeg_read_line;
215
216 jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
217 jpeg->jerr.output_message = image_decoder_jpeg_output_message;
218 jpeg->jerr.error_exit = image_decoder_jpeg_error_exit;
219 jpeg_create_decompress(&jpeg->cinfo);
220
221 return &jpeg->decoder;
222 }
223
224 /* vim:ts=8:sw=4:et
225 */
226