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)
44struct my_error_mgr
45{
46    struct jpeg_error_mgr errmgr;
47    jmp_buf escape;
48};
49
50typedef 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 */
63static boolean
64fill_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 */
84static void
85skip_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
99static void
100term_source(j_decompress_ptr __sane_unused__ cinfo)
101{
102    return;
103}
104
105static void
106init_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 */
115static void
116jpeg_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
135static void
136my_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
143static void
144output_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 */
155SANE_Status
156get_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    surface = malloc(w * h * cinfo.output_components);
236    if (surface == NULL) {
237        jpeg_destroy_decompress(&cinfo);
238        DBG( 1, "Escl Jpeg : Memory allocation problem\n");
239        if (scanner->tmp) {
240           fclose(scanner->tmp);
241           scanner->tmp = NULL;
242        }
243        return (SANE_STATUS_NO_MEM);
244    }
245    jpeg_start_decompress(&cinfo);
246    if (x_off > 0 || w < cinfo.output_width)
247       jpeg_crop_scanline(&cinfo, &x_off, &w);
248    lineSize = w * cinfo.output_components;
249    if (y_off > 0)
250        jpeg_skip_scanlines(&cinfo, y_off);
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
271SANE_Status
272get_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