oil 0.0.1

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.
Files changed (6) hide show
  1. data/README.rdoc +61 -0
  2. data/Rakefile +40 -0
  3. data/ext/extconf.rb +13 -0
  4. data/ext/oil.c +413 -0
  5. data/test/test_jpeg.rb +190 -0
  6. metadata +52 -0
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = Oil
2
+
3
+ http://github.com/ender672/oil
4
+
5
+ == DESCRIPTION:
6
+
7
+ Resize JPEG images.
8
+
9
+ == SYNOPSIS:
10
+
11
+ # Resize a JPEG image
12
+ require 'oil'
13
+
14
+ io = File.open('image.jpg', 'rb')
15
+ jpeg = Oil::JPEG.new(io, 200, 300)
16
+ jpeg.each do |data|
17
+ # yields successive parts of the jpeg file
18
+ end
19
+
20
+ == REQUIREMENTS:
21
+
22
+ * IJG JPEG Library Version 6b or later.
23
+
24
+ Installing libjpeg headers (OSX):
25
+
26
+ $ brew install libjpeg # requires homebrew (http://mxcl.github.com/homebrew/)
27
+
28
+ Installing libjpeg headers (Debian/Ubuntu):
29
+
30
+ $ sudo apt-get install libjpeg-dev
31
+
32
+ == INSTALLATION:
33
+
34
+ $ gem install oil
35
+
36
+ == LICENSE:
37
+
38
+ (The MIT License)
39
+
40
+ Copyright (c) 2012
41
+
42
+ * {Timothy Elliott}[http://holymonkey.com]
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining
45
+ a copy of this software and associated documentation files (the
46
+ 'Software'), to deal in the Software without restriction, including
47
+ without limitation the rights to use, copy, modify, merge, publish,
48
+ distribute, sublicense, and/or sell copies of the Software, and to
49
+ permit persons to whom the Software is furnished to do so, subject to
50
+ the following conditions:
51
+
52
+ The above copyright notice and this permission notice shall be
53
+ included in all copies or substantial portions of the Software.
54
+
55
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
56
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
57
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
58
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
59
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
60
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
61
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+
4
+ file 'ext/Makefile' do
5
+ cd 'ext' do
6
+ ruby "extconf.rb #{ENV['EXTOPTS']}"
7
+ end
8
+ end
9
+
10
+ file 'ext/oil.so' => FileList.new('ext/Makefile', 'ext/oil.c') do
11
+ cd 'ext' do
12
+ sh 'make'
13
+ end
14
+ end
15
+
16
+ desc 'Clean up Rubinius .rbc files.'
17
+ namespace :clean do
18
+ task :rbc do
19
+ system "find . -name *.rbc -delete"
20
+ end
21
+ end
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs = ['ext']
25
+ t.test_files = FileList['test/test*.rb']
26
+ end
27
+
28
+ CLEAN.add('ext/*{.o,.so,.log}', 'ext/Makefile')
29
+ CLOBBER.add('*.gem')
30
+
31
+ desc 'Build the gem'
32
+ task :gem do
33
+ system "gem build oil.gemspec"
34
+ end
35
+
36
+ desc 'Compile the extension'
37
+ task :compile => "ext/oil.so"
38
+
39
+ task :test => :compile
40
+ task :default => :test
data/ext/extconf.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('jpeg')
4
+
5
+ unless have_header('jpeglib.h')
6
+ abort "libjpeg headers were not found."
7
+ end
8
+
9
+ unless have_library('jpeg')
10
+ abort "libjpeg was not found."
11
+ end
12
+
13
+ create_makefile('oil')
data/ext/oil.c ADDED
@@ -0,0 +1,413 @@
1
+ #include <ruby.h>
2
+ #include <jpeglib.h>
3
+
4
+ #define BUF_LEN 8192
5
+
6
+ static ID id_read, id_seek;
7
+
8
+ struct thumbdata {
9
+ VALUE io;
10
+ long width;
11
+ long height;
12
+ };
13
+
14
+ static struct jpeg_error_mgr jerr;
15
+
16
+ struct jpeg_dest {
17
+ struct jpeg_destination_mgr mgr;
18
+ VALUE buffer;
19
+ };
20
+
21
+ struct jpeg_src {
22
+ struct jpeg_source_mgr mgr;
23
+ VALUE io;
24
+ VALUE buffer;
25
+ };
26
+
27
+ /* jpeglib error callbacks */
28
+
29
+ static void output_message(j_common_ptr cinfo) {}
30
+
31
+ static void
32
+ error_exit(j_common_ptr cinfo)
33
+ {
34
+ char buffer[JMSG_LENGTH_MAX];
35
+ (*cinfo->err->format_message) (cinfo, buffer);
36
+ rb_raise(rb_eRuntimeError, "jpeglib: %s", buffer);
37
+ }
38
+
39
+ /* jpeglib source callbacks */
40
+
41
+ static void term_source(j_decompress_ptr cinfo){};
42
+ static void init_source(j_decompress_ptr cinfo){};
43
+
44
+ static boolean
45
+ fill_input_buffer(j_decompress_ptr cinfo)
46
+ {
47
+ struct jpeg_src *src = (struct jpeg_src *)cinfo->src;
48
+ VALUE ret, string = src->buffer;
49
+ long len;
50
+ char *buf;
51
+
52
+ ret = rb_funcall(src->io, id_read, 2, INT2FIX(BUF_LEN), string);
53
+ len = RSTRING_LEN(string);
54
+
55
+ if (!len || NIL_P(ret)) {
56
+ rb_str_resize(string, 2);
57
+ buf = RSTRING_PTR(string);
58
+ buf[0] = 0xFF;
59
+ buf[1] = JPEG_EOI;
60
+ len = 2;
61
+ }
62
+ src->mgr.next_input_byte = (JOCTET *)RSTRING_PTR(string);
63
+ src->mgr.bytes_in_buffer = (size_t)len;
64
+
65
+ return TRUE;
66
+ }
67
+
68
+ static void
69
+ skip_input_data(j_decompress_ptr cinfo, long num_bytes)
70
+ {
71
+ struct jpeg_src *src = (struct jpeg_src *)cinfo->src;
72
+
73
+ if (num_bytes > (long)src->mgr.bytes_in_buffer) {
74
+ num_bytes -= (long)src->mgr.bytes_in_buffer;
75
+ rb_funcall(src->io, id_seek, 2, INT2FIX(num_bytes), INT2FIX(SEEK_CUR));
76
+ src->mgr.bytes_in_buffer = 0;
77
+ } else {
78
+ src->mgr.next_input_byte += (size_t)num_bytes;
79
+ src->mgr.bytes_in_buffer -= (size_t)num_bytes;
80
+ }
81
+ }
82
+
83
+ /* jpeglib destination callbacks */
84
+
85
+ static void init_destination(j_compress_ptr cinfo) {}
86
+
87
+ static boolean
88
+ empty_output_buffer(j_compress_ptr cinfo)
89
+ {
90
+ struct jpeg_dest *dest = (struct jpeg_dest *)cinfo->dest;
91
+ rb_yield(dest->buffer);
92
+ dest->mgr.next_output_byte = RSTRING_PTR(dest->buffer);
93
+ dest->mgr.free_in_buffer = RSTRING_LEN(dest->buffer);
94
+ return TRUE;
95
+ }
96
+
97
+ static void
98
+ term_destination(j_compress_ptr cinfo)
99
+ {
100
+ struct jpeg_dest *dest = (struct jpeg_dest *)cinfo->dest;
101
+ size_t len = BUF_LEN - dest->mgr.free_in_buffer;
102
+
103
+ if (len) {
104
+ rb_str_resize(dest->buffer, len);
105
+ rb_yield(dest->buffer);
106
+ }
107
+ }
108
+
109
+ /* Helper functions */
110
+
111
+ static void
112
+ advance_scanlines(struct jpeg_decompress_struct *dinfo, JSAMPROW *sl1,
113
+ JSAMPROW *sl2, int n)
114
+ {
115
+ int i;
116
+ JSAMPROW tmp;
117
+ for (i = 0; i < n; i++) {
118
+ tmp = *sl1;
119
+ *sl1 = *sl2;
120
+ *sl2 = tmp;
121
+ jpeg_read_scanlines(dinfo, sl2, 1);
122
+ }
123
+ }
124
+
125
+ static void
126
+ bilinear3(struct jpeg_compress_struct *cinfo, char *sl1, char *sl2,
127
+ char *sl_out, double width_ratio_inv, double ty)
128
+ {
129
+ double sample_x, tx, _tx, p00, p10, p01, p11;
130
+ unsigned char *c00, *c10, *c01, *c11;
131
+ size_t sample_x_i, i, j, cmp = cinfo->input_components;
132
+
133
+ for (i = 0; i < cinfo->image_width; i++) {
134
+ sample_x = i * width_ratio_inv;
135
+ sample_x_i = (int)sample_x;
136
+
137
+ tx = sample_x - sample_x_i;
138
+ _tx = 1 - tx;
139
+
140
+ p11 = tx * ty;
141
+ p01 = _tx * ty;
142
+ p10 = tx - p11;
143
+ p00 = _tx - p01;
144
+
145
+ c00 = sl1 + sample_x_i * cmp;
146
+ c10 = c00 + cmp;
147
+ c01 = sl2 + sample_x_i * cmp;
148
+ c11 = c01 + cmp;
149
+
150
+ for (j = 0; j < cmp; j++)
151
+ *sl_out++ = p00 * c00[j] + p10 * c10[j] + p01 * c01[j] +
152
+ p11 * c11[j];
153
+ }
154
+ }
155
+
156
+ static VALUE
157
+ bilinear2(VALUE *args)
158
+ {
159
+ struct jpeg_compress_struct *cinfo;
160
+ struct jpeg_decompress_struct *dinfo;
161
+ size_t i, smpy_i, smpy_last=0, sl_len;
162
+ double smpy, ty, inv_scale_y, inv_scale_x;
163
+ JSAMPROW sl1, sl2, sl_out;
164
+
165
+ cinfo = (struct jpeg_compress_struct *)args[0];
166
+ dinfo = (struct jpeg_decompress_struct *)args[1];
167
+ sl_len = *(size_t *)args[2];
168
+ sl1 = (JSAMPROW)args[3];
169
+ sl2 = (JSAMPROW)args[4];
170
+ sl_out = (JSAMPROW)args[5];
171
+
172
+ inv_scale_x = dinfo->output_width / (float)cinfo->image_width;
173
+ inv_scale_y = dinfo->output_height / (float)cinfo->image_height;
174
+ advance_scanlines(dinfo, &sl1, &sl2, 1);
175
+
176
+ for (i = 0; i < cinfo->image_height; i++) {
177
+ smpy = i * inv_scale_y;
178
+ smpy_i = (int)smpy;
179
+ ty = smpy - smpy_i;
180
+
181
+ advance_scanlines(dinfo, &sl1, &sl2, smpy_i - smpy_last);
182
+ smpy_last = smpy_i;
183
+
184
+ sl1[sl_len - 1] = sl1[sl_len - 2];
185
+ sl2[sl_len - 1] = sl2[sl_len - 2];
186
+ bilinear3(cinfo, smpy_i ? sl1 : sl2, sl2, sl_out, inv_scale_x, ty);
187
+ jpeg_write_scanlines(cinfo, &sl_out, 1);
188
+ }
189
+
190
+ return Qnil;
191
+ }
192
+
193
+ static VALUE
194
+ free_sl_bufs(VALUE *args)
195
+ {
196
+ free((char *)args[3]);
197
+ free((char *)args[4]);
198
+ free((char *)args[5]);
199
+ return Qnil;
200
+ }
201
+
202
+ static void
203
+ bilinear(struct jpeg_compress_struct *cinfo,
204
+ struct jpeg_decompress_struct *dinfo)
205
+ {
206
+ VALUE ensure_args[6];
207
+ size_t sl_len;
208
+ JSAMPROW sl1, sl2, sl_out;
209
+
210
+ sl_len = (dinfo->output_width + 1) * dinfo->output_components;
211
+ sl1 = (JSAMPROW)malloc(sl_len);
212
+ sl2 = (JSAMPROW)malloc(sl_len);
213
+ sl_out = (JSAMPROW)malloc(cinfo->image_width * cinfo->input_components);
214
+
215
+ ensure_args[0] = (VALUE)cinfo;
216
+ ensure_args[1] = (VALUE)dinfo;
217
+ ensure_args[2] = (VALUE)&sl_len;
218
+ ensure_args[3] = (VALUE)sl1;
219
+ ensure_args[4] = (VALUE)sl2;
220
+ ensure_args[5] = (VALUE)sl_out;
221
+ rb_ensure(bilinear2, (VALUE)ensure_args, free_sl_bufs, (VALUE)ensure_args);
222
+ }
223
+
224
+ static void
225
+ fix_aspect_ratio(struct jpeg_compress_struct *cinfo,
226
+ struct jpeg_decompress_struct *dinfo)
227
+ {
228
+ double x, y;
229
+
230
+ x = cinfo->image_width / (float)dinfo->output_width;
231
+ y = cinfo->image_height / (float)dinfo->output_height;
232
+
233
+ if (x < y) cinfo->image_height = dinfo->output_height * x;
234
+ else cinfo->image_width = dinfo->output_width * y;
235
+ }
236
+
237
+ static void
238
+ set_header(VALUE self, struct jpeg_compress_struct *cinfo,
239
+ struct jpeg_decompress_struct *dinfo)
240
+ {
241
+ struct thumbdata *data;
242
+ Data_Get_Struct(self, struct thumbdata, data);
243
+
244
+ cinfo->image_width = data->width;
245
+ cinfo->image_height = data->height;
246
+ cinfo->input_components = dinfo->output_components;
247
+ cinfo->in_color_space = dinfo->out_color_space;
248
+ jpeg_set_defaults(cinfo);
249
+ }
250
+
251
+ static void
252
+ pre_scale(struct jpeg_compress_struct *cinfo,
253
+ struct jpeg_decompress_struct *dinfo)
254
+ {
255
+ int inv_scale = dinfo->output_width / cinfo->image_width;
256
+
257
+ if (inv_scale >= 8) dinfo->scale_denom = 8;
258
+ else if (inv_scale >= 4) dinfo->scale_denom = 4;
259
+ else if (inv_scale >= 2) dinfo->scale_denom = 2;
260
+ jpeg_calc_output_dimensions(dinfo);
261
+ }
262
+
263
+ /* Ruby allocator, deallocator, mark, and methods */
264
+
265
+ static void
266
+ deallocate(struct thumbdata *data)
267
+ {
268
+ free(data);
269
+ }
270
+
271
+ static void
272
+ mark(struct thumbdata *data)
273
+ {
274
+ if (!NIL_P(data->io))
275
+ rb_gc_mark(data->io);
276
+ }
277
+
278
+ static VALUE
279
+ allocate(VALUE klass)
280
+ {
281
+ struct thumbdata *data;
282
+ return Data_Make_Struct(klass, struct thumbdata, mark, deallocate, data);
283
+ }
284
+
285
+ /*
286
+ * call-seq:
287
+ * JPEGThumb.new(io, width, height) -> jpeg_thumb
288
+ *
289
+ * Creates a new JPEG thumbnailer. +io_in+ must be an IO-like object that
290
+ * responds to #read(size).
291
+ *
292
+ * The resulting image will be scaled to fit in the box given by +width+ and
293
+ * +height+ while preserving the aspect ratio.
294
+ *
295
+ * If both box dimensions are larger than the source image, then the image will
296
+ * be unchanged. The thumbnailer will not enlarge images.
297
+ */
298
+
299
+ static VALUE
300
+ initialize(VALUE self, VALUE io, VALUE width, VALUE height)
301
+ {
302
+ struct thumbdata *data;
303
+
304
+ Data_Get_Struct(self, struct thumbdata, data);
305
+ data->io = io;
306
+ data->width = FIX2INT(width);
307
+ data->height = FIX2INT(height);
308
+
309
+ return self;
310
+ }
311
+
312
+ static VALUE
313
+ each2(VALUE *args)
314
+ {
315
+ VALUE self = args[0];
316
+ struct jpeg_decompress_struct *dinfo;
317
+ struct jpeg_compress_struct *cinfo;
318
+
319
+ dinfo = (struct jpeg_decompress_struct *)args[1];
320
+ cinfo = (struct jpeg_compress_struct *)args[2];
321
+
322
+ jpeg_read_header(dinfo, TRUE);
323
+ jpeg_calc_output_dimensions(dinfo);
324
+ set_header(self, cinfo, dinfo);
325
+ fix_aspect_ratio(cinfo, dinfo);
326
+ pre_scale(cinfo, dinfo);
327
+
328
+ jpeg_start_compress(cinfo, TRUE);
329
+ jpeg_start_decompress(dinfo);
330
+
331
+ bilinear(cinfo, dinfo);
332
+
333
+ jpeg_abort_decompress(dinfo);
334
+ jpeg_finish_compress(cinfo);
335
+
336
+ return Qnil;
337
+ }
338
+
339
+ static VALUE
340
+ destroy_info(VALUE *args)
341
+ {
342
+ jpeg_destroy_decompress((struct jpeg_decompress_struct *)args[1]);
343
+ jpeg_destroy_compress((struct jpeg_compress_struct *)args[2]);
344
+ return Qnil;
345
+ }
346
+
347
+ /*
348
+ * call-seq:
349
+ * resized_jpeg.each(&block) -> self
350
+ *
351
+ * Yields a series of binary strings that make up the resized JPEG image.
352
+ */
353
+
354
+ static VALUE
355
+ each(VALUE self)
356
+ {
357
+ VALUE ensure_args[3];
358
+ struct jpeg_src src;
359
+ struct jpeg_decompress_struct dinfo;
360
+ struct jpeg_dest dest;
361
+ struct jpeg_compress_struct cinfo;
362
+ struct thumbdata *data;
363
+ Data_Get_Struct(self, struct thumbdata, data);
364
+
365
+ dinfo.err = cinfo.err = &jerr;
366
+ jpeg_create_decompress(&dinfo);
367
+ jpeg_create_compress(&cinfo);
368
+
369
+ memset(&src, 0, sizeof(struct jpeg_src));
370
+ src.mgr.init_source = init_source;
371
+ src.mgr.fill_input_buffer = fill_input_buffer;
372
+ src.mgr.skip_input_data = skip_input_data;
373
+ src.mgr.resync_to_restart = jpeg_resync_to_restart;
374
+ src.mgr.term_source = term_source;
375
+ src.io = data->io;
376
+ src.buffer = rb_str_new(0, BUF_LEN);
377
+
378
+ memset(&dest, 0, sizeof(struct jpeg_dest));
379
+ dest.mgr.init_destination = init_destination;
380
+ dest.mgr.empty_output_buffer = empty_output_buffer;
381
+ dest.mgr.term_destination = term_destination;
382
+ dest.buffer = rb_str_new(0, BUF_LEN);
383
+ dest.mgr.next_output_byte = RSTRING_PTR(dest.buffer);
384
+ dest.mgr.free_in_buffer = BUF_LEN;
385
+
386
+ dinfo.src = (struct jpeg_source_mgr *)&src;
387
+ cinfo.dest = (struct jpeg_destination_mgr *)&dest;
388
+
389
+ ensure_args[0] = self;
390
+ ensure_args[1] = (VALUE)&dinfo;
391
+ ensure_args[2] = (VALUE)&cinfo;
392
+ rb_ensure(each2, (VALUE)ensure_args, destroy_info, (VALUE)ensure_args);
393
+
394
+ return self;
395
+ }
396
+
397
+ void
398
+ Init_oil()
399
+ {
400
+ VALUE mOil = rb_define_module("Oil");
401
+ VALUE cJPEG = rb_define_class_under(mOil, "JPEG", rb_cObject);
402
+ rb_define_alloc_func(cJPEG, allocate);
403
+ rb_define_method(cJPEG, "initialize", initialize, 3);
404
+ rb_define_method(cJPEG, "each", each, 0);
405
+
406
+ jpeg_std_error(&jerr);
407
+ jerr.error_exit = error_exit;
408
+ jerr.output_message = output_message;
409
+
410
+ id_read = rb_intern("read");
411
+ id_seek = rb_intern("seek");
412
+ }
413
+
data/test/test_jpeg.rb ADDED
@@ -0,0 +1,190 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'oil'
4
+ require 'stringio'
5
+
6
+ module Oil
7
+ class TestJPEG < MiniTest::Unit::TestCase
8
+ # http://www.techsupportteam.org/forum/digital-imaging-photography/1892-worlds-smallest-valid-jpeg.html
9
+ JPEG_DATA = "\
10
+ \xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\
11
+ \x00\xFF\xDB\x00\x43\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
12
+ \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
13
+ \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
14
+ \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC2\x00\x0B\x08\x00\
15
+ \x01\x00\x01\x01\x01\x11\x00\xFF\xC4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\
16
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xDA\x00\x08\x01\x01\x00\x01\x3F\
17
+ \x10\xFF\xD9"
18
+
19
+ def test_valid_jpeg
20
+ validate_jpeg resize_string(JPEG_DATA)
21
+ end
22
+
23
+ def test_jpeg_missing_eof
24
+ validate_jpeg resize_string(JPEG_DATA[0..-2])
25
+ end
26
+
27
+ def test_jpeg_bogus_header_marker
28
+ str = JPEG_DATA.dup
29
+ str[3] = "\x10"
30
+ assert_raises(RuntimeError) { resize_string(str) }
31
+ end
32
+
33
+ def test_jpeg_bogus_body_marker
34
+ str = JPEG_DATA.dup
35
+ str[-1] = "\x10"
36
+ assert_raises(RuntimeError) { resize_string(str) }
37
+ end
38
+
39
+ def test_calls_each_during_yield
40
+ io = StringIO.new(JPEG_DATA)
41
+ j = JPEG.new(io, 600, 600)
42
+ assert_raises(RuntimeError) do
43
+ j.each{ |d| j.each { |e| j.each { |f| } } }
44
+ end
45
+ end
46
+
47
+ # Test io source handler
48
+
49
+ def test_io_returns_too_much_data
50
+ proc = Proc.new do |io, size, buf|
51
+ buf.slice!(0,0)
52
+ buf << (io.read(size)[0..-2] * 2)
53
+ end
54
+ assert_raises(RuntimeError) { custom_io proc, JPEG_DATA }
55
+ #custom_io proc, JPEG_DATA
56
+ end
57
+
58
+ def test_io_does_nothing
59
+ assert_raises(RuntimeError) { custom_io(nil) }
60
+ end
61
+
62
+ def test_io_raises_exception_immediately
63
+ proc = Proc.new{ raise CustomError }
64
+ assert_raises(CustomError) { custom_io proc }
65
+ end
66
+
67
+ def test_io_throws_immediately
68
+ proc = Proc.new{ throw(:foo) }
69
+ catch(:foo){ custom_io proc }
70
+ end
71
+
72
+ def test_io_raises_exception_in_body
73
+ flag = false
74
+ proc = Proc.new do |parent, size, buf|
75
+ raise CustomError if flag
76
+ flag = true
77
+ parent.read(size, buf)
78
+ end
79
+ assert_raises(CustomError) { custom_io proc, big_jpeg }
80
+ end
81
+
82
+ def test_io_throws_in_body
83
+ flag = false
84
+ proc = Proc.new do |parent, size, buf|
85
+ throw :foo if flag
86
+ flag = true
87
+ parent.read(size, buf)
88
+ end
89
+ catch(:foo){ custom_io proc, big_jpeg }
90
+ end
91
+
92
+ def test_io_shrinks_buffer
93
+ proc = Proc.new do |parent, size, buf|
94
+ parent.read(size, buf)
95
+ buf.slice!(0, 10)
96
+ end
97
+ assert_raises(RuntimeError) { custom_io(proc, big_jpeg) }
98
+ end
99
+
100
+ def test_io_enlarges_buffer
101
+ proc = Proc.new do |parent, size, buf|
102
+ res = parent.read(size, buf)
103
+ buf << res
104
+ end
105
+ validate_jpeg custom_io(proc, big_jpeg) # how is this okay?
106
+ end
107
+
108
+ def test_io_seek
109
+ # Craft a JPEG with header content that will be skipped.
110
+ # Make sure an actual seek happens, and it doesn't just skip buffer.
111
+ end
112
+
113
+ # Test yielding
114
+
115
+ def test_raise_in_each
116
+ assert_raises(CustomError) do
117
+ io = StringIO.new(JPEG_DATA)
118
+ JPEG.new(io, 200, 200).each { raise CustomError }
119
+ end
120
+ end
121
+
122
+ def test_throw_in_each
123
+ catch(:foo) do
124
+ io = StringIO.new(JPEG_DATA)
125
+ JPEG.new(io, 200, 200).each { throw :foo }
126
+ end
127
+ end
128
+
129
+ def test_each_shrinks_buffer
130
+ io = StringIO.new(JPEG_DATA)
131
+ io_out = binary_stringio
132
+ JPEG.new(io, 200, 200).each { |d| io_out << d; d.slice!(0, 4) }
133
+ validate_jpeg(io_out.string)
134
+ end
135
+
136
+ def test_each_enlarges_buffer
137
+ io = StringIO.new(JPEG_DATA)
138
+ io_out = binary_stringio
139
+ JPEG.new(io, 200, 200).each { |d| io_out << d; d << "foobar" }
140
+ validate_jpeg(io_out.string)
141
+ end
142
+
143
+ private
144
+
145
+ def io(io, width=nil, height=nil)
146
+ width ||= 100
147
+ height ||= 200
148
+ out = binary_stringio
149
+ JPEG.new(io, width, height).each{ |d| out << d }
150
+ return out.string
151
+ end
152
+
153
+ def custom_io(*args)
154
+ io CustomIO.new(*args)
155
+ end
156
+
157
+ def resize_string(str, width=nil, height=nil)
158
+ io(StringIO.new(str), width, height)
159
+ end
160
+
161
+ def validate_jpeg(data)
162
+ assert_equal "\xFF\xD8", data[0,2]
163
+ assert_equal "\xFF\xD9", data[-2, 2]
164
+ end
165
+
166
+ def big_jpeg
167
+ resize_string(JPEG_DATA, 1000, 1000)
168
+ end
169
+
170
+ def binary_stringio
171
+ io = StringIO.new
172
+ io.set_encoding 'ASCII-8BIT' if RUBY_VERSION >= '1.9'
173
+ io
174
+ end
175
+ end
176
+
177
+ class CustomError < RuntimeError; end
178
+
179
+ class CustomIO
180
+ def initialize(proc, *args)
181
+ @parent = StringIO.new(*args)
182
+ @proc = proc
183
+ end
184
+
185
+ def read(size, buf)
186
+ @proc.call(@parent, size, buf) if @proc
187
+ end
188
+ end
189
+ end
190
+
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oil
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Timothy Elliott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-27 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: tle@holymonkey.com
16
+ executables: []
17
+ extensions:
18
+ - ext/extconf.rb
19
+ extra_rdoc_files:
20
+ - README.rdoc
21
+ files:
22
+ - Rakefile
23
+ - README.rdoc
24
+ - ext/oil.c
25
+ - ext/extconf.rb
26
+ - test/test_jpeg.rb
27
+ homepage: http://github.com/ender672/oil
28
+ licenses: []
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - ext
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 1.8.17
48
+ signing_key:
49
+ specification_version: 3
50
+ summary: Resize JPEG images.
51
+ test_files:
52
+ - test/test_jpeg.rb