rcomposite 0.3.1

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/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
+