morandi 0.12.0 → 0.99.03

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.
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