psd 2.1.2 → 3.1.2
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/psd.rb +8 -6
- data/lib/psd/blend_mode.rb +46 -38
- data/lib/psd/channel_image.rb +9 -5
- data/lib/psd/descriptor.rb +39 -16
- data/lib/psd/header.rb +33 -32
- data/lib/psd/image_formats/rle.rb +4 -10
- data/lib/psd/image_modes/rgb.rb +4 -4
- data/lib/psd/layer.rb +1 -15
- data/lib/psd/layer/blend_modes.rb +12 -12
- data/lib/psd/layer/helpers.rb +8 -10
- data/lib/psd/layer/info.rb +9 -7
- data/lib/psd/layer/position_and_channels.rb +0 -4
- data/lib/psd/layer_info.rb +0 -4
- data/lib/psd/layer_info/blend_clipping_elements.rb +4 -2
- data/lib/psd/layer_info/blend_interior_elements.rb +4 -2
- data/lib/psd/layer_info/fill_opacity.rb +4 -2
- data/lib/psd/layer_info/layer_group.rb +4 -2
- data/lib/psd/layer_info/layer_id.rb +4 -2
- data/lib/psd/layer_info/layer_name_source.rb +4 -2
- data/lib/psd/layer_info/layer_section_divider.rb +4 -2
- data/lib/psd/layer_info/legacy_typetool.rb +5 -3
- data/lib/psd/layer_info/locked.rb +4 -2
- data/lib/psd/layer_info/metadata_setting.rb +4 -2
- data/lib/psd/layer_info/object_effects.rb +4 -2
- data/lib/psd/layer_info/pattern.rb +14 -0
- data/lib/psd/layer_info/placed_layer.rb +4 -2
- data/lib/psd/layer_info/reference_point.rb +4 -2
- data/lib/psd/layer_info/sheet_color.rb +18 -0
- data/lib/psd/layer_info/solid_color.rb +36 -0
- data/lib/psd/layer_info/typetool.rb +4 -2
- data/lib/psd/layer_info/unicode_name.rb +4 -2
- data/lib/psd/layer_info/vector_mask.rb +4 -2
- data/lib/psd/layer_info/vector_origination.rb +14 -0
- data/lib/psd/layer_info/vector_stroke.rb +4 -2
- data/lib/psd/layer_info/vector_stroke_content.rb +4 -2
- data/lib/psd/layer_mask.rb +2 -8
- data/lib/psd/lazy_execute.rb +5 -1
- data/lib/psd/node.rb +112 -48
- data/lib/psd/nodes/ancestry.rb +80 -75
- data/lib/psd/nodes/build_preview.rb +4 -4
- data/lib/psd/nodes/group.rb +35 -0
- data/lib/psd/nodes/layer.rb +40 -0
- data/lib/psd/nodes/root.rb +90 -0
- data/lib/psd/nodes/search.rb +19 -19
- data/lib/psd/path_record.rb +1 -71
- data/lib/psd/renderer.rb +6 -5
- data/lib/psd/renderer/blender.rb +10 -5
- data/lib/psd/renderer/cairo_helpers.rb +46 -0
- data/lib/psd/renderer/canvas.rb +39 -19
- data/lib/psd/renderer/canvas_management.rb +2 -2
- data/lib/psd/renderer/clipping_mask.rb +5 -4
- data/lib/psd/renderer/compose.rb +61 -68
- data/lib/psd/renderer/layer_styles.rb +15 -5
- data/lib/psd/renderer/layer_styles/color_overlay.rb +46 -27
- data/lib/psd/renderer/mask.rb +26 -22
- data/lib/psd/renderer/mask_canvas.rb +12 -0
- data/lib/psd/renderer/vector_shape.rb +239 -0
- data/lib/psd/resource_section.rb +4 -7
- data/lib/psd/resources.rb +4 -19
- data/lib/psd/resources/base.rb +27 -0
- data/lib/psd/resources/guides.rb +6 -4
- data/lib/psd/resources/layer_comps.rb +6 -4
- data/lib/psd/resources/slices.rb +7 -5
- data/lib/psd/version.rb +1 -1
- data/psd.gemspec +1 -2
- data/spec/files/blendmodes.psd +0 -0
- data/spec/hierarchy_spec.rb +5 -0
- metadata +27 -26
- data/lib/psd/layer_info/vector_mask_2.rb +0 -10
- data/lib/psd/node_exporting.rb +0 -20
- data/lib/psd/node_group.rb +0 -86
- data/lib/psd/node_layer.rb +0 -81
- data/lib/psd/node_root.rb +0 -93
- data/lib/psd/nodes/has_children.rb +0 -13
- data/lib/psd/nodes/lock_to_origin.rb +0 -7
- data/lib/psd/nodes/parse_layers.rb +0 -18
- data/lib/psd/renderer/layer_styles/drop_shadow.rb +0 -75
- data/lib/psd/section.rb +0 -26
@@ -1,12 +1,12 @@
|
|
1
1
|
class PSD
|
2
|
-
|
2
|
+
module Node
|
3
3
|
module BuildPreview
|
4
|
-
def renderer
|
5
|
-
PSD::Renderer.new(self)
|
4
|
+
def renderer(opts = {})
|
5
|
+
PSD::Renderer.new(self, opts)
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_png
|
9
|
-
renderer.to_png
|
9
|
+
@png ||= renderer.to_png
|
10
10
|
end
|
11
11
|
|
12
12
|
def save_as_png(output)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'psd/node'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
module Node
|
5
|
+
# Represents a group, or folder, in the PSD document. It can have
|
6
|
+
# zero or more children nodes.
|
7
|
+
class Group < PSD::Node::Base
|
8
|
+
def passthru_blending?
|
9
|
+
blending_mode == 'passthru'
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
children.each do |child|
|
14
|
+
return false unless child.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
|
20
|
+
# Export this layer and it's children to a hash recursively.
|
21
|
+
def to_hash
|
22
|
+
super.merge({
|
23
|
+
type: :group,
|
24
|
+
children: children.map(&:to_hash)
|
25
|
+
})
|
26
|
+
end
|
27
|
+
|
28
|
+
# If the method is missing, we blindly send it to the layer.
|
29
|
+
# The layer handles the case in which the method doesn't exist.
|
30
|
+
def method_missing(method, *args, &block)
|
31
|
+
@layer.send(method, *args, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'psd/node'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
module Node
|
5
|
+
class Layer < PSD::Node::Base
|
6
|
+
attr_reader :layer
|
7
|
+
|
8
|
+
[:text, :ref_x, :ref_y, :blending_mode].each do |prop|
|
9
|
+
delegate prop, to: :@layer
|
10
|
+
delegate "#{prop}=", to: :@layer
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
width == 0 || height == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
# Exports this layer to a Hash.
|
18
|
+
def to_hash
|
19
|
+
super.merge({
|
20
|
+
type: :layer,
|
21
|
+
text: @layer.text,
|
22
|
+
ref_x: @layer.reference_point.x,
|
23
|
+
ref_y: @layer.reference_point.y,
|
24
|
+
mask: @layer.mask.to_hash,
|
25
|
+
image: {
|
26
|
+
width: @layer.image.width,
|
27
|
+
height: @layer.image.height,
|
28
|
+
channels: @layer.channels_info
|
29
|
+
}
|
30
|
+
})
|
31
|
+
end
|
32
|
+
|
33
|
+
# If the method is missing, we blindly send it to the layer.
|
34
|
+
# The layer handles the case in which the method doesn't exist.
|
35
|
+
def method_missing(method, *args, &block)
|
36
|
+
@layer.send(method, *args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'psd/node'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
module Node
|
5
|
+
# Represents the root node of a Photoshop document
|
6
|
+
class Root < PSD::Node::Base
|
7
|
+
include Ancestry
|
8
|
+
include Search
|
9
|
+
include BuildPreview
|
10
|
+
|
11
|
+
attr_accessor :children
|
12
|
+
attr_reader :psd
|
13
|
+
|
14
|
+
alias_method :document_width, :width
|
15
|
+
alias_method :document_height, :height
|
16
|
+
|
17
|
+
RootLayer = Struct.new("RootLayer", :node, *Base::PROPERTIES)
|
18
|
+
|
19
|
+
def self.layer_for_psd(psd)
|
20
|
+
RootLayer.new.tap do |layer|
|
21
|
+
layer.top = 0
|
22
|
+
layer.left = 0
|
23
|
+
layer.right = psd.header.width.to_i
|
24
|
+
layer.bottom = psd.header.height.to_i
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Stores a reference to the parsed PSD and builds the
|
29
|
+
# tree hierarchy.
|
30
|
+
def initialize(psd)
|
31
|
+
super self.class.layer_for_psd(psd)
|
32
|
+
|
33
|
+
@psd = psd
|
34
|
+
build_hierarchy
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the width and height of the entire PSD document.
|
38
|
+
def document_dimensions
|
39
|
+
[document_width, document_height]
|
40
|
+
end
|
41
|
+
|
42
|
+
# The depth of the root node is always 0.
|
43
|
+
def depth
|
44
|
+
0
|
45
|
+
end
|
46
|
+
|
47
|
+
def opacity; 255; end
|
48
|
+
def fill_opacity; 255; end
|
49
|
+
|
50
|
+
# Recursively exports the hierarchy to a Hash
|
51
|
+
def to_hash
|
52
|
+
{
|
53
|
+
children: children.map(&:to_hash),
|
54
|
+
document: {
|
55
|
+
width: document_width,
|
56
|
+
height: document_height,
|
57
|
+
resources: {
|
58
|
+
layer_comps: @psd.layer_comps,
|
59
|
+
guides: @psd.guides,
|
60
|
+
slices: @psd.slices
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def build_hierarchy
|
69
|
+
current_group = self
|
70
|
+
parse_stack = []
|
71
|
+
|
72
|
+
# First we build the hierarchy
|
73
|
+
@psd.layers.each do |layer|
|
74
|
+
if layer.folder?
|
75
|
+
parse_stack.push current_group
|
76
|
+
current_group = PSD::Node::Group.new(layer, parse_stack.last)
|
77
|
+
elsif layer.folder_end?
|
78
|
+
parent = parse_stack.pop
|
79
|
+
parent.children.push current_group
|
80
|
+
current_group = parent
|
81
|
+
else
|
82
|
+
current_group.children.push PSD::Node::Layer.new(layer, current_group)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
update_dimensions!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/psd/nodes/search.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class PSD
|
2
|
-
|
2
|
+
module Node
|
3
3
|
module Search
|
4
4
|
# Searches the tree structure for a node at the given path. The path is
|
5
5
|
# defined by the layer/folder names. Because the PSD format does not
|
@@ -8,6 +8,7 @@ class PSD
|
|
8
8
|
def children_at_path(path, opts={})
|
9
9
|
path = path.split('/').delete_if { |p| p == "" } unless path.is_a?(Array)
|
10
10
|
|
11
|
+
path = path.dup
|
11
12
|
query = path.shift
|
12
13
|
matches = children.select do |c|
|
13
14
|
if opts[:case_sensitive]
|
@@ -20,7 +21,7 @@ class PSD
|
|
20
21
|
if path.length == 0
|
21
22
|
return matches
|
22
23
|
else
|
23
|
-
return matches.map { |m| m.children_at_path(path, opts) }.flatten
|
24
|
+
return matches.map { |m| m.children_at_path(path.dup, opts) }.flatten
|
24
25
|
end
|
25
26
|
end
|
26
27
|
alias :children_with_path :children_at_path
|
@@ -39,34 +40,32 @@ class PSD
|
|
39
40
|
end
|
40
41
|
|
41
42
|
root = PSD::Node::Root.new(psd)
|
42
|
-
|
43
|
-
|
44
|
-
return root
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def filter_for_comp!(comp, node)
|
43
|
+
|
50
44
|
# Force layers to be visible if they are enabled for the comp
|
51
|
-
|
45
|
+
root.descendants.each do |c|
|
52
46
|
set_visibility(comp, c) if Resource::Section::LayerComps.visibility_captured?(comp)
|
53
47
|
set_position(comp, c) if Resource::Section::LayerComps.position_captured?(comp)
|
54
48
|
|
55
|
-
|
49
|
+
PSD.logger.debug "#{c.path}: visible = #{c.visible?}, position = #{c.left}, #{c.top}"
|
56
50
|
end
|
51
|
+
|
52
|
+
return root
|
57
53
|
end
|
58
54
|
|
55
|
+
private
|
56
|
+
|
59
57
|
def set_visibility(comp, c)
|
60
58
|
visible = true
|
59
|
+
found = false
|
61
60
|
|
62
61
|
c
|
63
62
|
.metadata
|
64
63
|
.data[:layer_comp]['layerSettings'].each do |l|
|
65
64
|
visible = l['enab'] if l.has_key?('enab')
|
66
|
-
break if l['compList'].include?(comp[:id])
|
65
|
+
found = true and break if l['compList'].include?(comp[:id])
|
67
66
|
end
|
68
67
|
|
69
|
-
c.force_visible = visible
|
68
|
+
c.force_visible = found && visible
|
70
69
|
end
|
71
70
|
|
72
71
|
def set_position(comp, c)
|
@@ -76,15 +75,16 @@ class PSD
|
|
76
75
|
c
|
77
76
|
.metadata
|
78
77
|
.data[:layer_comp]['layerSettings'].each do |l|
|
79
|
-
|
78
|
+
if l.has_key?('Ofst')
|
79
|
+
x = l['Ofst']['Hrzn']
|
80
|
+
y = l['Ofst']['Vrtc']
|
81
|
+
end
|
80
82
|
|
81
|
-
x = l['Ofst']['Hrzn']
|
82
|
-
y = l['Ofst']['Vrtc']
|
83
83
|
break if l['compList'].include?(comp[:id])
|
84
84
|
end
|
85
85
|
|
86
|
-
c.
|
87
|
-
c.
|
86
|
+
c.left_offset = x
|
87
|
+
c.top_offset = y
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
data/lib/psd/path_record.rb
CHANGED
@@ -25,22 +25,6 @@ class PSD
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
# Writes out the path to file.
|
29
|
-
def write(outfile)
|
30
|
-
outfile.write_short @record_type
|
31
|
-
case @record_type
|
32
|
-
when 0 then write_path_record(outfile)
|
33
|
-
when 3 then write_path_record(outfile)
|
34
|
-
when 1 then write_bezier_point(outfile)
|
35
|
-
when 2 then write_bezier_point(outfile)
|
36
|
-
when 4 then write_bezier_point(outfile)
|
37
|
-
when 5 then write_bezier_point(outfile)
|
38
|
-
when 7 then write_clipboard_record(outfile)
|
39
|
-
when 8 then write_initial_fill(outfile)
|
40
|
-
else outfile.seek(24, IO::SEEK_CUR)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
28
|
# Exports the path record to an easier to work with hash.
|
45
29
|
def to_hash
|
46
30
|
case @record_type
|
@@ -51,6 +35,7 @@ class PSD
|
|
51
35
|
when 1, 2, 4, 5
|
52
36
|
{
|
53
37
|
linked: @linked,
|
38
|
+
closed: [1, 2].include?(@record_type),
|
54
39
|
preceding: {
|
55
40
|
vert: @preceding_vert,
|
56
41
|
horiz: @preceding_horiz
|
@@ -83,34 +68,6 @@ class PSD
|
|
83
68
|
end.merge({ record_type: @record_type })
|
84
69
|
end
|
85
70
|
|
86
|
-
# Attempts to translate the path
|
87
|
-
def translate(x=0, y=0)
|
88
|
-
return unless is_bezier_point?
|
89
|
-
|
90
|
-
document_width, document_height = @layer.document_dimensions
|
91
|
-
translate_x_ratio = x.to_f / document_width.to_f
|
92
|
-
translate_y_ratio = y.to_f / document_height.to_f
|
93
|
-
|
94
|
-
@preceding_vert += translate_y_ratio
|
95
|
-
@preceding_horiz += translate_x_ratio
|
96
|
-
@anchor_vert += translate_y_ratio
|
97
|
-
@anchor_horiz += translate_x_ratio
|
98
|
-
@leaving_vert += translate_y_ratio
|
99
|
-
@leaving_horiz += translate_x_ratio
|
100
|
-
end
|
101
|
-
|
102
|
-
# Attempts to scale the path
|
103
|
-
def scale(xr, yr)
|
104
|
-
return unless is_bezier_point?
|
105
|
-
|
106
|
-
@preceding_vert *= yr
|
107
|
-
@preceding_horiz *= xr
|
108
|
-
@anchor_vert *= yr
|
109
|
-
@anchor_horiz *= xr
|
110
|
-
@leaving_vert *= yr
|
111
|
-
@leaving_horiz *= xr
|
112
|
-
end
|
113
|
-
|
114
71
|
# Is this record a bezier point?
|
115
72
|
def is_bezier_point?
|
116
73
|
[1,2,4,5].include? @record_type
|
@@ -123,11 +80,6 @@ class PSD
|
|
123
80
|
@file.seek(22, IO::SEEK_CUR)
|
124
81
|
end
|
125
82
|
|
126
|
-
def write_path_record(file)
|
127
|
-
file.write_short @num_points
|
128
|
-
file.seek(22, IO::SEEK_CUR)
|
129
|
-
end
|
130
|
-
|
131
83
|
def read_bezier_point
|
132
84
|
@linked = [1,4].include? @record_type
|
133
85
|
|
@@ -141,15 +93,6 @@ class PSD
|
|
141
93
|
@leaving_horiz = @file.read_path_number
|
142
94
|
end
|
143
95
|
|
144
|
-
def write_bezier_point(outfile)
|
145
|
-
outfile.write_path_number @preceding_vert
|
146
|
-
outfile.write_path_number @preceding_horiz
|
147
|
-
outfile.write_path_number @anchor_vert
|
148
|
-
outfile.write_path_number @anchor_horiz
|
149
|
-
outfile.write_path_number @leaving_vert
|
150
|
-
outfile.write_path_number @leaving_horiz
|
151
|
-
end
|
152
|
-
|
153
96
|
def read_clipboard_record
|
154
97
|
@clipboard_top = @file.read_path_number
|
155
98
|
@clipboard_left = @file.read_path_number
|
@@ -159,22 +102,9 @@ class PSD
|
|
159
102
|
@file.seek(4, IO::SEEK_CUR)
|
160
103
|
end
|
161
104
|
|
162
|
-
def write_clipboard_record(file)
|
163
|
-
[@clipboard_top, @clipboard_left, @clipboard_bottom,
|
164
|
-
@clipboard_right, @clipboard_resolution].each do |point|
|
165
|
-
file.write_path_number point
|
166
|
-
end
|
167
|
-
file.seek(4, IO::SEEK_CUR)
|
168
|
-
end
|
169
|
-
|
170
105
|
def read_initial_fill
|
171
106
|
@initial_fill = @file.read_short
|
172
107
|
@file.seek(22, IO::SEEK_CUR)
|
173
108
|
end
|
174
|
-
|
175
|
-
def write_initial_fill(file)
|
176
|
-
file.write_short @initial_fill
|
177
|
-
file.seek(22, IO::SEEK_CUR)
|
178
|
-
end
|
179
109
|
end
|
180
110
|
end
|
data/lib/psd/renderer.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
|
1
|
+
require 'psd/renderer/canvas_management'
|
2
2
|
|
3
3
|
class PSD
|
4
4
|
class Renderer
|
5
5
|
include CanvasManagement
|
6
6
|
|
7
|
-
def initialize(node)
|
7
|
+
def initialize(node, opts = {})
|
8
8
|
@root_node = node
|
9
|
+
@opts = opts
|
9
10
|
|
10
11
|
# Our canvas always starts as the full document size because
|
11
12
|
# all measurements are relative to this size. We can later crop
|
@@ -23,7 +24,7 @@ class PSD
|
|
23
24
|
PSD.logger.debug "Beginning render process"
|
24
25
|
|
25
26
|
# Create our base canvas
|
26
|
-
create_group_canvas(active_node, active_node.width, active_node.height)
|
27
|
+
create_group_canvas(active_node, active_node.width, active_node.height, base: true)
|
27
28
|
|
28
29
|
# Begin the rendering process
|
29
30
|
execute_pipeline
|
@@ -34,7 +35,7 @@ class PSD
|
|
34
35
|
def execute_pipeline
|
35
36
|
PSD.logger.debug "Executing pipeline on #{active_node.debug_name}"
|
36
37
|
children.reverse.each do |child|
|
37
|
-
# We skip over hidden nodes.
|
38
|
+
# We skip over hidden nodes.
|
38
39
|
next unless child.visible?
|
39
40
|
|
40
41
|
if child.group?
|
@@ -56,7 +57,7 @@ class PSD
|
|
56
57
|
pop_node and next
|
57
58
|
end
|
58
59
|
|
59
|
-
canvas = Canvas.new(child)
|
60
|
+
canvas = Canvas.new(child, nil, nil, @opts)
|
60
61
|
canvas.paint_to active_canvas
|
61
62
|
end
|
62
63
|
end
|
data/lib/psd/renderer/blender.rb
CHANGED
@@ -16,7 +16,8 @@ class PSD
|
|
16
16
|
# Composes the foreground Canvas onto the background Canvas using the
|
17
17
|
# blending mode specified by the foreground.
|
18
18
|
def compose!
|
19
|
-
PSD.logger.debug "
|
19
|
+
PSD.logger.debug "#{fg.node.debug_name} -> #{bg.node.debug_name}: #{fg.node.blending_mode} blending"
|
20
|
+
PSD.logger.debug "fg: (#{fg.left}, #{fg.top}) #{fg.width}x#{fg.height}; bg: (#{bg.left}, #{bg.top}) #{bg.width}x#{bg.height}"
|
20
21
|
|
21
22
|
offset_x = fg.left - bg.left
|
22
23
|
offset_y = fg.top - bg.top
|
@@ -30,12 +31,12 @@ class PSD
|
|
30
31
|
|
31
32
|
color = Compose.send(
|
32
33
|
fg.node.blending_mode,
|
33
|
-
fg.
|
34
|
-
bg.
|
35
|
-
|
34
|
+
fg.get_pixel(x, y),
|
35
|
+
bg.get_pixel(base_x, base_y),
|
36
|
+
calculated_opacity
|
36
37
|
)
|
37
38
|
|
38
|
-
bg.
|
39
|
+
bg.set_pixel base_x, base_y, color
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -48,6 +49,10 @@ class PSD
|
|
48
49
|
fill_opacity: @fill_opacity
|
49
50
|
}
|
50
51
|
end
|
52
|
+
|
53
|
+
def calculated_opacity
|
54
|
+
@calculated_opacity ||= compose_options[:opacity] * compose_options[:fill_opacity] / 255
|
55
|
+
end
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|