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.
@@ -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
@@ -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
+ }
@@ -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
@@ -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'
@@ -1,12 +1,90 @@
1
+ require 'stringio'
2
+
1
3
  class CustomError < RuntimeError; end
2
4
 
3
- class CustomIO
4
- def initialize(proc, *args)
5
- @parent = StringIO.new(*args)
6
- @proc = proc
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 read(size, buf=nil)
10
- @proc.call(@parent, size, buf) if @proc
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
+
@@ -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
- module Oil
8
- class TestJPEG < MiniTest::Unit::TestCase
9
- # http://www.techsupportteam.org/forum/digital-imaging-photography/1892-worlds-smallest-valid-jpeg.html
10
- JPEG_DATA = "\
11
- \xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\
12
- \x00\xFF\xDB\x00\x43\x00\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\xFF\xFF\xFF\xFF\xFF\
15
- \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC2\x00\x0B\x08\x00\
16
- \x01\x00\x01\x01\x01\x11\x00\xFF\xC4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\
17
- \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xDA\x00\x08\x01\x01\x00\x01\x3F\
18
- \x10\xFF\xD9"
19
-
20
- def test_valid
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
- def test_missing_eof
25
- validate_jpeg resize_string(JPEG_DATA[0..-2])
26
- end
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
- def test_bogus_header_marker
29
- str = JPEG_DATA.dup
30
- str[3] = "\x10"
31
- assert_raises(RuntimeError) { resize_string(str) }
32
- end
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
- def test_bogus_body_marker
35
- str = JPEG_DATA.dup
36
- str[-1] = "\x10"
37
- assert_raises(RuntimeError) { resize_string(str) }
38
- end
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
- def test_calls_each_during_yield
41
- io = StringIO.new(JPEG_DATA)
42
- j = JPEG.new(io, 600, 600)
43
- assert_raises(RuntimeError) do
44
- j.each{ |d| j.each { |e| j.each { |f| } } }
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
- def test_io_raises_exception_immediately
84
- proc = Proc.new{ raise CustomError }
85
- assert_raises(CustomError) { custom_io proc }
86
- end
46
+ # Allocation tests
87
47
 
88
- def test_io_throws_immediately
89
- proc = Proc.new{ throw(:foo) }
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
- def test_io_throws_in_body
108
- flag = false
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
- def test_io_shrinks_buffer
122
- proc = Proc.new do |parent, size, buf|
123
- if buf
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
- def test_io_enlarges_buffer_with_eof
135
- proc = Proc.new do |parent, size, buf|
136
- if buf
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
- def test_io_seek
153
- # Craft a JPEG with header content that will be skipped.
154
- # Make sure an actual seek happens, and it doesn't just skip buffer.
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
- # Test yielding
68
+ def resize(io)
69
+ o = Oil.new(io, 20, 10).each{ |d| }
70
+ end
158
71
 
159
- def test_raise_in_each
160
- assert_raises(CustomError) do
161
- io = StringIO.new(JPEG_DATA)
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
- def test_throw_in_each
167
- catch(:foo) do
168
- io = StringIO.new(JPEG_DATA)
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
- def test_each_shrinks_buffer
174
- io = StringIO.new(JPEG_DATA)
175
- io_out = binary_stringio
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
- private
188
-
189
- def io(io, width=nil, height=nil)
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
- def custom_io(*args)
198
- io CustomIO.new(*args)
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
- def resize_string(str, width=nil, height=nil)
202
- io(StringIO.new(str), width, height)
104
+ def test_not_string_io
105
+ iotest(NotStringIO) do |io|
106
+ assert_raises(TypeError) { resize(io) }
203
107
  end
204
-
205
- def validate_jpeg(data)
206
- assert_equal "\xFF\xD8", data[0,2]
207
- assert_equal "\xFF\xD9", data[-2, 2]
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
- def big_jpeg
211
- resize_string(JPEG_DATA, 1000, 1000)
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
- def binary_stringio
215
- io = StringIO.new
216
- io.set_encoding 'ASCII-8BIT' if RUBY_VERSION >= '1.9'
217
- io
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