morandi 0.12.0 → 0.99.03

Sign up to get free protection for your applications and to get access to all the features.
data/lib/morandi.rb CHANGED
@@ -1,20 +1,42 @@
1
- require "morandi/version"
2
- require 'gtk2/base'
3
- require 'cairo'
4
- require 'gdk_pixbuf2'
5
- require 'pixbufutils'
6
- require 'redeye'
1
+ # frozen_string_literal: true
7
2
 
3
+ require 'morandi/version'
4
+ require 'morandi_native'
5
+
6
+ require 'morandi/cairo_ext'
7
+ require 'morandi/pixbuf_ext'
8
8
  require 'morandi/image_processor'
9
- require 'morandi/utils'
10
- require 'morandi/image-ops'
9
+ require 'morandi/image_operation'
11
10
  require 'morandi/redeye'
11
+ require 'morandi/crop_utils'
12
12
 
13
+ # Morandi namespace should contain all the functionality of the gem
13
14
  module Morandi
14
15
  module_function
15
- def process(file_in, options, out_file, local_options = {})
16
+
17
+ # The main entry point for the libray
18
+ #
19
+ # @param in_file [String|GdkPixbuf::Pixbuf] source image
20
+ # @param settings [Hash]
21
+ # @param out_file [String] target location for image
22
+ # @param local_options [Hash]
23
+ #
24
+ # Settings Key | Values | Description
25
+ # -------------|--------|---------------
26
+ # brighten | Integer -20..20 | Change image brightness
27
+ # gamma | Float | Gamma correct image
28
+ # contrast | Integer -20..20 | Change image contrast
29
+ # sharpen | Integer -5..5 | Sharpen / Blur (negative value)
30
+ # redeye | Array[[Integer,Integer],...] | Apply redeye correction at point
31
+ # angle | Integer 0,90,180,270 | Rotate image
32
+ # crop | Array[Integer,Integer,Integer,Integer] | Crop image
33
+ # fx | String greyscale,sepia,bluetone | Apply colour filters
34
+ # border-style | String square,retro | Set border style
35
+ # background-style | String retro,black,white | Set border colour
36
+ # quality | String '1'..'100' | Set JPG compression value, defaults to 97%
37
+ def process(file_in, options, file_out, local_options = {})
16
38
  pro = ImageProcessor.new(file_in, options, local_options)
17
- pro.process!
18
- pro.write_to_jpeg(out_file)
39
+ pro.result
40
+ pro.write_to_jpeg(file_out)
19
41
  end
20
42
  end
Binary file
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.12.0
4
+ version: 0.99.03
5
5
  platform: ruby
6
6
  authors:
7
7
  - |+
@@ -11,193 +11,130 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2020-12-10 00:00:00.000000000 Z
14
+ date: 2024-09-19 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
- name: gtk2
18
- requirement: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: '0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- - !ruby/object:Gem::Dependency
31
- name: gdk_pixbuf2
17
+ name: atk
32
18
  requirement: !ruby/object:Gem::Requirement
33
19
  requirements:
34
20
  - - "~>"
35
21
  - !ruby/object:Gem::Version
36
- version: 3.4.0
22
+ version: '4.0'
37
23
  type: :runtime
38
24
  prerelease: false
39
25
  version_requirements: !ruby/object:Gem::Requirement
40
26
  requirements:
41
27
  - - "~>"
42
28
  - !ruby/object:Gem::Version
43
- version: 3.4.0
29
+ version: '4.0'
44
30
  - !ruby/object:Gem::Dependency
45
31
  name: cairo
46
32
  requirement: !ruby/object:Gem::Requirement
47
33
  requirements:
48
- - - ">="
34
+ - - "~>"
49
35
  - !ruby/object:Gem::Version
50
- version: '0'
36
+ version: '1.0'
51
37
  type: :runtime
52
38
  prerelease: false
53
39
  version_requirements: !ruby/object:Gem::Requirement
54
40
  requirements:
55
- - - ">="
41
+ - - "~>"
56
42
  - !ruby/object:Gem::Version
57
- version: '0'
43
+ version: '1.0'
58
44
  - !ruby/object:Gem::Dependency
59
- name: pixbufutils
45
+ name: colorscore
60
46
  requirement: !ruby/object:Gem::Requirement
61
47
  requirements:
62
- - - ">="
48
+ - - "~>"
63
49
  - !ruby/object:Gem::Version
64
- version: '0'
50
+ version: '0.0'
65
51
  type: :runtime
66
52
  prerelease: false
67
53
  version_requirements: !ruby/object:Gem::Requirement
68
54
  requirements:
69
- - - ">="
55
+ - - "~>"
70
56
  - !ruby/object:Gem::Version
71
- version: '0'
57
+ version: '0.0'
72
58
  - !ruby/object:Gem::Dependency
73
- name: redeye
59
+ name: gdk_pixbuf2
74
60
  requirement: !ruby/object:Gem::Requirement
75
61
  requirements:
76
- - - ">="
62
+ - - "~>"
77
63
  - !ruby/object:Gem::Version
78
- version: '0'
64
+ version: '4.0'
79
65
  type: :runtime
80
66
  prerelease: false
81
67
  version_requirements: !ruby/object:Gem::Requirement
82
68
  requirements:
83
- - - ">="
69
+ - - "~>"
84
70
  - !ruby/object:Gem::Version
85
- version: '0'
71
+ version: '4.0'
86
72
  - !ruby/object:Gem::Dependency
87
73
  name: pango
88
74
  requirement: !ruby/object:Gem::Requirement
89
75
  requirements:
90
- - - ">="
76
+ - - "~>"
91
77
  - !ruby/object:Gem::Version
92
- version: '0'
78
+ version: '4.0'
93
79
  type: :runtime
94
80
  prerelease: false
95
81
  version_requirements: !ruby/object:Gem::Requirement
96
82
  requirements:
97
- - - ">="
83
+ - - "~>"
98
84
  - !ruby/object:Gem::Version
99
- version: '0'
85
+ version: '4.0'
100
86
  - !ruby/object:Gem::Dependency
101
- name: colorscore
87
+ name: rake-compiler
102
88
  requirement: !ruby/object:Gem::Requirement
103
89
  requirements:
104
- - - ">="
90
+ - - "~>"
105
91
  - !ruby/object:Gem::Version
106
- version: '0'
92
+ version: '1.2'
107
93
  type: :runtime
108
94
  prerelease: false
109
95
  version_requirements: !ruby/object:Gem::Requirement
110
96
  requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: '0'
114
- - !ruby/object:Gem::Dependency
115
- name: bundler
116
- requirement: !ruby/object:Gem::Requirement
117
- requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- version: '0'
121
- type: :development
122
- prerelease: false
123
- version_requirements: !ruby/object:Gem::Requirement
124
- requirements:
125
- - - ">="
126
- - !ruby/object:Gem::Version
127
- version: '0'
128
- - !ruby/object:Gem::Dependency
129
- name: pry
130
- requirement: !ruby/object:Gem::Requirement
131
- requirements:
132
- - - ">="
133
- - !ruby/object:Gem::Version
134
- version: '0'
135
- type: :development
136
- prerelease: false
137
- version_requirements: !ruby/object:Gem::Requirement
138
- requirements:
139
- - - ">="
140
- - !ruby/object:Gem::Version
141
- version: '0'
142
- - !ruby/object:Gem::Dependency
143
- name: rake
144
- requirement: !ruby/object:Gem::Requirement
145
- requirements:
146
- - - ">="
147
- - !ruby/object:Gem::Version
148
- version: '0'
149
- type: :development
150
- prerelease: false
151
- version_requirements: !ruby/object:Gem::Requirement
152
- requirements:
153
- - - ">="
154
- - !ruby/object:Gem::Version
155
- version: '0'
156
- - !ruby/object:Gem::Dependency
157
- name: rspec
158
- requirement: !ruby/object:Gem::Requirement
159
- requirements:
160
- - - ">="
161
- - !ruby/object:Gem::Version
162
- version: '0'
163
- type: :development
164
- prerelease: false
165
- version_requirements: !ruby/object:Gem::Requirement
166
- requirements:
167
- - - ">="
97
+ - - "~>"
168
98
  - !ruby/object:Gem::Version
169
- version: '0'
99
+ version: '1.2'
170
100
  description: Apply simple edits to images
171
101
  email:
172
102
  - git@intersect-uk.co.uk
173
103
  executables: []
174
- extensions: []
104
+ extensions:
105
+ - ext/morandi_native/extconf.rb
106
+ - ext/gdk_pixbuf_cairo/extconf.rb
175
107
  extra_rdoc_files: []
176
108
  files:
177
- - ".gitignore"
178
- - ".rspec"
179
- - ".ruby-version"
180
109
  - CHANGELOG.md
181
- - Gemfile
182
110
  - LICENSE.txt
183
111
  - README.md
184
- - Rakefile
112
+ - ext/gdk_pixbuf_cairo/extconf.rb
113
+ - ext/gdk_pixbuf_cairo/gdk_pixbuf_cairo.c
114
+ - ext/morandi_native/extconf.rb
115
+ - ext/morandi_native/filter.h
116
+ - ext/morandi_native/gamma.h
117
+ - ext/morandi_native/mask.h
118
+ - ext/morandi_native/morandi_native.c
119
+ - ext/morandi_native/rotate.h
120
+ - ext/morandi_native/tint.h
121
+ - lib/gdk_pixbuf_cairo.so
185
122
  - lib/morandi.rb
186
- - lib/morandi/image-ops.rb
123
+ - lib/morandi/cairo_ext.rb
124
+ - lib/morandi/crop_utils.rb
125
+ - lib/morandi/image_operation.rb
187
126
  - lib/morandi/image_processor.rb
127
+ - lib/morandi/pixbuf_ext.rb
188
128
  - lib/morandi/profiled_pixbuf.rb
189
129
  - lib/morandi/redeye.rb
190
- - lib/morandi/utils.rb
191
130
  - lib/morandi/version.rb
192
- - morandi.gemspec
193
- - sample/100_mb_image.jpg
194
- - sample/sample.jpg
195
- - spec/morandi_spec.rb
196
- - spec/spec_helper.rb
197
- homepage: ''
131
+ - lib/morandi_native.so
132
+ homepage: https://github.com/livelink/morandi-rb
198
133
  licenses:
199
134
  - MIT
200
- metadata: {}
135
+ metadata:
136
+ source_code_uri: https://github.com/livelink/morandi-rb
137
+ rubygems_mfa_required: 'true'
201
138
  post_install_message:
202
139
  rdoc_options: []
203
140
  require_paths:
@@ -206,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
206
143
  requirements:
207
144
  - - ">="
208
145
  - !ruby/object:Gem::Version
209
- version: '0'
146
+ version: '2.0'
210
147
  required_rubygems_version: !ruby/object:Gem::Requirement
211
148
  requirements:
212
149
  - - ">="
@@ -217,7 +154,5 @@ rubygems_version: 3.1.2
217
154
  signing_key:
218
155
  specification_version: 4
219
156
  summary: Simple Image Edits
220
- test_files:
221
- - spec/morandi_spec.rb
222
- - spec/spec_helper.rb
157
+ test_files: []
223
158
  ...
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .DS_Store
5
- .config
6
- .yardoc
7
- Gemfile.lock
8
- InstalledFiles
9
- _yardoc
10
- coverage
11
- doc/
12
- lib/bundler/man
13
- pkg
14
- rdoc
15
- spec/reports
16
- test/tmp
17
- test/version_tmp
18
- tmp
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --format progress
3
- --require spec_helper
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.7.1
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in morandi.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- require "bundler/gem_tasks"
@@ -1,307 +0,0 @@
1
- require 'pango'
2
- require 'colorscore'
3
-
4
- module Morandi
5
- class ImageOp
6
- class << self
7
- def new_from_hash(hash)
8
- op = allocate()
9
- hash.each_pair do |key,val|
10
- op.instance_variable_set("@#{key}", val) if op.respond_to?(key.intern)
11
- end
12
- op
13
- end
14
- end
15
- def initialize()
16
- end
17
- def priority
18
- 100
19
- end
20
- end
21
- class Crop < ImageOp
22
- attr_accessor :area
23
- def initialize(area=nil)
24
- super()
25
- @area = area
26
- end
27
- def constrain(val,min,max)
28
- if val < min
29
- min
30
- elsif val > max
31
- max
32
- else
33
- val
34
- end
35
- end
36
- def call(image, pixbuf)
37
- if @area and (not @area.width.zero?) and (not @area.height.zero?)
38
- # NB: Cheap - fast & shares memory
39
- GdkPixbuf::Pixbuf.new(pixbuf, @area.x, @area.y,
40
- @area.width, @area.height)
41
- else
42
- pixbuf
43
- end
44
- end
45
- end
46
- class Rotate < ImageOp
47
- attr_accessor :angle
48
- def initialize(angle=0)
49
- super()
50
- @angle = angle
51
- end
52
- def call(image, pixbuf)
53
- if @angle.zero?
54
- pixbuf
55
- else
56
- case @angle
57
- when 0, 90, 180, 270
58
- PixbufUtils::rotate(pixbuf, @angle)
59
- else
60
- raise "Not a valid angle"
61
- end
62
- end
63
- end
64
- end
65
- class Straighten < ImageOp
66
- attr_accessor :angle
67
- def initialize(angle=0)
68
- super()
69
- @angle = angle
70
- end
71
- def call(image, pixbuf)
72
- if @angle.zero?
73
- pixbuf
74
- else
75
- surface = Cairo::ImageSurface.new(:rgb24, pixbuf.width, pixbuf.height)
76
-
77
- rotationValueRad = @angle * (Math::PI/180)
78
-
79
- ratio = pixbuf.width.to_f/pixbuf.height
80
- rh = (pixbuf.height) / ((ratio * Math.sin(rotationValueRad.abs)) + Math.cos(rotationValueRad.abs))
81
- scale = pixbuf.height / rh.to_f.abs
82
-
83
- a_ratio = pixbuf.height.to_f/pixbuf.width
84
- a_rh = (pixbuf.width) / ((a_ratio * Math.sin(rotationValueRad.abs)) + Math.cos(rotationValueRad.abs))
85
- a_scale = pixbuf.width / a_rh.to_f.abs
86
-
87
- scale = a_scale if a_scale > scale
88
-
89
- cr = Cairo::Context.new(surface)
90
- #p [@angle, rotationValueRad, rh, scale, pixbuf.height]
91
-
92
- cr.translate(pixbuf.width / 2.0, pixbuf.height / 2.0)
93
- cr.rotate(rotationValueRad)
94
- cr.scale(scale, scale)
95
- cr.translate(pixbuf.width / -2.0, pixbuf.height / - 2.0)
96
- cr.set_source_pixbuf(pixbuf)
97
-
98
- cr.rectangle(0, 0, pixbuf.width, pixbuf.height)
99
- cr.paint(1.0)
100
- final_pb = surface.to_pixbuf
101
- cr.destroy
102
- surface.destroy
103
- return final_pb
104
- end
105
- end
106
- end
107
-
108
- class ImageCaption < ImageOp
109
- attr_accessor :text, :font, :position
110
- def initialize()
111
- super()
112
- end
113
-
114
- def font
115
- @font || "Open Sans Condensed Light #{ ([@pixbuf.width,@pixbuf.height].max/80.0).to_i }"
116
- end
117
-
118
- def position
119
- @position ||
120
- (@pixbuf ? ([ [@pixbuf.width,@pixbuf.height].max/20 ] * 2) : [100, 100])
121
- end
122
-
123
- def call(image, pixbuf)
124
- @pixbuf = pixbuf
125
- surface = Cairo::ImageSurface.new(:rgb24, pixbuf.width, pixbuf.height)
126
- cr = Cairo::Context.new(surface)
127
-
128
- cr.save do
129
- cr.set_source_pixbuf(pixbuf)
130
- #cr.rectangle(0, 0, pixbuf.width, pixbuf.height)
131
- cr.paint(1.0)
132
- cr.translate(*self.position)
133
-
134
- layout = cr.create_pango_layout
135
- layout.set_text(self.text)
136
- fd = Pango::FontDescription.new(self.font)
137
- layout.font_description = fd
138
- layout.set_width((pixbuf.width - self.position[0] - 100)*Pango::SCALE)
139
- layout.context_changed
140
- ink, _ = layout.pixel_extents
141
- cr.set_source_rgba(0, 0, 0, 0.3)
142
- cr.rectangle(-25, -25, ink.width + 50, ink.height + 50)
143
- cr.fill
144
- cr.set_source_rgb(1, 1, 1)
145
- cr.show_pango_layout(layout)
146
- end
147
-
148
-
149
- final_pb = surface.to_pixbuf
150
- cr.destroy
151
- surface.destroy
152
- return final_pb
153
- end
154
- end
155
-
156
- class ImageBorder < ImageOp
157
- attr_accessor :style, :colour, :crop, :size, :print_size, :shrink, :border_size
158
- def initialize(style='none', colour='white')
159
- super()
160
- @style = style
161
- @colour = colour
162
- end
163
-
164
- def call(image, pixbuf)
165
- return pixbuf unless %w[square retro].include? @style
166
- surface = Cairo::ImageSurface.new(:rgb24, pixbuf.width, pixbuf.height)
167
- cr = Cairo::Context.new(surface)
168
-
169
- img_width = pixbuf.width
170
- img_height = pixbuf.height
171
-
172
- cr.save do
173
- if @crop
174
- if @crop[0] < 0 || @crop[1] < 0
175
- img_width = size[0]
176
- img_height = size[1]
177
- cr.translate( - @crop[0], - @crop[1])
178
- end
179
- end
180
-
181
- cr.save do
182
- cr.set_operator :source
183
- cr.set_source_rgb 1, 1, 1
184
- cr.paint
185
-
186
- cr.rectangle(0, 0, img_width, img_height)
187
- case colour
188
- when 'dominant'
189
- pixbuf.scale_max(400).save(fn="/tmp/hist-#{$$}.#{Time.now.to_i}", 'jpeg')
190
- hgram = Colorscore::Histogram.new(fn)
191
- File.unlink(fn) rescue nil
192
- col = hgram.scores.first[1]
193
- cr.set_source_rgb col.red/256.0, col.green/256.0, col.blue/256.0
194
- when 'retro'
195
- cr.set_source_rgb 1, 1, 0.8
196
- when 'black'
197
- cr.set_source_rgb 0, 0, 0
198
- else
199
- cr.set_source_rgb 1, 1, 1
200
- end
201
- cr.fill
202
- end
203
- end
204
-
205
- border_scale = [img_width,img_height].max.to_f / print_size.max.to_i
206
- size = @border_size
207
- size *= border_scale
208
- x, y = size, size
209
-
210
- # This biggest impact will be on the smallest side, so to avoid white
211
- # edges between photo and border scale by the longest changed side.
212
- longest_side = [pixbuf.width, pixbuf.height].max.to_f
213
-
214
- # Should be less than 1
215
- pb_scale = (longest_side - (size * 2)) / longest_side
216
-
217
- if @crop
218
- if @crop[0] < 0 || @crop[1] < 0
219
- x -= @crop[0]
220
- y -= @crop[1]
221
- end
222
- end
223
-
224
- case style
225
- when 'retro'
226
- # WARNING: CairoUtils class is not available in this gem!
227
- CairoUtils.rounded_rectangle(cr, x, y,
228
- img_width + x - (size*2), img_height+y-(size*2), size)
229
- when 'square'
230
- cr.rectangle(x, y, img_width - (size*2), img_height - (size*2))
231
- end
232
- cr.clip()
233
-
234
- if @shrink
235
- cr.translate(size, size)
236
- cr.scale(pb_scale, pb_scale)
237
- end
238
- cr.set_source_pixbuf(pixbuf)
239
- cr.rectangle(0, 0, pixbuf.width, pixbuf.height)
240
-
241
- cr.paint(1.0)
242
- final_pb = surface.to_pixbuf
243
- cr.destroy
244
- surface.destroy
245
- return final_pb
246
- end
247
- end
248
-
249
- class Gamma < ImageOp
250
- attr_reader :gamma
251
- def initialize(gamma=1.0)
252
- super()
253
- @gamma = gamma
254
- end
255
- def call(image, pixbuf)
256
- if @gamma == 1.0
257
- pixbuf
258
- else
259
- PixbufUtils.gamma(pixbuf, @gamma)
260
- end
261
- end
262
- def priority
263
- 90
264
- end
265
- end
266
-
267
- class Colourify < ImageOp
268
- attr_reader :op
269
- def initialize(op, alpha=255)
270
- super()
271
- @op = op
272
- @alpha = alpha
273
- end
274
-
275
- def alpha
276
- @alpha || 255
277
- end
278
-
279
- def sepia(pixbuf)
280
- PixbufUtils.tint(pixbuf, 25, 5, -25, alpha)
281
- end
282
-
283
- def bluetone(pixbuf)
284
- PixbufUtils.tint(pixbuf, -10, 5, 25, alpha)
285
- end
286
-
287
- def null(pixbuf)
288
- pixbuf
289
- end
290
- alias :full :null # WebKiosk
291
- alias :colour :null # WebKiosk
292
-
293
- def greyscale(pixbuf)
294
- PixbufUtils.tint(pixbuf, 0, 0, 0, alpha)
295
- end
296
- alias :bw :greyscale # WebKiosk
297
-
298
- def call(image, pixbuf)
299
- if @op and respond_to?(@op)
300
- __send__(@op, pixbuf)
301
- else
302
- pixbuf # Default is nothing
303
- end
304
- end
305
- end
306
-
307
- end