Files
2020-07-25 01:50:14 +03:00

226 lines
5.9 KiB
C

/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* JPEG image decoder
*/
#include "airscan.h"
#include <jpeglib.h>
#include <setjmp.h>
#include <string.h>
/* JPEG image decoder
*/
typedef struct {
image_decoder decoder; /* Base class */
struct jpeg_decompress_struct cinfo; /* libjpeg decoder */
struct jpeg_error_mgr jerr; /* libjpeg error manager */
jmp_buf jmpb; /* For longjmp from libjpeg */
char errbuf[ /* Error buffer */
JMSG_LENGTH_MAX + 16];
JDIMENSION num_lines; /* Num of lines left to read */
} image_decoder_jpeg;
/* Free JPEG decoder
*/
static void
image_decoder_jpeg_free (image_decoder *decoder)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
jpeg_destroy_decompress(&jpeg->cinfo);
mem_free(jpeg);
}
/* Begin JPEG decoding
*/
static error
image_decoder_jpeg_begin (image_decoder *decoder, const void *data,
size_t size)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
int rc;
if (!setjmp(jpeg->jmpb)) {
jpeg_mem_src(&jpeg->cinfo, (unsigned char*) data, size);
rc = jpeg_read_header(&jpeg->cinfo, true);
if (rc != JPEG_HEADER_OK) {
jpeg_abort((j_common_ptr) &jpeg->cinfo);
return ERROR("JPEG: invalid header");
}
if (jpeg->cinfo.num_components != 1) {
jpeg->cinfo.out_color_space = JCS_RGB;
}
jpeg_start_decompress(&jpeg->cinfo);
jpeg->num_lines = jpeg->cinfo.image_height;
return NULL;
}
return ERROR(jpeg->errbuf);
}
/* Reset JPEG decoder
*/
static void
image_decoder_jpeg_reset (image_decoder *decoder)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
jpeg_abort((j_common_ptr) &jpeg->cinfo);
}
/* Get bytes count per pixel
*/
static int
image_decoder_jpeg_get_bytes_per_pixel (image_decoder *decoder)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
return jpeg->cinfo.num_components;
}
/* Get image parameters
*/
static void
image_decoder_jpeg_get_params (image_decoder *decoder, SANE_Parameters *params)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
params->last_frame = SANE_TRUE;
params->pixels_per_line = jpeg->cinfo.image_width;
params->lines = jpeg->cinfo.image_height;
params->depth = 8;
if (jpeg->cinfo.num_components == 1) {
params->format = SANE_FRAME_GRAY;
params->bytes_per_line = params->pixels_per_line;
} else {
params->format = SANE_FRAME_RGB;
params->bytes_per_line = params->pixels_per_line * 3;
}
}
/* Set clipping window
*/
static error
image_decoder_jpeg_set_window (image_decoder *decoder, image_window *win)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
/* Note, image clipping cannot be supported on rather
* old libjpeg version (i.e., on Ubuntu 16.04, because
* jpeg_crop_scanline() and jpeg_skip_scanlines() functions
* are missed. The safe default is to update window to
* match the entire image dimensions.
*/
#if 1
win->x_off = win->y_off = 0;
win->wid = jpeg->cinfo.image_width;
win->hei = jpeg->cinfo.image_height;
return NULL;
#else
JDIMENSION x_off = win->x_off;
JDIMENSION wid = win->wid;
if (!setjmp(jpeg->jmpb)) {
jpeg_crop_scanline(&jpeg->cinfo, &x_off, &wid);
if (win->y_off > 0) {
jpeg_skip_scanlines(&jpeg->cinfo, win->y_off);
}
jpeg->num_lines = win->hei;
win->x_off = x_off;
win->wid = wid;
return NULL;
}
return ERROR(jpeg->errbuf);
#endif
}
/* Read next line of image
*/
static error
image_decoder_jpeg_read_line (image_decoder *decoder, void *buffer)
{
image_decoder_jpeg *jpeg = (image_decoder_jpeg*) decoder;
JSAMPROW lines[1] = {buffer};
if (!jpeg->num_lines) {
return ERROR("JPEG: end of file");
}
if (!setjmp(jpeg->jmpb)) {
if (jpeg_read_scanlines(&jpeg->cinfo, lines, 1) == 0) {
return ERROR(jpeg->errbuf);
}
jpeg->num_lines --;
return NULL;
}
return ERROR(jpeg->errbuf);
}
/* "Output error message" callback for JPEG decoder
*/
static void
image_decoder_jpeg_output_message (j_common_ptr cinfo)
{
image_decoder_jpeg *jpeg = OUTER_STRUCT(cinfo, image_decoder_jpeg, cinfo);
memcpy(jpeg->errbuf, "JPEG: ", 6);
(*cinfo->err->format_message)(cinfo, jpeg->errbuf + 6);
}
/* error_exit callback for JPEG decoder. The default callback
* terminates a program, which is not good for us
*/
static void
image_decoder_jpeg_error_exit (j_common_ptr cinfo)
{
image_decoder_jpeg *jpeg = OUTER_STRUCT(cinfo, image_decoder_jpeg, cinfo);
image_decoder_jpeg_output_message(cinfo);
jpeg_abort(cinfo);
longjmp(jpeg->jmpb, 1);
}
/* Create JPEG image decoder
*/
image_decoder*
image_decoder_jpeg_new (void)
{
image_decoder_jpeg *jpeg = mem_new(image_decoder_jpeg, 1);
jpeg->decoder.content_type = "image/jpeg";
jpeg->decoder.free = image_decoder_jpeg_free;
jpeg->decoder.begin = image_decoder_jpeg_begin;
jpeg->decoder.reset = image_decoder_jpeg_reset;
jpeg->decoder.get_bytes_per_pixel = image_decoder_jpeg_get_bytes_per_pixel;
jpeg->decoder.get_params = image_decoder_jpeg_get_params;
jpeg->decoder.set_window = image_decoder_jpeg_set_window;
jpeg->decoder.read_line = image_decoder_jpeg_read_line;
jpeg->cinfo.err = jpeg_std_error(&jpeg->jerr);
jpeg->jerr.output_message = image_decoder_jpeg_output_message;
jpeg->jerr.error_exit = image_decoder_jpeg_error_exit;
jpeg_create_decompress(&jpeg->cinfo);
return &jpeg->decoder;
}
/* vim:ts=8:sw=4:et
*/