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