Skip to content

Commit a2ae62b

Browse files
committed
Initial Commit
Signed-off-by: Derek Buitenhuis <[email protected]>
0 parents  commit a2ae62b

15 files changed

+829
-0
lines changed

COPYING

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright (c) 2014, Derek Buitenhuis
2+
3+
Permission to use, copy, modify, and/or distribute this software for any
4+
purpose with or without fee is hereby granted, provided that the above
5+
copyright notice and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Makefile

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
OBJ = ijgdec.o \
2+
ijgenc.o \
3+
metric.o \
4+
api.o \
5+
main.o
6+
7+
LIBOBJ = ijgdec.lo \
8+
ijgenc.lo \
9+
metric.lo \
10+
api.lo
11+
12+
PREFIX=/usr/local
13+
CFLAGS=-Wall
14+
LDFLAGS=-s
15+
LIBS=-ljpeg -lm
16+
17+
all: smallfry lib
18+
19+
smallfry: $(OBJ)
20+
$(CC) -o $@ $^ $(LIBS)
21+
22+
$(LIBOBJ):
23+
$(CC) -fPIC $(CFLAGS) -c $(@:.lo=.c) -o $@
24+
25+
libsmallfry.so: $(LIBOBJ)
26+
$(CC) -Wl,--version-script,smallfry.ver -shared -o $@ $^ $(LIBS)
27+
28+
lib: libsmallfry.so
29+
30+
clean:
31+
@rm -vf ./*.o
32+
@rm -vf ./smallfry
33+
@rm -vf ./*.lo
34+
@rm -vf ./libsmallfry.so
35+
36+
install: smallfry
37+
@cp -vf ./smallfry $(PREFIX)/bin/smallfry
38+
39+
uninstall:
40+
@rm -vf $(PREFIX)/bin/smallfry

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## SmallFry ##
2+
3+
SmallFry is a small proof-of-concept JPEG "optimizer" with a permissive license. What it does is try and find the most you can compress a JPEG without suffering obvious perceived quality loss. It works similarly to how [JPEGmini](http://www.jpegmini.com) works, with some borrowed metrics from their SPIE papers. If you want a nice GUI and easier batch automation, take a look at JPEGmini.
4+
5+
**Background**
6+
7+
I wrote this hack-of-a-program in early 2013, and largely ignored it since. Now that [mozjpeg](https://github.com/mozilla/mozjpeg) has gain some small mindshared, I thought it might be useful to others, since now it can take advantage of trellis quantization and scan optimization provided by it.
8+
9+
**Usability**
10+
11+
A small CLI util is provided, along with a (crappy) API and library. The code is just C89, and can be compiled with pretty much any compiler. Binaries are provided for Windows in the releases section.
12+
13+
The code is very sub-optimal and slow. Pull requests are welcome. I am lazy.
14+
15+
Currently only JPEG input is supported, but it is absolutely trivial to add other input via [ImageMagick](http://www.imagemagick.org) or [libavcodec](http://ffmpeg.org) (maybe via [FFMS2](https://github.com/FFMS/ffms2)). The current code is just what I had hacked together previously; it is not optimal.
16+
17+
**Disclaimer**
18+
19+
This code is entirely mine, and unrelated to my employer in any way, and is not used by my employer.
20+
21+
Users of this code may want to look into any patents they may require to deploy it. I am not a lawyer.

api.c

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright (c) 2014, Derek Buitenhuis
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#include <errno.h>
18+
#include <stdint.h>
19+
#include <stdlib.h>
20+
#include <stdio.h>
21+
#include <string.h>
22+
23+
#include "smallfry.h"
24+
#include "ijgdec.h"
25+
#include "ijgenc.h"
26+
#include "metric.h"
27+
28+
void smallfryfreectx(smallfryctx **in)
29+
{
30+
smallfryctx *ctx = *in;
31+
32+
if (!ctx)
33+
return;
34+
35+
if (ctx->outbuf)
36+
free(ctx->outbuf);
37+
38+
if (ctx->decinput)
39+
free(ctx->decinput);
40+
41+
if (ctx->decoutput)
42+
free(ctx->decoutput);
43+
44+
if (ctx->inplanar)
45+
free(ctx->inplanar);
46+
47+
if (ctx->outplanar)
48+
free(ctx->outplanar);
49+
50+
free(ctx);
51+
52+
*in = NULL;
53+
}
54+
55+
smallfryctx *smallfryallocctx(uint8_t *inbuf, uint64_t size)
56+
{
57+
smallfryctx *ret;
58+
int bufsize;
59+
60+
ret = calloc(sizeof(smallfryctx), 1);
61+
if (!ret)
62+
goto allocfail;
63+
64+
ret->inbuf = inbuf;
65+
ret->isize = size;
66+
ret->osize = 0;
67+
68+
ret->res = jpeg_get_res(ret->inbuf, ret->isize);
69+
if (!ret->res.width)
70+
goto allocfail;
71+
72+
bufsize = ret->res.width * ret->res.height * 3;
73+
74+
ret->outbuf = malloc(bufsize);
75+
ret->decinput = malloc(bufsize);
76+
ret->decoutput = malloc(bufsize);
77+
ret->inplanar = malloc(bufsize / 3);
78+
ret->outplanar = malloc(bufsize / 3);
79+
if (!ret->outbuf || !ret->decinput || !ret->decoutput || !ret->inplanar || !ret->outplanar)
80+
goto allocfail;
81+
82+
return ret;
83+
84+
allocfail:
85+
smallfryfreectx(&ret);
86+
return NULL;
87+
}
88+
89+
static void process_planar(uint8_t *interleaved, uint8_t *planar, jinfo res)
90+
{
91+
int i;
92+
int j = 0;
93+
uint8_t *out = planar;
94+
95+
for (i = 0; i < res.width * res.height * 3; i += 3) {
96+
out[j] = interleaved[i];
97+
j++;
98+
}
99+
}
100+
101+
int smallfryoptimize(smallfryctx *ctx)
102+
{
103+
const double threshold = 99.91 + 4.13 / 2;
104+
105+
int ret;
106+
int qmin = 0, qmax = 100;
107+
double cur = threshold + 1;
108+
109+
ret = jpeg_decode(ctx->inbuf, ctx->decinput, ctx->isize);
110+
if (ret < 0)
111+
return EINVAL;
112+
113+
process_planar(ctx->decinput, ctx->inplanar, ctx->res);
114+
115+
while (qmin <= qmax) {
116+
int size;
117+
int quality = (qmin + qmax) / 2;
118+
119+
size = jpeg_encode(ctx->decinput, ctx->outbuf, ctx->res, quality);
120+
121+
jpeg_decode(ctx->outbuf, ctx->decoutput, size);
122+
123+
process_planar(ctx->decoutput, ctx->outplanar, ctx->res);
124+
125+
cur = getmetric(ctx->inplanar, ctx->outplanar, ctx->res.width, ctx->res.height);
126+
127+
if (cur < threshold)
128+
qmin = quality + 1;
129+
else
130+
qmax = quality - 1;
131+
}
132+
133+
printf("Using quality = %d.\n", qmax + 1);
134+
135+
ctx->osize = jpeg_encode(ctx->decinput, ctx->outbuf, ctx->res, qmax + 1);
136+
137+
return 0;
138+
}
139+
140+
uint64_t smallfrygetsize(smallfryctx *ctx)
141+
{
142+
return ctx->osize;
143+
}
144+
145+
uint8_t *smallfrygetbuffer(smallfryctx *ctx)
146+
{
147+
return ctx->outbuf;
148+
}

build.bat

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@echo off
2+
cl /MT /GL /c /I. *.c
3+
link /LTCG /out:smallfry.exe *.obj jpeg-static.lib
4+
link /LTCG /dll /def:smallfry.def /out:smallfry.dll *.obj jpeg-static.lib

ijgdec.c

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (c) 2014, Derek Buitenhuis
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#include <stdint.h>
18+
#include <stdio.h>
19+
#include <string.h>
20+
21+
#include <jerror.h>
22+
#include <jpeglib.h>
23+
24+
#include "ijgdec.h"
25+
26+
static const JOCTET EOI_BUFFER[1] = { JPEG_EOI };
27+
28+
static void init(j_decompress_ptr cinfo)
29+
{
30+
}
31+
32+
static boolean input(j_decompress_ptr cinfo)
33+
{
34+
cinfo->src->next_input_byte = EOI_BUFFER;
35+
cinfo->src->bytes_in_buffer = 1;
36+
37+
return TRUE;
38+
}
39+
40+
static void skip(j_decompress_ptr cinfo, long num_bytes)
41+
{
42+
if (cinfo->src->bytes_in_buffer < num_bytes) {
43+
cinfo->src->next_input_byte = EOI_BUFFER;
44+
cinfo->src->bytes_in_buffer = 1;
45+
} else {
46+
cinfo->src->next_input_byte += num_bytes;
47+
cinfo->src->bytes_in_buffer -= num_bytes;
48+
}
49+
}
50+
51+
static void term(j_decompress_ptr cinfo)
52+
{
53+
}
54+
55+
jinfo jpeg_get_res(uint8_t *inb, uint64_t insize)
56+
{
57+
struct jpeg_decompress_struct cinfo;
58+
struct jpeg_source_mgr s;
59+
struct jpeg_error_mgr jerr;
60+
jinfo ret;
61+
62+
jpeg_create_decompress(&cinfo);
63+
64+
cinfo.err = jpeg_std_error(&jerr);
65+
cinfo.src = &s;
66+
67+
cinfo.src->init_source = init;
68+
cinfo.src->fill_input_buffer = input;
69+
cinfo.src->skip_input_data = skip;
70+
cinfo.src->resync_to_restart = jpeg_resync_to_restart;
71+
cinfo.src->term_source = term;
72+
cinfo.src->bytes_in_buffer = insize;
73+
cinfo.src->next_input_byte = (const JOCTET *) inb;
74+
75+
jpeg_read_header(&cinfo, TRUE);
76+
jpeg_start_decompress(&cinfo);
77+
78+
ret.width = cinfo.output_width;
79+
ret.height = cinfo.output_height;
80+
ret.vsamp = cinfo.comp_info[0].v_samp_factor;
81+
ret.hsamp = cinfo.comp_info[0].h_samp_factor;
82+
83+
jpeg_destroy_decompress(&cinfo);
84+
85+
return ret;
86+
}
87+
88+
int jpeg_decode(uint8_t *inb, uint8_t *o, uint64_t insize)
89+
{
90+
JSAMPARRAY outb;
91+
struct jpeg_decompress_struct cinfo;
92+
struct jpeg_source_mgr s;
93+
struct jpeg_error_mgr jerr;
94+
95+
jpeg_create_decompress(&cinfo);
96+
97+
cinfo.err = jpeg_std_error(&jerr);
98+
cinfo.src = &s;
99+
100+
cinfo.src->init_source = init;
101+
cinfo.src->fill_input_buffer = input;
102+
cinfo.src->skip_input_data = skip;
103+
cinfo.src->resync_to_restart = jpeg_resync_to_restart;
104+
cinfo.src->term_source = term;
105+
cinfo.src->bytes_in_buffer = insize;
106+
cinfo.src->next_input_byte = (const JOCTET *) inb;
107+
108+
jpeg_read_header(&cinfo, TRUE);
109+
110+
cinfo.out_color_space = JCS_YCbCr;
111+
112+
jpeg_start_decompress(&cinfo);
113+
114+
outb = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,
115+
cinfo.output_width * cinfo.output_components, 1);
116+
if (!outb)
117+
return -1;
118+
119+
while (cinfo.output_scanline < cinfo.output_height) {
120+
jpeg_read_scanlines(&cinfo, outb, 1);
121+
memcpy(o + cinfo.output_width * cinfo.output_components * (cinfo.output_scanline - 1),
122+
outb[0], cinfo.output_width * cinfo.output_components);
123+
}
124+
125+
jpeg_finish_decompress(&cinfo);
126+
127+
jpeg_destroy_decompress(&cinfo);
128+
129+
return 0;
130+
}

ijgdec.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2014, Derek Buitenhuis
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#ifndef IJGDEC_H
18+
#define IJGDEC_H
19+
20+
typedef struct jinfo {
21+
int width;
22+
int height;
23+
int vsamp;
24+
int hsamp;
25+
} jinfo;
26+
27+
jinfo jpeg_get_res(uint8_t *inb, uint64_t insize);
28+
int jpeg_decode(uint8_t *inb, uint8_t *o, uint64_t insize);
29+
30+
#endif

0 commit comments

Comments
 (0)