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