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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 900d7bd9c19efc86e18ee14f04c0085672a9f79e
4
- data.tar.gz: ac8f159eb6161af5dc5032633462168d8a11a665
2
+ SHA256:
3
+ metadata.gz: 7344a27d23f240ea551b8db61d3f8eba90e514e506c023420bd92beed2a814d9
4
+ data.tar.gz: 46f1fa4a072cd1a25fd309f9f414b3200c94fa5cc3d065fc7ab8a19ee575ec1a
5
5
  SHA512:
6
- metadata.gz: 63decb5a032f645892ff836208f5a0d8f51b18e93bf64cb994e5392832c17d2ada038efc950581a999f740f4215836911679fab31fb046e4ec7793dc6eea6ea9
7
- data.tar.gz: 3d64f2099dc8a9b8f99d9c815992f030d352118c553eabf865e1680bd47b79fa769feb438ac7c0f765f6d12c03e2573f2397dc5b9e7a90866a340b2a6b0bfc88
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('../label.jpg')
4
- gif = Lili.jpeg_to_gif(jpeg)
5
- # puts gif.length
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
- File.open('out.gif', 'wb') { |f| f.write(gif) })
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) }
@@ -1,3 +1,4 @@
1
1
  require 'mkmf'
2
2
  CONFIG['warnflags'].slice!(/ -Wdeclaration-after-statement/)
3
+ $objs = ["lilimg.o", "lodepng.o"]
3
4
  create_makefile('lilimg/lilimg')
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
- unsigned char * rgb_to_rgba(unsigned char * buf, int w, int h) {
10
- int newsize = 4 * w * h;
11
- unsigned char * dest = (unsigned char *)malloc(newsize);
12
- int s = 0, d = 0;
13
- while (d < newsize) {
14
- dest[d++] = buf[s++];
15
- dest[d++] = buf[s++];
16
- dest[d++] = buf[s++];
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 = INT2NUM(max_colors);
27
- int size = RSTRING_LEN(buf);
28
- unsigned char * jpeg = (unsigned char *)StringValuePtr(buf);
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 (%x %x should be FF D8)!", jpeg[0], jpeg[1]);
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
- free(jpeg);
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
- if (!pixels) {
45
- free(jpeg);
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 * out;
51
- size_t bufsize;
52
- FILE * fp = open_memstream(&out, &bufsize);
53
- // FILE * fp = fopen("output.gif", "wb");
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
- if (!fp) {
56
- free(jpeg);
57
- rb_raise(rb_eRuntimeError, "Cannot initialize memstream");
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 char * rgba = rgb_to_rgba(pixels, njGetWidth(), njGetHeight());
62
- jo_gif_t gif = jo_gif_start(fp, njGetWidth(), njGetHeight(), 0, maxColors);
63
- jo_gif_frame(&gif, rgba, 0, 0);
64
- jo_gif_end(&gif);
100
+ unsigned err, w, h;
101
+ LodePNGState state;
102
+ lodepng_state_init(&state);
103
+ unsigned char * rgba = NULL;
65
104
 
66
- VALUE rstr = rb_str_new(out, bufsize);
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
- // free(jpeg); <-- this will cause all sorts of errors (FIXME)
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
  }