• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Touboul Nathane
4    Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
5 
6    This file is part of the SANE package.
7 
8    SANE is free software; you can redistribute it and/or modify it under
9    the terms of the GNU General Public License as published by the Free
10    Software Foundation; either version 3 of the License, or (at your
11    option) any later version.
12 
13    SANE is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16    for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with sane; see the file COPYING.
20    If not, see <https://www.gnu.org/licenses/>.
21 
22    This file implements a SANE backend for eSCL scanners.  */
23 
24 #define DEBUG_DECLARE_ONLY
25 #include "../include/sane/config.h"
26 
27 #include  "escl.h"
28 
29 #include "../include/sane/sanei.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #if(defined HAVE_LIBJPEG)
36 #  include <jpeglib.h>
37 #endif
38 
39 #include <setjmp.h>
40 
41 #define INPUT_BUFFER_SIZE 4096
42 
43 #if(defined HAVE_LIBJPEG)
44 struct my_error_mgr
45 {
46     struct jpeg_error_mgr errmgr;
47     jmp_buf escape;
48 };
49 
50 typedef struct
51 {
52     struct jpeg_source_mgr pub;
53     FILE *ctx;
54     unsigned char buffer[INPUT_BUFFER_SIZE];
55 } my_source_mgr;
56 
57 /**
58  * \fn static boolean fill_input_buffer(j_decompress_ptr cinfo)
59  * \brief Called in the "skip_input_data" function.
60  *
61  * \return TRUE (everything is OK)
62  */
63 static boolean
fill_input_buffer(j_decompress_ptr cinfo)64 fill_input_buffer(j_decompress_ptr cinfo)
65 {
66     my_source_mgr *src = (my_source_mgr *) cinfo->src;
67     int nbytes = 0;
68 
69     nbytes = fread(src->buffer, 1, INPUT_BUFFER_SIZE, src->ctx);
70     if (nbytes <= 0) {
71         src->buffer[0] = (unsigned char) 0xFF;
72         src->buffer[1] = (unsigned char) JPEG_EOI;
73         nbytes = 2;
74     }
75     src->pub.next_input_byte = src->buffer;
76     src->pub.bytes_in_buffer = nbytes;
77     return (TRUE);
78 }
79 
80 /**
81  * \fn static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
82  * \brief Called in the "jpeg_RW_src" function.
83  */
84 static void
skip_input_data(j_decompress_ptr cinfo,long num_bytes)85 skip_input_data(j_decompress_ptr cinfo, long num_bytes)
86 {
87     my_source_mgr *src = (my_source_mgr *) cinfo->src;
88 
89     if (num_bytes > 0) {
90         while (num_bytes > (long) src->pub.bytes_in_buffer) {
91             num_bytes -= (long) src->pub.bytes_in_buffer;
92             (void) src->pub.fill_input_buffer(cinfo);
93         }
94         src->pub.next_input_byte += (size_t) num_bytes;
95         src->pub.bytes_in_buffer -= (size_t) num_bytes;
96     }
97 }
98 
99 static void
term_source(j_decompress_ptr __sane_unused__ cinfo)100 term_source(j_decompress_ptr __sane_unused__ cinfo)
101 {
102     return;
103 }
104 
105 static void
init_source(j_decompress_ptr __sane_unused__ cinfo)106 init_source(j_decompress_ptr __sane_unused__ cinfo)
107 {
108     return;
109 }
110 
111 /**
112  * \fn static void jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
113  * \brief Called in the "escl_sane_decompressor" function.
114  */
115 static void
jpeg_RW_src(j_decompress_ptr cinfo,FILE * ctx)116 jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
117 {
118     my_source_mgr *src;
119 
120     if (cinfo->src == NULL) {
121         cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)
122             ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
123     }
124     src = (my_source_mgr *) cinfo->src;
125     src->pub.init_source = init_source;
126     src->pub.fill_input_buffer = fill_input_buffer;
127     src->pub.skip_input_data = skip_input_data;
128     src->pub.resync_to_restart = jpeg_resync_to_restart;
129     src->pub.term_source = term_source;
130     src->ctx = ctx;
131     src->pub.bytes_in_buffer = 0;
132     src->pub.next_input_byte = NULL;
133 }
134 
135 static void
my_error_exit(j_common_ptr cinfo)136 my_error_exit(j_common_ptr cinfo)
137 {
138     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
139 
140     longjmp(err->escape, 1);
141 }
142 
143 static void
output_no_message(j_common_ptr __sane_unused__ cinfo)144 output_no_message(j_common_ptr __sane_unused__ cinfo)
145 {
146 }
147 
148 /**
149  * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
150  * \brief Function that aims to decompress the jpeg image to SANE be able to read the image.
151  *        This function is called in the "sane_read" function.
152  *
153  * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
154  */
155 SANE_Status
get_JPEG_data(capabilities_t * scanner,int * width,int * height,int * bps)156 get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps)
157 {
158     int start = 0;
159     struct jpeg_decompress_struct cinfo;
160     JSAMPROW rowptr[1];
161     unsigned char *surface = NULL;
162     struct my_error_mgr jerr;
163     int lineSize = 0;
164     JDIMENSION x_off = 0;
165     JDIMENSION y_off = 0;
166     JDIMENSION w = 0;
167     JDIMENSION h = 0;
168     int pos = 0;
169 
170     if (scanner->tmp == NULL)
171         return (SANE_STATUS_INVAL);
172     fseek(scanner->tmp, SEEK_SET, 0);
173     start = ftell(scanner->tmp);
174     cinfo.err = jpeg_std_error(&jerr.errmgr);
175     jerr.errmgr.error_exit = my_error_exit;
176     jerr.errmgr.output_message = output_no_message;
177     if (setjmp(jerr.escape)) {
178         jpeg_destroy_decompress(&cinfo);
179         if (surface != NULL)
180             free(surface);
181 	fseek(scanner->tmp, start, SEEK_SET);
182         DBG( 1, "Escl Jpeg : Error reading jpeg\n");
183         if (scanner->tmp) {
184            fclose(scanner->tmp);
185            scanner->tmp = NULL;
186         }
187         return (SANE_STATUS_INVAL);
188     }
189     jpeg_create_decompress(&cinfo);
190     jpeg_RW_src(&cinfo, scanner->tmp);
191     jpeg_read_header(&cinfo, TRUE);
192     cinfo.out_color_space = JCS_RGB;
193     cinfo.quantize_colors = FALSE;
194     jpeg_calc_output_dimensions(&cinfo);
195     double ratio = (double)cinfo.output_width / (double)scanner->caps[scanner->source].width;
196     int rw = (int)((double)scanner->caps[scanner->source].width * ratio);
197     int rh = (int)((double)scanner->caps[scanner->source].height * ratio);
198     int rx = (int)((double)scanner->caps[scanner->source].pos_x * ratio);
199     int ry = (int)((double)scanner->caps[scanner->source].pos_y * ratio);
200 
201 
202     if (cinfo.output_width < (unsigned int)rw)
203           rw = cinfo.output_width;
204     if (rx < 0)
205           rx = 0;
206 
207     if (cinfo.output_height < (unsigned int)rh)
208           rh = cinfo.output_height;
209     if (ry < 0)
210           ry = 0;
211     DBG(10, "1-JPEF Geometry [%dx%d|%dx%d]\n",
212 	        rx,
213 	        ry,
214 	        rw,
215 	        rh);
216     x_off = rx;
217     if (x_off > (unsigned int)rw) {
218        w = rw;
219        x_off = 0;
220     }
221     else
222        w = rw - x_off;
223     y_off = ry;
224     if(y_off > (unsigned int)rh) {
225        h = rh;
226        y_off = 0;
227     }
228     else
229        h = rh - y_off;
230     DBG(10, "2-JPEF Geometry [%dx%d|%dx%d]\n",
231 	        x_off,
232 	        y_off,
233 	        w,
234 	        h);
235     jpeg_start_decompress(&cinfo);
236     if (x_off > 0 || w < cinfo.output_width)
237        jpeg_crop_scanline(&cinfo, &x_off, &w);
238     lineSize = w * cinfo.output_components;
239     if (y_off > 0)
240         jpeg_skip_scanlines(&cinfo, y_off);
241     surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
242     if (surface == NULL) {
243         jpeg_destroy_decompress(&cinfo);
244         DBG( 1, "Escl Jpeg : Memory allocation problem\n");
245         if (scanner->tmp) {
246            fclose(scanner->tmp);
247            scanner->tmp = NULL;
248         }
249         return (SANE_STATUS_NO_MEM);
250     }
251     pos = 0;
252     while (cinfo.output_scanline < (unsigned int)rh) {
253         rowptr[0] = (JSAMPROW)surface + (lineSize * pos); // ..cinfo.output_scanline);
254         jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
255        pos++;
256      }
257     scanner->img_data = surface;
258     scanner->img_size = lineSize * h;
259     scanner->img_read = 0;
260     *width = w;
261     *height = h;
262     *bps = cinfo.output_components;
263     // jpeg_finish_decompress(&cinfo);
264     jpeg_destroy_decompress(&cinfo);
265     fclose(scanner->tmp);
266     scanner->tmp = NULL;
267     return (SANE_STATUS_GOOD);
268 }
269 #else
270 
271 SANE_Status
get_JPEG_data(capabilities_t __sane_unused__ * scanner,int __sane_unused__ * width,int __sane_unused__ * height,int __sane_unused__ * bps)272 get_JPEG_data(capabilities_t __sane_unused__ *scanner,
273               int __sane_unused__ *width,
274               int __sane_unused__ *height,
275               int __sane_unused__ *bps)
276 {
277     return (SANE_STATUS_INVAL);
278 }
279 
280 #endif
281