/*
* jpegsrc.c
*
* Copyright (C) 2022 Timo Kokkonen
* All Rights Reserved.
*
* Custom libjpeg "Source Manager" for reading from a file handle
* and optionally saving the input also into a memory buffer.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of JPEGoptim.
*
* JPEGoptim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JPEGoptim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JPEGoptim. If not, see .
*/
#include
#include
#include
#include
#include
#include "jpegoptim.h"
#define STDIO_BUFFER_SIZE 4096
/* Custom jpeg source manager object */
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
unsigned char **buf_ptr;
size_t *bufsize_ptr;
size_t *bufused_ptr;
size_t incsize;
unsigned char *buf;
size_t bufsize;
size_t bufused;
FILE *infile;
JOCTET *stdio_buffer;
boolean start_of_file;
} jpeg_custom_source_mgr;
typedef jpeg_custom_source_mgr* jpeg_custom_source_mgr_ptr;
void custom_init_source (j_decompress_ptr dinfo)
{
jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
src->bufused = 0;
if (src->bufused_ptr)
*src->bufused_ptr = 0;
src->start_of_file = TRUE;
}
boolean custom_fill_input_buffer (j_decompress_ptr dinfo)
{
jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
size_t bytes_read;
unsigned char *newbuf;
bytes_read = fread(src->stdio_buffer, 1, STDIO_BUFFER_SIZE, src->infile);
if (bytes_read <= 0) {
if (src->start_of_file)
ERREXIT(dinfo, JERR_INPUT_EMPTY);
WARNMS(dinfo, JWRN_JPEG_EOF);
/* Insert fake EOI marker if read failed */
src->stdio_buffer[0] = (JOCTET) 0xff;
src->stdio_buffer[1] = (JOCTET) JPEG_EOI;
bytes_read = 2;
} else if (src->buf_ptr && src->buf) {
if (bytes_read > (src->bufsize - src->bufused)) {
/* Need to allocate more memory for the buffer. */
src->bufsize += src->incsize;
newbuf = realloc(src->buf, src->bufsize);
if (!newbuf) ERREXIT1(dinfo, JERR_OUT_OF_MEMORY, 42);
src->buf = newbuf;
*src->buf_ptr = newbuf;
src->incsize *= 2;
}
memcpy(&src->buf[src->bufused], src->stdio_buffer, bytes_read);
src->bufused += bytes_read;
if (src->bufused_ptr)
*src->bufused_ptr = src->bufused;
}
src->pub.next_input_byte = src->stdio_buffer;
src->pub.bytes_in_buffer = bytes_read;
src->start_of_file = FALSE;
return TRUE;
}
void custom_skip_input_data (j_decompress_ptr dinfo, long num_bytes)
{
jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
if (num_bytes <= 0)
return;
/* skip "num_bytes" bytes of data from input... */
while (num_bytes > (long) src->pub.bytes_in_buffer) {
num_bytes -= src->pub.bytes_in_buffer;
(void) custom_fill_input_buffer(dinfo);
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
void custom_term_source (j_decompress_ptr dinfo)
{
jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
if (src->bufused_ptr)
*src->bufused_ptr = src->bufused;
}
void jpeg_custom_src(j_decompress_ptr dinfo, FILE *infile,
unsigned char **bufptr, size_t *bufsizeptr, size_t *bufusedptr, size_t incsize)
{
jpeg_custom_source_mgr_ptr src;
if (!dinfo->src) {
/* Allocate source manager object if needed */
dinfo->src = (struct jpeg_source_mgr *)
(*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
sizeof(jpeg_custom_source_mgr));
src = (jpeg_custom_source_mgr_ptr) dinfo->src;
src->stdio_buffer = (JOCTET *)
(*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
STDIO_BUFFER_SIZE * sizeof(JOCTET));
} else {
src = (jpeg_custom_source_mgr_ptr) dinfo->src;
}
src->pub.init_source = custom_init_source;
src->pub.fill_input_buffer = custom_fill_input_buffer;
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.skip_input_data = custom_skip_input_data;
src->pub.term_source = custom_term_source;
src->infile = infile;
src->pub.bytes_in_buffer = 0;
src->pub.next_input_byte = NULL;
src->buf_ptr = bufptr;
src->buf = (bufptr ? *bufptr : NULL);
src->bufsize_ptr = bufsizeptr;
src->bufused_ptr = bufusedptr;
src->bufsize = (bufsizeptr ? *bufsizeptr : 0);
src->incsize = incsize;
}
/* eof :-) */