oil 0.0.3 → 0.1.0

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.
@@ -1,209 +1,145 @@
1
- require 'rubygems'
2
1
  require 'minitest/autorun'
3
2
  require 'oil'
4
3
  require 'stringio'
5
4
  require 'helper'
6
5
 
7
- module Oil
8
- class TestPNG < MiniTest::Unit::TestCase
9
- # http://garethrees.org/2007/11/14/pngcrush/
10
- PNG_DATA = "\
6
+ class TestPNG < MiniTest::Test
7
+ # http://garethrees.org/2007/11/14/pngcrush/
8
+ PNG_DATA = "\
11
9
  \x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\
12
10
  \x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x37\x6E\xF9\x24\x00\x00\x00\x10\x49\
13
11
  \x44\x41\x54\x78\x9C\x62\x60\x01\x00\x00\x00\xFF\xFF\x03\x00\x00\x06\x00\x05\
14
- \x57\xBF\xAB\xD4\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82"
12
+ \x57\xBF\xAB\xD4\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82".b
15
13
 
16
- def test_valid
17
- validate_png resize_string(PNG_DATA)
18
- end
14
+ BIG_PNG = begin
15
+ resize_string(PNG_DATA, 500, 1000)
16
+ end
19
17
 
20
- def test_bogus_header_chunk
21
- str = PNG_DATA.dup
22
- str[15] = "\x10"
23
- assert_raises(RuntimeError) { resize_string(str) }
24
- end
18
+ def test_valid
19
+ o = Oil.new(png_io, 10 ,20)
20
+ assert_equal 1, o.width
21
+ assert_equal 1, o.height
22
+ end
25
23
 
26
- def test_bogus_body_chunk
27
- str = PNG_DATA.dup
28
- str[37] = "\x10"
29
- assert_raises(RuntimeError) { resize_string(str) }
30
- end
24
+ def test_bogus_header_chunk
25
+ str = PNG_DATA.dup
26
+ str[15] = "\x10"
27
+ assert_raises(RuntimeError) { resize_string(str) }
28
+ end
31
29
 
32
- def test_bogus_end_chunk
33
- str = PNG_DATA.dup
34
- str[-6] = "\x10"
35
- validate_png resize_string(str)
36
- end
30
+ def test_bogus_body_chunk
31
+ str = PNG_DATA.dup
32
+ str[37] = "\x10"
33
+ assert_raises(RuntimeError) { resize_string(str) }
34
+ end
37
35
 
38
- def test_calls_each_during_yield
39
- io = StringIO.new(PNG_DATA)
40
- j = PNG.new(io, 600, 600)
41
- assert_raises(RuntimeError) do
42
- j.each{ |d| j.each { |e| j.each { |f| } } }
43
- end
44
- end
45
-
46
- def test_alloc_each
47
- io = PNG.allocate
48
- assert_raises(NoMethodError){ io.each{ |f| } }
49
- end
36
+ def test_bogus_end_chunk
37
+ str = PNG_DATA.dup
38
+ str[-6] = "\x10"
39
+ io = StringIO.new(str)
40
+ o = Oil.new(png_io, 10 ,20)
41
+ assert_equal 1, o.width
42
+ assert_equal 1, o.height
43
+ end
50
44
 
51
- # Test dimensions
52
-
53
- def test_zero_dim
54
- assert_raises(ArgumentError){ resize_string(PNG_DATA, 0, 0) }
55
- end
56
-
57
- def test_neg_dim
58
- assert_raises(ArgumentError){ resize_string(PNG_DATA, -1231, -123) }
59
- end
60
-
61
- # Test io source handler
62
-
63
- def test_io_returns_too_much_data
64
- proc = Proc.new do |io, size, buf|
65
- str = io.read(size)
66
- str = str[0..-2] * 2 if str
67
- if buf
68
- buf.slice!(0,0) # this empties the buffer
69
- buf << str
70
- else
71
- str
72
- end
73
- end
74
- assert_raises(RuntimeError) { custom_io proc, PNG_DATA }
75
- end
76
-
77
- def test_io_does_nothing
78
- assert_raises(RuntimeError) { custom_io(nil) }
79
- end
45
+ # Allocation tests
80
46
 
81
- def test_io_raises_exception_immediately
82
- proc = Proc.new{ raise CustomError }
83
- assert_raises(CustomError) { custom_io proc }
84
- end
47
+ def test_multiple_initialize_leak
48
+ o = Oil::PNGReader.allocate
85
49
 
86
- def test_io_throws_immediately
87
- proc = Proc.new{ throw(:foo) }
88
- catch(:foo){ custom_io proc }
89
- end
90
-
91
- def test_io_raises_exception_in_body
92
- flag = false
93
- proc = Proc.new do |parent, size, buf|
94
- raise CustomError if flag
95
- flag = true
96
- if buf
97
- parent.read(size, buf)
98
- else
99
- parent.read(size)
100
- end
101
- end
102
- assert_raises(CustomError) { custom_io proc, big_png }
103
- end
50
+ o.send(:initialize, png_io)
51
+ o.each{ |d| }
104
52
 
105
- def test_io_throws_in_body
106
- flag = false
107
- proc = Proc.new do |parent, size, buf|
108
- throw :foo if flag
109
- flag = true
110
- if buf
111
- parent.read(size, buf)
112
- else
113
- parent.read(size)
114
- end
115
- end
116
- catch(:foo){ custom_io proc, big_png }
117
- end
53
+ o.send(:initialize, png_io)
54
+ o.each{ |d| }
55
+ end
118
56
 
119
- def test_io_shrinks_buffer
120
- proc = Proc.new do |parent, size, buf|
121
- if buf
122
- parent.read(size, buf)
123
- buf.slice!(0, 10)
124
- else
125
- res = parent.read(size)
126
- res[10, -1]
127
- end
128
- end
129
- assert_raises(RuntimeError) { custom_io(proc, big_png) }
130
- end
57
+ # Test io
131
58
 
132
- def test_io_enlarges_buffer_with_eof
133
- proc = Proc.new do |parent, size, buf|
134
- if buf
135
- parent.read(size, buf)
136
- buf << buf
137
- else
138
- str = parent.read(size)
139
- str * 2
140
- end
141
- end
142
-
143
- assert_raises(RuntimeError) { custom_io(proc, big_png) }
59
+ IO_OFFSETS = [0, 10, 20]#, 8191, 8192, 8193, 12000]
60
+
61
+ def iotest(io_class)
62
+ IO_OFFSETS.each do |i|
63
+ yield io_class.new(BIG_PNG, :byte_count => i)
144
64
  end
65
+ end
145
66
 
146
- # Test yielding
67
+ def resize(io)
68
+ Oil.new(io, 20, 10).each{ |d| }
69
+ end
147
70
 
148
- def test_raise_in_each
149
- assert_raises(CustomError) do
150
- io = StringIO.new(PNG_DATA)
151
- PNG.new(io, 200, 200).each { raise CustomError }
152
- end
71
+ def test_io_too_much_data
72
+ iotest(GrowIO) do |io|
73
+ assert_raises(RuntimeError) { resize(io) }
153
74
  end
75
+ end
154
76
 
155
- def test_throw_in_each
156
- catch(:foo) do
157
- io = StringIO.new(PNG_DATA)
158
- PNG.new(io, 200, 200).each { throw :foo }
159
- end
77
+ def test_io_does_nothing
78
+ iotest(NilIO) do |io|
79
+ assert_raises(TypeError) { resize(io) }
160
80
  end
81
+ end
161
82
 
162
- def test_each_shrinks_buffer
163
- io = StringIO.new(PNG_DATA)
164
- io_out = binary_stringio
165
- PNG.new(io, 200, 200).each { |d| io_out << d; d.slice!(0, 4) }
166
- validate_png(io_out.string)
167
- end
168
-
169
- def test_each_enlarges_buffer
170
- io = StringIO.new(PNG_DATA)
171
- io_out = binary_stringio
172
- PNG.new(io, 200, 200).each { |d| io_out << d; d << "foobar" }
173
- validate_png(io_out.string)
83
+ def test_io_raises_exception
84
+ iotest(RaiseIO) do |io|
85
+ assert_raises(CustomError) { resize(io) }
174
86
  end
87
+ end
175
88
 
176
- private
177
-
178
- def io(io, width=nil, height=nil)
179
- width ||= 100
180
- height ||= 200
181
- out = binary_stringio
182
- PNG.new(io, width, height).each{ |d| out << d }
183
- return out.string
89
+ def test_io_throws
90
+ iotest(ThrowIO) do |io|
91
+ assert_throws(:foo) { resize(io) }
184
92
  end
93
+ end
185
94
 
186
- def custom_io(*args)
187
- io CustomIO.new(*args)
95
+ def test_io_shrinks_buffer
96
+ iotest(ShrinkIO) do |io|
97
+ assert_raises(RuntimeError) { resize(io) }
188
98
  end
99
+ end
189
100
 
190
- def resize_string(str, width=nil, height=nil)
191
- io(StringIO.new(str), width, height)
101
+ def test_not_string_io
102
+ iotest(NotStringIO) do |io|
103
+ assert_raises(TypeError) { resize(io) }
192
104
  end
193
-
194
- def validate_png(data)
195
- assert_equal "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", data[0,8]
196
- assert_equal "\x49\x45\x4E\x44\xAE\x42\x60\x82", data[-8, 8]
105
+ end
106
+
107
+ # Test yielding
108
+
109
+ def test_raise_in_each
110
+ assert_raises(CustomError) do
111
+ Oil.new(png_io, 10, 20).each { raise CustomError }
197
112
  end
113
+ end
198
114
 
199
- def big_png
200
- resize_string(PNG_DATA, 200, 200)
115
+ def test_throw_in_each
116
+ catch(:foo) do
117
+ Oil.new(png_io, 10, 20).each { throw :foo }
201
118
  end
119
+ end
202
120
 
203
- def binary_stringio
204
- io = StringIO.new
205
- io.set_encoding 'ASCII-8BIT' if RUBY_VERSION >= '1.9'
206
- io
121
+ def test_each_in_each
122
+ o = Oil.new(png_io, 10, 20)
123
+ o.each do |d|
124
+ assert_raises(RuntimeError){ o.each { |e| } }
207
125
  end
208
126
  end
127
+
128
+ def test_each_shrinks_buffer
129
+ io = StringIO.new(PNG_DATA)
130
+ io_out = binary_stringio
131
+ Oil.new(io, 200, 200).each { |d| io_out << d; d.slice!(0, 4) }
132
+ end
133
+
134
+ def test_each_enlarges_buffer
135
+ io = StringIO.new(PNG_DATA)
136
+ io_out = binary_stringio
137
+ Oil.new(io, 200, 200).each { |d| io_out << d; d << "foobar" }
138
+ end
139
+
140
+ private
141
+
142
+ def png_io
143
+ StringIO.new(PNG_DATA)
144
+ end
209
145
  end
metadata CHANGED
@@ -1,63 +1,61 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: oil
3
- version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
6
5
  platform: ruby
7
- authors:
8
- - Timothy Elliott
6
+ authors:
7
+ - Timothy Elliott
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
-
13
- date: 2012-03-28 00:00:00 Z
11
+ date: 2014-11-15 00:00:00.000000000 Z
14
12
  dependencies: []
15
-
16
- description: Oil resizes JPEG and PNG images. It aims for fast performance and low memory use.
13
+ description: Resize JPEG and PNG images, aiming for fast performance and low memory
14
+ use.
17
15
  email: tle@holymonkey.com
18
16
  executables: []
19
-
20
- extensions:
21
- - ext/extconf.rb
22
- extra_rdoc_files:
23
- - README.rdoc
24
- files:
25
- - Rakefile
26
- - README.rdoc
27
- - ext/oil.c
28
- - ext/extconf.rb
29
- - ext/oil.jar
30
- - test/helper.rb
31
- - test/test_jpeg.rb
32
- - test/test_png.rb
17
+ extensions:
18
+ - ext/oil/extconf.rb
19
+ extra_rdoc_files:
20
+ - README.rdoc
21
+ files:
22
+ - MIT-LICENSE
23
+ - README.rdoc
24
+ - Rakefile
25
+ - ext/oil/extconf.rb
26
+ - ext/oil/jpeg.c
27
+ - ext/oil/oil.c
28
+ - ext/oil/png.c
29
+ - ext/oil/resample.c
30
+ - ext/oil/resample.h
31
+ - ext/oil/yscaler.c
32
+ - ext/oil/yscaler.h
33
+ - lib/oil.rb
34
+ - test/helper.rb
35
+ - test/test_jpeg.rb
36
+ - test/test_png.rb
33
37
  homepage: http://github.com/ender672/oil
34
- licenses: []
35
-
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
36
41
  post_install_message:
37
42
  rdoc_options: []
38
-
39
- require_paths:
40
- - ext
41
- required_ruby_version: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: "0"
47
- required_rubygems_version: !ruby/object:Gem::Requirement
48
- none: false
49
- requirements:
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: "0"
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
53
55
  requirements: []
54
-
55
56
  rubyforge_project:
56
- rubygems_version: 1.8.21
57
+ rubygems_version: 2.4.3
57
58
  signing_key:
58
- specification_version: 3
59
- summary: Oil resizes JPEG and PNG images.
60
- test_files:
61
- - test/helper.rb
62
- - test/test_jpeg.rb
63
- - test/test_png.rb
59
+ specification_version: 4
60
+ summary: Resize JPEG and PNG images.
61
+ test_files: []
data/ext/oil.c DELETED
@@ -1,692 +0,0 @@
1
- #include <ruby.h>
2
- #include <jpeglib.h>
3
- #include <png.h>
4
-
5
- #define BUF_LEN 8192
6
-
7
- static ID id_read, id_seek;
8
-
9
- struct thumbdata {
10
- VALUE io;
11
- unsigned int width;
12
- unsigned int height;
13
- };
14
-
15
- static struct jpeg_error_mgr jerr;
16
-
17
- struct jpeg_dest {
18
- struct jpeg_destination_mgr mgr;
19
- VALUE buffer;
20
- };
21
-
22
- struct jpeg_src {
23
- struct jpeg_source_mgr mgr;
24
- VALUE io;
25
- VALUE buffer;
26
- };
27
-
28
- struct png_src {
29
- VALUE io;
30
- VALUE buffer;
31
- };
32
-
33
- struct interpolation {
34
- long sw;
35
- long sh;
36
- long dw;
37
- long dh;
38
- int cmp;
39
- void (*read)(char *, void *);
40
- void (*write)(char *, void *);
41
- void *read_data;
42
- void *write_data;
43
- };
44
-
45
- struct bitmap {
46
- long rowlen;
47
- char *cur;
48
- };
49
-
50
- /* bitmap source callback */
51
-
52
- static void
53
- bitmap_read(char *sl, void *data)
54
- {
55
- struct bitmap *b = (struct bitmap *)data;
56
- memcpy(sl, b->cur, b->rowlen);
57
- b->cur += b->rowlen;
58
- }
59
-
60
- /* jpeglib error callbacks */
61
-
62
- static void output_message(j_common_ptr cinfo) {}
63
-
64
- static void
65
- error_exit(j_common_ptr cinfo)
66
- {
67
- char buffer[JMSG_LENGTH_MAX];
68
- (*cinfo->err->format_message) (cinfo, buffer);
69
- rb_raise(rb_eRuntimeError, "jpeglib: %s", buffer);
70
- }
71
-
72
- /* jpeglib source callbacks */
73
-
74
- static void term_source(j_decompress_ptr cinfo){};
75
- static void init_source(j_decompress_ptr cinfo){};
76
-
77
- static boolean
78
- fill_input_buffer(j_decompress_ptr cinfo)
79
- {
80
- struct jpeg_src *src = (struct jpeg_src *)cinfo->src;
81
- VALUE ret, string = src->buffer;
82
- long len;
83
- char *buf;
84
-
85
- ret = rb_funcall(src->io, id_read, 2, INT2FIX(BUF_LEN), string);
86
- len = RSTRING_LEN(string);
87
-
88
- if (!len || NIL_P(ret)) {
89
- rb_str_resize(string, 2);
90
- buf = RSTRING_PTR(string);
91
- buf[0] = 0xFF;
92
- buf[1] = JPEG_EOI;
93
- len = 2;
94
- }
95
- src->mgr.next_input_byte = (JOCTET *)RSTRING_PTR(string);
96
- src->mgr.bytes_in_buffer = (size_t)len;
97
-
98
- return TRUE;
99
- }
100
-
101
- static void
102
- skip_input_data(j_decompress_ptr cinfo, long num_bytes)
103
- {
104
- struct jpeg_src *src = (struct jpeg_src *)cinfo->src;
105
-
106
- if (num_bytes > (long)src->mgr.bytes_in_buffer) {
107
- num_bytes -= (long)src->mgr.bytes_in_buffer;
108
- rb_funcall(src->io, id_seek, 2, INT2FIX(num_bytes), INT2FIX(SEEK_CUR));
109
- src->mgr.bytes_in_buffer = 0;
110
- } else {
111
- src->mgr.next_input_byte += (size_t)num_bytes;
112
- src->mgr.bytes_in_buffer -= (size_t)num_bytes;
113
- }
114
- }
115
-
116
- /* jpeglib destination callbacks */
117
-
118
- static void init_destination(j_compress_ptr cinfo) {}
119
-
120
- static boolean
121
- empty_output_buffer(j_compress_ptr cinfo)
122
- {
123
- struct jpeg_dest *dest = (struct jpeg_dest *)cinfo->dest;
124
- rb_yield(dest->buffer);
125
- dest->mgr.next_output_byte = RSTRING_PTR(dest->buffer);
126
- dest->mgr.free_in_buffer = RSTRING_LEN(dest->buffer);
127
- return TRUE;
128
- }
129
-
130
- static void
131
- term_destination(j_compress_ptr cinfo)
132
- {
133
- struct jpeg_dest *dest = (struct jpeg_dest *)cinfo->dest;
134
- size_t len = BUF_LEN - dest->mgr.free_in_buffer;
135
-
136
- if (len) {
137
- rb_str_resize(dest->buffer, len);
138
- rb_yield(dest->buffer);
139
- }
140
- }
141
-
142
- /* libpng error callbacks */
143
-
144
- static void png_warning_fn(png_structp png_ptr, png_const_charp message) {}
145
-
146
- static void
147
- png_error_fn(png_structp png_ptr, png_const_charp message)
148
- {
149
- rb_raise(rb_eRuntimeError, "pnglib: %s", message);
150
- }
151
-
152
- /* libpng io callbacks */
153
-
154
- void png_flush_data(png_structp png_ptr){}
155
-
156
- void
157
- png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
158
- {
159
- if (!png_ptr) return;
160
- rb_yield(rb_str_new(data, length));
161
- }
162
-
163
- void
164
- png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
165
- {
166
- VALUE ret, buf;
167
- png_size_t rlen;
168
- struct png_src *io_ptr;
169
-
170
- if (!png_ptr) return;
171
- io_ptr = (struct png_src *)png_get_io_ptr(png_ptr);
172
- buf = io_ptr->buffer;
173
- ret = rb_funcall(io_ptr->io, id_read, 2, INT2FIX(length), buf);
174
-
175
- rlen = RSTRING_LEN(buf);
176
- if (rlen > length)
177
- rb_raise(rb_eRuntimeError, "IO return buffer is too big.");
178
- if (!NIL_P(ret)) memcpy(data, RSTRING_PTR(buf), rlen);
179
- }
180
-
181
- /* png read/write callbacks */
182
-
183
- static void
184
- png_read(char *sl, void *data)
185
- {
186
- png_read_row((png_structp)data, (png_bytep)sl, (png_bytep)NULL);
187
- }
188
-
189
- static void
190
- png_write(char *sl, void *data)
191
- {
192
- png_write_row((png_structp)data, (png_bytep)sl);
193
- }
194
-
195
- /* Bilinear Interpolation */
196
-
197
- static void
198
- bilinear_get_scanlines(struct interpolation *bi, char **sl1, char **sl2,
199
- size_t *next, double ysmp)
200
- {
201
- int i, j, n, len=bi->cmp * bi->sw - 1;
202
- int line=ysmp;
203
- char *tmp;
204
-
205
- n = line + 1 - *next;
206
- if (line < bi->sh - 1) n++;
207
- *next += n;
208
-
209
- for (i=0; i<n; i++) {
210
- tmp = *sl1;
211
- *sl1 = *sl2;
212
- *sl2 = tmp;
213
- bi->read(*sl2, bi->read_data);
214
-
215
- for (j=0; j<bi->cmp; j++)
216
- sl2[0][len + bi->cmp - j] = sl2[0][len - j];
217
- }
218
-
219
- if (line >= bi->sh - 1) *sl1 = *sl2;
220
- }
221
-
222
- static void
223
- bilinear3(char *sl1, char *sl2, char *sl_out, size_t sw, size_t dw, int cmp,
224
- double ysmp)
225
- {
226
- double xsmp, tx, _tx, p1, p2, p3, p4;
227
- unsigned char *c1, *c2, *c3, *c4;
228
- size_t xsmp_i, i, j;
229
- double xscale_inv, xscale_ctr, ty;
230
-
231
- if (ysmp < 0) ysmp = 0;
232
-
233
- xscale_inv = sw / (float)dw;
234
- xscale_ctr = (xscale_inv - 1) / 2;
235
- ty = ysmp - (int)ysmp;
236
-
237
- for (i = 0; i < dw; i++) {
238
- xsmp = i * xscale_inv + xscale_ctr;
239
- if (xsmp < 0) xsmp = 0;
240
- xsmp_i = (int)xsmp;
241
-
242
- tx = xsmp - xsmp_i;
243
- _tx = 1 - tx;
244
-
245
- p4 = tx * ty;
246
- p3 = _tx * ty;
247
- p2 = tx - p4;
248
- p1 = _tx - p3;
249
-
250
- c1 = sl1 + xsmp_i * cmp;
251
- c2 = c1 + cmp;
252
- c3 = sl2 + xsmp_i * cmp;
253
- c4 = c3 + cmp;
254
-
255
- for (j = 0; j < cmp; j++)
256
- *sl_out++ = p1 * c1[j] + p2 * c2[j] + p3 * c3[j] + p4 * c4[j];
257
- }
258
- }
259
-
260
- static VALUE
261
- bilinear2(VALUE _args)
262
- {
263
- VALUE *args = (VALUE *)_args;
264
- struct interpolation *bi=(struct interpolation *)args[0];
265
- char *sl1=(char *)args[1], *sl2=(char *)args[2], *sl_out=(char *)args[3];
266
- size_t i, line=0;
267
- double ysmp, yscale_inv, yscale_ctr;
268
-
269
- yscale_inv = bi->sh / (float)bi->dh;
270
- yscale_ctr = (yscale_inv - 1) / 2;
271
-
272
- for (i = 0; i<bi->dh; i++) {
273
- ysmp = i * yscale_inv + yscale_ctr;
274
-
275
- bilinear_get_scanlines(bi, &sl1, &sl2, &line, ysmp);
276
- bilinear3(sl1, sl2, sl_out, bi->sw, bi->dw, bi->cmp, ysmp);
277
- bi->write(sl_out, bi->write_data);
278
- }
279
-
280
- return Qnil;
281
- }
282
-
283
- static void
284
- bilinear(struct interpolation *bi)
285
- {
286
- VALUE args[4];
287
- int state, in_len;
288
- char *sl1, *sl2, *sl_out;
289
-
290
- in_len = (bi->sw + 1) * bi->cmp;
291
- sl1 = malloc(in_len);
292
- sl2 = malloc(in_len);
293
- sl_out = malloc(bi->dw * bi->cmp);
294
-
295
- args[0] = (VALUE)bi;
296
- args[1] = (VALUE)sl1;
297
- args[2] = (VALUE)sl2;
298
- args[3] = (VALUE)sl_out;
299
- rb_protect(bilinear2, (VALUE)args, &state);
300
-
301
- free(sl1);
302
- free(sl2);
303
- free(sl_out);
304
-
305
- if (state) rb_jump_tag(state);
306
- }
307
-
308
- /* helper functions */
309
-
310
- static void
311
- fix_ratio(unsigned int sw, unsigned int sh, unsigned int *dw, unsigned int *dh)
312
- {
313
- double x, y;
314
- x = *dw / (float)sw;
315
- y = *dh / (float)sh;
316
- if (x<y) *dh = sh * x;
317
- else *dw = sw * y;
318
- if (!*dh) *dh = 1;
319
- if (!*dw) *dw = 1;
320
- }
321
-
322
- /* jpeg helper functions */
323
-
324
- static void
325
- jpeg_set_output_header(j_compress_ptr cinfo, j_decompress_ptr dinfo)
326
- {
327
- cinfo->input_components = dinfo->output_components;
328
- cinfo->in_color_space = dinfo->out_color_space;
329
- jpeg_set_defaults(cinfo);
330
- }
331
-
332
- static void
333
- jpeg_pre_scale(j_compress_ptr cinfo, j_decompress_ptr dinfo)
334
- {
335
- int inv_scale = dinfo->output_width / cinfo->image_width;
336
-
337
- if (inv_scale >= 8) dinfo->scale_denom = 8;
338
- else if (inv_scale >= 4) dinfo->scale_denom = 4;
339
- else if (inv_scale >= 2) dinfo->scale_denom = 2;
340
- jpeg_calc_output_dimensions(dinfo);
341
- }
342
-
343
- static void
344
- jpeg_read(char *sl, void *data)
345
- {
346
- jpeg_read_scanlines((j_decompress_ptr)data, (JSAMPROW *)&sl, 1);
347
- }
348
-
349
- static void
350
- jpeg_write(char *sl, void *data)
351
- {
352
- jpeg_write_scanlines((j_compress_ptr)data, (JSAMPROW *)&sl, 1);
353
- }
354
-
355
- static VALUE
356
- jpeg_each3(VALUE _args)
357
- {
358
- VALUE *args = (VALUE *)_args;
359
- j_decompress_ptr dinfo = (j_decompress_ptr)args[0];
360
- j_compress_ptr cinfo = (j_compress_ptr)args[1];
361
- struct interpolation intrp;
362
-
363
- jpeg_read_header(dinfo, TRUE);
364
- jpeg_calc_output_dimensions(dinfo);
365
- jpeg_set_output_header(cinfo, dinfo);
366
- fix_ratio(dinfo->output_width, dinfo->output_height, &cinfo->image_width,
367
- &cinfo->image_height);
368
- jpeg_pre_scale(cinfo, dinfo);
369
-
370
- jpeg_start_compress(cinfo, TRUE);
371
- jpeg_start_decompress(dinfo);
372
-
373
- intrp.sw = dinfo->output_width;
374
- intrp.sh = dinfo->output_height;
375
- intrp.dw = cinfo->image_width;
376
- intrp.dh = cinfo->image_height;
377
- intrp.cmp = dinfo->output_components;
378
- intrp.read = jpeg_read;
379
- intrp.write = jpeg_write;
380
- intrp.read_data = dinfo;
381
- intrp.write_data = cinfo;
382
-
383
- bilinear(&intrp);
384
-
385
- jpeg_abort_decompress(dinfo);
386
- jpeg_finish_compress(cinfo);
387
-
388
- return Qnil;
389
- }
390
-
391
- static void
392
- jpeg_each2(struct thumbdata *data, struct jpeg_src *src, struct jpeg_dest *dest)
393
- {
394
- int state;
395
- VALUE args[2];
396
- struct jpeg_decompress_struct dinfo;
397
- struct jpeg_compress_struct cinfo;
398
-
399
- dinfo.err = cinfo.err = &jerr;
400
-
401
- jpeg_create_compress(&cinfo);
402
- cinfo.dest = &dest->mgr;
403
- cinfo.image_width = data->width;
404
- cinfo.image_height = data->height;
405
-
406
- jpeg_create_decompress(&dinfo);
407
- dinfo.src = &src->mgr;
408
-
409
- args[0] = (VALUE)&dinfo;
410
- args[1] = (VALUE)&cinfo;
411
- rb_protect(jpeg_each3, (VALUE)args, &state);
412
-
413
- jpeg_destroy_decompress(&dinfo);
414
- jpeg_destroy_compress(&cinfo);
415
-
416
- if (state) rb_jump_tag(state);
417
- }
418
-
419
- /* Ruby allocator, deallocator, mark, and methods */
420
-
421
- static void
422
- deallocate(struct thumbdata *data)
423
- {
424
- free(data);
425
- }
426
-
427
- static void
428
- mark(struct thumbdata *data)
429
- {
430
- if (!NIL_P(data->io))
431
- rb_gc_mark(data->io);
432
- }
433
-
434
- static VALUE
435
- allocate(VALUE klass)
436
- {
437
- struct thumbdata *data;
438
- return Data_Make_Struct(klass, struct thumbdata, mark, deallocate, data);
439
- }
440
-
441
- /*
442
- * call-seq:
443
- * Class.new(io, width, height) -> obj
444
- *
445
- * Creates a new resizer. +io_in+ must be an IO-like object that responds to
446
- * #read(size, buffer) and #seek(size).
447
- *
448
- * The resulting image will be scaled to fit in the box given by +width+ and
449
- * +height+ while preserving the original aspect ratio.
450
- */
451
-
452
- static VALUE
453
- initialize(VALUE self, VALUE io, VALUE rb_width, VALUE rb_height)
454
- {
455
- int width=FIX2INT(rb_width), height=FIX2INT(rb_height);
456
- struct thumbdata *data;
457
-
458
- if (width<1 || height<1) rb_raise(rb_eArgError, "Dimensions must be > 0");
459
-
460
- Data_Get_Struct(self, struct thumbdata, data);
461
- data->io = io;
462
- data->width = width;
463
- data->height = height;
464
-
465
- return self;
466
- }
467
-
468
- /*
469
- * call-seq:
470
- * jpeg.each(&block) -> self
471
- *
472
- * Yields a series of binary strings that make up the resized JPEG image.
473
- */
474
-
475
- static VALUE
476
- jpeg_each(VALUE self)
477
- {
478
- struct jpeg_src src;
479
- struct jpeg_dest dest;
480
- struct thumbdata *data;
481
- Data_Get_Struct(self, struct thumbdata, data);
482
-
483
- memset(&src, 0, sizeof(struct jpeg_src));
484
- src.io = data->io;
485
- src.buffer = rb_str_new(0, 0);
486
- src.mgr.init_source = init_source;
487
- src.mgr.fill_input_buffer = fill_input_buffer;
488
- src.mgr.skip_input_data = skip_input_data;
489
- src.mgr.resync_to_restart = jpeg_resync_to_restart;
490
- src.mgr.term_source = term_source;
491
-
492
- memset(&dest, 0, sizeof(struct jpeg_dest));
493
- dest.buffer = rb_str_new(0, BUF_LEN);
494
- dest.mgr.next_output_byte = RSTRING_PTR(dest.buffer);
495
- dest.mgr.free_in_buffer = BUF_LEN;
496
- dest.mgr.init_destination = init_destination;
497
- dest.mgr.empty_output_buffer = empty_output_buffer;
498
- dest.mgr.term_destination = term_destination;
499
-
500
- jpeg_each2(data, &src, &dest);
501
-
502
- return self;
503
- }
504
-
505
- static void
506
- png_normalize_input(png_structp read_ptr, png_infop read_i_ptr)
507
- {
508
- png_byte ctype;
509
- int bit_depth;
510
-
511
- bit_depth = png_get_bit_depth(read_ptr, read_i_ptr);
512
- ctype = png_get_color_type(read_ptr, read_i_ptr);
513
-
514
- if (ctype == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
515
- png_set_gray_1_2_4_to_8(read_ptr);
516
-
517
- if (bit_depth < 8)
518
- png_set_packing(read_ptr);
519
- else if (bit_depth == 16)
520
- #if PNG_LIBPNG_VER >= 10504
521
- png_set_scale_16(read_ptr);
522
- #else
523
- png_set_strip_16(read_ptr);
524
- #endif
525
-
526
- if (png_get_valid(read_ptr, read_i_ptr, PNG_INFO_tRNS))
527
- png_set_tRNS_to_alpha(read_ptr);
528
-
529
- if (ctype == PNG_COLOR_TYPE_PALETTE)
530
- png_set_palette_to_rgb(read_ptr);
531
-
532
- png_read_update_info(read_ptr, read_i_ptr);
533
- }
534
-
535
- static VALUE
536
- png_interlaced2(VALUE arg)
537
- {
538
- bilinear((struct interpolation *)arg);
539
- }
540
-
541
- static void
542
- png_interlaced(png_structp rpng, struct interpolation *intrp)
543
- {
544
- struct bitmap b;
545
- png_bytep *rows;
546
- char *data;
547
- int i, state;
548
-
549
- b.rowlen = intrp->sw * intrp->cmp;
550
- data = malloc(b.rowlen * intrp->sh);
551
- b.cur = data;
552
-
553
- rows = malloc(intrp->sh * sizeof(png_bytep));
554
- for (i=0; i<intrp->sh; i++)
555
- rows[i] = data + (i * b.rowlen);
556
-
557
- png_read_image(rpng, rows);
558
-
559
- intrp->read = bitmap_read;
560
- intrp->read_data = (void *)&b;
561
-
562
- rb_protect(png_interlaced2, (VALUE)intrp, &state);
563
-
564
- free(rows);
565
- free(data);
566
-
567
- if (state) rb_jump_tag(state);
568
- }
569
-
570
- static VALUE
571
- png_each2(VALUE _args)
572
- {
573
- VALUE *args = (VALUE *)_args;
574
- png_structp write_ptr=(png_structp)args[0];
575
- png_infop write_i_ptr=(png_infop)args[1];
576
- png_structp read_ptr=(png_structp)args[2];
577
- png_infop read_i_ptr=(png_infop)args[3];
578
- struct thumbdata *thumb=(struct thumbdata *)args[4];
579
- struct interpolation intrp;
580
- png_byte ctype;
581
-
582
- png_read_info(read_ptr, read_i_ptr);
583
- png_normalize_input(read_ptr, read_i_ptr);
584
- ctype = png_get_color_type(read_ptr, read_i_ptr);
585
-
586
- intrp.sw = png_get_image_width(read_ptr, read_i_ptr);
587
- intrp.sh = png_get_image_height(read_ptr, read_i_ptr);
588
- fix_ratio(intrp.sw, intrp.sh, &thumb->width, &thumb->height);
589
- png_set_IHDR(write_ptr, write_i_ptr, thumb->width, thumb->height, 8,
590
- ctype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
591
- PNG_FILTER_TYPE_DEFAULT);
592
- png_write_info(write_ptr, write_i_ptr);
593
-
594
- switch (ctype) {
595
- case PNG_COLOR_TYPE_GRAY: intrp.cmp = 1; break;
596
- case PNG_COLOR_TYPE_GRAY_ALPHA: intrp.cmp = 2; break;
597
- case PNG_COLOR_TYPE_RGB: intrp.cmp = 3; break;
598
- case PNG_COLOR_TYPE_RGB_ALPHA: intrp.cmp = 4; break;
599
- default: rb_raise(rb_eRuntimeError, "png color type not supported.");
600
- }
601
-
602
- intrp.dw = thumb->width;
603
- intrp.dh = thumb->height;
604
- intrp.write = png_write;
605
- intrp.write_data = write_ptr;
606
-
607
- switch (png_get_interlace_type(read_ptr, read_i_ptr)) {
608
- case PNG_INTERLACE_NONE:
609
- intrp.read = png_read;
610
- intrp.read_data = read_ptr;
611
- bilinear(&intrp);
612
- break;
613
- case PNG_INTERLACE_ADAM7:
614
- png_interlaced(read_ptr, &intrp);
615
- break;
616
- default: rb_raise(rb_eRuntimeError, "png interlace type not supported.");
617
- }
618
-
619
- png_write_end(write_ptr, write_i_ptr);
620
-
621
- return Qnil;
622
- }
623
-
624
- /*
625
- * call-seq:
626
- * png.each(&block) -> self
627
- *
628
- * Yields a series of binary strings that make up the resized PNG image.
629
- */
630
-
631
- static VALUE
632
- png_each(VALUE self)
633
- {
634
- int state;
635
- VALUE args[5];
636
- png_structp read_ptr, write_ptr;
637
- png_infop read_i_ptr, write_i_ptr;
638
- struct png_src src;
639
- struct thumbdata *thumb;
640
- Data_Get_Struct(self, struct thumbdata, thumb);
641
-
642
- write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
643
- (png_error_ptr)png_error_fn,
644
- (png_error_ptr)png_warning_fn);
645
- write_i_ptr = png_create_info_struct(write_ptr);
646
- png_set_write_fn(write_ptr, NULL, png_write_data, png_flush_data);
647
-
648
- src.io = thumb->io;
649
- src.buffer = rb_str_new(0, 0);
650
-
651
- read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
652
- (png_error_ptr)png_error_fn,
653
- (png_error_ptr)png_warning_fn);
654
- read_i_ptr = png_create_info_struct(read_ptr);
655
- png_set_read_fn(read_ptr, (void *)&src, png_read_data);
656
-
657
- args[0] = (VALUE)write_ptr;
658
- args[1] = (VALUE)write_i_ptr;
659
- args[2] = (VALUE)read_ptr;
660
- args[3] = (VALUE)read_i_ptr;
661
- args[4] = (VALUE)thumb;
662
- rb_protect(png_each2, (VALUE)args, &state);
663
-
664
- png_destroy_read_struct(&read_ptr, &read_i_ptr, (png_info **)NULL);
665
- png_destroy_write_struct(&write_ptr, &write_i_ptr);
666
-
667
- if (state) rb_jump_tag(state);
668
- return self;
669
- }
670
-
671
- void
672
- Init_oil(void)
673
- {
674
- VALUE mOil = rb_define_module("Oil");
675
-
676
- VALUE cJPEG = rb_define_class_under(mOil, "JPEG", rb_cObject);
677
- rb_define_alloc_func(cJPEG, allocate);
678
- rb_define_method(cJPEG, "initialize", initialize, 3);
679
- rb_define_method(cJPEG, "each", jpeg_each, 0);
680
-
681
- VALUE cPNG = rb_define_class_under(mOil, "PNG", rb_cObject);
682
- rb_define_alloc_func(cPNG, allocate);
683
- rb_define_method(cPNG, "initialize", initialize, 3);
684
- rb_define_method(cPNG, "each", png_each, 0);
685
-
686
- jpeg_std_error(&jerr);
687
- jerr.error_exit = error_exit;
688
- jerr.output_message = output_message;
689
-
690
- id_read = rb_intern("read");
691
- id_seek = rb_intern("seek");
692
- }