Skip to content

Commit

Permalink
Support optimizing files read from stdin same as files read from a file.
Browse files Browse the repository at this point in the history
New "source manager" added that saves the input image in a memory buffer
when reading image from stdin.
  • Loading branch information
tjko committed Apr 6, 2022
1 parent 9a9c5c0 commit 05c3f85
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
DIRNAME = $(shell basename `pwd`)
DISTNAME = $(PKGNAME)-$(Version)

OBJS = $(PKGNAME).o jpegdest.o misc.o @GNUGETOPT@
OBJS = $(PKGNAME).o jpegdest.o jpegsrc.o misc.o @GNUGETOPT@

all: $(PKGNAME)

Expand Down
32 changes: 28 additions & 4 deletions jpegoptim.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,10 @@ int main(int argc, char **argv)
struct stat file_stat;
jpeg_saved_marker_ptr cmarker;
unsigned char *outbuffer = NULL;
size_t outbuffersize;
size_t outbuffersize = 0;
unsigned char *inbuffer = NULL;
size_t inbuffersize = 0;
size_t inbufferused = 0;
char *outfname = NULL;
FILE *infile = NULL, *outfile = NULL;
int marker_in_count, marker_in_size;
Expand Down Expand Up @@ -478,7 +481,7 @@ int main(int argc, char **argv)
i++;
}

if (stdin_mode) { stdout_mode=1; force=1; }
if (stdin_mode) { stdout_mode=1; }
if (stdout_mode) { logs_to_stdout=0; }

if (all_normal && all_progressive)
Expand Down Expand Up @@ -575,11 +578,17 @@ int main(int argc, char **argv)
}

/* prepare to decompress */
if (stdin_mode) {
if (inbuffer) free(inbuffer);
inbuffersize=65536;
inbuffer=malloc(inbuffersize);
if (!inbuffer) fatal("not enough memory");
}
global_error_counter=0;
jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
for (j=0;j<=15;j++)
jpeg_save_markers(&dinfo, JPEG_APP0+j, 0xffff);
jpeg_stdio_src(&dinfo, infile);
jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, 65536);
jpeg_read_header(&dinfo, TRUE);

/* check for Exif/IPTC/ICC/XMP markers */
Expand Down Expand Up @@ -667,6 +676,9 @@ int main(int argc, char **argv)
fflush(LOG_FH);
}

if (stdin_mode)
insize = inbufferused;

if (nofix_mode && global_error_counter != 0) {
/* skip files containing any errors (warnings) */
goto abort_decompress;
Expand Down Expand Up @@ -898,6 +910,8 @@ int main(int argc, char **argv)

if (preserve_mode) {
/* preserve file modification time */
if (verbose_mode > 1 && !quiet_mode)
fprintf(LOG_FH,"set file modification time same as in original: %s\n", outfname);
#if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
struct timespec time_save[2];
time_save[0].tv_sec = 0;
Expand All @@ -917,6 +931,8 @@ int main(int argc, char **argv)

if (preserve_perms && !dest) {
/* original file was already replaced, remove backup... */
if (verbose_mode > 1 && !quiet_mode)
fprintf(LOG_FH,"removing backup file: %s\n", tmpfilename);
if (delete_file(tmpfilename))
warn("failed to remove backup file: %s",tmpfilename);
} else {
Expand All @@ -939,6 +955,11 @@ int main(int argc, char **argv)
}
} else {
if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "skipped\n" : "skipped.\n");
if (stdout_mode) {
set_filemode_binary(stdout);
if (fwrite(inbuffer,insize,1,stdout) != 1)
fatal("%s, write failed to stdout",(stdin_mode?"stdin":argv[i]));
}
}


Expand All @@ -950,7 +971,10 @@ int main(int argc, char **argv)
average_count, average_rate/average_count, total_save);
jpeg_destroy_decompress(&dinfo);
jpeg_destroy_compress(&cinfo);
free(outbuffer);
if (outbuffer)
free(outbuffer);
if (inbuffer)
free(inbuffer);

return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);;
}
Expand Down
8 changes: 7 additions & 1 deletion jpegoptim.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ void warn(const char *format, ...);


/* jpegdest.c */
void jpeg_memory_dest (j_compress_ptr cinfo, unsigned char **bufptr, size_t *bufsizeptr, size_t incsize);
void jpeg_memory_dest (j_compress_ptr cinfo, unsigned char **bufptr,
size_t *bufsizeptr, size_t incsize);

/* jpegsrc.c */
void jpeg_custom_src(j_decompress_ptr dinfo, FILE *infile,
unsigned char **bufptr, size_t *bufsizeptr, size_t *bufusedptr, size_t incsize);




Expand Down
155 changes: 155 additions & 0 deletions jpegsrc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* 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.
*
* $Id$
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <jerror.h>

#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;

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. */
newbuf = realloc(src->buf,src->bufsize + src->incsize);
if (!newbuf) ERREXIT1(dinfo, JERR_OUT_OF_MEMORY, 42);
*src->buf_ptr = newbuf;
src->buf = newbuf;
src->bufsize += src->incsize;
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.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;
src->bufused = 0;
}

0 comments on commit 05c3f85

Please sign in to comment.