rubyshop 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rubyshop.rb +2 -31
- data/lib/rubyshop/adjustmentlayer.rb +44 -0
- data/lib/rubyshop/canvas.rb +5 -67
- data/lib/rubyshop/filllayer.rb +40 -0
- data/lib/rubyshop/layer.rb +65 -10
- data/lib/rubyshop/layermask.rb +16 -0
- data/lib/rubyshop/layerset.rb +91 -5
- data/lib/rubyshop/rubyshop.rb +7 -0
- metadata +4 -2
data/lib/rubyshop.rb
CHANGED
@@ -1,37 +1,8 @@
|
|
1
1
|
require 'rmagick'
|
2
2
|
|
3
|
-
require 'rubyshop/canvas'
|
4
3
|
require 'rubyshop/layerset'
|
5
4
|
require 'rubyshop/layer'
|
5
|
+
require 'rubyshop/canvas'
|
6
6
|
require 'rubyshop/filllayer'
|
7
7
|
require 'rubyshop/adjustmentlayer'
|
8
|
-
|
9
|
-
module Rubyshop
|
10
|
-
# Layer mode constants
|
11
|
-
# some of photoshops layer modes are directly equvilent to RMagick
|
12
|
-
# some are not, and some arent supported at all.
|
13
|
-
|
14
|
-
Normal = Magick::OverCompositeOp # PS equivilent
|
15
|
-
Dissolve = Magick::DissolveCompositeOp
|
16
|
-
Darken = Magick::DarkenCompositeOp # PS equivilent
|
17
|
-
Multiply = Magick::MultiplyCompositeOp # PS equivilent
|
18
|
-
#ColorBurn
|
19
|
-
#LinearBurn
|
20
|
-
Lighten = Magick::LightenCompositeOp # PS equivilent
|
21
|
-
Screen = Magick::ScreenCompositeOp # PS equivilent
|
22
|
-
#ColorDodge
|
23
|
-
#LinearDodge
|
24
|
-
Overlay = Magick::OverlayCompositeOp # PS equivilent
|
25
|
-
#SoftLight
|
26
|
-
#HardLight
|
27
|
-
#VividLight
|
28
|
-
#LinearLight
|
29
|
-
#PinLight
|
30
|
-
#HardMix
|
31
|
-
Difference = Magick::DifferenceCompositeOp # PS equivilent
|
32
|
-
#Exclusion
|
33
|
-
Hue = Magick::HueCompositeOp
|
34
|
-
Saturation = Magick::SaturateCompositeOp
|
35
|
-
Color = Magick::ColorizeCompositeOp
|
36
|
-
Luminosity = Magick::LuminizeCompositeOp
|
37
|
-
end
|
8
|
+
require 'rubyshop/layermask'
|
@@ -1,4 +1,48 @@
|
|
1
1
|
module Rubyshop
|
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
|
+
|
2
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
|
3
47
|
end
|
4
48
|
end
|
data/lib/rubyshop/canvas.rb
CHANGED
@@ -1,78 +1,16 @@
|
|
1
1
|
module Rubyshop
|
2
|
-
class Canvas
|
2
|
+
class Canvas < LayerSet
|
3
3
|
attr_reader :width, :height
|
4
|
+
attr_accessor :image
|
4
5
|
|
5
6
|
def initialize(width, height, &block)
|
6
7
|
@image = Magick::Image.new width, height
|
7
8
|
@width = width
|
8
9
|
@height = height
|
9
|
-
|
10
|
-
|
10
|
+
$RubyshopCanvasWidth = @width
|
11
|
+
$RubyshopCanvasHeight = @height
|
11
12
|
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def layer_set(name, &block)
|
16
|
-
if name.is_a? Rubyshop::LayerSet
|
17
|
-
set = name
|
18
|
-
@layers << set
|
19
|
-
@sets[set.name] = set
|
20
|
-
elsif @sets[name]
|
21
|
-
set = @sets[name]
|
22
|
-
else
|
23
|
-
set = LayerSet.new name
|
24
|
-
@layers << set
|
25
|
-
@sets[name] = set
|
26
|
-
end
|
27
|
-
|
28
|
-
set.instance_eval &block if block_given?
|
29
|
-
|
30
|
-
return set
|
31
|
-
end
|
32
|
-
|
33
|
-
def layer(filename, &block)
|
34
|
-
layer = Layer.new filename
|
35
|
-
@layers << layer
|
36
|
-
|
37
|
-
layer.instace_eval &block if block_given?
|
38
|
-
|
39
|
-
return layer
|
40
|
-
end
|
41
|
-
|
42
|
-
def flatten
|
43
|
-
layers = @layers
|
44
|
-
layers.reverse.each do |layer|
|
45
|
-
if layer.is_a? Rubyshop::Layer
|
46
|
-
@image.composite!(layer.image, layer.offset_x, layer.offset_y, layer.mode)
|
47
|
-
elsif layer.is_a? Rubyshop::LayerSet
|
48
|
-
layer.layers.reverse.each do |set_layer|
|
49
|
-
image.composite!(set_layer.image, set_layer.offset_x, set_layer.offset_y, set_layer.mode)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
@layers.delete(layer)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def render
|
57
|
-
image = @image
|
58
|
-
@layers.reverse.each do |layer|
|
59
|
-
if layer.is_a? Rubyshop::Layer
|
60
|
-
image.composite!(layer.image, layer.offset_x, layer.offset_y, layer.mode)
|
61
|
-
elsif layer.is_a? Rubyshop::LayerSet
|
62
|
-
layer.layers.reverse.each do |set_layer|
|
63
|
-
image.composite!(set_layer.image, set_layer.offset_x, set_layer.offset_y, set_layer.mode)
|
64
|
-
end
|
65
|
-
elsif layer.is_a? Rubyshop::AdjustmentLayer
|
66
|
-
# nothing to do
|
67
|
-
elsif layer.is_a? Rubyshop::FillLayer
|
68
|
-
# nother to do
|
69
|
-
end
|
70
|
-
end
|
71
|
-
return image
|
72
|
-
end
|
73
|
-
|
74
|
-
def save_as(filename)
|
75
|
-
render.write(filename)
|
13
|
+
super(:canvas, &block)
|
76
14
|
end
|
77
15
|
end
|
78
16
|
end
|
data/lib/rubyshop/filllayer.rb
CHANGED
@@ -1,4 +1,44 @@
|
|
1
1
|
module Rubyshop
|
2
|
+
# Fill layer types
|
3
|
+
|
4
|
+
SolidColor = 0
|
5
|
+
Gradient = 1
|
6
|
+
Pattern = 2
|
7
|
+
|
2
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($RubyshopCanvasWidth, $RubyshopCanvasHeight, fill)
|
36
|
+
|
37
|
+
@image.matte = true
|
38
|
+
@mode = Normal
|
39
|
+
@offset_x = 0
|
40
|
+
@offset_y = 0
|
41
|
+
@opacity_percent = 100
|
42
|
+
end
|
3
43
|
end
|
4
44
|
end
|
data/lib/rubyshop/layer.rb
CHANGED
@@ -1,17 +1,70 @@
|
|
1
1
|
module Rubyshop
|
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
|
+
|
2
30
|
class Layer
|
3
|
-
attr_accessor :image
|
31
|
+
attr_accessor :image
|
4
32
|
attr_reader :offset_x, :offset_y
|
5
33
|
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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)
|
15
68
|
end
|
16
69
|
|
17
70
|
def width
|
@@ -39,6 +92,7 @@ module Rubyshop
|
|
39
92
|
return true
|
40
93
|
end
|
41
94
|
|
95
|
+
|
42
96
|
def mode(mode = nil)
|
43
97
|
return @mode if mode.nil?
|
44
98
|
@mode = mode
|
@@ -55,5 +109,6 @@ module Rubyshop
|
|
55
109
|
def leave_set(set)
|
56
110
|
set.remove_layer(self)
|
57
111
|
end
|
112
|
+
|
58
113
|
end
|
59
114
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rubyshop
|
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
|
data/lib/rubyshop/layerset.rb
CHANGED
@@ -3,22 +3,108 @@ module Rubyshop
|
|
3
3
|
attr_accessor :layers
|
4
4
|
attr_reader :min_x, :min_y, :max_x, :max_y, :bounding_width, :bounding_height, :name
|
5
5
|
|
6
|
-
def initialize(name)
|
6
|
+
def initialize(name, &block)
|
7
7
|
@layers = []
|
8
|
+
@layer_sets = {}
|
9
|
+
|
8
10
|
bounding_box
|
9
11
|
@name = name
|
10
|
-
|
12
|
+
|
13
|
+
self.instance_eval &block if block_given?
|
11
14
|
end
|
12
15
|
|
13
|
-
def
|
14
|
-
|
16
|
+
def layer_set(set_or_name, &block)
|
17
|
+
if set_or_name.is_a? Rubyshop::LayerSet
|
18
|
+
# referencing a previously instanstiated LayerSet and adding to Canvas.
|
19
|
+
set = set_or_name
|
20
|
+
@layers << set
|
21
|
+
@layer_sets[set.name] = set
|
22
|
+
|
23
|
+
elsif @layer_sets[set_or_name]
|
24
|
+
# referencing a previously added LayerSet.
|
25
|
+
set = @layer_sets[set_or_name]
|
26
|
+
|
27
|
+
else
|
28
|
+
# creating a new LayerSet and adding to Canvas.
|
29
|
+
set = LayerSet.new set_or_name
|
30
|
+
@layers << set
|
31
|
+
@layer_sets[set_or_name] = set
|
32
|
+
end
|
33
|
+
|
34
|
+
# tie floating block methods (layer, rotate, offset, etc) to LayerSet object.
|
35
|
+
set.instance_eval &block if block_given?
|
36
|
+
|
37
|
+
return set
|
38
|
+
end
|
39
|
+
|
40
|
+
def layer(options, &block)
|
41
|
+
if options.is_a? Rubyshop::Layer
|
42
|
+
layer = options
|
43
|
+
else
|
44
|
+
layer = Layer.new options
|
45
|
+
end
|
46
|
+
@layers << layer
|
47
|
+
|
48
|
+
# tie floating block methods (opacity, mode, offset, etc) to Layer object.
|
49
|
+
layer.instance_eval &block if block_given?
|
50
|
+
|
51
|
+
return layer
|
52
|
+
end
|
53
|
+
|
54
|
+
def fill_layer(type, *args, &block)
|
55
|
+
layer = FillLayer.new type, *args
|
15
56
|
@layers << layer
|
16
57
|
|
17
|
-
|
58
|
+
# tie floating block methods (opacity, mode, offset, etc) to Layer object.
|
59
|
+
layer.instance_eval &block if block_given?
|
18
60
|
|
19
61
|
return layer
|
20
62
|
end
|
21
63
|
|
64
|
+
def adjustment_layer(type, *args, &block)
|
65
|
+
layer = AdjustmentLayer.new type, *args
|
66
|
+
@layers << layer
|
67
|
+
|
68
|
+
# tie floating block methods (opacity, mode, offset, etc) to Layer object.
|
69
|
+
layer.instance_eval &block if block_given?
|
70
|
+
|
71
|
+
return layer
|
72
|
+
end
|
73
|
+
|
74
|
+
def flatten
|
75
|
+
flattened_image = render
|
76
|
+
|
77
|
+
# clear layers and sets
|
78
|
+
@layers.clear
|
79
|
+
@layer_sets.clear
|
80
|
+
|
81
|
+
# only 1 flattened layer now
|
82
|
+
@layers << Layer.new(flattened_image)
|
83
|
+
end
|
84
|
+
|
85
|
+
def render
|
86
|
+
return composite_layers(@image.clone, @layers)
|
87
|
+
end
|
88
|
+
|
89
|
+
def composite_layers(image, layers)
|
90
|
+
layers.reverse.each do |layer|
|
91
|
+
case layer
|
92
|
+
when Rubyshop::LayerSet
|
93
|
+
image = composite_layers image, layer.layers
|
94
|
+
when Rubyshop::Layer
|
95
|
+
layer.merge_down image
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
return image
|
100
|
+
end
|
101
|
+
|
102
|
+
def save_as(filename)
|
103
|
+
render.write(filename)
|
104
|
+
end
|
105
|
+
|
106
|
+
################################################################################################################################
|
107
|
+
|
22
108
|
def add_layer(layer)
|
23
109
|
@layers << layer
|
24
110
|
bounding_box
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rubyshop
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2006-11-
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2006-11-10 00:00:00 -05:00
|
8
8
|
summary: Powerful layer based image compositing made easy as pie.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -34,7 +34,9 @@ files:
|
|
34
34
|
- lib/rubyshop/canvas.rb
|
35
35
|
- lib/rubyshop/filllayer.rb
|
36
36
|
- lib/rubyshop/layer.rb
|
37
|
+
- lib/rubyshop/layermask.rb
|
37
38
|
- lib/rubyshop/layerset.rb
|
39
|
+
- lib/rubyshop/rubyshop.rb
|
38
40
|
- LICENSE
|
39
41
|
test_files: []
|
40
42
|
|