morandi 0.9.3 → 0.10.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 +4 -4
- data/lib/morandi.rb +2 -245
- data/lib/morandi/image_processor.rb +242 -0
- data/lib/morandi/profiled_pixbuf.rb +46 -0
- data/lib/morandi/version.rb +1 -1
- data/spec/morandi_spec.rb +7 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9709ee0d1808ab9b751fa10623c35081f774bb24
|
4
|
+
data.tar.gz: b10b4fadc2db8deb3e922b104f72245d6df1202a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b0bc4172387a86d735142205c98e3747f018f8a7ddf40e0a3277782669ab017ba3d7b5b8204d83edfbeeef92589771820960a527ae10005a839bfda7314916
|
7
|
+
data.tar.gz: a1b0dff226051a77efa68291f2e6d207505fff81878ab41180f35eca8e7df66fcd9631f3e05683751b943d0a26efbae9c4e0162b79e839cfe53f7c096478d85a
|
data/lib/morandi.rb
CHANGED
@@ -5,260 +5,17 @@ require 'gdk_pixbuf2'
|
|
5
5
|
require 'pixbufutils'
|
6
6
|
require 'redeye'
|
7
7
|
|
8
|
+
require 'morandi/image_processor'
|
8
9
|
require 'morandi/utils'
|
9
10
|
require 'morandi/image-ops'
|
10
11
|
require 'morandi/redeye'
|
11
12
|
|
12
13
|
module Morandi
|
13
|
-
# Your code goes here...
|
14
|
-
class ImageProcessor
|
15
|
-
attr_reader :options, :pb
|
16
|
-
attr_accessor :config
|
17
|
-
|
18
|
-
def valid_jpeg?(filename)
|
19
|
-
return false unless File.exist?(filename)
|
20
|
-
return false unless File.size(filename) > 0
|
21
|
-
|
22
|
-
type, _, _ = Gdk::Pixbuf.get_file_info(filename)
|
23
|
-
|
24
|
-
type.name == 'jpeg'
|
25
|
-
rescue
|
26
|
-
false
|
27
|
-
end
|
28
|
-
def self.default_icc_path(path)
|
29
|
-
"#{path}.icc.jpg"
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize(file, user_options, local_options={})
|
33
|
-
@file = file
|
34
|
-
|
35
|
-
user_options.keys.grep(/^path/).each { |k| user_options.delete(k) }
|
36
|
-
|
37
|
-
# Give priority to user_options
|
38
|
-
@options = (local_options || {}).merge(user_options || {})
|
39
|
-
@local_options = local_options
|
40
|
-
|
41
|
-
@scale_to = @options['output.max']
|
42
|
-
@width, @height = @options['output.width'], @options['output.height']
|
43
|
-
|
44
|
-
load_file = @file
|
45
|
-
type, width, height = Gdk::Pixbuf.get_file_info(load_file)
|
46
|
-
|
47
|
-
if type.name.eql?('jpeg')
|
48
|
-
icc_file = local_options['path.icc'] || ImageProcessor.default_icc_path(@file)
|
49
|
-
if valid_jpeg?(icc_file) || system("jpgicc", "-q97", @file, icc_file)
|
50
|
-
load_file = icc_file
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
if @scale_to
|
55
|
-
@pb = Gdk::Pixbuf.new(load_file, @scale_to, @scale_to)
|
56
|
-
@src_max = [width, height].max
|
57
|
-
@actual_max = [@pb.width, @pb.height].max
|
58
|
-
else
|
59
|
-
@pb = Gdk::Pixbuf.new(load_file)
|
60
|
-
@src_max = [@pb.width, @pb.height].max
|
61
|
-
@actual_max = [@pb.width, @pb.height].max
|
62
|
-
end
|
63
|
-
|
64
|
-
@scale = @actual_max / @src_max.to_f
|
65
|
-
end
|
66
|
-
|
67
|
-
def process!
|
68
|
-
# Apply Red-Eye corrections
|
69
|
-
apply_redeye!
|
70
|
-
|
71
|
-
# Apply contrast, brightness etc
|
72
|
-
apply_colour_manipulations!
|
73
|
-
|
74
|
-
# apply rotation
|
75
|
-
apply_rotate!
|
76
|
-
|
77
|
-
# apply crop
|
78
|
-
apply_crop!
|
79
|
-
|
80
|
-
# apply filter
|
81
|
-
apply_filters!
|
82
|
-
|
83
|
-
# add border
|
84
|
-
apply_decorations!
|
85
|
-
|
86
|
-
if @options['output.limit'] && @width && @height
|
87
|
-
@pb = @pb.scale_max([@width, @height].max)
|
88
|
-
end
|
89
|
-
|
90
|
-
@pb
|
91
|
-
end
|
92
|
-
|
93
|
-
SHARPEN = [
|
94
|
-
-1, -1, -1, -1, -1,
|
95
|
-
-1, 2, 2, 2, -1,
|
96
|
-
-1, 2, 8, 2, -1,
|
97
|
-
-1, 2, 2, 2, -1,
|
98
|
-
-1, -1, -1, -1, -1,
|
99
|
-
]
|
100
|
-
BLUR = [
|
101
|
-
0, 1, 1, 1, 0,
|
102
|
-
1, 1, 1, 1, 1,
|
103
|
-
1, 1, 1, 1, 1,
|
104
|
-
1, 1, 1, 1, 1,
|
105
|
-
0, 1, 1, 1, 0,
|
106
|
-
]
|
107
|
-
|
108
|
-
def apply_colour_manipulations!
|
109
|
-
#STDERR.puts "FILTER: #{options.inspect}"
|
110
|
-
if options['brighten'].to_i.nonzero?
|
111
|
-
brighten = [ [ 5 * options['brighten'], -100 ].max, 100 ].min
|
112
|
-
#STDERR.puts([:brighten, brighten].inspect)
|
113
|
-
@pb = PixbufUtils.brightness(@pb, brighten)
|
114
|
-
end
|
115
|
-
if options['gamma'] && (options['gamma'] != 1.0)
|
116
|
-
@pb = PixbufUtils.gamma(@pb, options['gamma'])
|
117
|
-
end
|
118
|
-
if options['contrast'].to_i.nonzero?
|
119
|
-
@pb = PixbufUtils.contrast(@pb, [ [ 5 * options['contrast'], -100 ].max, 100 ].min)
|
120
|
-
end
|
121
|
-
if options['sharpen'].to_i.nonzero?
|
122
|
-
if options['sharpen'] > 0
|
123
|
-
[options['sharpen'], 5].min.times do
|
124
|
-
@pb = PixbufUtils.filter(@pb, SHARPEN, SHARPEN.inject(0, &:+))
|
125
|
-
end
|
126
|
-
elsif options['sharpen'] < 0
|
127
|
-
[ (options['sharpen']*-1), 5].min.times do
|
128
|
-
@pb = PixbufUtils.filter(@pb, BLUR, BLUR.inject(0, &:+))
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def apply_redeye!
|
135
|
-
for eye in options['redeye'] || []
|
136
|
-
@pb = Morandi::RedEye.tap_on(@pb, eye[0] * @scale, eye[1] * @scale)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def angle
|
141
|
-
a = options['angle'].to_i
|
142
|
-
if a
|
143
|
-
(360-a)%360
|
144
|
-
else
|
145
|
-
nil
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# modifies @pb with any applied rotation
|
150
|
-
def apply_rotate!
|
151
|
-
a = angle()
|
152
|
-
|
153
|
-
unless (a%360).zero?
|
154
|
-
@pb = @pb.rotate(a)
|
155
|
-
end
|
156
|
-
|
157
|
-
unless options['straighten'].to_f.zero?
|
158
|
-
@pb = Morandi::Straighten.new(options['straighten'].to_f).call(nil, @pb)
|
159
|
-
end
|
160
|
-
|
161
|
-
@image_width = @pb.width
|
162
|
-
@image_height = @pb.height
|
163
|
-
end
|
164
|
-
|
165
|
-
DEFAULT_CONFIG = {
|
166
|
-
'border-size-mm' => 5
|
167
|
-
}
|
168
|
-
def config_for(key)
|
169
|
-
return options[key] if options && options.has_key?(key)
|
170
|
-
return @config[key] if @config && @config.has_key?(key)
|
171
|
-
DEFAULT_CONFIG[key]
|
172
|
-
end
|
173
|
-
|
174
|
-
#
|
175
|
-
def apply_crop!
|
176
|
-
crop = options['crop']
|
177
|
-
|
178
|
-
if crop.nil? && config_for('image.auto-crop').eql?(false)
|
179
|
-
return
|
180
|
-
end
|
181
|
-
|
182
|
-
if crop.is_a?(String) && crop =~ /^\d+,\d+,\d+,\d+/
|
183
|
-
crop = crop.split(/,/).map(&:to_i)
|
184
|
-
end
|
185
|
-
|
186
|
-
crop = nil unless crop.is_a?(Array) && crop.size.eql?(4) && crop.all? { |i|
|
187
|
-
i.kind_of?(Numeric)
|
188
|
-
}
|
189
|
-
|
190
|
-
# can't crop, won't crop
|
191
|
-
return if @width.nil? && @height.nil? && crop.nil?
|
192
|
-
|
193
|
-
if crop && @scale != 1.0
|
194
|
-
crop = crop.map { |s| (s.to_f * @scale).floor }
|
195
|
-
end
|
196
|
-
|
197
|
-
crop ||= Morandi::Utils.autocrop_coords(@pb.width, @pb.height, @width, @height)
|
198
|
-
|
199
|
-
@pb = Morandi::Utils.apply_crop(@pb, crop[0], crop[1], crop[2], crop[3])
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
def apply_filters!
|
204
|
-
filter = options['fx']
|
205
|
-
|
206
|
-
case filter
|
207
|
-
when 'greyscale'
|
208
|
-
op = Morandi::Colourify.new_from_hash('op' => filter)
|
209
|
-
when 'sepia', 'bluetone'
|
210
|
-
op = Morandi::Colourify.new_from_hash('op' => filter, 'alpha' => (0.85 * 255).to_i)
|
211
|
-
else
|
212
|
-
return
|
213
|
-
end
|
214
|
-
@pb = op.call(nil, @pb)
|
215
|
-
end
|
216
|
-
|
217
|
-
def apply_decorations!
|
218
|
-
style, colour = options['border-style'], options['background-style']
|
219
|
-
|
220
|
-
return if style.nil? or style.eql?('none')
|
221
|
-
return if colour.eql?('none')
|
222
|
-
colour ||= 'black'
|
223
|
-
|
224
|
-
crop = options['crop']
|
225
|
-
crop = crop.map { |s| (s.to_f * @scale).floor } if crop && @scale != 1.0
|
226
|
-
|
227
|
-
op = Morandi::ImageBorder.new_from_hash(data={
|
228
|
-
'style' => style,
|
229
|
-
'colour' => colour || '#000000',
|
230
|
-
'crop' => crop,
|
231
|
-
'size' => [@image_width, @image_height],
|
232
|
-
'print_size' => [@width, @height],
|
233
|
-
'shrink' => true,
|
234
|
-
'border_size' => @scale * config_for('border-size-mm').to_i * 300 / 25.4 # 5mm at 300dpi
|
235
|
-
})
|
236
|
-
|
237
|
-
@pb = op.call(nil, @pb)
|
238
|
-
end
|
239
|
-
|
240
|
-
def write_to_png(fn, orientation=:any)
|
241
|
-
pb = @pb
|
242
|
-
|
243
|
-
case orientation
|
244
|
-
when :landscape
|
245
|
-
pb = @pb.rotate(90) if @pb.width < @pb.height
|
246
|
-
when :portrait
|
247
|
-
pb = @pb.rotate(90) if @pb.width > @pb.height
|
248
|
-
end
|
249
|
-
pb.save(fn, 'png')
|
250
|
-
end
|
251
|
-
|
252
|
-
def write_to_jpeg(fn, quality = 97)
|
253
|
-
@pb.save(fn, 'jpeg', :quality => quality)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
14
|
module_function
|
258
15
|
def process(file_in, options, out_file, local_options = {})
|
259
16
|
pro = ImageProcessor.new(file_in, options, local_options)
|
260
17
|
pro.process!
|
261
18
|
pro.write_to_jpeg(out_file)
|
262
19
|
end
|
263
|
-
|
264
20
|
end
|
21
|
+
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'morandi/profiled_pixbuf'
|
2
|
+
|
3
|
+
class Morandi::ImageProcessor
|
4
|
+
attr_reader :options, :pb
|
5
|
+
attr_accessor :config
|
6
|
+
|
7
|
+
def self.default_icc_path(path)
|
8
|
+
"#{path}.icc.jpg"
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(file, user_options, local_options={})
|
12
|
+
@file = file
|
13
|
+
|
14
|
+
user_options.keys.grep(/^path/).each { |k| user_options.delete(k) }
|
15
|
+
|
16
|
+
# Give priority to user_options
|
17
|
+
@options = (local_options || {}).merge(user_options || {})
|
18
|
+
@local_options = local_options
|
19
|
+
|
20
|
+
@scale_to = @options['output.max']
|
21
|
+
@width, @height = @options['output.width'], @options['output.height']
|
22
|
+
|
23
|
+
if @file.is_a?(String)
|
24
|
+
get_pixbuf
|
25
|
+
elsif @file.is_a?(Gdk::Pixbuf) or @file.is_a?(Morandi::ProfiledPixbuf)
|
26
|
+
@pb = @file
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def process!
|
31
|
+
# Apply Red-Eye corrections
|
32
|
+
apply_redeye!
|
33
|
+
|
34
|
+
# Apply contrast, brightness etc
|
35
|
+
apply_colour_manipulations!
|
36
|
+
|
37
|
+
# apply rotation
|
38
|
+
apply_rotate!
|
39
|
+
|
40
|
+
# apply crop
|
41
|
+
apply_crop!
|
42
|
+
|
43
|
+
# apply filter
|
44
|
+
apply_filters!
|
45
|
+
|
46
|
+
# add border
|
47
|
+
apply_decorations!
|
48
|
+
|
49
|
+
if @options['output.limit'] && @width && @height
|
50
|
+
@pb = @pb.scale_max([@width, @height].max)
|
51
|
+
end
|
52
|
+
|
53
|
+
@pb
|
54
|
+
end
|
55
|
+
|
56
|
+
def result
|
57
|
+
@pb
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_to_png(fn, orientation=:any)
|
61
|
+
pb = @pb
|
62
|
+
|
63
|
+
case orientation
|
64
|
+
when :landscape
|
65
|
+
pb = @pb.rotate(90) if @pb.width < @pb.height
|
66
|
+
when :portrait
|
67
|
+
pb = @pb.rotate(90) if @pb.width > @pb.height
|
68
|
+
end
|
69
|
+
pb.save(fn, 'png')
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_to_jpeg(fn, quality = 97)
|
73
|
+
@pb.save(fn, 'jpeg', :quality => quality)
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def get_pixbuf
|
78
|
+
_, width, height = Gdk::Pixbuf.get_file_info(@file)
|
79
|
+
|
80
|
+
if @scale_to
|
81
|
+
@pb = Morandi::ProfiledPixbuf.new(@file, @scale_to, @scale_to, @local_options)
|
82
|
+
@src_max = [width, height].max
|
83
|
+
@actual_max = [@pb.width, @pb.height].max
|
84
|
+
else
|
85
|
+
@pb = Morandi::ProfiledPixbuf.new(@file, @local_options)
|
86
|
+
@src_max = [@pb.width, @pb.height].max
|
87
|
+
@actual_max = [@pb.width, @pb.height].max
|
88
|
+
end
|
89
|
+
|
90
|
+
@scale = @actual_max / @src_max.to_f
|
91
|
+
end
|
92
|
+
|
93
|
+
SHARPEN = [
|
94
|
+
-1, -1, -1, -1, -1,
|
95
|
+
-1, 2, 2, 2, -1,
|
96
|
+
-1, 2, 8, 2, -1,
|
97
|
+
-1, 2, 2, 2, -1,
|
98
|
+
-1, -1, -1, -1, -1,
|
99
|
+
]
|
100
|
+
BLUR = [
|
101
|
+
0, 1, 1, 1, 0,
|
102
|
+
1, 1, 1, 1, 1,
|
103
|
+
1, 1, 1, 1, 1,
|
104
|
+
1, 1, 1, 1, 1,
|
105
|
+
0, 1, 1, 1, 0,
|
106
|
+
]
|
107
|
+
|
108
|
+
def apply_colour_manipulations!
|
109
|
+
if options['brighten'].to_i.nonzero?
|
110
|
+
brighten = [ [ 5 * options['brighten'], -100 ].max, 100 ].min
|
111
|
+
@pb = PixbufUtils.brightness(@pb, brighten)
|
112
|
+
end
|
113
|
+
|
114
|
+
if options['gamma'] && (options['gamma'] != 1.0)
|
115
|
+
@pb = PixbufUtils.gamma(@pb, options['gamma'])
|
116
|
+
end
|
117
|
+
|
118
|
+
if options['contrast'].to_i.nonzero?
|
119
|
+
@pb = PixbufUtils.contrast(@pb, [ [ 5 * options['contrast'], -100 ].max, 100 ].min)
|
120
|
+
end
|
121
|
+
|
122
|
+
if options['sharpen'].to_i.nonzero?
|
123
|
+
if options['sharpen'] > 0
|
124
|
+
[options['sharpen'], 5].min.times do
|
125
|
+
@pb = PixbufUtils.filter(@pb, SHARPEN, SHARPEN.inject(0, &:+))
|
126
|
+
end
|
127
|
+
elsif options['sharpen'] < 0
|
128
|
+
[ (options['sharpen']*-1), 5].min.times do
|
129
|
+
@pb = PixbufUtils.filter(@pb, BLUR, BLUR.inject(0, &:+))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def apply_redeye!
|
136
|
+
for eye in options['redeye'] || []
|
137
|
+
@pb = Morandi::RedEye.tap_on(@pb, eye[0] * @scale, eye[1] * @scale)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def angle
|
142
|
+
a = options['angle'].to_i
|
143
|
+
if a
|
144
|
+
(360-a)%360
|
145
|
+
else
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# modifies @pb with any applied rotation
|
151
|
+
def apply_rotate!
|
152
|
+
a = angle()
|
153
|
+
|
154
|
+
unless (a%360).zero?
|
155
|
+
@pb = @pb.rotate(a)
|
156
|
+
end
|
157
|
+
|
158
|
+
unless options['straighten'].to_f.zero?
|
159
|
+
@pb = Morandi::Straighten.new(options['straighten'].to_f).call(nil, @pb)
|
160
|
+
end
|
161
|
+
|
162
|
+
@image_width = @pb.width
|
163
|
+
@image_height = @pb.height
|
164
|
+
end
|
165
|
+
|
166
|
+
DEFAULT_CONFIG = {
|
167
|
+
'border-size-mm' => 5
|
168
|
+
}
|
169
|
+
def config_for(key)
|
170
|
+
return options[key] if options && options.has_key?(key)
|
171
|
+
return @config[key] if @config && @config.has_key?(key)
|
172
|
+
DEFAULT_CONFIG[key]
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
def apply_crop!
|
177
|
+
crop = options['crop']
|
178
|
+
|
179
|
+
if crop.nil? && config_for('image.auto-crop').eql?(false)
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
183
|
+
if crop.is_a?(String) && crop =~ /^\d+,\d+,\d+,\d+/
|
184
|
+
crop = crop.split(/,/).map(&:to_i)
|
185
|
+
end
|
186
|
+
|
187
|
+
crop = nil unless crop.is_a?(Array) && crop.size.eql?(4) && crop.all? { |i|
|
188
|
+
i.kind_of?(Numeric)
|
189
|
+
}
|
190
|
+
|
191
|
+
# can't crop, won't crop
|
192
|
+
return if @width.nil? && @height.nil? && crop.nil?
|
193
|
+
|
194
|
+
if crop && @scale != 1.0
|
195
|
+
crop = crop.map { |s| (s.to_f * @scale).floor }
|
196
|
+
end
|
197
|
+
|
198
|
+
crop ||= Morandi::Utils.autocrop_coords(@pb.width, @pb.height, @width, @height)
|
199
|
+
|
200
|
+
@pb = Morandi::Utils.apply_crop(@pb, crop[0], crop[1], crop[2], crop[3])
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
def apply_filters!
|
205
|
+
filter = options['fx']
|
206
|
+
|
207
|
+
case filter
|
208
|
+
when 'greyscale'
|
209
|
+
op = Morandi::Colourify.new_from_hash('op' => filter)
|
210
|
+
when 'sepia', 'bluetone'
|
211
|
+
op = Morandi::Colourify.new_from_hash('op' => filter, 'alpha' => (0.85 * 255).to_i)
|
212
|
+
else
|
213
|
+
return
|
214
|
+
end
|
215
|
+
@pb = op.call(nil, @pb)
|
216
|
+
end
|
217
|
+
|
218
|
+
def apply_decorations!
|
219
|
+
style, colour = options['border-style'], options['background-style']
|
220
|
+
|
221
|
+
return if style.nil? or style.eql?('none')
|
222
|
+
return if colour.eql?('none')
|
223
|
+
colour ||= 'black'
|
224
|
+
|
225
|
+
crop = options['crop']
|
226
|
+
crop = crop.map { |s| (s.to_f * @scale).floor } if crop && @scale != 1.0
|
227
|
+
|
228
|
+
op = Morandi::ImageBorder.new_from_hash(data={
|
229
|
+
'style' => style,
|
230
|
+
'colour' => colour || '#000000',
|
231
|
+
'crop' => crop,
|
232
|
+
'size' => [@image_width, @image_height],
|
233
|
+
'print_size' => [@width, @height],
|
234
|
+
'shrink' => true,
|
235
|
+
'border_size' => @scale * config_for('border-size-mm').to_i * 300 / 25.4 # 5mm at 300dpi
|
236
|
+
})
|
237
|
+
|
238
|
+
@pb = op.call(nil, @pb)
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'gdk_pixbuf2'
|
2
|
+
|
3
|
+
class Morandi::ProfiledPixbuf < Gdk::Pixbuf
|
4
|
+
def valid_jpeg?(filename)
|
5
|
+
return false unless File.exist?(filename)
|
6
|
+
return false unless File.size(filename) > 0
|
7
|
+
|
8
|
+
type, _, _ = Gdk::Pixbuf.get_file_info(filename)
|
9
|
+
|
10
|
+
type && type.name.eql?('jpeg')
|
11
|
+
rescue
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.default_icc_path(path)
|
16
|
+
"#{path}.icc.jpg"
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
@local_options = args.last.is_a?(Hash) && args.pop || {}
|
21
|
+
|
22
|
+
if args[0].is_a?(String)
|
23
|
+
@file = args[0]
|
24
|
+
|
25
|
+
if suitable_for_jpegicc?
|
26
|
+
icc_file = icc_cache_path
|
27
|
+
|
28
|
+
args[0] = icc_file if valid_jpeg?(icc_file) || system("jpgicc", "-q97", @file, icc_file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
super(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
protected
|
37
|
+
def suitable_for_jpegicc?
|
38
|
+
type, _, _ = Gdk::Pixbuf.get_file_info(@file)
|
39
|
+
|
40
|
+
type && type.name.eql?('jpeg')
|
41
|
+
end
|
42
|
+
|
43
|
+
def icc_cache_path
|
44
|
+
@local_options['path.icc'] || Morandi::ProfiledPixbuf.default_icc_path(@file)
|
45
|
+
end
|
46
|
+
end
|
data/lib/morandi/version.rb
CHANGED
data/spec/morandi_spec.rb
CHANGED
@@ -18,6 +18,13 @@ RSpec.describe Morandi, "#process_to_file" do
|
|
18
18
|
expect(original[2]).to eq(w)
|
19
19
|
end
|
20
20
|
|
21
|
+
it "should accept pixbufs as an argument" do
|
22
|
+
pixbuf = Gdk::Pixbuf.new("sample/sample.jpg")
|
23
|
+
pro = Morandi::ImageProcessor.new(pixbuf, {}, {})
|
24
|
+
pro.process!
|
25
|
+
expect(pixbuf.width).to eq(pro.result.width)
|
26
|
+
end
|
27
|
+
|
21
28
|
it "should do cropping of images" do
|
22
29
|
Morandi.process("sample/sample.jpg", {
|
23
30
|
'crop' => [10,10,300,300]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morandi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- |+
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-12-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: gtk2
|
@@ -168,6 +168,8 @@ files:
|
|
168
168
|
- Rakefile
|
169
169
|
- lib/morandi.rb
|
170
170
|
- lib/morandi/image-ops.rb
|
171
|
+
- lib/morandi/image_processor.rb
|
172
|
+
- lib/morandi/profiled_pixbuf.rb
|
171
173
|
- lib/morandi/redeye.rb
|
172
174
|
- lib/morandi/utils.rb
|
173
175
|
- lib/morandi/version.rb
|