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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4036eeaab5f839b40269e35db9d1c5e74f905a2d
|
4
|
+
data.tar.gz: c369f27885d7edc5353b0012973075b7b1969aac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ff2f9e88da8fd2c1845e63ceffc1fd460d58a1ede777734c812eaad8a195bc7007575aa1221cd80c529a4f2ce0ddabaa23b900dd996c489c8dbe10fb07555025
|
7
|
+
data.tar.gz: ad0f4b2dfee53093fa6daf65a3d7deba426799e2921f0d4d6e76e6742efc423ad5f9a4429addf1f9b702eab2d6f0927d3f75c9d9014dd070b34414a2b55142d8
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Timothy Elliott
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -4,8 +4,8 @@ http://github.com/ender672/oil
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
Oil
|
8
|
-
use.
|
7
|
+
Oil is a Ruby extension for resizing JPEG and PNG images. It aims for fast
|
8
|
+
performance and low memory use.
|
9
9
|
|
10
10
|
== INSTALLATION:
|
11
11
|
|
@@ -14,24 +14,23 @@ use.
|
|
14
14
|
== SYNOPSIS:
|
15
15
|
|
16
16
|
require 'oil'
|
17
|
-
io = File.open('image.jpg', 'rb')
|
18
17
|
|
19
|
-
#
|
20
|
-
|
18
|
+
# Oil uses IO objects for input & output.
|
19
|
+
io_in = File.open('image.jpg', 'rb')
|
20
|
+
io_out = File.open('image_resized.jpg', 'w')
|
21
21
|
|
22
|
-
#
|
23
|
-
|
22
|
+
# Read the source image header and prepare to fit it into a 200x300 box.
|
23
|
+
img = Oil.new(io_in, 200, 300)
|
24
|
+
|
25
|
+
# Write the resized image to disk
|
26
|
+
img.each { |data| io_out << data }
|
24
27
|
|
25
28
|
== REQUIREMENTS:
|
26
29
|
|
27
30
|
These requirements do not apply to the JRuby gem.
|
28
31
|
|
29
|
-
*
|
30
|
-
* libpng
|
31
|
-
|
32
|
-
Installing libjpeg headers (OSX):
|
33
|
-
|
34
|
-
$ brew install libjpeg # requires homebrew (http://mxcl.github.com/homebrew/)
|
32
|
+
* libjpeg-turbo
|
33
|
+
* libpng
|
35
34
|
|
36
35
|
Installing libjpeg and libpng headers (Debian/Ubuntu):
|
37
36
|
|
@@ -47,43 +46,12 @@ Compile & run unit tests. Should show no warnings and no failing tests:
|
|
47
46
|
Valgrind should not complain (ruby-1.9.3p125, compiled with -O3):
|
48
47
|
|
49
48
|
$ valgrind /path/to/ruby -Iext:test test/test_jpeg.rb
|
50
|
-
$ valgrind
|
49
|
+
$ valgrind /path/to/ruby -Iext:test test/test_png.rb
|
51
50
|
|
52
51
|
Tests should not leak memory:
|
53
52
|
|
54
|
-
$ ruby -Iext:test -e "require 'test_jpeg.rb'; require 'test_png.rb'; loop{ MiniTest
|
53
|
+
$ ruby -Iext:test -e "require 'test_jpeg.rb'; require 'test_png.rb'; loop{ MiniTest.run }"
|
55
54
|
|
56
55
|
Changes to the interpolator should be analyzed using ResampleScope:
|
57
56
|
|
58
57
|
https://github.com/jsummers/resamplescope
|
59
|
-
|
60
|
-
== TODO:
|
61
|
-
|
62
|
-
* Windows
|
63
|
-
|
64
|
-
== LICENSE:
|
65
|
-
|
66
|
-
(The MIT License)
|
67
|
-
|
68
|
-
Copyright (c) 2012
|
69
|
-
|
70
|
-
* {Timothy Elliott}[http://holymonkey.com]
|
71
|
-
|
72
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
73
|
-
a copy of this software and associated documentation files (the
|
74
|
-
'Software'), to deal in the Software without restriction, including
|
75
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
76
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
77
|
-
permit persons to whom the Software is furnished to do so, subject to
|
78
|
-
the following conditions:
|
79
|
-
|
80
|
-
The above copyright notice and this permission notice shall be
|
81
|
-
included in all copies or substantial portions of the Software.
|
82
|
-
|
83
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
84
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
85
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
86
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
87
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
88
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
89
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,41 +1,45 @@
|
|
1
|
-
require 'rake/
|
1
|
+
require 'rake/extensiontask'
|
2
|
+
require 'rubygems/package_task'
|
2
3
|
require 'rake/testtask'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
sh "javac -g -cp #{Config::CONFIG['prefix']}/lib/jruby.jar #{FileList['*.java']}"
|
7
|
-
quoted_files = (FileList.new('*.class').to_a.map { |f| "'#{f}'" }).join(' ')
|
8
|
-
sh "jar cf oil.jar #{quoted_files}"
|
9
|
-
end
|
5
|
+
Rake::ExtensionTask.new('oil') do |ext|
|
6
|
+
ext.lib_dir = 'lib/oil'
|
10
7
|
end
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
s = Gem::Specification.new('oil', '0.1.0') do |s|
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.summary = 'Resize JPEG and PNG images.'
|
12
|
+
s.description = 'Resize JPEG and PNG images, aiming for fast performance and low memory use.'
|
13
|
+
s.authors = ['Timothy Elliott']
|
14
|
+
s.email = 'tle@holymonkey.com'
|
15
|
+
s.files = %w{
|
16
|
+
Rakefile
|
17
|
+
README.rdoc
|
18
|
+
MIT-LICENSE
|
19
|
+
lib/oil.rb
|
20
|
+
ext/oil/resample.c
|
21
|
+
ext/oil/resample.h
|
22
|
+
ext/oil/yscaler.c
|
23
|
+
ext/oil/yscaler.h
|
24
|
+
ext/oil/jpeg.c
|
25
|
+
ext/oil/png.c
|
26
|
+
ext/oil/oil.c
|
27
|
+
ext/oil/extconf.rb
|
28
|
+
test/helper.rb
|
29
|
+
test/test_jpeg.rb
|
30
|
+
test/test_png.rb
|
31
|
+
}
|
32
|
+
s.homepage = 'http://github.com/ender672/oil'
|
33
|
+
s.extensions << 'ext/oil/extconf.rb'
|
34
|
+
s.extra_rdoc_files = ['README.rdoc']
|
16
35
|
end
|
17
36
|
|
18
|
-
|
19
|
-
cd 'ext' do
|
20
|
-
sh 'make'
|
21
|
-
end
|
22
|
-
end
|
37
|
+
Gem::PackageTask.new(s){}
|
23
38
|
|
24
39
|
Rake::TestTask.new do |t|
|
25
|
-
t.libs = ['
|
26
|
-
t.test_files = FileList['test/
|
40
|
+
t.libs = ['lib', 'test']
|
41
|
+
t.test_files = FileList['test/test_jpeg.rb', 'test/test_png.rb']
|
27
42
|
end
|
28
43
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
desc 'Build the gem and include the java library'
|
33
|
-
task :gem => "ext/oil.jar" do
|
34
|
-
system "gem build oil.gemspec"
|
35
|
-
end
|
36
|
-
|
37
|
-
desc 'Compile the extension'
|
38
|
-
task :compile => "ext/oil.#{RUBY_PLATFORM =~ /java/ ? 'jar' : 'so'}"
|
39
|
-
|
40
|
-
task :test => :compile
|
41
|
-
task :default => :test
|
44
|
+
task test: :compile
|
45
|
+
task default: :test
|
@@ -1,16 +1,6 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
-
|
4
|
-
if RUBY_PLATFORM =~ /darwin/
|
5
|
-
png_idefault = '/usr/X11/include'
|
6
|
-
png_ldefault = '/usr/X11/lib'
|
7
|
-
else
|
8
|
-
png_idefault = nil
|
9
|
-
png_ldefault = nil
|
10
|
-
end
|
11
|
-
|
12
|
-
dir_config('jpeg')
|
13
|
-
dir_config('png', png_idefault, png_ldefault)
|
3
|
+
$CFLAGS += " -O3 -ffast-math -march=native"
|
14
4
|
|
15
5
|
unless have_header('jpeglib.h')
|
16
6
|
abort "libjpeg headers were not found."
|
@@ -28,4 +18,4 @@ unless have_library('png')
|
|
28
18
|
abort "libpng was not found."
|
29
19
|
end
|
30
20
|
|
31
|
-
create_makefile('oil')
|
21
|
+
create_makefile('oil/oil')
|
data/ext/oil/jpeg.c
ADDED
@@ -0,0 +1,861 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <jpeglib.h>
|
3
|
+
#include "resample.h"
|
4
|
+
#include "yscaler.h"
|
5
|
+
|
6
|
+
#define READ_SIZE 1024
|
7
|
+
#define WRITE_SIZE 1024
|
8
|
+
|
9
|
+
static ID id_GRAYSCALE, id_RGB, id_YCbCr, id_CMYK, id_YCCK, id_UNKNOWN;
|
10
|
+
static ID id_APP0, id_APP1, id_APP2, id_APP3, id_APP4, id_APP5, id_APP6,
|
11
|
+
id_APP7, id_APP8, id_APP9, id_APP10, id_APP11, id_APP12, id_APP13,
|
12
|
+
id_APP14, id_APP15, id_COM;
|
13
|
+
static ID id_read;
|
14
|
+
|
15
|
+
static VALUE sym_quality, sym_markers;
|
16
|
+
|
17
|
+
/* Color Space Conversion Helpers. */
|
18
|
+
|
19
|
+
static ID j_color_space_to_id(J_COLOR_SPACE cs)
|
20
|
+
{
|
21
|
+
switch (cs) {
|
22
|
+
case JCS_GRAYSCALE:
|
23
|
+
return id_GRAYSCALE;
|
24
|
+
case JCS_RGB:
|
25
|
+
return id_RGB;
|
26
|
+
case JCS_YCbCr:
|
27
|
+
return id_YCbCr;
|
28
|
+
case JCS_CMYK:
|
29
|
+
return id_CMYK;
|
30
|
+
case JCS_YCCK:
|
31
|
+
return id_YCCK;
|
32
|
+
default:
|
33
|
+
return id_UNKNOWN;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
static J_COLOR_SPACE sym_to_j_color_space(VALUE sym)
|
38
|
+
{
|
39
|
+
ID rb = SYM2ID(sym);
|
40
|
+
|
41
|
+
if (rb == id_GRAYSCALE) {
|
42
|
+
return JCS_GRAYSCALE;
|
43
|
+
} else if (rb == id_RGB) {
|
44
|
+
return JCS_RGB;
|
45
|
+
} else if (rb == id_YCbCr) {
|
46
|
+
return JCS_YCbCr;
|
47
|
+
} else if (rb == id_CMYK) {
|
48
|
+
return JCS_CMYK;
|
49
|
+
} else if (rb == id_YCCK) {
|
50
|
+
return JCS_YCCK;
|
51
|
+
}
|
52
|
+
rb_raise(rb_eRuntimeError, "Color space not recognized.");
|
53
|
+
}
|
54
|
+
|
55
|
+
static int sym_to_marker_code(VALUE sym)
|
56
|
+
{
|
57
|
+
ID rb = SYM2ID(sym);
|
58
|
+
|
59
|
+
if (rb == id_COM) {
|
60
|
+
return JPEG_COM;
|
61
|
+
} else if (rb == id_APP0) {
|
62
|
+
return JPEG_APP0;
|
63
|
+
} else if (rb == id_APP1) {
|
64
|
+
return JPEG_APP0 + 1;
|
65
|
+
} else if (rb == id_APP2) {
|
66
|
+
return JPEG_APP0 + 2;
|
67
|
+
} else if (rb == id_APP3) {
|
68
|
+
return JPEG_APP0 + 3;
|
69
|
+
} else if (rb == id_APP4) {
|
70
|
+
return JPEG_APP0 + 4;
|
71
|
+
} else if (rb == id_APP5) {
|
72
|
+
return JPEG_APP0 + 5;
|
73
|
+
} else if (rb == id_APP6) {
|
74
|
+
return JPEG_APP0 + 6;
|
75
|
+
} else if (rb == id_APP7) {
|
76
|
+
return JPEG_APP0 + 7;
|
77
|
+
} else if (rb == id_APP8) {
|
78
|
+
return JPEG_APP0 + 8;
|
79
|
+
} else if (rb == id_APP9) {
|
80
|
+
return JPEG_APP0 + 9;
|
81
|
+
} else if (rb == id_APP10) {
|
82
|
+
return JPEG_APP0 + 10;
|
83
|
+
} else if (rb == id_APP11) {
|
84
|
+
return JPEG_APP0 + 11;
|
85
|
+
} else if (rb == id_APP12) {
|
86
|
+
return JPEG_APP0 + 12;
|
87
|
+
} else if (rb == id_APP13) {
|
88
|
+
return JPEG_APP0 + 13;
|
89
|
+
} else if (rb == id_APP14) {
|
90
|
+
return JPEG_APP0 + 14;
|
91
|
+
} else if (rb == id_APP15) {
|
92
|
+
return JPEG_APP0 + 15;
|
93
|
+
}
|
94
|
+
rb_raise(rb_eRuntimeError, "Marker code not recognized.");
|
95
|
+
}
|
96
|
+
|
97
|
+
static VALUE marker_code_to_sym(int marker_code)
|
98
|
+
{
|
99
|
+
switch(marker_code) {
|
100
|
+
case JPEG_COM:
|
101
|
+
return ID2SYM(id_COM);
|
102
|
+
case JPEG_APP0:
|
103
|
+
return ID2SYM(id_APP0);
|
104
|
+
case JPEG_APP0 + 1:
|
105
|
+
return ID2SYM(id_APP1);
|
106
|
+
case JPEG_APP0 + 2:
|
107
|
+
return ID2SYM(id_APP2);
|
108
|
+
case JPEG_APP0 + 3:
|
109
|
+
return ID2SYM(id_APP3);
|
110
|
+
case JPEG_APP0 + 4:
|
111
|
+
return ID2SYM(id_APP4);
|
112
|
+
case JPEG_APP0 + 5:
|
113
|
+
return ID2SYM(id_APP5);
|
114
|
+
case JPEG_APP0 + 6:
|
115
|
+
return ID2SYM(id_APP6);
|
116
|
+
case JPEG_APP0 + 7:
|
117
|
+
return ID2SYM(id_APP7);
|
118
|
+
case JPEG_APP0 + 8:
|
119
|
+
return ID2SYM(id_APP8);
|
120
|
+
case JPEG_APP0 + 9:
|
121
|
+
return ID2SYM(id_APP9);
|
122
|
+
case JPEG_APP0 + 10:
|
123
|
+
return ID2SYM(id_APP10);
|
124
|
+
case JPEG_APP0 + 11:
|
125
|
+
return ID2SYM(id_APP11);
|
126
|
+
case JPEG_APP0 + 12:
|
127
|
+
return ID2SYM(id_APP12);
|
128
|
+
case JPEG_APP0 + 13:
|
129
|
+
return ID2SYM(id_APP13);
|
130
|
+
case JPEG_APP0 + 14:
|
131
|
+
return ID2SYM(id_APP14);
|
132
|
+
case JPEG_APP0 + 15:
|
133
|
+
return ID2SYM(id_APP15);
|
134
|
+
}
|
135
|
+
rb_raise(rb_eRuntimeError, "Marker code not recognized.");
|
136
|
+
}
|
137
|
+
|
138
|
+
/* JPEG Error Handler -- raise a ruby exception. */
|
139
|
+
|
140
|
+
void output_message(j_common_ptr cinfo)
|
141
|
+
{
|
142
|
+
char buffer[JMSG_LENGTH_MAX];
|
143
|
+
cinfo->err->format_message(cinfo, buffer);
|
144
|
+
rb_warning("jpeglib: %s", buffer);
|
145
|
+
}
|
146
|
+
|
147
|
+
static void error_exit(j_common_ptr dinfo)
|
148
|
+
{
|
149
|
+
char buffer[JMSG_LENGTH_MAX];
|
150
|
+
(*dinfo->err->format_message) (dinfo, buffer);
|
151
|
+
rb_raise(rb_eRuntimeError, "jpeglib: %s", buffer);
|
152
|
+
}
|
153
|
+
|
154
|
+
/* JPEG Data Source */
|
155
|
+
|
156
|
+
struct readerdata {
|
157
|
+
struct jpeg_decompress_struct dinfo;
|
158
|
+
struct jpeg_source_mgr mgr;
|
159
|
+
struct jpeg_error_mgr jerr;
|
160
|
+
int locked;
|
161
|
+
VALUE source_io;
|
162
|
+
VALUE buffer;
|
163
|
+
uint32_t scale_width;
|
164
|
+
uint32_t scale_height;
|
165
|
+
};
|
166
|
+
|
167
|
+
static void null_jdecompress(j_decompress_ptr dinfo) {}
|
168
|
+
|
169
|
+
static boolean fill_input_buffer(j_decompress_ptr dinfo)
|
170
|
+
{
|
171
|
+
VALUE string;
|
172
|
+
long strl;
|
173
|
+
struct readerdata *reader;
|
174
|
+
|
175
|
+
reader = (struct readerdata *)dinfo;
|
176
|
+
|
177
|
+
string = rb_funcall(reader->source_io, id_read, 1, INT2FIX(READ_SIZE));
|
178
|
+
Check_Type(string, T_STRING);
|
179
|
+
|
180
|
+
strl = RSTRING_LEN(string);
|
181
|
+
if (strl > READ_SIZE) {
|
182
|
+
rb_raise(rb_eRuntimeError, "IO returned too much data.");
|
183
|
+
}
|
184
|
+
|
185
|
+
if (!strl) {
|
186
|
+
string = rb_str_new2("\xFF\xD9");
|
187
|
+
strl = 2;
|
188
|
+
}
|
189
|
+
|
190
|
+
reader->buffer = string;
|
191
|
+
reader->mgr.bytes_in_buffer = strl;
|
192
|
+
reader->mgr.next_input_byte = (unsigned char *)RSTRING_PTR(string);
|
193
|
+
|
194
|
+
return TRUE;
|
195
|
+
}
|
196
|
+
|
197
|
+
static void skip_input_data(j_decompress_ptr dinfo, long num_bytes)
|
198
|
+
{
|
199
|
+
struct jpeg_source_mgr * src = dinfo->src;
|
200
|
+
|
201
|
+
if (num_bytes > 0) {
|
202
|
+
while (num_bytes > (long) src->bytes_in_buffer) {
|
203
|
+
num_bytes -= (long) src->bytes_in_buffer;
|
204
|
+
(void) (*src->fill_input_buffer) (dinfo);
|
205
|
+
}
|
206
|
+
src->next_input_byte += (size_t) num_bytes;
|
207
|
+
src->bytes_in_buffer -= (size_t) num_bytes;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
/* Ruby GC */
|
212
|
+
|
213
|
+
static void deallocate(struct readerdata *reader)
|
214
|
+
{
|
215
|
+
jpeg_destroy_decompress(&reader->dinfo);
|
216
|
+
free(reader);
|
217
|
+
}
|
218
|
+
|
219
|
+
static void mark(struct readerdata *reader)
|
220
|
+
{
|
221
|
+
if (!NIL_P(reader->source_io)) {
|
222
|
+
rb_gc_mark(reader->source_io);
|
223
|
+
}
|
224
|
+
|
225
|
+
if (!NIL_P(reader->buffer)) {
|
226
|
+
rb_gc_mark(reader->buffer);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
static VALUE allocate(VALUE klass)
|
231
|
+
{
|
232
|
+
struct readerdata *reader;
|
233
|
+
VALUE self;
|
234
|
+
self = Data_Make_Struct(klass, struct readerdata, mark, deallocate, reader);
|
235
|
+
|
236
|
+
jpeg_std_error(&reader->jerr);
|
237
|
+
reader->jerr.error_exit = error_exit;
|
238
|
+
reader->jerr.output_message = output_message;
|
239
|
+
reader->dinfo.err = &reader->jerr;
|
240
|
+
reader->mgr.init_source = null_jdecompress;
|
241
|
+
reader->mgr.fill_input_buffer = fill_input_buffer;
|
242
|
+
reader->mgr.skip_input_data = skip_input_data;
|
243
|
+
reader->mgr.resync_to_restart = jpeg_resync_to_restart;
|
244
|
+
reader->mgr.term_source = null_jdecompress;
|
245
|
+
return self;
|
246
|
+
}
|
247
|
+
|
248
|
+
/* Helper that raises an exception if the reader is locked. */
|
249
|
+
|
250
|
+
static void raise_if_locked(struct readerdata *reader)
|
251
|
+
{
|
252
|
+
if (reader->locked) {
|
253
|
+
rb_raise(rb_eRuntimeError, "Can't modify a Reader after decompress started.");
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
/*
|
258
|
+
* call-seq:
|
259
|
+
* Reader.new(io_in [, markers]) -> reader
|
260
|
+
*
|
261
|
+
* Creates a new JPEG Reader. +io_in+ must be an IO-like object that responds
|
262
|
+
* to read(size).
|
263
|
+
*
|
264
|
+
* +markers+ should be an array of valid JPEG header marker symbols. Valid
|
265
|
+
* symbols are :APP0 through :APP15 and :COM.
|
266
|
+
*
|
267
|
+
* If performance is important, you can avoid reading any header markers by
|
268
|
+
* supplying an empty array, [].
|
269
|
+
*
|
270
|
+
* When markers are not specified, we read all known JPEG markers.
|
271
|
+
*
|
272
|
+
* io = File.open("image.jpg", "r")
|
273
|
+
* reader = Oil::JPEGReader.new(io)
|
274
|
+
*
|
275
|
+
* io = File.open("image.jpg", "r")
|
276
|
+
* reader = Oil::JPEGReader.new(io, [:APP1, :APP2])
|
277
|
+
*/
|
278
|
+
|
279
|
+
static VALUE initialize(int argc, VALUE *argv, VALUE self)
|
280
|
+
{
|
281
|
+
struct readerdata *reader;
|
282
|
+
VALUE io, markers;
|
283
|
+
struct jpeg_decompress_struct *dinfo;
|
284
|
+
int i, marker_code;
|
285
|
+
|
286
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
287
|
+
dinfo = &reader->dinfo;
|
288
|
+
|
289
|
+
/* If source_io has already been set, then this is a re-used jpeg reader
|
290
|
+
* object. This means we need to abort the previous decompress to
|
291
|
+
* prevent memory leaks.
|
292
|
+
*/
|
293
|
+
if (reader->source_io) {
|
294
|
+
jpeg_abort_decompress(dinfo);
|
295
|
+
}
|
296
|
+
|
297
|
+
jpeg_create_decompress(&reader->dinfo);
|
298
|
+
reader->dinfo.src = &reader->mgr;
|
299
|
+
|
300
|
+
rb_scan_args(argc, argv, "11", &io, &markers);
|
301
|
+
reader->source_io = io;
|
302
|
+
reader->mgr.bytes_in_buffer = 0;
|
303
|
+
|
304
|
+
if(!NIL_P(markers)) {
|
305
|
+
Check_Type(markers, T_ARRAY);
|
306
|
+
for (i=0; i<RARRAY_LEN(markers); i++) {
|
307
|
+
marker_code = sym_to_marker_code(RARRAY_PTR(markers)[i]);
|
308
|
+
jpeg_save_markers(dinfo, marker_code, 0xFFFF);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
/* Be warned that this can raise a ruby exception and longjmp away. */
|
313
|
+
jpeg_read_header(&reader->dinfo, TRUE);
|
314
|
+
|
315
|
+
jpeg_calc_output_dimensions(dinfo);
|
316
|
+
|
317
|
+
return self;
|
318
|
+
}
|
319
|
+
|
320
|
+
/*
|
321
|
+
* call-seq:
|
322
|
+
* reader.components -> number
|
323
|
+
*
|
324
|
+
* Retrieve the number of components as stored in the JPEG image.
|
325
|
+
*/
|
326
|
+
|
327
|
+
static VALUE components(VALUE self)
|
328
|
+
{
|
329
|
+
struct jpeg_decompress_struct * dinfo;
|
330
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
331
|
+
return INT2FIX(dinfo->num_components);
|
332
|
+
}
|
333
|
+
|
334
|
+
/*
|
335
|
+
* call-seq:
|
336
|
+
* reader.color_space -> symbol
|
337
|
+
*
|
338
|
+
* Returns a symbol representing the color model in which the JPEG is stored.
|
339
|
+
*
|
340
|
+
* This does not have to be set explicitly and can be relied upon when the file
|
341
|
+
* conforms to JFIF or Adobe conventions. Otherwise it is guessed.
|
342
|
+
*
|
343
|
+
* Possible color models are: :GRAYSCALE, :RGB, :YCbCr, :CMYK, and :YCCK. This
|
344
|
+
* method will return :UNKNOWN if the color model is not recognized.
|
345
|
+
*/
|
346
|
+
|
347
|
+
static VALUE color_space(VALUE self)
|
348
|
+
{
|
349
|
+
struct jpeg_decompress_struct * dinfo;
|
350
|
+
ID id;
|
351
|
+
|
352
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
353
|
+
id = j_color_space_to_id(dinfo->jpeg_color_space);
|
354
|
+
|
355
|
+
return ID2SYM(id);
|
356
|
+
}
|
357
|
+
|
358
|
+
/*
|
359
|
+
* call-seq:
|
360
|
+
* reader.out_color_space -> symbol
|
361
|
+
*
|
362
|
+
* Returns a symbol representing the color model to which the image will be
|
363
|
+
* converted on decompress.
|
364
|
+
*/
|
365
|
+
|
366
|
+
static VALUE out_color_space(VALUE self)
|
367
|
+
{
|
368
|
+
struct jpeg_decompress_struct * dinfo;
|
369
|
+
ID id;
|
370
|
+
|
371
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
372
|
+
id = j_color_space_to_id(dinfo->jpeg_color_space);
|
373
|
+
|
374
|
+
return ID2SYM(id);
|
375
|
+
}
|
376
|
+
|
377
|
+
/*
|
378
|
+
* call-seq:
|
379
|
+
* reader.out_color_space = symbol
|
380
|
+
*
|
381
|
+
* Set the color model to which teh image will be converted on decompress.
|
382
|
+
*/
|
383
|
+
|
384
|
+
static VALUE set_out_color_space(VALUE self, VALUE cs)
|
385
|
+
{
|
386
|
+
struct readerdata *reader;
|
387
|
+
|
388
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
389
|
+
raise_if_locked(reader);
|
390
|
+
|
391
|
+
reader->dinfo.jpeg_color_space = sym_to_j_color_space(cs);
|
392
|
+
jpeg_calc_output_dimensions(&reader->dinfo);
|
393
|
+
return cs;
|
394
|
+
}
|
395
|
+
|
396
|
+
/*
|
397
|
+
* call-seq:
|
398
|
+
* reader.width -> number
|
399
|
+
*
|
400
|
+
* Retrieve the width of the image.
|
401
|
+
*/
|
402
|
+
|
403
|
+
static VALUE width(VALUE self)
|
404
|
+
{
|
405
|
+
struct jpeg_decompress_struct * dinfo;
|
406
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
407
|
+
return INT2FIX(dinfo->image_width);
|
408
|
+
}
|
409
|
+
|
410
|
+
/*
|
411
|
+
* call-seq:
|
412
|
+
* reader.height -> number
|
413
|
+
*
|
414
|
+
* Retrieve the height of the image.
|
415
|
+
*/
|
416
|
+
|
417
|
+
static VALUE height(VALUE self)
|
418
|
+
{
|
419
|
+
struct jpeg_decompress_struct * dinfo;
|
420
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
421
|
+
return INT2FIX(dinfo->image_height);
|
422
|
+
}
|
423
|
+
|
424
|
+
/*
|
425
|
+
* call-seq:
|
426
|
+
* reader.markers -> hash
|
427
|
+
*
|
428
|
+
* Get a hash of raw marker data from the JPEG.
|
429
|
+
*
|
430
|
+
* The keys in the hash are the marker codes as symbols. The values are arrays.
|
431
|
+
*
|
432
|
+
* Arrays since there may be multiple instances of a single marker in a JPEG
|
433
|
+
* marker.
|
434
|
+
*/
|
435
|
+
|
436
|
+
static VALUE markers(VALUE self)
|
437
|
+
{
|
438
|
+
struct jpeg_decompress_struct *dinfo;
|
439
|
+
jpeg_saved_marker_ptr marker;
|
440
|
+
VALUE hash, ary, key, val;
|
441
|
+
|
442
|
+
hash = rb_hash_new();
|
443
|
+
|
444
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
445
|
+
|
446
|
+
for (marker=dinfo->marker_list; marker; marker=marker->next) {
|
447
|
+
key = marker_code_to_sym(marker->marker);
|
448
|
+
ary = rb_hash_aref(hash, key);
|
449
|
+
if (NIL_P(ary)) {
|
450
|
+
ary = rb_ary_new();
|
451
|
+
rb_hash_aset(hash, key, ary);
|
452
|
+
}
|
453
|
+
val = rb_str_new((char *)marker->data, marker->data_length);
|
454
|
+
rb_ary_push(ary, val);
|
455
|
+
}
|
456
|
+
|
457
|
+
return hash;
|
458
|
+
}
|
459
|
+
|
460
|
+
/*
|
461
|
+
* call-seq:
|
462
|
+
* reader.scale_num -> number
|
463
|
+
*
|
464
|
+
* Retrieve the numerator of the fraction by which the JPEG will be scaled as
|
465
|
+
* it is read. This is always 1 for libjpeg version 6b. In version 8b this can
|
466
|
+
* be 1 to 16.
|
467
|
+
*/
|
468
|
+
|
469
|
+
static VALUE scale_num(VALUE self)
|
470
|
+
{
|
471
|
+
struct jpeg_decompress_struct *dinfo;
|
472
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
473
|
+
return INT2FIX(dinfo->scale_num);
|
474
|
+
}
|
475
|
+
|
476
|
+
/*
|
477
|
+
* call-seq:
|
478
|
+
* reader.scale_num = number
|
479
|
+
*
|
480
|
+
* Set the numerator of the fraction by which the JPEG will be scaled as it is
|
481
|
+
* read. This must always be 1 for libjpeg version 6b. In version 8b this can
|
482
|
+
* be set to 1 through 16.
|
483
|
+
*/
|
484
|
+
|
485
|
+
static VALUE set_scale_num(VALUE self, VALUE scale_num)
|
486
|
+
{
|
487
|
+
struct readerdata *reader;
|
488
|
+
|
489
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
490
|
+
raise_if_locked(reader);
|
491
|
+
|
492
|
+
reader->dinfo.scale_num = NUM2INT(scale_num);
|
493
|
+
jpeg_calc_output_dimensions(&reader->dinfo);
|
494
|
+
return scale_num;
|
495
|
+
}
|
496
|
+
|
497
|
+
/*
|
498
|
+
* call-seq:
|
499
|
+
* reader.scale_denom -> number
|
500
|
+
*
|
501
|
+
* Retrieve the denominator of the fraction by which the JPEG will be scaled as
|
502
|
+
* it is read. This is 1, 2, 4, or 8 for libjpeg version 6b. In version 8b this
|
503
|
+
* is always the source DCT size, which is 8 for baseline JPEG.
|
504
|
+
*/
|
505
|
+
|
506
|
+
static VALUE scale_denom(VALUE self)
|
507
|
+
{
|
508
|
+
struct jpeg_decompress_struct *dinfo;
|
509
|
+
Data_Get_Struct(self, struct jpeg_decompress_struct, dinfo);
|
510
|
+
return INT2FIX(dinfo->scale_denom);
|
511
|
+
}
|
512
|
+
|
513
|
+
/*
|
514
|
+
* call-seq:
|
515
|
+
* reader.scale_denom = number
|
516
|
+
*
|
517
|
+
* Set the denominator of the fraction by which the JPEG will be scaled as it
|
518
|
+
* is read. This can be set to 1, 2, 4, or 8 for libjpeg version 6b. In version
|
519
|
+
* 8b this must always be the source DCT size, which is 8 for baseline JPEG.
|
520
|
+
*
|
521
|
+
* Prior to version 1.2, libjpeg-turbo will not scale down images on
|
522
|
+
* decompression, and this option will do nothing.
|
523
|
+
*/
|
524
|
+
|
525
|
+
static VALUE set_scale_denom(VALUE self, VALUE scale_denom)
|
526
|
+
{
|
527
|
+
struct readerdata *reader;
|
528
|
+
|
529
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
530
|
+
raise_if_locked(reader);
|
531
|
+
|
532
|
+
reader->dinfo.scale_denom = NUM2INT(scale_denom);
|
533
|
+
jpeg_calc_output_dimensions(&reader->dinfo);
|
534
|
+
return scale_denom;
|
535
|
+
}
|
536
|
+
|
537
|
+
/*
|
538
|
+
* call-seq:
|
539
|
+
* reader.scale_width -> number
|
540
|
+
*
|
541
|
+
* Retrieve the width to which the image will be resized after decompression. A
|
542
|
+
* width of 0 means the image will remain at original width.
|
543
|
+
*/
|
544
|
+
|
545
|
+
static VALUE scale_width(VALUE self)
|
546
|
+
{
|
547
|
+
struct readerdata *reader;
|
548
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
549
|
+
return INT2FIX(reader->scale_width);
|
550
|
+
}
|
551
|
+
|
552
|
+
/*
|
553
|
+
* call-seq:
|
554
|
+
* reader.scale_width = number
|
555
|
+
*
|
556
|
+
* Set the width to which the image will be resized after decompression. A
|
557
|
+
* width of 0 means the image will remain at original width.
|
558
|
+
*/
|
559
|
+
|
560
|
+
static VALUE set_scale_width(VALUE self, VALUE scale_width)
|
561
|
+
{
|
562
|
+
struct readerdata *reader;
|
563
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
564
|
+
raise_if_locked(reader);
|
565
|
+
reader->scale_width = NUM2INT(scale_width);
|
566
|
+
return scale_width;
|
567
|
+
}
|
568
|
+
|
569
|
+
/*
|
570
|
+
* call-seq:
|
571
|
+
* reader.scale_height -> number
|
572
|
+
*
|
573
|
+
* Retrieve the height to which the image will be resized after decompression. A
|
574
|
+
* height of 0 means the image will remain at original height.
|
575
|
+
*/
|
576
|
+
|
577
|
+
static VALUE scale_height(VALUE self)
|
578
|
+
{
|
579
|
+
struct readerdata *reader;
|
580
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
581
|
+
return INT2FIX(reader->scale_height);
|
582
|
+
}
|
583
|
+
|
584
|
+
/*
|
585
|
+
* call-seq:
|
586
|
+
* reader.scale_height = number
|
587
|
+
*
|
588
|
+
* Set the height to which the image will be resized after decompression. A
|
589
|
+
* height of 0 means the image will remain at original height.
|
590
|
+
*/
|
591
|
+
|
592
|
+
static VALUE set_scale_height(VALUE self, VALUE scale_height)
|
593
|
+
{
|
594
|
+
struct readerdata *reader;
|
595
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
596
|
+
raise_if_locked(reader);
|
597
|
+
reader->scale_height = NUM2INT(scale_height);
|
598
|
+
return scale_height;
|
599
|
+
}
|
600
|
+
|
601
|
+
/* JPEG Data Destination */
|
602
|
+
|
603
|
+
struct writerdata {
|
604
|
+
struct jpeg_compress_struct cinfo;
|
605
|
+
struct jpeg_destination_mgr mgr;
|
606
|
+
VALUE buffer;
|
607
|
+
};
|
608
|
+
|
609
|
+
static void init_destination(j_compress_ptr cinfo)
|
610
|
+
{
|
611
|
+
struct writerdata *writer;
|
612
|
+
|
613
|
+
writer = (struct writerdata *)cinfo;
|
614
|
+
writer->buffer = rb_str_new(NULL, WRITE_SIZE);
|
615
|
+
writer->mgr.next_output_byte = (JOCTET *)RSTRING_PTR(writer->buffer);
|
616
|
+
writer->mgr.free_in_buffer = WRITE_SIZE;
|
617
|
+
}
|
618
|
+
|
619
|
+
static boolean empty_output_buffer(j_compress_ptr cinfo)
|
620
|
+
{
|
621
|
+
struct writerdata *writer;
|
622
|
+
|
623
|
+
writer = (struct writerdata *)cinfo;
|
624
|
+
rb_yield(writer->buffer);
|
625
|
+
init_destination(cinfo);
|
626
|
+
return TRUE;
|
627
|
+
}
|
628
|
+
|
629
|
+
static void term_destination(j_compress_ptr cinfo)
|
630
|
+
{
|
631
|
+
struct writerdata *writer;
|
632
|
+
size_t datacount;
|
633
|
+
|
634
|
+
writer = (struct writerdata *)cinfo;
|
635
|
+
datacount = WRITE_SIZE - writer->mgr.free_in_buffer;
|
636
|
+
|
637
|
+
if (datacount > 0) {
|
638
|
+
rb_str_set_len(writer->buffer, datacount);
|
639
|
+
rb_yield(writer->buffer);
|
640
|
+
}
|
641
|
+
}
|
642
|
+
|
643
|
+
static int markerhash_each(VALUE marker_code_v, VALUE marker_ary, VALUE cinfo_v)
|
644
|
+
{
|
645
|
+
struct jpeg_compress_struct *cinfo;
|
646
|
+
int i, marker_code;
|
647
|
+
size_t strl;
|
648
|
+
VALUE marker;
|
649
|
+
|
650
|
+
cinfo = (struct jpeg_compress_struct *)cinfo_v;
|
651
|
+
marker_code = sym_to_marker_code(marker_code_v);
|
652
|
+
|
653
|
+
Check_Type(marker_ary, T_ARRAY);
|
654
|
+
for (i=0; i<RARRAY_LEN(marker_ary); i++) {
|
655
|
+
marker = rb_ary_entry(marker_ary, i);
|
656
|
+
Check_Type(marker, T_STRING);
|
657
|
+
strl = RSTRING_LEN(marker);
|
658
|
+
jpeg_write_marker(cinfo, marker_code, (JOCTET *)RSTRING_PTR(marker), strl);
|
659
|
+
}
|
660
|
+
|
661
|
+
return ST_CONTINUE;
|
662
|
+
}
|
663
|
+
|
664
|
+
struct write_jpeg_args {
|
665
|
+
VALUE opts;
|
666
|
+
struct readerdata *reader;
|
667
|
+
struct writerdata *writer;
|
668
|
+
unsigned char *inwidthbuf;
|
669
|
+
unsigned char *outwidthbuf;
|
670
|
+
struct yscaler *ys;
|
671
|
+
};
|
672
|
+
|
673
|
+
static VALUE each2(struct write_jpeg_args *args)
|
674
|
+
{
|
675
|
+
struct writerdata *writer;
|
676
|
+
struct jpeg_decompress_struct *dinfo;
|
677
|
+
struct jpeg_compress_struct *cinfo;
|
678
|
+
unsigned char *inwidthbuf, *outwidthbuf, *yinbuf;
|
679
|
+
struct yscaler *ys;
|
680
|
+
uint32_t i, scalex, scaley;
|
681
|
+
VALUE quality, markers;
|
682
|
+
int cmp, opts;
|
683
|
+
|
684
|
+
writer = args->writer;
|
685
|
+
inwidthbuf = args->inwidthbuf;
|
686
|
+
outwidthbuf = args->outwidthbuf;
|
687
|
+
ys = args->ys;
|
688
|
+
dinfo = &args->reader->dinfo;
|
689
|
+
cinfo = &writer->cinfo;
|
690
|
+
scalex = args->reader->scale_width;
|
691
|
+
scaley = args->reader->scale_height;
|
692
|
+
|
693
|
+
cmp = dinfo->output_components;
|
694
|
+
opts = dinfo->out_color_space == JCS_EXT_RGBX ? OIL_FILLER : 0;
|
695
|
+
|
696
|
+
writer->mgr.init_destination = init_destination;
|
697
|
+
writer->mgr.empty_output_buffer = empty_output_buffer;
|
698
|
+
writer->mgr.term_destination = term_destination;
|
699
|
+
writer->cinfo.dest = &writer->mgr;
|
700
|
+
writer->cinfo.image_width = scalex;
|
701
|
+
writer->cinfo.image_height = scaley;
|
702
|
+
writer->cinfo.in_color_space = dinfo->out_color_space;
|
703
|
+
writer->cinfo.input_components = cmp;
|
704
|
+
|
705
|
+
jpeg_set_defaults(cinfo);
|
706
|
+
|
707
|
+
if (!NIL_P(args->opts)) {
|
708
|
+
quality = rb_hash_aref(args->opts, sym_quality);
|
709
|
+
if (!NIL_P(quality)) {
|
710
|
+
jpeg_set_quality(cinfo, FIX2INT(quality), FALSE);
|
711
|
+
}
|
712
|
+
|
713
|
+
markers = rb_hash_aref(args->opts, sym_markers);
|
714
|
+
if (!NIL_P(markers)) {
|
715
|
+
Check_Type(markers, T_HASH);
|
716
|
+
rb_hash_foreach(markers, markerhash_each, (VALUE)cinfo);
|
717
|
+
}
|
718
|
+
}
|
719
|
+
|
720
|
+
jpeg_start_compress(cinfo, TRUE);
|
721
|
+
jpeg_start_decompress(dinfo);
|
722
|
+
|
723
|
+
for(i=0; i<scaley; i++) {
|
724
|
+
while ((yinbuf = yscaler_next(ys))) {
|
725
|
+
jpeg_read_scanlines(dinfo, (JSAMPARRAY)&inwidthbuf, 1);
|
726
|
+
xscale(inwidthbuf, dinfo->output_width, yinbuf, scalex, cmp, opts);
|
727
|
+
}
|
728
|
+
yscaler_scale(ys, outwidthbuf, scalex, cmp, opts);
|
729
|
+
jpeg_write_scanlines(cinfo, (JSAMPARRAY)&outwidthbuf, 1);
|
730
|
+
}
|
731
|
+
|
732
|
+
jpeg_finish_compress(cinfo);
|
733
|
+
|
734
|
+
return Qnil;
|
735
|
+
}
|
736
|
+
|
737
|
+
/*
|
738
|
+
* call-seq:
|
739
|
+
* reader.each(opts, &block) -> self
|
740
|
+
*
|
741
|
+
* Yields a series of binary strings that make up the output JPEG image.
|
742
|
+
*
|
743
|
+
* Options is a hash which may have the following symbols:
|
744
|
+
*
|
745
|
+
* :quality - JPEG quality setting. Betweein 0 and 100.
|
746
|
+
* :markers - Custom markers to include in the output JPEG. Must be a hash where
|
747
|
+
* the keys are :APP[0-15] or :COM and the values are arrays of strings that
|
748
|
+
* will be inserted into the markers.
|
749
|
+
*/
|
750
|
+
|
751
|
+
static VALUE each(int argc, VALUE *argv, VALUE self)
|
752
|
+
{
|
753
|
+
struct readerdata *reader;
|
754
|
+
struct writerdata writer;
|
755
|
+
int cmp, state;
|
756
|
+
struct write_jpeg_args args;
|
757
|
+
unsigned char *inwidthbuf, *outwidthbuf;
|
758
|
+
struct yscaler ys;
|
759
|
+
VALUE opts;
|
760
|
+
|
761
|
+
rb_scan_args(argc, argv, "01", &opts);
|
762
|
+
|
763
|
+
Data_Get_Struct(self, struct readerdata, reader);
|
764
|
+
|
765
|
+
if (!reader->scale_width) {
|
766
|
+
reader->scale_width = reader->dinfo.output_width;
|
767
|
+
}
|
768
|
+
if (!reader->scale_height) {
|
769
|
+
reader->scale_height = reader->dinfo.output_height;
|
770
|
+
}
|
771
|
+
|
772
|
+
writer.cinfo.err = &reader->jerr;
|
773
|
+
jpeg_create_compress(&writer.cinfo);
|
774
|
+
|
775
|
+
cmp = reader->dinfo.output_components;
|
776
|
+
inwidthbuf = malloc(reader->dinfo.output_width * cmp);
|
777
|
+
outwidthbuf = malloc(reader->scale_width * cmp);
|
778
|
+
yscaler_init(&ys, reader->dinfo.output_height, reader->scale_height,
|
779
|
+
reader->scale_width * cmp);
|
780
|
+
|
781
|
+
args.reader = reader;
|
782
|
+
args.opts = opts;
|
783
|
+
args.writer = &writer;
|
784
|
+
args.inwidthbuf = inwidthbuf;
|
785
|
+
args.outwidthbuf = outwidthbuf;
|
786
|
+
args.ys = &ys;
|
787
|
+
reader->locked = 1;
|
788
|
+
rb_protect((VALUE(*)(VALUE))each2, (VALUE)&args, &state);
|
789
|
+
|
790
|
+
yscaler_free(&ys);
|
791
|
+
free(inwidthbuf);
|
792
|
+
free(outwidthbuf);
|
793
|
+
jpeg_destroy_compress(&writer.cinfo);
|
794
|
+
|
795
|
+
if (state) {
|
796
|
+
rb_jump_tag(state);
|
797
|
+
}
|
798
|
+
|
799
|
+
return self;
|
800
|
+
}
|
801
|
+
|
802
|
+
/*
|
803
|
+
* Document-class: Oil::JPEGReader
|
804
|
+
*
|
805
|
+
* Read a compressed JPEG image given an IO object.
|
806
|
+
*/
|
807
|
+
|
808
|
+
void Init_jpeg()
|
809
|
+
{
|
810
|
+
VALUE mOil, cJPEGReader;
|
811
|
+
|
812
|
+
mOil = rb_const_get(rb_cObject, rb_intern("Oil"));
|
813
|
+
|
814
|
+
cJPEGReader = rb_define_class_under(mOil, "JPEGReader", rb_cObject);
|
815
|
+
rb_define_alloc_func(cJPEGReader, allocate);
|
816
|
+
rb_define_method(cJPEGReader, "initialize", initialize, -1);
|
817
|
+
rb_define_method(cJPEGReader, "markers", markers, 0);
|
818
|
+
rb_define_method(cJPEGReader, "color_space", color_space, 0);
|
819
|
+
rb_define_method(cJPEGReader, "out_color_space", out_color_space, 0);
|
820
|
+
rb_define_method(cJPEGReader, "color_space=", set_out_color_space, 1);
|
821
|
+
rb_define_method(cJPEGReader, "components", components, 0);
|
822
|
+
rb_define_method(cJPEGReader, "width", width, 0);
|
823
|
+
rb_define_method(cJPEGReader, "height", height, 0);
|
824
|
+
rb_define_method(cJPEGReader, "each", each, -1);
|
825
|
+
rb_define_method(cJPEGReader, "scale_num", scale_num, 0);
|
826
|
+
rb_define_method(cJPEGReader, "scale_num=", set_scale_num, 1);
|
827
|
+
rb_define_method(cJPEGReader, "scale_denom", scale_denom, 0);
|
828
|
+
rb_define_method(cJPEGReader, "scale_denom=", set_scale_denom, 1);
|
829
|
+
rb_define_method(cJPEGReader, "scale_width", scale_width, 0);
|
830
|
+
rb_define_method(cJPEGReader, "scale_width=", set_scale_width, 1);
|
831
|
+
rb_define_method(cJPEGReader, "scale_height", scale_height, 0);
|
832
|
+
rb_define_method(cJPEGReader, "scale_height=", set_scale_height, 1);
|
833
|
+
|
834
|
+
id_GRAYSCALE = rb_intern("GRAYSCALE");
|
835
|
+
id_RGB = rb_intern("RGB");
|
836
|
+
id_YCbCr = rb_intern("YCbCr");
|
837
|
+
id_CMYK = rb_intern("CMYK");
|
838
|
+
id_YCCK = rb_intern("YCCK");
|
839
|
+
id_UNKNOWN = rb_intern("UNKNOWN");
|
840
|
+
id_APP0 = rb_intern("APP0");
|
841
|
+
id_APP1 = rb_intern("APP1");
|
842
|
+
id_APP2 = rb_intern("APP2");
|
843
|
+
id_APP3 = rb_intern("APP3");
|
844
|
+
id_APP4 = rb_intern("APP4");
|
845
|
+
id_APP5 = rb_intern("APP5");
|
846
|
+
id_APP6 = rb_intern("APP6");
|
847
|
+
id_APP7 = rb_intern("APP7");
|
848
|
+
id_APP8 = rb_intern("APP8");
|
849
|
+
id_APP9 = rb_intern("APP9");
|
850
|
+
id_APP10 = rb_intern("APP10");
|
851
|
+
id_APP11 = rb_intern("APP11");
|
852
|
+
id_APP12 = rb_intern("APP12");
|
853
|
+
id_APP13 = rb_intern("APP13");
|
854
|
+
id_APP14 = rb_intern("APP14");
|
855
|
+
id_APP15 = rb_intern("APP15");
|
856
|
+
id_COM = rb_intern("COM");
|
857
|
+
id_read = rb_intern("read");
|
858
|
+
|
859
|
+
sym_quality = ID2SYM(rb_intern("quality"));
|
860
|
+
sym_markers = ID2SYM(rb_intern("markers"));
|
861
|
+
}
|