mirror of
https://github.com/openharmony/third_party_sane-airscan.git
synced 2026-07-01 21:14:00 -04:00
265 lines
6.7 KiB
C
265 lines
6.7 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
|
|
*
|
|
* PNG image decoder
|
|
*/
|
|
|
|
#include "airscan.h"
|
|
|
|
#include <png.h>
|
|
#include <setjmp.h>
|
|
#include <string.h>
|
|
|
|
/* PNG image decoder
|
|
*/
|
|
typedef struct {
|
|
image_decoder decoder; /* Base class */
|
|
png_struct *png_ptr; /* Underlying libpng decoder */
|
|
png_info *info_ptr; /* libpng info struct */
|
|
const uint8_t *image_data; /* Remaining image data */
|
|
size_t image_size; /* Remaining image data */
|
|
char error[1024]; /* Error message buffer */
|
|
png_uint_32 width, height; /* Image size in pixels */
|
|
int bit_depth; /* 1/2/4/8/16 */
|
|
int color_type; /* PNG_COLOR_TYPE_XXX */
|
|
int interlace_type; /* PNG_INTERLACE_XXX */
|
|
unsigned int num_lines; /* Num of lines left to read */
|
|
} image_decoder_png;
|
|
|
|
/* Free PNG decoder
|
|
*/
|
|
static void
|
|
image_decoder_png_free (image_decoder *decoder)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
image_decoder_reset(decoder);
|
|
mem_free(png);
|
|
}
|
|
|
|
/* libpng error callback
|
|
*/
|
|
static void
|
|
image_decoder_png_error_fn (png_struct *png_ptr, const char *message)
|
|
{
|
|
image_decoder_png *png = png_get_error_ptr(png_ptr);
|
|
|
|
snprintf(png->error, sizeof(png->error), "PNG: %s", message);
|
|
}
|
|
|
|
/* libpng warning callback
|
|
*/
|
|
static void
|
|
image_decoder_png_warning_fn (png_struct *png_ptr, const char *message)
|
|
{
|
|
(void) png_ptr;
|
|
(void) message;
|
|
}
|
|
|
|
/* libpng malloc callback
|
|
*/
|
|
static void*
|
|
image_decoder_png_malloc_fn (png_struct *png_ptr, size_t size)
|
|
{
|
|
(void) png_ptr;
|
|
return mem_new(char, size);
|
|
}
|
|
|
|
/* libpng free callback
|
|
*/
|
|
static void
|
|
image_decoder_png_free_fn (png_struct *png_ptr, void *p)
|
|
{
|
|
(void) png_ptr;
|
|
mem_free(p);
|
|
}
|
|
|
|
/* libpng read callback
|
|
*/
|
|
static void
|
|
image_decoder_png_read_fn (png_struct *png_ptr, png_bytep data, size_t size)
|
|
{
|
|
image_decoder_png *png = png_get_io_ptr(png_ptr);
|
|
|
|
if (size > png->image_size) {
|
|
png_error(png_ptr, "unexpected EOF");
|
|
}
|
|
|
|
memcpy(data, png->image_data, size);
|
|
png->image_data += size;
|
|
png->image_size -= size;
|
|
}
|
|
|
|
/* Begin PNG decoding
|
|
*/
|
|
static error
|
|
image_decoder_png_begin (image_decoder *decoder, const void *data,
|
|
size_t size)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
/* Create libpng structures */
|
|
png->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
|
png, image_decoder_png_error_fn, image_decoder_png_warning_fn,
|
|
png, image_decoder_png_malloc_fn, image_decoder_png_free_fn);
|
|
|
|
if (png->png_ptr == NULL) {
|
|
return ERROR("PNG: png_create_read_struct_2() failed");
|
|
}
|
|
|
|
png->info_ptr = png_create_info_struct(png->png_ptr);
|
|
if (png->info_ptr == NULL) {
|
|
image_decoder_reset(decoder);
|
|
return ERROR("PNG: png_create_info_struct() failed");
|
|
}
|
|
|
|
/* Setup read function */
|
|
png_set_read_fn(png->png_ptr, png, image_decoder_png_read_fn);
|
|
|
|
png->image_data = data;
|
|
png->image_size = size;
|
|
|
|
/* Read image info */
|
|
if (setjmp(png_jmpbuf(png->png_ptr))) {
|
|
image_decoder_reset(decoder);
|
|
return ERROR(png->error);
|
|
}
|
|
|
|
png_read_info(png->png_ptr, png->info_ptr);
|
|
png_get_IHDR(png->png_ptr, png->info_ptr, &png->width, &png->height,
|
|
&png->bit_depth, &png->color_type, &png->interlace_type, NULL, NULL);
|
|
|
|
png->num_lines = png->height;
|
|
|
|
/* Reject interlaced images */
|
|
if (png->interlace_type != PNG_INTERLACE_NONE) {
|
|
image_decoder_reset(decoder);
|
|
return ERROR("PNG: interlaced images not supported");
|
|
}
|
|
|
|
/* Setup input transformations */
|
|
if (png->color_type == PNG_COLOR_TYPE_PALETTE) {
|
|
png_set_palette_to_rgb(png->png_ptr);
|
|
}
|
|
|
|
if (png->color_type == PNG_COLOR_TYPE_GRAY && png->bit_depth < 8) {
|
|
png_set_expand_gray_1_2_4_to_8(png->png_ptr);
|
|
png->bit_depth = 8;
|
|
}
|
|
|
|
if ((png->color_type & PNG_COLOR_MASK_ALPHA) != 0) {
|
|
png_set_strip_alpha(png->png_ptr);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Reset PNG decoder
|
|
*/
|
|
static void
|
|
image_decoder_png_reset (image_decoder *decoder)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
if (png->png_ptr != NULL) {
|
|
png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
|
|
png->png_ptr = NULL;
|
|
png->info_ptr = NULL;
|
|
}
|
|
}
|
|
|
|
/* Get bytes count per pixel
|
|
*/
|
|
static int
|
|
image_decoder_png_get_bytes_per_pixel (image_decoder *decoder)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
int bit_depth = png->bit_depth;
|
|
|
|
if ((png->color_type & PNG_COLOR_MASK_COLOR) != 0) {
|
|
bit_depth *= 3;
|
|
}
|
|
|
|
return bit_depth / 3;
|
|
}
|
|
|
|
/* Get image parameters
|
|
*/
|
|
static void
|
|
image_decoder_png_get_params (image_decoder *decoder, SANE_Parameters *params)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
params->last_frame = SANE_TRUE;
|
|
params->pixels_per_line = png->width;
|
|
params->lines = png->height;
|
|
params->depth = png->bit_depth;
|
|
|
|
if ((png->color_type & PNG_COLOR_MASK_COLOR) != 0) {
|
|
params->format = SANE_FRAME_RGB;
|
|
params->bytes_per_line = params->pixels_per_line * 3;
|
|
} else {
|
|
params->format = SANE_FRAME_GRAY;
|
|
params->bytes_per_line = params->pixels_per_line;
|
|
}
|
|
}
|
|
|
|
/* Set clipping window
|
|
*/
|
|
static error
|
|
image_decoder_png_set_window (image_decoder *decoder, image_window *win)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
win->x_off = win->y_off = 0;
|
|
win->wid = png->width;
|
|
win->hei = png->height;
|
|
return NULL;
|
|
}
|
|
|
|
/* Read next line of image
|
|
*/
|
|
static error
|
|
image_decoder_png_read_line (image_decoder *decoder, void *buffer)
|
|
{
|
|
image_decoder_png *png = (image_decoder_png*) decoder;
|
|
|
|
if (!png->num_lines) {
|
|
return ERROR("PNG: end of file");
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(png->png_ptr))) {
|
|
image_decoder_reset(decoder);
|
|
return ERROR(png->error);
|
|
}
|
|
|
|
png_read_row(png->png_ptr, buffer, NULL);
|
|
png->num_lines --;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Create PNG image decoder
|
|
*/
|
|
image_decoder*
|
|
image_decoder_png_new (void)
|
|
{
|
|
image_decoder_png *png = mem_new(image_decoder_png, 1);
|
|
|
|
png->decoder.content_type = "image/png";
|
|
png->decoder.free = image_decoder_png_free;
|
|
png->decoder.begin = image_decoder_png_begin;
|
|
png->decoder.reset = image_decoder_png_reset;
|
|
png->decoder.get_bytes_per_pixel = image_decoder_png_get_bytes_per_pixel;
|
|
png->decoder.get_params = image_decoder_png_get_params;
|
|
png->decoder.set_window = image_decoder_png_set_window;
|
|
png->decoder.read_line = image_decoder_png_read_line;
|
|
|
|
return &png->decoder;
|
|
}
|
|
|
|
/* vim:ts=8:sw=4:et
|
|
*/
|