psd 2.1.2 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|