rcomposite 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Corban Brook
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,191 @@
1
+
2
+ RComposite
3
+
4
+ RComposite is an RMagick abstraction library to easily manipulate and composite
5
+ images through the use of a canvas, layers, layer masks, adjustment layers, fill
6
+ layers, and layer sets (much like in Photoshop).
7
+
8
+
9
+ Install:
10
+
11
+ Add the github repository to your gem sources:
12
+
13
+ sudo gem sources -a http://gems.github.com
14
+
15
+ Install the gem from github:
16
+
17
+ sudo gem install corbanbrook-rcomposite
18
+
19
+ Require:
20
+
21
+ require 'rubygems'
22
+ require 'rcomposite'
23
+
24
+ include RComposite # optional: if you wish to have the RComposite module in your project scope
25
+
26
+ ...
27
+
28
+
29
+ Class Summary:
30
+
31
+ * Layers: There are 4 types of Layer Models,
32
+
33
+ * Layer: The base layer model. Can load an image from disk, blob,
34
+ or RMagick Image object. Contains methods to transform, change layer mode,
35
+ change opacity level, and more.
36
+
37
+ * FillLayer: A solid color, gradient, or pattern fill layer.
38
+
39
+ * AdjustmentLayer: Invert, Threshold, Levels, Blur adjustment layer.
40
+ Applies layer effect over everything under it in the layer stack.
41
+
42
+ * LayerMask: Mask any of the above layer types. LayerMasks are a special
43
+ type of layer which is a Alpha Channel mask that can be applied to any
44
+ layer type. Since LayerMask is also a layer, it can also be transformed
45
+ in any way.
46
+
47
+ * LayerSets: 2 types of LayerSet Models,
48
+
49
+ * LayerSet: The LayerSet is a container, or directory in which to bundle
50
+ layers. LayerSet contains methods to do group transforms on its bundled
51
+ layers. Transforms like rotation, movement and scaling are performed on
52
+ all the layers it contains while maintaining each layer on a seperate plane
53
+ without merging.
54
+
55
+ * Canvas: The canvas is a special LayerSet which is a blank workspace containing
56
+ other layers and layer sets.
57
+
58
+
59
+ Usage:
60
+
61
+ * Creating a blank canvas:
62
+
63
+ canvas = RComposite::Canvas.new(640, 480)
64
+
65
+ Canvas also accepts an optional block for easily adding layers to the stack
66
+
67
+ canvas = Rcomposite::Canvas.new(640, 480) do
68
+ layer :file => 'butterfly.png'
69
+ end
70
+
71
+ * Creating a layer:
72
+
73
+ photo = RComposite::Layer.new :file => 'photo.jpg'
74
+
75
+ * Creating a fill layer:
76
+
77
+ blue = RCompisite::FillLayer.new '#000090'
78
+
79
+ * Changing a Layers properties:
80
+
81
+ blue.opacity 40
82
+ blue.mode Multiply
83
+
84
+ * Adding layers to the canvas. (Whatever is added first will be on top):
85
+
86
+ canvas.layer blue
87
+ canvas.layer photo
88
+
89
+ * Saving the canvas:
90
+
91
+ canvas.save_as 'bluesky.jpg'
92
+
93
+ * Adding an alpha channel mask to a layer:
94
+
95
+ Masks are used to punch holes into layers to add transparency.
96
+
97
+ blue.layer_mask :file => 'alpha_channel.png'
98
+
99
+ You can add layer options to the mask just like it was a normal layer.
100
+
101
+ fill_layer SolidColor, '#f83898FF' do
102
+ layer_mask :file => 'butterfly-mask.png' do
103
+ offset -30, -30
104
+ rotate 40
105
+ end
106
+ end
107
+
108
+
109
+ The real power of RComposite lies in its use of blocks and simplified syntax.
110
+ Take a look at the following examples:
111
+
112
+ * Just like the Canvas, all layer types also accept an optional block parametre for setting layer options:
113
+
114
+ butterfly = RComposite::Layer :file => 'butterfly.png' do
115
+ offset 100, 100 # moves the layer on the canvas
116
+ rotate 25
117
+ end
118
+
119
+ or..
120
+
121
+ canvas.layer :file => 'butterfly.png' do
122
+ ..
123
+ end
124
+
125
+ * DSL Example
126
+
127
+ Canvas.new(320,240) do
128
+
129
+ layer_set :slides do
130
+ layer :file => 'slide1.png' do
131
+ opacity 65
132
+ offset 12, 12
133
+ mode Lighten
134
+ end
135
+
136
+ adjustment_layer Blur, 0.5, 1.5 do
137
+ layer_mask :file => 'butterfly-mask.png'
138
+ end
139
+
140
+ layer :file => 'slide2.png' do
141
+ offset 28, 28
142
+ opacity 30
143
+ mode Darken
144
+ end
145
+
146
+ rotate 45
147
+ offset 70, 50
148
+ scale 80, 80
149
+ end
150
+
151
+ fill_layer SolidColor, '#f83898FF' do
152
+ opacity 70
153
+ mode Multiply
154
+
155
+ layer_mask :file => 'butterfly-mask.png' do
156
+ offset -30, -30
157
+ image.rotate! 40
158
+ end
159
+ end
160
+
161
+ fill_layer Gradient, 0, 0, 0, 50, '#606', '#033' do
162
+ opacity 90
163
+ mode Overlay
164
+
165
+ layer_mask :file => 'butterfly-mask.png' do
166
+ offset -20, -20
167
+ end
168
+ end
169
+
170
+ layer :file => 'background.png' do
171
+ image.resize!(320, 240)
172
+ end
173
+
174
+ save_as 'composite.jpg'
175
+ end
176
+
177
+
178
+ The nice thing about RComposite is that it doesnt hide the underlaying RMagick
179
+ Image object from you. It is always available through the image accessor
180
+
181
+ layer :file => 'photo.jpg' do
182
+ image.crop_resized!(100,300) # crop_resized! is a RMagick method
183
+ end
184
+
185
+
186
+
187
+ @corban weare.buildingsky.net
188
+ ________________________________________________________________________________
189
+
190
+ Copyright (c) 2009 Corban Brook, released under the MIT license
191
+
@@ -0,0 +1,48 @@
1
+ module RComposite
2
+ Levels = 1
3
+ Curves = 2
4
+ ColorBalance = 3
5
+ BrightnessContrast = 4
6
+ HueSaturation = 5
7
+ SelectiveColor = 6
8
+ ChannelMixer = 7
9
+ GradientMap = 8
10
+ PhotoFilter = 9
11
+ Invert = 10 # negate
12
+ Threshold = 11 # threshold
13
+ Polarize = 12 #
14
+ Blur = 13
15
+
16
+ class AdjustmentLayer < Layer
17
+ attr_reader :type, :args
18
+
19
+ def initialize(type, *args, &block)
20
+ @type = type
21
+ @args = args
22
+ @mode = Normal
23
+ @offset_x = 0
24
+ @offset_y = 0
25
+ @opacity_percent = 100
26
+ @layer_mask = nil
27
+
28
+ case type
29
+ when Invert
30
+ @proc = proc { negate }
31
+ when Threshold
32
+ @proc = proc { threshold *args }
33
+ when Levels
34
+ @proc = proc { level *args }
35
+ when Blur
36
+ @proc = proc { gaussian_blur *args }
37
+ end
38
+ @proc = proc { |*args| @proc }.call(*@args)
39
+ end
40
+
41
+ def merge_down(image)
42
+ @image = image.clone.instance_eval &@proc
43
+ @image.matte = true
44
+
45
+ super(image)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ module RComposite
2
+ class Canvas < LayerSet
3
+ attr_reader :width, :height
4
+ attr_accessor :image
5
+
6
+ def initialize(width, height, &block)
7
+ @image = Magick::Image.new width, height
8
+ @width = width
9
+ @height = height
10
+ $RCompositeCanvasWidth = @width
11
+ $RCompositeCanvasHeight = @height
12
+
13
+ super(:canvas, &block)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,44 @@
1
+ module RComposite
2
+ # Fill layer types
3
+
4
+ SolidColor = 0
5
+ Gradient = 1
6
+ Pattern = 2
7
+
8
+ class FillLayer < Layer
9
+ attr_accessor :image
10
+ attr_reader :offset_x, :offset_y
11
+
12
+ def initialize(type, *args, &block)
13
+ case type
14
+ when SolidColor
15
+ color = args[0]
16
+
17
+ fill = Magick::GradientFill.new(0, 0, 0, 0, color, color)
18
+
19
+ when Gradient
20
+ x1 = args[0]
21
+ y1 = args[1]
22
+ x2 = args[2]
23
+ y2 = args[3]
24
+ color1 = args[4]
25
+ color2 = args[5]
26
+
27
+ fill = Magick::GradientFill.new(x1, y1, x2, y2, color1, color2)
28
+
29
+ when Pattern
30
+ image = args[0]
31
+
32
+ fill = Magick::TextureFill.new(image)
33
+ end
34
+
35
+ @image = Magick::Image.new($RCompositeCanvasWidth, $RCompositeCanvasHeight, fill)
36
+
37
+ @image.matte = true
38
+ @mode = Normal
39
+ @offset_x = 0
40
+ @offset_y = 0
41
+ @opacity_percent = 100
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,118 @@
1
+ module RComposite
2
+ # Layer mode constants
3
+ # some of photoshops layer modes are directly equvilent to RMagick
4
+ # some are not, and some arent supported at all.
5
+
6
+ Normal = Magick::OverCompositeOp # PS equivilent
7
+ Dissolve = Magick::DissolveCompositeOp
8
+ Darken = Magick::DarkenCompositeOp # PS equivilent
9
+ Multiply = Magick::MultiplyCompositeOp # PS equivilent
10
+ #ColorBurn
11
+ #LinearBurn
12
+ Lighten = Magick::LightenCompositeOp # PS equivilent
13
+ Screen = Magick::ScreenCompositeOp # PS equivilent
14
+ #ColorDodge
15
+ #LinearDodge
16
+ Overlay = Magick::OverlayCompositeOp # PS equivilent
17
+ #SoftLight
18
+ #HardLight
19
+ #VividLight
20
+ #LinearLight
21
+ #PinLight
22
+ #HardMix
23
+ Difference = Magick::DifferenceCompositeOp # PS equivilent
24
+ #Exclusion
25
+ Hue = Magick::HueCompositeOp
26
+ Saturation = Magick::SaturateCompositeOp
27
+ Color = Magick::ColorizeCompositeOp
28
+ Luminosity = Magick::LuminizeCompositeOp
29
+
30
+ class Layer
31
+ attr_accessor :image
32
+ attr_reader :offset_x, :offset_y
33
+
34
+ def initialize(options = {})
35
+ if options[:file]
36
+ @image = Magick::Image.read(options[:file]).first
37
+ @image.background_color = 'transparent'
38
+ elsif options[:blob]
39
+ @image = Magick::Image.from_blob(options[:blob]).first
40
+ @image.background_color = 'transparent'
41
+ elsif options[:image]
42
+ if options[:image].is_a? Magick::Image
43
+ @image = options[:image]
44
+ end
45
+ end
46
+
47
+ if @image
48
+ @image.matte = true
49
+ @mode = Normal
50
+ @offset_x = 0
51
+ @offset_y = 0
52
+ @opacity_percent = 100
53
+ @layer_mask = nil
54
+ else
55
+ raise "Layer not created -- no layer source."
56
+ end
57
+ end
58
+
59
+ def layer_mask(options, &block)
60
+ @layer_mask = LayerMask.new options
61
+
62
+ @layer_mask.instance_eval &block if block_given?
63
+ end
64
+
65
+ def merge_down(image)
66
+ @layer_mask.apply self if @layer_mask
67
+ image.composite!(@image, @offset_x, @offset_y, @mode)
68
+ end
69
+
70
+ def width
71
+ @image.columns
72
+ end
73
+
74
+ def height
75
+ @image.rows
76
+ end
77
+
78
+ def offset(x, y)
79
+ @offset_x = x
80
+ @offset_y = y
81
+ end
82
+
83
+ def rotate(degrees)
84
+ @image.rotate!(degrees)
85
+ end
86
+
87
+ def opacity(percent)
88
+ @opacity_percent = percent
89
+ # intercept original alpha channel with pixel intensity
90
+ alpha_channel = @image.channel(Magick::AlphaChannel).negate
91
+ intensity = (Magick::MaxRGB * (percent/100.0)).round
92
+ alpha_channel.composite!(Magick::Image.new(width, height) { self.background_color = Magick::Pixel.new(intensity,intensity,intensity) }, Magick::CenterGravity, Multiply)
93
+ alpha_channel.matte = false
94
+
95
+ @image.composite!(alpha_channel, Magick::CenterGravity, Magick::CopyOpacityCompositeOp)
96
+ return true
97
+ end
98
+
99
+
100
+ def mode(mode = nil)
101
+ return @mode if mode.nil?
102
+ @mode = mode
103
+ end
104
+
105
+ def save_as(filename)
106
+ @image.write(filename)
107
+ end
108
+
109
+ def join_set(set)
110
+ set.add_layer(self)
111
+ end
112
+
113
+ def leave_set(set)
114
+ set.remove_layer(self)
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,16 @@
1
+ module RComposite
2
+ class LayerMask < Layer
3
+ def apply(layer)
4
+ # apply layer mask to layer before merging down
5
+ fill = Magick::GradientFill.new(0,0,0,0, '#000', '#000')
6
+ mask = Magick::Image.new(layer.width, layer.height, fill)
7
+ mask.composite!(@image, @offset_x, @offset_y, Magick::OverCompositeOp)
8
+
9
+ alpha_channel = layer.image.channel(Magick::AlphaChannel).negate
10
+ alpha_channel.composite!(mask, 0, 0, Magick::MultiplyCompositeOp)
11
+ alpha_channel.matte = false
12
+ layer.image.composite!(alpha_channel, 0, 0, Magick::CopyOpacityCompositeOp)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,196 @@
1
+ module RComposite
2
+ class LayerSet
3
+ attr_accessor :layers
4
+ attr_reader :min_x, :min_y, :max_x, :max_y, :bounding_width, :bounding_height, :name
5
+
6
+ def initialize(name, &block)
7
+ @layers = []
8
+
9
+ bounding_box
10
+ @name = name
11
+
12
+ self.instance_eval &block if block_given?
13
+ end
14
+
15
+ def stack(&block)
16
+ self.instance_eval &block if block_given?
17
+ end
18
+
19
+ def layer_set(ref, position = :bottom, &block)
20
+ if ref.is_a? RComposite::LayerSet
21
+ # referencing a previously instanstiated LayerSet.
22
+ set = ref
23
+ else
24
+ # creating a new LayerSet
25
+ set = LayerSet.new ref
26
+ end
27
+
28
+ # Add layer set to render pipeline
29
+ if position == :bottom
30
+ @layers << set
31
+ elsif position == :top
32
+ @layers.unshift set
33
+ end
34
+
35
+ # tie floating block methods (layer, rotate, offset, etc) to LayerSet object.
36
+ set.instance_eval &block if block_given?
37
+
38
+ return set
39
+ end
40
+
41
+ def layer(options, &block)
42
+ if options.is_a? RComposite::Layer
43
+ layer = options
44
+ else
45
+ layer = Layer.new options
46
+ end
47
+
48
+ # Add layer to render pipeline
49
+ @layers << layer
50
+
51
+ # tie floating block methods (opacity, mode, offset, etc) to Layer object.
52
+ layer.instance_eval &block if block_given?
53
+
54
+ return layer
55
+ end
56
+
57
+ def fill_layer(type, *args, &block)
58
+ layer = FillLayer.new type, *args
59
+ @layers << layer
60
+
61
+ # tie floating block methods (opacity, mode, offset, etc) to Layer object.
62
+ layer.instance_eval &block if block_given?
63
+
64
+ return layer
65
+ end
66
+
67
+ def adjustment_layer(type, *args, &block)
68
+ layer = AdjustmentLayer.new type, *args
69
+ @layers << layer
70
+
71
+ # tie floating block methods (opacity, mode, offset, etc) to Layer object.
72
+ layer.instance_eval &block if block_given?
73
+
74
+ return layer
75
+ end
76
+
77
+ def flatten
78
+ flattened_image = render
79
+
80
+ # clear layers and sets
81
+ @layers.clear
82
+
83
+ # only 1 flattened layer now
84
+ @layers << Layer.new(flattened_image)
85
+ end
86
+
87
+ def render
88
+ return composite_layers(@image.clone, @layers)
89
+ end
90
+
91
+ def composite_layers(image, layers)
92
+ layers.reverse.each do |layer|
93
+ case layer
94
+ when RComposite::LayerSet
95
+ image = composite_layers image, layer.layers
96
+ when RComposite::Layer
97
+ layer.merge_down image
98
+ end
99
+ end
100
+
101
+ return image
102
+ end
103
+
104
+ def save_as(filename)
105
+ render.write(filename)
106
+ end
107
+
108
+ ################################################################################################################################
109
+
110
+ def add_layer(layer)
111
+ @layers << layer
112
+ bounding_box
113
+ end
114
+
115
+ def remove_layer(layer)
116
+ @layers.delete(layer)
117
+ bounding_box
118
+ end
119
+
120
+ def bounding_box
121
+ if @layers.size > 0
122
+ x1 = []
123
+ y1 = []
124
+ x2 = []
125
+ y2 = []
126
+ @layers.each do |layer|
127
+ x1 << layer.offset_x
128
+ y1 << layer.offset_y
129
+ x2 << layer.width + layer.offset_x
130
+ y2 << layer.height + layer.offset_y
131
+ end
132
+
133
+ @min_x = x1.min
134
+ @min_y = y1.min
135
+ @max_x = x2.max
136
+ @max_y = y2.max
137
+ @bounding_width = @max_x - @min_x
138
+ @bounding_height = @max_y - @min_y
139
+ else
140
+ @min_x = 0
141
+ @min_y = 0
142
+ @max_x = 0
143
+ @max_y = 0
144
+ @bounding_width = 0
145
+ @boudning_height = 0
146
+ end
147
+ end
148
+
149
+ def offset(x, y)
150
+ bounding_box # recalculate bounding box
151
+ shift_x = x - @min_x
152
+ shift_y = y - @min_y
153
+ @layers.each do |layer|
154
+ layer.offset(layer.offset_x + shift_x, layer.offset_y + shift_y)
155
+ end
156
+ bounding_box
157
+ end
158
+
159
+ def scale(width, height)
160
+ bounding_box # recalculate bounding box
161
+ width_scale = width.to_f / bounding_width.to_f
162
+ height_scale = height.to_f / bounding_height.to_f
163
+ @layers.each do |layer|
164
+ layer.image.scale!((layer.width * width_scale).round, (layer.height * height_scale).round)
165
+ layer.offset(((layer.offset_x - @min_x) * width_scale).round + @min_x, ((layer.offset_y - @min_y) * height_scale).round + @min_y)
166
+ end
167
+ bounding_box
168
+ end
169
+
170
+ def rotate(degrees)
171
+ bounding_box # recalculate bounding box
172
+ bounding_mid_x = @bounding_width / 2
173
+ bounding_mid_y = @bounding_height / 2
174
+ @layers.each do |layer|
175
+ layer_mid_x = (layer.width/2.0).round + layer.offset_x - @min_x - bounding_mid_x
176
+ layer_mid_y = ((layer.height/2.0).round + layer.offset_y - @min_y - bounding_mid_y) * -1
177
+
178
+ radians = (degrees * -1) / (180 / Math::PI)
179
+
180
+ cos = Math.cos(radians)
181
+ sin = Math.sin(radians)
182
+
183
+ new_mid_x = ((layer_mid_x * cos) - (layer_mid_y * sin)).round
184
+ new_mid_y = ((layer_mid_x * sin) + (layer_mid_y * cos)).round
185
+
186
+ layer.rotate(degrees)
187
+
188
+ new_offset_x = new_mid_x - (layer.width/2.0).round + @min_x + bounding_mid_x
189
+ new_offset_y = new_mid_y * -1 - (layer.height/2.0).round + @min_y + bounding_mid_y
190
+
191
+ layer.offset(new_offset_x, new_offset_y)
192
+ end
193
+ bounding_box
194
+ end
195
+ end
196
+ end
data/lib/rcomposite.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rmagick'
2
+
3
+ require 'rcomposite/layerset'
4
+ require 'rcomposite/layer'
5
+ require 'rcomposite/canvas'
6
+ require 'rcomposite/filllayer'
7
+ require 'rcomposite/adjustmentlayer'
8
+ require 'rcomposite/layermask'
9
+
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rcomposite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Corban Brook
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-13 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rmagick
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.0
24
+ version:
25
+ description: RComposite is an RMagick abstraction library to easily manipulate and composite images through the use of a canvas, layers, layer masks, adjustment layers, fill layers, and layer sets (much like in Photoshop)
26
+ email: corbanbrook@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - MIT-LICENSE
34
+ files:
35
+ - README
36
+ - MIT-LICENSE
37
+ - lib/rcomposite.rb
38
+ - lib/rcomposite/layer.rb
39
+ - lib/rcomposite/layerset.rb
40
+ - lib/rcomposite/adjustmentlayer.rb
41
+ - lib/rcomposite/filllayer.rb
42
+ - lib/rcomposite/canvas.rb
43
+ - lib/rcomposite/layermask.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/corbanbrook/rcomposite
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: An RMagick abstration layer for easy image compositing
72
+ test_files: []
73
+