rszr 0.4.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +129 -12
- data/Rakefile +6 -0
- data/ext/rszr/errors.c +68 -0
- data/ext/rszr/errors.h +15 -0
- data/ext/rszr/extconf.rb +18 -0
- data/ext/rszr/image.c +482 -0
- data/ext/rszr/image.h +12 -0
- data/ext/rszr/rszr.c +17 -0
- data/ext/rszr/rszr.h +11 -0
- data/lib/rszr/batch_transformation.rb +24 -0
- data/lib/rszr/buffered.rb +25 -0
- data/lib/rszr/identification.rb +60 -0
- data/lib/rszr/image.rb +151 -98
- data/lib/rszr/image_processing.rb +82 -0
- data/lib/rszr/orientation.rb +107 -0
- data/lib/rszr/stream.rb +61 -0
- data/lib/rszr/version.rb +1 -1
- data/lib/rszr.rb +21 -7
- metadata +27 -114
- data/lib/rszr/base.rb +0 -29
- data/lib/rszr/errors.rb +0 -42
- data/lib/rszr/handle.rb +0 -37
- data/lib/rszr/lib.rb +0 -73
- data/lib/rszr/lock.rb +0 -23
data/lib/rszr/image.rb
CHANGED
@@ -1,97 +1,161 @@
|
|
1
1
|
module Rszr
|
2
2
|
class Image
|
3
|
-
|
4
|
-
|
3
|
+
extend Identification
|
4
|
+
include Buffered
|
5
|
+
include Orientation
|
6
|
+
|
5
7
|
class << self
|
6
|
-
|
7
|
-
|
8
|
-
protected :instantiate
|
9
|
-
|
10
|
-
def new(width, height)
|
11
|
-
ptr = with_lock { imlib_create_image(width, height) }
|
12
|
-
raise Error, 'Could not instantiate image' if ptr.null?
|
13
|
-
instantiate(ptr)
|
14
|
-
end
|
15
|
-
|
16
|
-
def load(path, options = {})
|
8
|
+
|
9
|
+
def load(path, autorotate: Rszr.autorotate, **opts)
|
17
10
|
path = path.to_s
|
18
11
|
raise FileNotFound unless File.exist?(path)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
imlib_load_image_with_error_return(path, load_error.ptr)
|
23
|
-
end
|
24
|
-
raise load_error, load_error.message if ptr.null?
|
25
|
-
return instantiate(ptr)
|
12
|
+
image = _load(path)
|
13
|
+
autorotate(image, path) if autorotate
|
14
|
+
image
|
26
15
|
end
|
27
16
|
alias :open :load
|
28
17
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
imlib_context_set_image(ptr)
|
34
|
-
imlib_free_image
|
18
|
+
def load_data(data, autorotate: Rszr.autorotate, **opts)
|
19
|
+
raise LoadError, 'Unknown format' unless format = identify(data)
|
20
|
+
with_tempfile(format, data) do |file|
|
21
|
+
load(file.path, autorotate: autorotate, **opts)
|
35
22
|
end
|
36
23
|
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(ptr)
|
41
|
-
@handle = Handle.new(self, ptr)
|
42
|
-
end
|
43
|
-
|
44
|
-
def width
|
45
|
-
with_image { imlib_image_get_width }
|
46
|
-
end
|
47
|
-
|
48
|
-
def height
|
49
|
-
with_image { imlib_image_get_height }
|
24
|
+
|
50
25
|
end
|
51
|
-
|
26
|
+
|
52
27
|
def dimensions
|
53
28
|
[width, height]
|
54
29
|
end
|
55
30
|
|
56
31
|
def format
|
57
|
-
|
58
|
-
|
59
|
-
str_ptr.to_s
|
32
|
+
fmt = _format
|
33
|
+
fmt == 'jpg' ? 'jpeg' : fmt
|
60
34
|
end
|
61
35
|
|
62
|
-
def
|
63
|
-
|
36
|
+
def format=(fmt)
|
37
|
+
fmt = fmt.to_s if fmt.is_a?(Symbol)
|
38
|
+
self._format = fmt
|
64
39
|
end
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
fmt = format
|
43
|
+
fmt = " #{fmt.upcase}" if fmt
|
44
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} #{width}x#{height}#{fmt}>"
|
69
45
|
end
|
46
|
+
|
47
|
+
module Transformations
|
48
|
+
def resize(*args, **opts)
|
49
|
+
_resize(false, *calculate_size(*args, **opts))
|
50
|
+
end
|
51
|
+
|
52
|
+
def resize!(*args, **opts)
|
53
|
+
_resize(true, *calculate_size(*args, **opts))
|
54
|
+
end
|
55
|
+
|
56
|
+
def crop(x, y, width, height)
|
57
|
+
_crop(false, x, y, width, height)
|
58
|
+
end
|
59
|
+
|
60
|
+
def crop!(x, y, width, height)
|
61
|
+
_crop(true, x, y, width, height)
|
62
|
+
end
|
63
|
+
|
64
|
+
def turn(orientation)
|
65
|
+
dup.turn!(orientation)
|
66
|
+
end
|
67
|
+
|
68
|
+
def turn!(orientation)
|
69
|
+
orientation = orientation.abs + 2 if orientation.negative?
|
70
|
+
_turn!(orientation % 4)
|
71
|
+
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
def rotate(deg)
|
74
|
+
_rotate(false, deg.to_f * Math::PI / 180.0)
|
75
|
+
end
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
def rotate!(deg)
|
78
|
+
_rotate(true, deg.to_f * Math::PI / 180.0)
|
79
|
+
end
|
80
|
+
|
81
|
+
# horizontal
|
82
|
+
def flop
|
83
|
+
dup.flop!
|
84
|
+
end
|
85
|
+
|
86
|
+
# vertical
|
87
|
+
def flip
|
88
|
+
dup.flip!
|
89
|
+
end
|
79
90
|
|
80
|
-
|
81
|
-
|
82
|
-
format ||= format_from_filename(path) || 'jpg'
|
83
|
-
imlib_image_set_format(format)
|
84
|
-
save_error = SaveError.new
|
85
|
-
imlib_save_image_with_error_return(path, save_error.ptr)
|
86
|
-
raise save_error, save_error.message if save_error.error?
|
87
|
-
true
|
91
|
+
def sharpen(radius)
|
92
|
+
dup.sharpen!(radius)
|
88
93
|
end
|
89
|
-
end
|
90
94
|
|
91
|
-
|
92
|
-
|
95
|
+
def sharpen!(radius)
|
96
|
+
raise ArgumentError, 'illegal radius' if radius < 0
|
97
|
+
_sharpen!(radius)
|
98
|
+
end
|
99
|
+
|
100
|
+
def blur(radius)
|
101
|
+
dup.blur!(radius)
|
102
|
+
end
|
103
|
+
|
104
|
+
def blur!(radius)
|
105
|
+
raise ArgumentError, 'illegal radius' if radius < 0
|
106
|
+
_sharpen!(-radius)
|
107
|
+
end
|
108
|
+
|
109
|
+
def filter(filter_expr)
|
110
|
+
dup.filter!(filter_expr)
|
111
|
+
end
|
112
|
+
|
113
|
+
def brighten!(value, r: nil, g: nil, b: nil, a: nil)
|
114
|
+
raise ArgumentError, 'illegal brightness' if value > 1 || value < -1
|
115
|
+
filter!("colormod(brightness=#{value.to_f});")
|
116
|
+
end
|
117
|
+
|
118
|
+
def brighten(*args, **opts)
|
119
|
+
dup.brighten!(*args, **opts)
|
120
|
+
end
|
121
|
+
|
122
|
+
def contrast!(value, r: nil, g: nil, b: nil, a: nil)
|
123
|
+
raise ArgumentError, 'illegal contrast (must be > 0)' if value < 0
|
124
|
+
filter!("colormod(contrast=#{value.to_f});")
|
125
|
+
end
|
126
|
+
|
127
|
+
def contrast(*args, **opts)
|
128
|
+
dup.contrast!(*args, **opts)
|
129
|
+
end
|
130
|
+
|
131
|
+
def gamma!(value, r: nil, g: nil, b: nil, a: nil)
|
132
|
+
#raise ArgumentError, 'illegal gamma (must be > 0)' if value < 0
|
133
|
+
filter!("colormod(gamma=#{value.to_f});")
|
134
|
+
end
|
135
|
+
|
136
|
+
def gamma(*args, **opts)
|
137
|
+
dup.gamma!(*args, **opts)
|
138
|
+
end
|
93
139
|
end
|
94
140
|
|
141
|
+
include Transformations
|
142
|
+
|
143
|
+
def save(path, format: nil, quality: nil)
|
144
|
+
format ||= format_from_filename(path) || self.format || 'jpg'
|
145
|
+
raise ArgumentError, "invalid quality #{quality.inspect}" if quality && !(0..100).cover?(quality)
|
146
|
+
ensure_path_is_writable(path)
|
147
|
+
_save(path.to_s, format.to_s, quality)
|
148
|
+
end
|
149
|
+
|
150
|
+
def save_data(format: nil, quality: nil)
|
151
|
+
format ||= self.format || 'jpg'
|
152
|
+
with_tempfile(format) do |file|
|
153
|
+
save(file.path, format: format, quality: quality)
|
154
|
+
file.rewind
|
155
|
+
file.read
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
95
159
|
private
|
96
160
|
|
97
161
|
# 0.5 0 < scale < 1
|
@@ -100,16 +164,16 @@ module Rszr
|
|
100
164
|
# :auto, 300 auto width, fit height
|
101
165
|
# 400, 300, crop: :center_middle
|
102
166
|
# 400, 300, background: rgba
|
103
|
-
# 400, 300,
|
167
|
+
# 400, 300, skew: true
|
104
168
|
|
105
|
-
def
|
169
|
+
def calculate_size(*args, crop: nil, skew: nil)
|
106
170
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
107
|
-
assert_valid_keys options, :crop, :background, :skew #:extend, :width, :height, :max_width, :max_height, :box
|
171
|
+
#assert_valid_keys options, :crop, :background, :skew #:extend, :width, :height, :max_width, :max_height, :box
|
108
172
|
original_width, original_height = width, height
|
109
173
|
x, y, = 0, 0
|
110
174
|
if args.size == 1
|
111
175
|
scale = args.first
|
112
|
-
raise ArgumentError, "scale #{scale.inspect} out of range" unless scale > 0 && scale < 1
|
176
|
+
raise ArgumentError, "scale factor #{scale.inspect} out of range" unless scale > 0 && scale < 1
|
113
177
|
new_width = original_width.to_f * scale
|
114
178
|
new_height = original_height.to_f * scale
|
115
179
|
elsif args.size == 2
|
@@ -121,9 +185,9 @@ module Rszr
|
|
121
185
|
new_width = box_width
|
122
186
|
new_height = box_width.to_f / original_width.to_f * original_height.to_f
|
123
187
|
elsif box_width.is_a?(Numeric) && box_height.is_a?(Numeric)
|
124
|
-
if
|
188
|
+
if skew
|
125
189
|
new_width, new_height = box_width, box_height
|
126
|
-
elsif
|
190
|
+
elsif crop
|
127
191
|
# TODO: calculate x, y offset if crop
|
128
192
|
else
|
129
193
|
scale = original_width.to_f / original_height.to_f
|
@@ -142,38 +206,27 @@ module Rszr
|
|
142
206
|
else
|
143
207
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
|
144
208
|
end
|
145
|
-
|
146
|
-
imlib_context_set_anti_alias(1)
|
147
|
-
imlib_create_cropped_scaled_image(x, y, imlib_image_get_width, imlib_image_get_height, new_width.round, new_height.round)
|
148
|
-
end
|
149
|
-
raise TransformationError, "error resizing image" if resized_ptr.null?
|
150
|
-
resized_ptr
|
151
|
-
end
|
152
|
-
|
153
|
-
def create_cropped_image(x, y, width, height)
|
154
|
-
cropped_ptr = with_image { imlib_create_cropped_image(x, y, width, height) }
|
155
|
-
raise TransformationError, 'error cropping image' if cropped_ptr.null?
|
156
|
-
cropped_ptr
|
209
|
+
[x, y, original_width, original_height, new_width.round, new_height.round]
|
157
210
|
end
|
158
|
-
|
211
|
+
|
159
212
|
def format_from_filename(path)
|
160
|
-
File.extname(path)[1..-1]
|
213
|
+
File.extname(path)[1..-1].to_s.downcase
|
161
214
|
end
|
162
215
|
|
163
|
-
def
|
164
|
-
|
216
|
+
def ensure_path_is_writable(path)
|
217
|
+
path = Pathname.new(path)
|
218
|
+
path.dirname.realpath.writable?
|
219
|
+
rescue Errno::ENOENT => e
|
220
|
+
raise SaveError, 'Non-existant path component'
|
221
|
+
rescue SystemCallError => e
|
222
|
+
raise SaveError, e.message
|
165
223
|
end
|
166
|
-
|
167
|
-
def
|
168
|
-
|
169
|
-
|
170
|
-
yield
|
224
|
+
|
225
|
+
def assert_valid_keys(hsh, *valid_keys)
|
226
|
+
if unknown_key = (hsh.keys - valid_keys).first
|
227
|
+
raise ArgumentError.new("Unknown key: #{unknown_key.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
|
171
228
|
end
|
172
229
|
end
|
173
230
|
|
174
|
-
def instantiate(ptr)
|
175
|
-
self.class.send(:instantiate, ptr)
|
176
|
-
end
|
177
|
-
|
178
231
|
end
|
179
232
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rszr'
|
2
|
+
require 'image_processing'
|
3
|
+
|
4
|
+
module ImageProcessing
|
5
|
+
module Rszr
|
6
|
+
extend Chainable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Returns whether the given image file is processable.
|
11
|
+
def valid_image?(file)
|
12
|
+
::Rszr::Image.load(file).width
|
13
|
+
true
|
14
|
+
rescue ::Rszr::Error
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class Processor < ImageProcessing::Processor
|
21
|
+
accumulator :image, ::Rszr::Image
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
# Loads the image on disk into a Rszr::Image object
|
26
|
+
def load_image(path_or_image, **options)
|
27
|
+
if path_or_image.is_a?(::Rszr::Image)
|
28
|
+
path_or_image
|
29
|
+
else
|
30
|
+
::Rszr::Image.load(path_or_image)
|
31
|
+
end
|
32
|
+
# TODO: image = image.autorot if autorot && !options.key?(:autorotate)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Writes the image object to disk.
|
36
|
+
# Accepts additional options (quality, format).
|
37
|
+
def save_image(image, destination_path, **options)
|
38
|
+
image.save(destination_path, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Calls the operation to perform the processing. If the operation is
|
42
|
+
# defined on the processor (macro), calls it. Otherwise calls the
|
43
|
+
# bang variant of the method directly on the Rszr image object.
|
44
|
+
def apply_operation(accumulator, (name, args, block))
|
45
|
+
return super if method_defined?(name)
|
46
|
+
accumulator.send("#{name}!", *args, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# Resizes the image to not be larger than the specified dimensions.
|
52
|
+
def resize_to_limit(width, height, **options)
|
53
|
+
width, height = default_dimensions(width, height)
|
54
|
+
thumbnail(width, height, **options)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Resizes the image to fit within the specified dimensions.
|
58
|
+
def resize_to_fit(width, height, **options)
|
59
|
+
width, height = default_dimensions(width, height)
|
60
|
+
thumbnail(width, height, **options)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Resizes the image to fill the specified dimensions, applying any
|
64
|
+
# necessary cropping.
|
65
|
+
def resize_to_fill(width, height, **options)
|
66
|
+
thumbnail(width, height, crop: :center, **options)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def thumbnail(width, height, **options)
|
72
|
+
image.resize!(width, height, **options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_dimensions(width, height)
|
76
|
+
raise Error, 'either width or height must be specified' unless width || height
|
77
|
+
[width || :auto, height || :auto]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Rszr
|
2
|
+
module Orientation
|
3
|
+
ROTATIONS = { 5 => 1, 6 => 1, 3 => 2, 4 => 2, 7 => 3, 8 => 3 }
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
base.attr_reader :original_orientation
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def autorotate(image, path)
|
15
|
+
return unless %w[jpeg tiff].include?(image.format)
|
16
|
+
File.open(path) do |file|
|
17
|
+
if orientation = send("parse_#{image.format}_orientation", file) and (1..8).member?(orientation)
|
18
|
+
image.instance_variable_set :@original_orientation, orientation
|
19
|
+
image.flop! if [2, 4, 5, 7].include?(orientation)
|
20
|
+
image.turn!(ROTATIONS[orientation]) if ROTATIONS.key?(orientation)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_tiff_orientation(data)
|
26
|
+
exif_parse_orientation(Stream.new(data))
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_jpeg_orientation(data)
|
30
|
+
stream = Stream.new(data)
|
31
|
+
exif = nil
|
32
|
+
state = nil
|
33
|
+
loop do
|
34
|
+
state = case state
|
35
|
+
when nil
|
36
|
+
stream.skip(2)
|
37
|
+
:started
|
38
|
+
when :started
|
39
|
+
stream.read_byte == 0xFF ? :sof : :started
|
40
|
+
when :sof
|
41
|
+
case stream.read_byte
|
42
|
+
when 0xe1 # APP1
|
43
|
+
skip_chars = stream.read_int - 2
|
44
|
+
app1 = Stream.new(stream.read(skip_chars))
|
45
|
+
if app1.read(4) == 'Exif'
|
46
|
+
app1.skip(2)
|
47
|
+
orientation = exif_parse_orientation(app1.fast_forward)# rescue nil
|
48
|
+
return orientation
|
49
|
+
end
|
50
|
+
:started
|
51
|
+
when 0xe0..0xef
|
52
|
+
:skipframe
|
53
|
+
when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF
|
54
|
+
:readsize
|
55
|
+
when 0xFF
|
56
|
+
:sof
|
57
|
+
else
|
58
|
+
:skipframe
|
59
|
+
end
|
60
|
+
when :skipframe
|
61
|
+
skip_chars = stream.read_int - 2
|
62
|
+
stream.skip(skip_chars)
|
63
|
+
:started
|
64
|
+
when :readsize
|
65
|
+
# stream.skip(3)
|
66
|
+
# height = stream.read_int
|
67
|
+
# width = stream.read_int
|
68
|
+
return exif&.orientation
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def exif_byte_order(stream)
|
74
|
+
byte_order = stream.read(2)
|
75
|
+
case byte_order
|
76
|
+
when 'II'
|
77
|
+
%w[v V]
|
78
|
+
when 'MM'
|
79
|
+
%w[n N]
|
80
|
+
else
|
81
|
+
raise LoadError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def exif_parse_ifd(stream, short)
|
86
|
+
tag_count = stream.read(2).unpack(short)[0]
|
87
|
+
tag_count.downto(1) do
|
88
|
+
type = stream.read(2).unpack(short)[0]
|
89
|
+
stream.read(6)
|
90
|
+
data = stream.read(2).unpack(short)[0]
|
91
|
+
return data if 0x0112 == type
|
92
|
+
stream.read(2)
|
93
|
+
end
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def exif_parse_orientation(stream)
|
98
|
+
short, long = exif_byte_order(stream)
|
99
|
+
stream.read(2) # 42
|
100
|
+
offset = stream.read(4).unpack(long)[0]
|
101
|
+
stream.skip(offset - 8)
|
102
|
+
exif_parse_ifd(stream, short)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
data/lib/rszr/stream.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Rszr
|
2
|
+
class Stream
|
3
|
+
attr_reader :pos, :data
|
4
|
+
protected :data
|
5
|
+
|
6
|
+
def initialize(data, start: 0)
|
7
|
+
raise ArgumentError, 'start must be > 0' if start < 0
|
8
|
+
@data = case data
|
9
|
+
when IO then data
|
10
|
+
when String then StringIO.new(data)
|
11
|
+
when Stream then data.data
|
12
|
+
else
|
13
|
+
raise ArgumentError, "data must be File or String, got #{data.class}"
|
14
|
+
end
|
15
|
+
@data.binmode
|
16
|
+
@data.seek(start, IO::SEEK_CUR)
|
17
|
+
@pos = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def read(n)
|
21
|
+
@data.read(n).tap { @pos += n }
|
22
|
+
end
|
23
|
+
|
24
|
+
def peek(n)
|
25
|
+
old_pos = @data.pos
|
26
|
+
@data.read(n)
|
27
|
+
ensure
|
28
|
+
@data.pos = old_pos
|
29
|
+
end
|
30
|
+
|
31
|
+
def skip(n)
|
32
|
+
@data.seek(n, IO::SEEK_CUR).tap { @pos += n }
|
33
|
+
end
|
34
|
+
|
35
|
+
def substream
|
36
|
+
self.class.new(self, pos)
|
37
|
+
end
|
38
|
+
|
39
|
+
def fast_forward
|
40
|
+
@pos = 0
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_byte
|
45
|
+
read(1)[0].ord
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_int
|
49
|
+
read(2).unpack('n')[0]
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_string_int
|
53
|
+
value = []
|
54
|
+
while read(1) =~ /(\d)/
|
55
|
+
value << $1
|
56
|
+
end
|
57
|
+
value.join.to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/rszr/version.rb
CHANGED
data/lib/rszr.rb
CHANGED
@@ -1,12 +1,26 @@
|
|
1
|
-
require 'fiddle'
|
2
|
-
require 'fiddle/import'
|
3
1
|
require 'rbconfig'
|
4
2
|
require 'pathname'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'stringio'
|
5
5
|
|
6
|
+
require 'rszr/rszr'
|
6
7
|
require 'rszr/version'
|
7
|
-
require 'rszr/
|
8
|
-
require 'rszr/
|
9
|
-
require 'rszr/
|
10
|
-
require 'rszr/
|
11
|
-
require 'rszr/base'
|
8
|
+
require 'rszr/stream'
|
9
|
+
require 'rszr/identification'
|
10
|
+
require 'rszr/orientation'
|
11
|
+
require 'rszr/buffered'
|
12
12
|
require 'rszr/image'
|
13
|
+
|
14
|
+
module Rszr
|
15
|
+
class << self
|
16
|
+
@@autorotate = nil
|
17
|
+
|
18
|
+
def autorotate
|
19
|
+
@@autorotate
|
20
|
+
end
|
21
|
+
|
22
|
+
def autorotate=(value)
|
23
|
+
@@autorotate = !!value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|