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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +14 -46
- data/Rakefile +35 -31
- data/ext/{extconf.rb → oil/extconf.rb} +2 -12
- data/ext/oil/jpeg.c +861 -0
- data/ext/oil/oil.c +10 -0
- data/ext/oil/png.c +337 -0
- data/ext/oil/resample.c +464 -0
- data/ext/oil/resample.h +46 -0
- data/ext/oil/yscaler.c +138 -0
- data/ext/oil/yscaler.h +31 -0
- data/lib/oil.rb +77 -0
- data/test/helper.rb +84 -6
- data/test/test_jpeg.rb +114 -184
- data/test/test_png.rb +105 -169
- metadata +47 -49
- data/ext/oil.c +0 -692
- data/ext/oil.jar +0 -0
data/ext/oil/resample.h
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#ifndef RESAMPLE_H
|
2
|
+
#define RESAMPLE_H
|
3
|
+
|
4
|
+
#define OIL_FILLER 1
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Scale scanline in to the scanline out.
|
8
|
+
*/
|
9
|
+
void xscale(unsigned char *in, long in_width, unsigned char *out,
|
10
|
+
long out_width, int cmp, int opts);
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Indicate how many taps will be required to scale an image. The number of taps
|
14
|
+
* required indicates how tall a strip needs to be.
|
15
|
+
*/
|
16
|
+
long calc_taps(long dim_in, long dim_out);
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Given input & output dimensions and an output position, return the
|
20
|
+
* corresponding input position and put the sub-pixel remainder in rest.
|
21
|
+
*/
|
22
|
+
long split_map(unsigned long dim_in, unsigned long dim_out, unsigned long pos,
|
23
|
+
float *rest);
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Scale a strip. The height parameter indicates the height of the strip, not
|
27
|
+
* the height of the image.
|
28
|
+
*
|
29
|
+
* The strip_height parameter indicates how many scanlines we are passing in. It
|
30
|
+
* must be a multiple of 4.
|
31
|
+
*
|
32
|
+
* The in parameter points to an array of scanlines, each with width samples in
|
33
|
+
* sample_fmt format. There must be at least strip_height scanlines in the
|
34
|
+
* array.
|
35
|
+
*
|
36
|
+
* The ty parameter indicates how far our mapped sampling position is from the
|
37
|
+
* center of the strip.
|
38
|
+
*
|
39
|
+
* Note that all scanlines in the strip must be populated, even when this
|
40
|
+
* requires scanlines that are less than 0 or larger than the height of the
|
41
|
+
* source image.
|
42
|
+
*/
|
43
|
+
void strip_scale(void **in, long strip_height, long width, void *out, float ty,
|
44
|
+
int cmp, int opts);
|
45
|
+
|
46
|
+
#endif
|
data/ext/oil/yscaler.c
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#include "yscaler.h"
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Initializes a strip of height scanlines.
|
7
|
+
*
|
8
|
+
* Scanlines are allocated with a size of len bytes.
|
9
|
+
*/
|
10
|
+
static void strip_init(struct strip *st, uint32_t height, size_t buflen)
|
11
|
+
{
|
12
|
+
uint32_t i;
|
13
|
+
|
14
|
+
st->height = height;
|
15
|
+
st->rr = 0;
|
16
|
+
st->sl = malloc(height * sizeof(uint8_t *));
|
17
|
+
st->virt = malloc(height * sizeof(uint8_t *));
|
18
|
+
for (i=0; i<height; i++) {
|
19
|
+
st->sl[i] = malloc(buflen);
|
20
|
+
st->virt[i] = st->sl[0];
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Frees memory allocated at initialization.
|
26
|
+
*/
|
27
|
+
static void strip_free(struct strip *st)
|
28
|
+
{
|
29
|
+
uint32_t i;
|
30
|
+
|
31
|
+
for (i=0; i<st->height; i++) {
|
32
|
+
free(st->sl[i]);
|
33
|
+
}
|
34
|
+
free(st->sl);
|
35
|
+
free(st->virt);
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Shifts the virtual scanline pointers downwards by one.
|
40
|
+
*
|
41
|
+
* The bottmomost virtual scanline pointer is left untouched since we may have
|
42
|
+
* reached the end of the source image and we may want to leave it pointing at
|
43
|
+
* the bottom.
|
44
|
+
*
|
45
|
+
* If we have not reached the bottom of the source image this call should be
|
46
|
+
* followed up by strip_bottom_shift() to complete the strip shift.
|
47
|
+
*/
|
48
|
+
static void strip_top_shift(struct strip *st)
|
49
|
+
{
|
50
|
+
uint32_t i;
|
51
|
+
for (i=1; i<st->height; i++) {
|
52
|
+
st->virt[i - 1] = st->virt[i];
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Points the bottommost virtual scanline to the next allocated scanline,
|
58
|
+
* discarding the topmost scanline.
|
59
|
+
*
|
60
|
+
* The caller is expected to fill the bottom virtual scanline with image data
|
61
|
+
* after this call.
|
62
|
+
*/
|
63
|
+
static void strip_bottom_shift(struct strip *st)
|
64
|
+
{
|
65
|
+
st->rr = (st->rr + 1) % st->height;
|
66
|
+
st->virt[st->height - 1] = st->sl[st->rr];
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Returns a pointer to the bottommost virtual scanline.
|
71
|
+
*/
|
72
|
+
static void *strip_bottom(struct strip *st)
|
73
|
+
{
|
74
|
+
return st->virt[st->height - 1];
|
75
|
+
}
|
76
|
+
|
77
|
+
static void yscaler_map_pos(struct yscaler *ys)
|
78
|
+
{
|
79
|
+
uint32_t target;
|
80
|
+
target = split_map(ys->in_height, ys->out_height, ys->out_pos, &ys->ty);
|
81
|
+
ys->in_target = target + ys->strip.height / 2 + 1;
|
82
|
+
}
|
83
|
+
|
84
|
+
void yscaler_init(struct yscaler *ys, uint32_t in_height, uint32_t out_height,
|
85
|
+
uint32_t buflen)
|
86
|
+
{
|
87
|
+
uint32_t taps;
|
88
|
+
|
89
|
+
ys->in_height = in_height;
|
90
|
+
ys->out_height = out_height;
|
91
|
+
ys->in_pos = 0;
|
92
|
+
ys->out_pos = 0;
|
93
|
+
|
94
|
+
taps = calc_taps(in_height, out_height);
|
95
|
+
strip_init(&ys->strip, taps, buflen);
|
96
|
+
yscaler_map_pos(ys);
|
97
|
+
}
|
98
|
+
|
99
|
+
void yscaler_free(struct yscaler *ys)
|
100
|
+
{
|
101
|
+
strip_free(&ys->strip);
|
102
|
+
}
|
103
|
+
|
104
|
+
unsigned char *yscaler_next(struct yscaler *ys)
|
105
|
+
{
|
106
|
+
struct strip *st;
|
107
|
+
|
108
|
+
st = &ys->strip;
|
109
|
+
|
110
|
+
/* We need the first scanline for top padding. */
|
111
|
+
if (ys->in_pos == 0) {
|
112
|
+
ys->in_pos++;
|
113
|
+
return strip_bottom(st);
|
114
|
+
}
|
115
|
+
|
116
|
+
while (ys->in_pos < ys->in_target) {
|
117
|
+
ys->in_pos++;
|
118
|
+
strip_top_shift(&ys->strip);
|
119
|
+
if (ys->in_pos <= ys->in_height) {
|
120
|
+
strip_bottom_shift(st);
|
121
|
+
return strip_bottom(st);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
return 0;
|
126
|
+
}
|
127
|
+
|
128
|
+
void yscaler_scale(struct yscaler *ys, uint8_t *out, uint32_t width,
|
129
|
+
uint8_t cmp, uint8_t opts)
|
130
|
+
{
|
131
|
+
struct strip *st;
|
132
|
+
|
133
|
+
st = &ys->strip;
|
134
|
+
strip_scale((void **)st->virt, st->height, width, (void *)out, ys->ty,
|
135
|
+
cmp, opts);
|
136
|
+
ys->out_pos++;
|
137
|
+
yscaler_map_pos(ys);
|
138
|
+
}
|
data/ext/oil/yscaler.h
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#ifndef YSCALER_H
|
2
|
+
#define YSCALER_H
|
3
|
+
|
4
|
+
#include "resample.h"
|
5
|
+
#include <stdint.h>
|
6
|
+
|
7
|
+
struct strip {
|
8
|
+
uint32_t height;
|
9
|
+
uint8_t **sl;
|
10
|
+
uint8_t **virt;
|
11
|
+
uint32_t rr;
|
12
|
+
};
|
13
|
+
|
14
|
+
struct yscaler {
|
15
|
+
uint32_t in_height;
|
16
|
+
uint32_t out_height;
|
17
|
+
struct strip strip;
|
18
|
+
uint32_t in_pos;
|
19
|
+
uint32_t out_pos;
|
20
|
+
uint32_t in_target;
|
21
|
+
float ty;
|
22
|
+
};
|
23
|
+
|
24
|
+
void yscaler_init(struct yscaler *ys, uint32_t in_height, uint32_t out_height,
|
25
|
+
uint32_t buflen);
|
26
|
+
void yscaler_free(struct yscaler *ys);
|
27
|
+
unsigned char *yscaler_next(struct yscaler *ys);
|
28
|
+
void yscaler_scale(struct yscaler *ys, uint8_t *out, uint32_t width,
|
29
|
+
uint8_t cmp, uint8_t opts);
|
30
|
+
|
31
|
+
#endif
|
data/lib/oil.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Oil
|
2
|
+
VERSION = "0.1.0"
|
3
|
+
|
4
|
+
def self.fix_ratio(sw, sh, boxw, boxh)
|
5
|
+
x = boxw / sw.to_f
|
6
|
+
y = boxh / sh.to_f
|
7
|
+
|
8
|
+
destw = boxw
|
9
|
+
desth = boxh
|
10
|
+
|
11
|
+
if x < y
|
12
|
+
desth = (sh * x).round
|
13
|
+
else
|
14
|
+
destw = (sw * y).round
|
15
|
+
end
|
16
|
+
|
17
|
+
if desth < 1
|
18
|
+
desth = 1
|
19
|
+
end
|
20
|
+
|
21
|
+
if destw < 1
|
22
|
+
destw = 1
|
23
|
+
end
|
24
|
+
|
25
|
+
return destw, desth
|
26
|
+
end
|
27
|
+
|
28
|
+
# JPEG Pre-scaling is equivalent to a box filter at an integer scale factor.
|
29
|
+
# We don't use this to scale down past 4x the target image size in order to
|
30
|
+
# get proper bicubic scaling in the final image.
|
31
|
+
def self.pre_scale(sw, sh, dw, dh)
|
32
|
+
inv_scale = sw / dw
|
33
|
+
inv_scale /= 4
|
34
|
+
|
35
|
+
if inv_scale >= 8
|
36
|
+
return 8
|
37
|
+
elsif inv_scale >= 4
|
38
|
+
return 4
|
39
|
+
elsif inv_scale >= 2
|
40
|
+
return 2
|
41
|
+
else
|
42
|
+
return 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.new(io, box_width, box_height)
|
47
|
+
a = io.getc
|
48
|
+
b = io.getc
|
49
|
+
io.ungetc(b)
|
50
|
+
io.ungetc(a)
|
51
|
+
|
52
|
+
if (a == "\xFF".b && b == "\xD8".b)
|
53
|
+
o = JPEGReader.new(io, [:COM, :APP1, :APP2])
|
54
|
+
if (o.color_space == :RGB)
|
55
|
+
o.out_color_space = :RGBX
|
56
|
+
end
|
57
|
+
elsif (a == "\x89".b && b == "P".b)
|
58
|
+
o = PNGReader.new(io)
|
59
|
+
else
|
60
|
+
raise "Unknown image file format."
|
61
|
+
end
|
62
|
+
|
63
|
+
destw, desth = self.fix_ratio(o.width, o.height, box_width, box_height)
|
64
|
+
pre = self.pre_scale(o.width, o.height, box_width, box_height)
|
65
|
+
|
66
|
+
o.scale_width = destw
|
67
|
+
o.scale_height = desth
|
68
|
+
|
69
|
+
if o.respond_to?(:scale_denom) and pre > 0
|
70
|
+
o.scale_denom = pre
|
71
|
+
end
|
72
|
+
|
73
|
+
return o
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require 'oil/oil.so'
|
data/test/helper.rb
CHANGED
@@ -1,12 +1,90 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
class CustomError < RuntimeError; end
|
2
4
|
|
3
|
-
class CustomIO
|
4
|
-
def initialize(
|
5
|
-
@
|
6
|
-
@
|
5
|
+
class CustomIO < StringIO
|
6
|
+
def initialize(str, options={})
|
7
|
+
@read_count = options[:read_count]
|
8
|
+
@byte_count = options[:byte_count]
|
9
|
+
@read_count = 0 unless @read_count || @byte_count
|
10
|
+
super(str)
|
7
11
|
end
|
8
12
|
|
9
|
-
def
|
10
|
-
|
13
|
+
def countdown(size)
|
14
|
+
ret = (@read_count && @read_count <= 0) || (@byte_count && @byte_count <= 0)
|
15
|
+
@read_count -= 1 if @read_count
|
16
|
+
@byte_count -= size if @byte_count
|
17
|
+
ret
|
11
18
|
end
|
12
19
|
end
|
20
|
+
|
21
|
+
class GrowIO < CustomIO
|
22
|
+
def read(*args)
|
23
|
+
size = args[0]
|
24
|
+
buf = args[1]
|
25
|
+
return super unless countdown(size)
|
26
|
+
str = super(size)
|
27
|
+
str = str[0..-2] * 3 if str
|
28
|
+
|
29
|
+
if buf
|
30
|
+
buf.slice!(0,0) # this empties the buffer
|
31
|
+
buf << str
|
32
|
+
else
|
33
|
+
str
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ShrinkIO < CustomIO
|
39
|
+
def read(*args)
|
40
|
+
size = args[0]
|
41
|
+
return super unless countdown(size)
|
42
|
+
res = super(size)
|
43
|
+
raise RuntimeError unless res
|
44
|
+
new_size = res.size / 2
|
45
|
+
res[-1 * new_size, new_size]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class RaiseIO < CustomIO
|
50
|
+
def read(*args)
|
51
|
+
return super unless countdown(size)
|
52
|
+
raise CustomError
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ThrowIO < CustomIO
|
57
|
+
def read(*args)
|
58
|
+
return super unless countdown(size)
|
59
|
+
throw(:foo)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class NilIO < CustomIO
|
64
|
+
def read(*args)
|
65
|
+
super unless countdown(size)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class NotStringIO < CustomIO
|
70
|
+
def read(*args)
|
71
|
+
return super unless countdown(size)
|
72
|
+
return 78887
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def resize_string(str, width=nil, height=nil)
|
77
|
+
io = StringIO.new(str)
|
78
|
+
width ||= 100
|
79
|
+
height ||= 200
|
80
|
+
out = binary_stringio
|
81
|
+
o = Oil.new(io, width, height).each{ |d| out << d }
|
82
|
+
out.string
|
83
|
+
end
|
84
|
+
|
85
|
+
def binary_stringio
|
86
|
+
io = StringIO.new
|
87
|
+
io.set_encoding 'ASCII-8BIT' if RUBY_VERSION >= '1.9'
|
88
|
+
io
|
89
|
+
end
|
90
|
+
|
data/test/test_jpeg.rb
CHANGED
@@ -1,220 +1,150 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'minitest/autorun'
|
3
2
|
require 'oil'
|
4
3
|
require 'stringio'
|
5
4
|
require 'helper'
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
\
|
12
|
-
\
|
13
|
-
\
|
14
|
-
\
|
15
|
-
\
|
16
|
-
\x01\
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
validate_jpeg resize_string(JPEG_DATA)
|
22
|
-
end
|
6
|
+
class TestJPEG < MiniTest::Test
|
7
|
+
# http://stackoverflow.com/a/2349470
|
8
|
+
JPEG_DATA = "\
|
9
|
+
\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\
|
10
|
+
\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\
|
11
|
+
\x03\x04\x06\x04\x04\x04\x04\x04\x08\x06\x06\x05\x06\x09\x08\x0a\x0a\x09\x08\
|
12
|
+
\x09\x09\x0a\x0c\x0f\x0c\x0a\x0b\x0e\x0b\x09\x09\x0d\x11\x0d\x0e\x0f\x10\x10\
|
13
|
+
\x11\x10\x0a\x0c\x12\x13\x12\x10\x13\x0f\x10\x10\x10\xff\xc9\x00\x0b\x08\x00\
|
14
|
+
\x01\x00\x01\x01\x01\x11\x00\xff\xcc\x00\x06\x00\x10\x10\x05\xff\xda\x00\x08\
|
15
|
+
\x01\x01\x00\x00\x3f\x00\xd2\xcf\x20\xff\xd9".b
|
16
|
+
|
17
|
+
BIG_JPEG = begin
|
18
|
+
resize_string(JPEG_DATA, 2000, 2000)
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
def test_valid
|
22
|
+
o = Oil::JPEGReader.new(jpeg_io)
|
23
|
+
assert_equal 1, o.width
|
24
|
+
assert_equal 1, o.height
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
def test_missing_eof
|
28
|
+
io = StringIO.new(JPEG_DATA[0..-2])
|
29
|
+
o = Oil::JPEGReader.new(io)
|
30
|
+
assert_equal 1, o.width
|
31
|
+
assert_equal 1, o.height
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def test_bogus_header_marker
|
35
|
+
str = JPEG_DATA.dup
|
36
|
+
str[3] = "\x10"
|
37
|
+
assert_raises(RuntimeError) { resize_string(str) }
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_alloc_each
|
49
|
-
io = JPEG.allocate
|
50
|
-
assert_raises(NoMethodError){ io.each{ |f| } }
|
51
|
-
end
|
52
|
-
|
53
|
-
# Test dimensions
|
54
|
-
|
55
|
-
def test_zero_dim
|
56
|
-
assert_raises(ArgumentError){ resize_string(JPEG_DATA, 0, 0) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_neg_dim
|
60
|
-
assert_raises(ArgumentError){ resize_string(JPEG_DATA, -1231, -123) }
|
61
|
-
end
|
62
|
-
|
63
|
-
# Test io source handler
|
64
|
-
|
65
|
-
def test_io_returns_too_much_data
|
66
|
-
proc = Proc.new do |io, size, buf|
|
67
|
-
str = io.read(size)
|
68
|
-
str = str[0..-2] * 2 if str
|
69
|
-
if buf
|
70
|
-
buf.slice!(0,0) # this empties the buffer
|
71
|
-
buf << str
|
72
|
-
else
|
73
|
-
str
|
74
|
-
end
|
75
|
-
end
|
76
|
-
assert_raises(RuntimeError) { custom_io proc, JPEG_DATA }
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_io_does_nothing
|
80
|
-
assert_raises(RuntimeError) { custom_io(nil) }
|
81
|
-
end
|
40
|
+
def test_bogus_body_marker
|
41
|
+
str = JPEG_DATA.dup
|
42
|
+
str[-22] = "\x10"
|
43
|
+
assert_raises(RuntimeError) { resize_string(str) }
|
44
|
+
end
|
82
45
|
|
83
|
-
|
84
|
-
proc = Proc.new{ raise CustomError }
|
85
|
-
assert_raises(CustomError) { custom_io proc }
|
86
|
-
end
|
46
|
+
# Allocation tests
|
87
47
|
|
88
|
-
|
89
|
-
|
90
|
-
catch(:foo){ custom_io proc }
|
91
|
-
end
|
92
|
-
|
93
|
-
def test_io_raises_exception_in_body
|
94
|
-
flag = false
|
95
|
-
proc = Proc.new do |parent, size, buf|
|
96
|
-
raise CustomError if flag
|
97
|
-
flag = true
|
98
|
-
if buf
|
99
|
-
parent.read(size, buf)
|
100
|
-
else
|
101
|
-
parent.read(size)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
assert_raises(CustomError) { custom_io proc, big_jpeg }
|
105
|
-
end
|
48
|
+
def test_multiple_initialize_leak
|
49
|
+
o = Oil::JPEGReader.allocate
|
106
50
|
|
107
|
-
|
108
|
-
|
109
|
-
proc = Proc.new do |parent, size, buf|
|
110
|
-
throw :foo if flag
|
111
|
-
flag = true
|
112
|
-
if buf
|
113
|
-
parent.read(size, buf)
|
114
|
-
else
|
115
|
-
parent.read(size)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
catch(:foo){ custom_io proc, big_jpeg }
|
119
|
-
end
|
51
|
+
o.send(:initialize, jpeg_io)
|
52
|
+
o.each{ |d| }
|
120
53
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
parent.read(size, buf)
|
125
|
-
buf.slice!(0, 10)
|
126
|
-
else
|
127
|
-
res = parent.read(size)
|
128
|
-
res[10, -1]
|
129
|
-
end
|
130
|
-
end
|
131
|
-
assert_raises(RuntimeError) { custom_io(proc, big_jpeg) }
|
132
|
-
end
|
54
|
+
o.send(:initialize, jpeg_io)
|
55
|
+
o.each{ |d| }
|
56
|
+
end
|
133
57
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
parent.read(size, buf)
|
138
|
-
buf << buf
|
139
|
-
else
|
140
|
-
str = parent.read(size)
|
141
|
-
str * 2
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
if(RUBY_PLATFORM =~ /java/)
|
146
|
-
assert_raises(RuntimeError) { custom_io(proc, big_jpeg) }
|
147
|
-
else
|
148
|
-
validate_jpeg custom_io(proc, big_jpeg) # libjpeg stops when reaches eof
|
149
|
-
end
|
150
|
-
end
|
58
|
+
# Test io
|
59
|
+
|
60
|
+
IO_OFFSETS = [0, 10, 20, 1023, 1024, 1025, 8191, 8192, 8193, 12000]
|
151
61
|
|
152
|
-
|
153
|
-
|
154
|
-
|
62
|
+
def iotest(io_class)
|
63
|
+
IO_OFFSETS.each do |i|
|
64
|
+
yield io_class.new(BIG_JPEG, :byte_count => i)
|
155
65
|
end
|
66
|
+
end
|
156
67
|
|
157
|
-
|
68
|
+
def resize(io)
|
69
|
+
o = Oil.new(io, 20, 10).each{ |d| }
|
70
|
+
end
|
158
71
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
JPEG.new(io, 200, 200).each { raise CustomError }
|
163
|
-
end
|
72
|
+
def test_io_too_much_data
|
73
|
+
iotest(GrowIO) do |io|
|
74
|
+
assert_raises(RuntimeError) { resize(io) }
|
164
75
|
end
|
76
|
+
end
|
165
77
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
JPEG.new(io, 200, 200).each { throw :foo }
|
170
|
-
end
|
78
|
+
def test_io_does_nothing
|
79
|
+
iotest(NilIO) do |io|
|
80
|
+
assert_raises(TypeError) { resize(io) }
|
171
81
|
end
|
82
|
+
end
|
172
83
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
JPEG.new(io, 200, 200).each { |d| io_out << d; d.slice!(0, 4) }
|
177
|
-
validate_jpeg(io_out.string)
|
178
|
-
end
|
179
|
-
|
180
|
-
def test_each_enlarges_buffer
|
181
|
-
io = StringIO.new(JPEG_DATA)
|
182
|
-
io_out = binary_stringio
|
183
|
-
JPEG.new(io, 200, 200).each { |d| io_out << d; d << "foobar" }
|
184
|
-
validate_jpeg(io_out.string)
|
84
|
+
def test_io_raises_exception
|
85
|
+
iotest(RaiseIO) do |io|
|
86
|
+
assert_raises(CustomError) { resize(io) }
|
185
87
|
end
|
88
|
+
end
|
186
89
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
width ||= 100
|
191
|
-
height ||= 200
|
192
|
-
out = binary_stringio
|
193
|
-
JPEG.new(io, width, height).each{ |d| out << d }
|
194
|
-
return out.string
|
90
|
+
def test_io_throws
|
91
|
+
iotest(ThrowIO) do |io|
|
92
|
+
assert_throws(:foo) { resize(io) }
|
195
93
|
end
|
94
|
+
end
|
196
95
|
|
197
|
-
|
198
|
-
|
96
|
+
# This causes valgrind errors, but I'm pretty sure that the libjpeg is
|
97
|
+
# returning uninitialized memory because the source jpeg is corrupt.
|
98
|
+
def test_io_shrinks_buffer
|
99
|
+
iotest(ShrinkIO) do |io|
|
100
|
+
resize(io) rescue nil
|
199
101
|
end
|
102
|
+
end
|
200
103
|
|
201
|
-
|
202
|
-
|
104
|
+
def test_not_string_io
|
105
|
+
iotest(NotStringIO) do |io|
|
106
|
+
assert_raises(TypeError) { resize(io) }
|
203
107
|
end
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
108
|
+
end
|
109
|
+
|
110
|
+
# Test yielding
|
111
|
+
|
112
|
+
def test_raise_in_each
|
113
|
+
assert_raises(CustomError) do
|
114
|
+
Oil.new(jpeg_io, 10, 20).each{ raise CustomError }
|
208
115
|
end
|
116
|
+
end
|
209
117
|
|
210
|
-
|
211
|
-
|
118
|
+
def test_throw_in_each
|
119
|
+
catch(:foo) do
|
120
|
+
Oil.new(jpeg_io, 10, 20).each{ throw :foo }
|
212
121
|
end
|
122
|
+
end
|
213
123
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
124
|
+
def test_each_in_each
|
125
|
+
o = Oil.new(jpeg_io, 10, 20)
|
126
|
+
o.each do |d|
|
127
|
+
assert_raises(RuntimeError) do
|
128
|
+
o.each { |e| }
|
129
|
+
end
|
218
130
|
end
|
219
131
|
end
|
132
|
+
|
133
|
+
def test_each_shrinks_buffer
|
134
|
+
io = StringIO.new(JPEG_DATA)
|
135
|
+
io_out = binary_stringio
|
136
|
+
Oil.new(io, 200, 200).each{ |d| io_out << d; d.slice!(0, 4) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_each_enlarges_buffer
|
140
|
+
io = StringIO.new(JPEG_DATA)
|
141
|
+
io_out = binary_stringio
|
142
|
+
o = Oil.new(io, 200, 200).each{ |d| io_out << d; d << "foobar" }
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def jpeg_io
|
148
|
+
StringIO.new(JPEG_DATA)
|
149
|
+
end
|
220
150
|
end
|