lilimg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fa73b9d4febb1427ef8da91517ccc7df809ae7df820525dee2c4abdeda75729a
4
+ data.tar.gz: c5a44effd659b3321be9694f6deab2b680e2c09b6bff7120995b257b3ea91839
5
+ SHA512:
6
+ metadata.gz: 93f8cf6b722e6cd944c6f3129af3e66bb23734e0c761897a69c566b6df9daa0657ba9196b72e44719314bf759b9f22ba393d5046b03d7e8dfe0f968551bf3473
7
+ data.tar.gz: c827a20106b4f6f00f9d5653fa9304affcf58c3bce510af57a82ca13634f720dcdb420b9992233620ab08d2190e2c8a2c8cf435a06efe125b57535082ea7bbf9
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ ext/Makefile
2
+ ext/*.o
3
+ ext/*.so
4
+ other
5
+ example
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/example/build.sh ADDED
@@ -0,0 +1,2 @@
1
+ # gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c -lm
2
+ gcc -O3 -o test test.c -I ../ext -lm
data/example/label.jpg ADDED
Binary file
data/example/test.c ADDED
@@ -0,0 +1,86 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include "nanojpeg.h"
5
+ #include "jo_gif.h"
6
+
7
+ unsigned char * rgb_to_rgba(unsigned char * buf, int w, int h) {
8
+ int newsize = 4 * w * h;
9
+ unsigned char * dest;
10
+ dest = malloc(newsize);
11
+ int s = 0, d = 0;
12
+ while (d < newsize) {
13
+ dest[d++] = buf[s++];
14
+ dest[d++] = buf[s++];
15
+ dest[d++] = buf[s++];
16
+ dest[d++] = 255; // fully opaque
17
+ }
18
+
19
+ return dest;
20
+ }
21
+
22
+ int write_file(const char * name, char * buf, int size) {
23
+ FILE * fp = fopen(name, "wb");
24
+ if (!fp) {
25
+ return -1;
26
+ }
27
+
28
+ fwrite(buf, size, 1, fp);
29
+ fclose(fp);
30
+ return 0;
31
+ }
32
+
33
+ int main(int argc, char* argv[]) {
34
+ int size;
35
+ char *buf;
36
+ FILE *f;
37
+
38
+ if (argc < 2) {
39
+ printf("Usage: %s <input.jpg> [<output.gif>]\n", argv[0]);
40
+ return 2;
41
+ }
42
+ f = fopen(argv[1], "rb");
43
+ if (!f) {
44
+ printf("Error opening the input file.\n");
45
+ return 1;
46
+ }
47
+
48
+ fseek(f, 0, SEEK_END);
49
+ size = (int) ftell(f);
50
+ buf = (char*) malloc(size);
51
+ fseek(f, 0, SEEK_SET);
52
+ size = (int) fread(buf, 1, size, f);
53
+ fclose(f);
54
+
55
+ printf("size: %d\n", size);
56
+
57
+ njInit();
58
+ if (njDecode(buf, size)) {
59
+ free((void*)buf);
60
+ printf("Error decoding the input file.\n");
61
+ return 1;
62
+ }
63
+ free((void*)buf);
64
+
65
+ unsigned char * pixels = njGetImage();
66
+ unsigned char * rgba = rgb_to_rgba(pixels, njGetWidth(), njGetHeight());
67
+ int maxColors = 8;
68
+
69
+ char * out;
70
+ size_t bufsize;
71
+ FILE * fp = open_memstream(&out, &bufsize);
72
+ // FILE * fp = fopen("output.gif", "wb");
73
+
74
+ jo_gif_t gif = jo_gif_start(fp, njGetWidth(), njGetHeight(), 0, maxColors);
75
+ jo_gif_frame(&gif, rgba, 0, 0);
76
+ jo_gif_end(&gif);
77
+
78
+ // fclose(fp);
79
+ printf(" --> %d %d\n", bufsize, strlen(rgba));
80
+ write_file("output.gif", out, bufsize);
81
+
82
+ free(rgba);
83
+ free(out);
84
+ njDone();
85
+ return 0;
86
+ }
data/example/test.rb ADDED
@@ -0,0 +1,7 @@
1
+ require('./lili')
2
+
3
+ jpeg = IO.read('../label.jpg')
4
+ gif = Lili.jpeg_to_gif(jpeg)
5
+ # puts gif.length
6
+
7
+ File.open('out.gif', 'wb') { |f| f.write(gif) })
data/ext/extconf.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+ extension_name = 'lilimg'
3
+ dir_config(extension_name)
4
+
5
+
6
+ CONFIG['warnflags'].slice!(/ -Wdeclaration-after-statement/)
7
+ create_makefile(extension_name)
data/ext/jo_gif.h ADDED
@@ -0,0 +1,427 @@
1
+ /* public domain, Simple, Minimalistic GIF writer - http://jonolick.com
2
+ *
3
+ * Quick Notes:
4
+ * Supports only 4 component input, alpha is currently ignored. (RGBX)
5
+ *
6
+ * Latest revisions:
7
+ * 1.00 (2015-11-03) initial release
8
+ *
9
+ * Basic usage:
10
+ * char *frame = new char[128*128*4]; // 4 component. RGBX format, where X is unused
11
+ * jo_gif_t gif = jo_gif_start("foo.gif", 128, 128, 0, 32);
12
+ * jo_gif_frame(&gif, frame, 4, false); // frame 1
13
+ * jo_gif_frame(&gif, frame, 4, false); // frame 2
14
+ * jo_gif_frame(&gif, frame, 4, false); // frame 3, ...
15
+ * jo_gif_end(&gif);
16
+ * */
17
+
18
+ #ifndef JO_INCLUDE_GIF_H
19
+ #define JO_INCLUDE_GIF_H
20
+
21
+ // typedef uint8_t bool;
22
+
23
+ #include <stdio.h>
24
+
25
+ // To get a header file for this, either cut and paste the header,
26
+ // or create jo_gif.h, #define JO_GIF_HEADER_FILE_ONLY, and
27
+ // then include jo_gif.cpp from it.
28
+
29
+ typedef struct {
30
+ FILE *fp;
31
+ char * buf;
32
+ size_t bufsize;
33
+ unsigned char palette[0x300];
34
+ short width, height, repeat;
35
+ int numColors, palSize;
36
+ int frame;
37
+ } jo_gif_t;
38
+
39
+ // width/height | the same for every frame
40
+ // repeat | 0 = loop forever, 1 = loop once, etc...
41
+ // palSize | must be power of 2 - 1. so, 255 not 256.
42
+ extern jo_gif_t jo_gif_start(FILE * fp, short width, short height, short repeat, int numColors);
43
+
44
+ // gif | the state (returned from jo_gif_start)
45
+ // rgba | the pixels
46
+ // delayCsec | amount of time in between frames (in centiseconds)
47
+ // localPalette | true if you want a unique palette generated for this frame (does not effect future frames)
48
+ extern void jo_gif_frame(jo_gif_t *gif, unsigned char *rgba, short delayCsec, int localPalette);
49
+
50
+ // gif | the state (returned from jo_gif_start)
51
+ extern void jo_gif_end(jo_gif_t *gif);
52
+
53
+ #endif
54
+
55
+ #ifndef JO_GIF_HEADER_FILE_ONLY
56
+
57
+ #if defined(_MSC_VER) && _MSC_VER >= 0x1400
58
+ #define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen()
59
+ #endif
60
+
61
+ #include <stdlib.h>
62
+ #include <memory.h>
63
+ #include <math.h>
64
+
65
+ // Based on NeuQuant algorithm
66
+ static void jo_gif_quantize(unsigned char *rgba, int rgbaSize, int sample, unsigned char *map, int numColors) {
67
+ // defs for freq and bias
68
+ const int intbiasshift = 16; /* bias for fractions */
69
+ const int intbias = (((int) 1) << intbiasshift);
70
+ const int gammashift = 10; /* gamma = 1024 */
71
+ const int betashift = 10;
72
+ const int beta = (intbias >> betashift); /* beta = 1/1024 */
73
+ const int betagamma = (intbias << (gammashift - betashift));
74
+
75
+ // defs for decreasing radius factor
76
+ const int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
77
+ const int radiusbias = (((int) 1) << radiusbiasshift);
78
+ const int radiusdec = 30; /* factor of 1/30 each cycle */
79
+
80
+ // defs for decreasing alpha factor
81
+ const int alphabiasshift = 10; /* alpha starts at 1.0 */
82
+ const int initalpha = (((int) 1) << alphabiasshift);
83
+
84
+ // radbias and alpharadbias used for radpower calculation
85
+ const int radbiasshift = 8;
86
+ const int radbias = (((int) 1) << radbiasshift);
87
+ const int alpharadbshift = (alphabiasshift + radbiasshift);
88
+ const int alpharadbias = (((int) 1) << alpharadbshift);
89
+
90
+ sample = sample < 1 ? 1 : sample > 30 ? 30 : sample;
91
+ int network[256][3];
92
+ int bias[256] = {}, freq[256];
93
+ int i;
94
+ for(i = 0; i < numColors; ++i) {
95
+ // Put nurons evenly through the luminance spectrum.
96
+ network[i][0] = network[i][1] = network[i][2] = (i << 12) / numColors;
97
+ freq[i] = intbias / numColors;
98
+ }
99
+ // Learn
100
+ {
101
+ const int primes[5] = {499, 491, 487, 503};
102
+ int step = 4;
103
+ for(i = 0; i < 4; ++i) {
104
+ if(rgbaSize > primes[i] * 4 && (rgbaSize % primes[i])) { // TODO/Error? primes[i]*4?
105
+ step = primes[i] * 4;
106
+ }
107
+ }
108
+ sample = step == 4 ? 1 : sample;
109
+
110
+ int alphadec = 30 + ((sample - 1) / 3);
111
+ int samplepixels = rgbaSize / (4 * sample);
112
+ int delta = samplepixels / 100;
113
+ int alpha = initalpha;
114
+ delta = delta == 0 ? 1 : delta;
115
+
116
+ int radius = (numColors >> 3) * radiusbias;
117
+ int rad = radius >> radiusbiasshift;
118
+ rad = rad <= 1 ? 0 : rad;
119
+ int radSq = rad*rad;
120
+ int radpower[32];
121
+ for (i = 0; i < rad; i++) {
122
+ radpower[i] = alpha * (((radSq - i * i) * radbias) / radSq);
123
+ }
124
+
125
+ // Randomly walk through the pixels and relax neurons to the "optimal" target.
126
+ int pix;
127
+ for(i = 0, pix = 0; i < samplepixels;) {
128
+ int r = rgba[pix + 0] << 4;
129
+ int g = rgba[pix + 1] << 4;
130
+ int b = rgba[pix + 2] << 4;
131
+ int j = -1;
132
+ {
133
+ // finds closest neuron (min dist) and updates freq
134
+ // finds best neuron (min dist-bias) and returns position
135
+ // for frequently chosen neurons, freq[k] is high and bias[k] is negative
136
+ // bias[k] = gamma*((1/numColors)-freq[k])
137
+
138
+ int bestd = 0x7FFFFFFF, bestbiasd = 0x7FFFFFFF, bestpos = -1;
139
+ int k;
140
+ for (k = 0; k < numColors; k++) {
141
+ int *n = network[k];
142
+ int dist = abs(n[0] - r) + abs(n[1] - g) + abs(n[2] - b);
143
+ if (dist < bestd) {
144
+ bestd = dist;
145
+ bestpos = k;
146
+ }
147
+ int biasdist = dist - ((bias[k]) >> (intbiasshift - 4));
148
+ if (biasdist < bestbiasd) {
149
+ bestbiasd = biasdist;
150
+ j = k;
151
+ }
152
+ int betafreq = freq[k] >> betashift;
153
+ freq[k] -= betafreq;
154
+ bias[k] += betafreq << gammashift;
155
+ }
156
+ freq[bestpos] += beta;
157
+ bias[bestpos] -= betagamma;
158
+ }
159
+
160
+ // Move neuron j towards biased (b,g,r) by factor alpha
161
+ network[j][0] -= (network[j][0] - r) * alpha / initalpha;
162
+ network[j][1] -= (network[j][1] - g) * alpha / initalpha;
163
+ network[j][2] -= (network[j][2] - b) * alpha / initalpha;
164
+ if (rad != 0) {
165
+ // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
166
+ int lo = j - rad;
167
+ lo = lo < -1 ? -1 : lo;
168
+ int hi = j + rad;
169
+ hi = hi > numColors ? numColors : hi;
170
+ int jj, m, k;
171
+ for(jj = j+1, m=1; jj < hi; ++jj) {
172
+ int a = radpower[m++];
173
+ network[jj][0] -= (network[jj][0] - r) * a / alpharadbias;
174
+ network[jj][1] -= (network[jj][1] - g) * a / alpharadbias;
175
+ network[jj][2] -= (network[jj][2] - b) * a / alpharadbias;
176
+ }
177
+ for(k = j-1, m=1; k > lo; --k) {
178
+ int a = radpower[m++];
179
+ network[k][0] -= (network[k][0] - r) * a / alpharadbias;
180
+ network[k][1] -= (network[k][1] - g) * a / alpharadbias;
181
+ network[k][2] -= (network[k][2] - b) * a / alpharadbias;
182
+ }
183
+ }
184
+
185
+ pix += step;
186
+ pix = pix >= rgbaSize ? pix - rgbaSize : pix;
187
+
188
+ // every 1% of the image, move less over the following iterations.
189
+ if(++i % delta == 0) {
190
+ alpha -= alpha / alphadec;
191
+ radius -= radius / radiusdec;
192
+ rad = radius >> radiusbiasshift;
193
+ rad = rad <= 1 ? 0 : rad;
194
+ radSq = rad*rad;
195
+ for (j = 0; j < rad; j++) {
196
+ radpower[j] = alpha * ((radSq - j * j) * radbias / radSq);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ // Unbias network to give byte values 0..255
202
+ for (i = 0; i < numColors; i++) {
203
+ map[i*3+0] = network[i][0] >>= 4;
204
+ map[i*3+1] = network[i][1] >>= 4;
205
+ map[i*3+2] = network[i][2] >>= 4;
206
+ }
207
+ }
208
+
209
+ typedef struct {
210
+ FILE *fp;
211
+ int numBits;
212
+ unsigned char buf[256];
213
+ unsigned char idx;
214
+ unsigned tmp;
215
+ int outBits;
216
+ int curBits;
217
+ } jo_gif_lzw_t;
218
+
219
+ static void jo_gif_lzw_write(jo_gif_lzw_t *s, int code) {
220
+ s->outBits |= code << s->curBits;
221
+ s->curBits += s->numBits;
222
+ while(s->curBits >= 8) {
223
+ s->buf[s->idx++] = s->outBits & 255;
224
+ s->outBits >>= 8;
225
+ s->curBits -= 8;
226
+ if (s->idx >= 255) {
227
+ putc(s->idx, s->fp);
228
+ fwrite(s->buf, s->idx, 1, s->fp);
229
+ s->idx = 0;
230
+ }
231
+ }
232
+ }
233
+
234
+ static void jo_gif_lzw_encode(unsigned char *in, int len, FILE *fp) {
235
+ jo_gif_lzw_t state = {fp, 9};
236
+ int maxcode = 511;
237
+
238
+ // Note: 30k stack space for dictionary =|
239
+ const int hashSize = 5003;
240
+ short codetab[hashSize];
241
+ int hashTbl[hashSize];
242
+ memset(hashTbl, 0xFF, sizeof(hashTbl));
243
+
244
+ jo_gif_lzw_write(&state, 0x100);
245
+
246
+ int free_ent = 0x102;
247
+ int ent = *in++;
248
+ CONTINUE:
249
+ while (--len) {
250
+ int c = *in++;
251
+ int fcode = (c << 12) + ent;
252
+ int key = (c << 4) ^ ent; // xor hashing
253
+ while(hashTbl[key] >= 0) {
254
+ if(hashTbl[key] == fcode) {
255
+ ent = codetab[key];
256
+ goto CONTINUE;
257
+ }
258
+ ++key;
259
+ key = key >= hashSize ? key - hashSize : key;
260
+ }
261
+ jo_gif_lzw_write(&state, ent);
262
+ ent = c;
263
+ if(free_ent < 4096) {
264
+ if(free_ent > maxcode) {
265
+ ++state.numBits;
266
+ if(state.numBits == 12) {
267
+ maxcode = 4096;
268
+ } else {
269
+ maxcode = (1<<state.numBits)-1;
270
+ }
271
+ }
272
+ codetab[key] = free_ent++;
273
+ hashTbl[key] = fcode;
274
+ } else {
275
+ memset(hashTbl, 0xFF, sizeof(hashTbl));
276
+ free_ent = 0x102;
277
+ jo_gif_lzw_write(&state, 0x100);
278
+ state.numBits = 9;
279
+ maxcode = 511;
280
+ }
281
+ }
282
+ jo_gif_lzw_write(&state, ent);
283
+ jo_gif_lzw_write(&state, 0x101);
284
+ jo_gif_lzw_write(&state, 0);
285
+ if(state.idx) {
286
+ putc(state.idx, fp);
287
+ fwrite(state.buf, state.idx, 1, fp);
288
+ }
289
+ }
290
+
291
+ static int jo_gif_clamp(int a, int b, int c) { return a < b ? b : a > c ? c : a; }
292
+
293
+ /*
294
+
295
+
296
+ jo_gif_t * jo_gif_start(FILE * fp, short width, short height, short repeat, int numColors) {
297
+ numColors = numColors > 255 ? 255 : numColors < 2 ? 2 : numColors;
298
+ jo_gif_t * gif; // = {};
299
+ gif->width = width;
300
+ gif->height = height;
301
+ gif->repeat = repeat;
302
+ gif->numColors = numColors;
303
+ gif->palSize = log2(numColors);
304
+
305
+ gif->fp = fp;
306
+ fwrite("GIF89a", 6, 1, gif->fp);
307
+ // Logical Screen Descriptor
308
+ fwrite(&gif->width, 2, 1, gif->fp);
309
+ fwrite(&gif->height, 2, 1, gif->fp);
310
+ putc(0xF0 | gif->palSize, gif->fp);
311
+ fwrite("\x00\x00", 2, 1, gif->fp); // bg color index (unused), aspect ratio
312
+ return gif;
313
+ }
314
+
315
+ */
316
+
317
+ jo_gif_t jo_gif_start(FILE * fp, short width, short height, short repeat, int numColors) {
318
+ numColors = numColors > 255 ? 255 : numColors < 2 ? 2 : numColors;
319
+ jo_gif_t gif = {};
320
+ gif.width = width;
321
+ gif.height = height;
322
+ gif.repeat = repeat;
323
+ gif.numColors = numColors;
324
+ gif.palSize = log2(numColors);
325
+
326
+ gif.fp = fp;
327
+ fwrite("GIF89a", 6, 1, gif.fp);
328
+ // Logical Screen Descriptor
329
+ fwrite(&gif.width, 2, 1, gif.fp);
330
+ fwrite(&gif.height, 2, 1, gif.fp);
331
+ putc(0xF0 | gif.palSize, gif.fp);
332
+ fwrite("\x00\x00", 2, 1, gif.fp); // bg color index (unused), aspect ratio
333
+ return gif;
334
+ }
335
+
336
+ void jo_gif_frame(jo_gif_t *gif, unsigned char * rgba, short delayCsec, int localPalette) {
337
+ if(!gif->fp) {
338
+ printf("Output not initialized.\n");
339
+ return;
340
+ }
341
+ short width = gif->width;
342
+ short height = gif->height;
343
+ int size = width * height;
344
+
345
+ unsigned char localPalTbl[0x300];
346
+ unsigned char *palette = gif->frame == 0 || !localPalette ? gif->palette : localPalTbl;
347
+ if(gif->frame == 0 || localPalette) {
348
+ jo_gif_quantize(rgba, size*4, 1, palette, gif->numColors);
349
+ }
350
+
351
+ unsigned char *indexedPixels = (unsigned char *)malloc(size);
352
+ {
353
+ unsigned char *ditheredPixels = (unsigned char*)malloc(size*4);
354
+ memcpy(ditheredPixels, rgba, size*4);
355
+ int i, k;
356
+ for(k = 0; k < size*4; k+=4) {
357
+ int rgb[3] = { ditheredPixels[k+0], ditheredPixels[k+1], ditheredPixels[k+2] };
358
+ int bestd = 0x7FFFFFFF, best = -1;
359
+ // TODO: exhaustive search. do something better.
360
+ for(i = 0; i < gif->numColors; ++i) {
361
+ int bb = palette[i*3+0]-rgb[0];
362
+ int gg = palette[i*3+1]-rgb[1];
363
+ int rr = palette[i*3+2]-rgb[2];
364
+ int d = bb*bb + gg*gg + rr*rr;
365
+ if(d < bestd) {
366
+ bestd = d;
367
+ best = i;
368
+ }
369
+ }
370
+ indexedPixels[k/4] = best;
371
+ int diff[3] = { ditheredPixels[k+0] - palette[indexedPixels[k/4]*3+0], ditheredPixels[k+1] - palette[indexedPixels[k/4]*3+1], ditheredPixels[k+2] - palette[indexedPixels[k/4]*3+2] };
372
+ // Floyd-Steinberg Error Diffusion
373
+ // TODO: Use something better -- http://caca.zoy.org/study/part3.html
374
+ if(k+4 < size*4) {
375
+ ditheredPixels[k+4+0] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+0]+(diff[0]*7/16), 0, 255);
376
+ ditheredPixels[k+4+1] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+1]+(diff[1]*7/16), 0, 255);
377
+ ditheredPixels[k+4+2] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+2]+(diff[2]*7/16), 0, 255);
378
+ }
379
+ if(k+width*4+4 < size*4) {
380
+ for(i = 0; i < 3; ++i) {
381
+ ditheredPixels[k-4+width*4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k-4+width*4+i]+(diff[i]*3/16), 0, 255);
382
+ ditheredPixels[k+width*4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k+width*4+i]+(diff[i]*5/16), 0, 255);
383
+ ditheredPixels[k+width*4+4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k+width*4+4+i]+(diff[i]*1/16), 0, 255);
384
+ }
385
+ }
386
+ }
387
+ free(ditheredPixels);
388
+ }
389
+ if(gif->frame == 0) {
390
+ // Global Color Table
391
+ fwrite(palette, 3*(1<<(gif->palSize+1)), 1, gif->fp);
392
+ if(gif->repeat >= 0) {
393
+ // Netscape Extension
394
+ fwrite("\x21\xff\x0bNETSCAPE2.0\x03\x01", 16, 1, gif->fp);
395
+ fwrite(&gif->repeat, 2, 1, gif->fp); // loop count (extra iterations, 0=repeat forever)
396
+ putc(0, gif->fp); // block terminator
397
+ }
398
+ }
399
+ // Graphic Control Extension
400
+ fwrite("\x21\xf9\x04\x00", 4, 1, gif->fp);
401
+ fwrite(&delayCsec, 2, 1, gif->fp); // delayCsec x 1/100 sec
402
+ fwrite("\x00\x00", 2, 1, gif->fp); // transparent color index (first byte), currently unused
403
+ // Image Descriptor
404
+ fwrite("\x2c\x00\x00\x00\x00", 5, 1, gif->fp); // header, x,y
405
+ fwrite(&width, 2, 1, gif->fp);
406
+ fwrite(&height, 2, 1, gif->fp);
407
+ if (gif->frame == 0 || !localPalette) {
408
+ putc(0, gif->fp);
409
+ } else {
410
+ putc(0x80|gif->palSize, gif->fp );
411
+ fwrite(palette, 3*(1<<(gif->palSize+1)), 1, gif->fp);
412
+ }
413
+ putc(8, gif->fp); // block terminator
414
+ jo_gif_lzw_encode(indexedPixels, size, gif->fp);
415
+ putc(0, gif->fp); // block terminator
416
+ ++gif->frame;
417
+ free(indexedPixels);
418
+ }
419
+
420
+ void jo_gif_end(jo_gif_t *gif) {
421
+ if(!gif->fp) {
422
+ return;
423
+ }
424
+ putc(0x3b, gif->fp); // gif trailer
425
+ fclose(gif->fp);
426
+ }
427
+ #endif
data/ext/lilimg.c ADDED
@@ -0,0 +1,71 @@
1
+ #include "ruby.h"
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include "nanojpeg.h"
7
+ #include "jo_gif.h"
8
+
9
+ unsigned char * rgb_to_rgba(unsigned char * buf, int w, int h) {
10
+ int newsize = 4 * w * h;
11
+ unsigned char * dest;
12
+ dest = malloc(newsize);
13
+ int s = 0, d = 0;
14
+ while (d < newsize) {
15
+ dest[d++] = buf[s++];
16
+ dest[d++] = buf[s++];
17
+ dest[d++] = buf[s++];
18
+ dest[d++] = 255; // fully opaque
19
+ }
20
+
21
+ return dest;
22
+ }
23
+
24
+ static VALUE jpeg_to_gif(VALUE self, VALUE buf) {
25
+ rb_check_type(buf, T_STRING);
26
+ int size = RSTRING_LEN(buf);
27
+ char * jpeg = StringValuePtr(buf);
28
+
29
+ njInit();
30
+ if (njDecode(jpeg, size)) {
31
+ free((void*)jpeg);
32
+ rb_raise(rb_eRuntimeError, "Error decoding input file (size %d)", size);
33
+ return Qnil;
34
+ }
35
+
36
+ free((void*)jpeg);
37
+
38
+ char * out;
39
+ size_t bufsize;
40
+ FILE * fp = open_memstream(&out, &bufsize);
41
+ // FILE * fp = fopen("output.gif", "wb");
42
+
43
+ if (!fp) {
44
+ rb_raise(rb_eRuntimeError, "Cannot initialize memstream");
45
+ return Qnil;
46
+ }
47
+
48
+ unsigned char * pixels = njGetImage();
49
+ unsigned char * rgba = rgb_to_rgba(pixels, njGetWidth(), njGetHeight());
50
+ int maxColors = 8;
51
+
52
+ jo_gif_t gif = jo_gif_start(fp, njGetWidth(), njGetHeight(), 0, maxColors);
53
+ jo_gif_frame(&gif, rgba, 0, 0);
54
+ jo_gif_end(&gif);
55
+
56
+ VALUE rstr;
57
+ rstr = rb_str_new(out, bufsize);
58
+
59
+ free(rgba);
60
+ free(out);
61
+ njDone();
62
+
63
+ return rstr;
64
+ }
65
+
66
+ VALUE Lilimg = Qnil;
67
+
68
+ void Init_lilimg() {
69
+ Lilimg = rb_define_module("Lilimg");
70
+ rb_define_module_function(Lilimg, "jpeg_to_gif", jpeg_to_gif, 1);
71
+ }