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.
- data/README.rdoc +61 -0
- data/Rakefile +40 -0
- data/ext/extconf.rb +13 -0
- data/ext/oil.c +413 -0
- data/test/test_jpeg.rb +190 -0
- 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
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
|