lilimg 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/example/gray.jpg +0 -0
- data/example/test.rb +10 -5
- data/ext/lilimg/extconf.rb +1 -0
- data/ext/lilimg/lilimg.c +87 -34
- data/ext/lilimg/lodepng.c +5992 -0
- data/ext/lilimg/lodepng.h +1919 -0
- data/lilimg.gemspec +2 -2
- data/spec/lilimg_spec.rb +75 -12
- metadata +6 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7344a27d23f240ea551b8db61d3f8eba90e514e506c023420bd92beed2a814d9
|
|
4
|
+
data.tar.gz: 46f1fa4a072cd1a25fd309f9f414b3200c94fa5cc3d065fc7ab8a19ee575ec1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 965fa9322f61aeafb20ba16c25585603b1cd1c387a7b9aa244fbfe458d73491ad15198a9ef8252964ca9eb6e7ffe2fc13a3540b47bf38b946f3001cc69387ffc
|
|
7
|
+
data.tar.gz: 89c780e97b318a3e005ef6fbb86f1e0e5d6ddb12e4dafcbdca67cf02e4580ec2445380264e5456bcb5d854e9b185448bb6328b04927dd97ec9cfa8af590ca937
|
data/example/gray.jpg
ADDED
|
Binary file
|
data/example/test.rb
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
require('./lilimg')
|
|
1
|
+
# require('./lilimg')
|
|
2
|
+
require_relative('../ext/lilimg/lilimg')
|
|
2
3
|
|
|
3
|
-
jpeg = IO.read('
|
|
4
|
-
gif =
|
|
5
|
-
|
|
4
|
+
jpeg = IO.read(__dir__ + '/label.jpg')
|
|
5
|
+
gif = Lilimg.jpeg_to_gif(jpeg, 4)
|
|
6
|
+
puts gif.length
|
|
7
|
+
File.open('out.gif', 'wb') { |f| f.write(gif) }
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
png = IO.read(__dir__ + '/label.png')
|
|
10
|
+
gif = Lilimg.png_to_gif(png, 4)
|
|
11
|
+
puts gif.length
|
|
12
|
+
File.open('out2.gif', 'wb') { |f| f.write(gif) }
|
data/ext/lilimg/extconf.rb
CHANGED
data/ext/lilimg/lilimg.c
CHANGED
|
@@ -4,71 +4,123 @@
|
|
|
4
4
|
#include <stdlib.h>
|
|
5
5
|
#include <string.h>
|
|
6
6
|
#include "nanojpeg.h"
|
|
7
|
+
#include "lodepng.h"
|
|
7
8
|
#include "jo_gif.h"
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
// channels: 3 for RGB, 1 for grayscale
|
|
11
|
+
unsigned char * to_rgba(unsigned char * buf, int w, int h, int channels) {
|
|
12
|
+
size_t pixels = (size_t)w * h;
|
|
13
|
+
unsigned char * dest = (unsigned char *)malloc(pixels * 4);
|
|
14
|
+
if (!dest) return NULL;
|
|
15
|
+
|
|
16
|
+
size_t s = 0, d = 0;
|
|
17
|
+
for (size_t i = 0; i < pixels; i++) {
|
|
18
|
+
if (channels == 1) {
|
|
19
|
+
dest[d++] = buf[s];
|
|
20
|
+
dest[d++] = buf[s];
|
|
21
|
+
dest[d++] = buf[s++];
|
|
22
|
+
} else {
|
|
23
|
+
dest[d++] = buf[s++];
|
|
24
|
+
dest[d++] = buf[s++];
|
|
25
|
+
dest[d++] = buf[s++];
|
|
26
|
+
}
|
|
17
27
|
dest[d++] = 255; // fully opaque
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
return dest;
|
|
21
31
|
}
|
|
22
32
|
|
|
33
|
+
// Encodes rgba pixels as a GIF. Returns a malloc'ed buffer (caller frees) or NULL.
|
|
34
|
+
// jo_gif_end() closes the memstream, so out/bufsize are valid afterwards.
|
|
35
|
+
static char * rgba_to_gif(unsigned char * rgba, int w, int h, int max_colors, size_t * bufsize) {
|
|
36
|
+
char * out = NULL;
|
|
37
|
+
FILE * fp = open_memstream(&out, bufsize);
|
|
38
|
+
if (!fp) return NULL;
|
|
39
|
+
|
|
40
|
+
jo_gif_t gif = jo_gif_start(fp, w, h, 0, max_colors);
|
|
41
|
+
jo_gif_frame(&gif, rgba, 0, 0);
|
|
42
|
+
jo_gif_end(&gif);
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// NOTE: buf points into the Ruby string, so it must never be freed here.
|
|
23
47
|
static VALUE jpeg_to_gif(VALUE self, VALUE buf, VALUE max_colors) {
|
|
24
48
|
rb_check_type(buf, T_STRING);
|
|
25
49
|
|
|
26
|
-
int maxColors =
|
|
27
|
-
int size = RSTRING_LEN(buf);
|
|
28
|
-
unsigned char * jpeg = (unsigned char *)
|
|
50
|
+
int maxColors = NUM2INT(max_colors);
|
|
51
|
+
int size = (int)RSTRING_LEN(buf);
|
|
52
|
+
unsigned char * jpeg = (unsigned char *)RSTRING_PTR(buf);
|
|
29
53
|
|
|
30
|
-
if (jpeg[0] != 0xFF || jpeg[1] != 0xD8) {
|
|
31
|
-
rb_raise(rb_eRuntimeError, "Not a JPEG image (
|
|
32
|
-
return Qnil;
|
|
54
|
+
if (size < 2 || jpeg[0] != 0xFF || jpeg[1] != 0xD8) {
|
|
55
|
+
rb_raise(rb_eRuntimeError, "Not a JPEG image (should start with FF D8)!");
|
|
33
56
|
}
|
|
34
57
|
|
|
35
58
|
njInit();
|
|
36
59
|
|
|
37
60
|
if (njDecode(jpeg, size)) {
|
|
38
|
-
|
|
61
|
+
njDone();
|
|
39
62
|
rb_raise(rb_eRuntimeError, "Error decoding input file (size %d)", size);
|
|
40
|
-
return Qnil;
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
unsigned char * pixels = njGetImage();
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
int w = njGetWidth(), h = njGetHeight();
|
|
67
|
+
if (!pixels || w <= 0 || h <= 0 || w > 32767 || h > 32767) {
|
|
68
|
+
njDone();
|
|
46
69
|
rb_raise(rb_eRuntimeError, "Couldn't decode image.");
|
|
47
|
-
return Qnil;
|
|
48
70
|
}
|
|
49
71
|
|
|
50
|
-
char *
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
unsigned char * rgba = to_rgba(pixels, w, h, njIsColor() ? 3 : 1);
|
|
73
|
+
njDone(); // frees pixels
|
|
74
|
+
if (!rgba) rb_raise(rb_eNoMemError, "Cannot allocate pixel buffer");
|
|
75
|
+
|
|
76
|
+
size_t bufsize = 0;
|
|
77
|
+
char * out = rgba_to_gif(rgba, w, h, maxColors, &bufsize);
|
|
78
|
+
free(rgba);
|
|
79
|
+
if (!out) rb_raise(rb_eRuntimeError, "Cannot initialize memstream");
|
|
80
|
+
|
|
81
|
+
VALUE rstr = rb_str_new(out, bufsize);
|
|
82
|
+
free(out);
|
|
83
|
+
|
|
84
|
+
return rstr;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// NOTE: buf points into the Ruby string, so it must never be freed here.
|
|
88
|
+
static VALUE png_to_gif(VALUE self, VALUE buf, VALUE max_colors) {
|
|
89
|
+
rb_check_type(buf, T_STRING);
|
|
90
|
+
|
|
91
|
+
int maxColors = NUM2INT(max_colors);
|
|
92
|
+
int size = (int)RSTRING_LEN(buf);
|
|
93
|
+
unsigned char * png = (unsigned char *)RSTRING_PTR(buf);
|
|
54
94
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
rb_raise(rb_eRuntimeError, "
|
|
58
|
-
return Qnil;
|
|
95
|
+
unsigned char png_header[] = {137, 80, 78, 71, 13, 10, 26, 10};
|
|
96
|
+
if (size < 8 || memcmp(png, png_header, 8) != 0) {
|
|
97
|
+
rb_raise(rb_eRuntimeError, "Not a PNG image!");
|
|
59
98
|
}
|
|
60
99
|
|
|
61
|
-
unsigned
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
100
|
+
unsigned err, w, h;
|
|
101
|
+
LodePNGState state;
|
|
102
|
+
lodepng_state_init(&state);
|
|
103
|
+
unsigned char * rgba = NULL;
|
|
65
104
|
|
|
66
|
-
|
|
105
|
+
err = lodepng_decode(&rgba, &w, &h, &state, png, size);
|
|
106
|
+
lodepng_state_cleanup(&state);
|
|
107
|
+
if (err) {
|
|
108
|
+
free(rgba);
|
|
109
|
+
rb_raise(rb_eRuntimeError, "Unable to decode PNG, error code %u\n", err);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (w == 0 || h == 0 || w > 32767 || h > 32767) {
|
|
113
|
+
free(rgba);
|
|
114
|
+
rb_raise(rb_eRuntimeError, "Invalid PNG dimensions (%ux%u)", w, h);
|
|
115
|
+
}
|
|
67
116
|
|
|
68
|
-
|
|
117
|
+
size_t bufsize = 0;
|
|
118
|
+
char * out = rgba_to_gif(rgba, w, h, maxColors, &bufsize);
|
|
69
119
|
free(rgba);
|
|
120
|
+
if (!out) rb_raise(rb_eRuntimeError, "Cannot initialize memstream");
|
|
121
|
+
|
|
122
|
+
VALUE rstr = rb_str_new(out, bufsize);
|
|
70
123
|
free(out);
|
|
71
|
-
njDone();
|
|
72
124
|
|
|
73
125
|
return rstr;
|
|
74
126
|
}
|
|
@@ -78,4 +130,5 @@ VALUE Lilimg = Qnil;
|
|
|
78
130
|
void Init_lilimg() {
|
|
79
131
|
Lilimg = rb_define_module("Lilimg");
|
|
80
132
|
rb_define_module_function(Lilimg, "jpeg_to_gif", jpeg_to_gif, 2);
|
|
133
|
+
rb_define_module_function(Lilimg, "png_to_gif", png_to_gif, 2);
|
|
81
134
|
}
|