psd 2.1.2 → 3.9.0
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 +5 -5
- data/.travis.yml +2 -0
- data/README.md +60 -20
- data/lib/psd/blend_mode.rb +46 -38
- data/lib/psd/channel_image.rb +11 -5
- data/lib/psd/color.rb +14 -5
- data/lib/psd/descriptor.rb +39 -16
- data/lib/psd/file.rb +9 -6
- data/lib/psd/header.rb +38 -33
- data/lib/psd/helpers.rb +2 -2
- data/lib/psd/image.rb +21 -16
- data/lib/psd/image_formats/layer_rle.rb +2 -2
- data/lib/psd/image_formats/rle.rb +4 -10
- data/lib/psd/image_modes/cmyk.rb +18 -5
- data/lib/psd/image_modes/greyscale.rb +6 -1
- data/lib/psd/image_modes/rgb.rb +17 -5
- data/lib/psd/layer/blend_modes.rb +15 -13
- data/lib/psd/layer/helpers.rb +8 -10
- data/lib/psd/layer/info/black_white.rb +33 -0
- data/lib/psd/{layer_info → layer/info}/blend_clipping_elements.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/blend_interior_elements.rb +4 -2
- data/lib/psd/layer/info/brightness_contrast.rb +21 -0
- data/lib/psd/layer/info/channel_blending_restrictions.rb +27 -0
- data/lib/psd/layer/info/channel_mixer.rb +27 -0
- data/lib/psd/layer/info/color_balance.rb +23 -0
- data/lib/psd/layer/info/color_lookup.rb +14 -0
- data/lib/psd/layer/info/content_generator.rb +42 -0
- data/lib/psd/layer/info/curves.rb +78 -0
- data/lib/psd/layer/info/effects_layer.rb +174 -0
- data/lib/psd/layer/info/exposure.rb +20 -0
- data/lib/psd/{layer_info → layer/info}/fill_opacity.rb +4 -2
- data/lib/psd/layer/info/gradient_fill.rb +14 -0
- data/lib/psd/layer/info/gradient_map.rb +69 -0
- data/lib/psd/layer/info/hue_saturation.rb +41 -0
- data/lib/psd/layer/info/invert.rb +17 -0
- data/lib/psd/layer/info/knockout.rb +22 -0
- data/lib/psd/layer/info/layer_effects.rb +16 -0
- data/lib/psd/{layer_info → layer/info}/layer_group.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/layer_id.rb +4 -2
- data/lib/psd/layer/info/layer_mask_as_global_mask.rb +16 -0
- data/lib/psd/{layer_info → layer/info}/layer_name_source.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/layer_section_divider.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/legacy_typetool.rb +6 -4
- data/lib/psd/layer/info/levels.rb +48 -0
- data/lib/psd/{layer_info → layer/info}/locked.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/metadata_setting.rb +5 -3
- data/lib/psd/{layer_info → layer/info}/object_effects.rb +5 -5
- data/lib/psd/layer/info/pattern.rb +14 -0
- data/lib/psd/layer/info/pattern_fill.rb +15 -0
- data/lib/psd/layer/info/photo_filter.rb +44 -0
- data/lib/psd/{layer_info → layer/info}/placed_layer.rb +4 -2
- data/lib/psd/layer/info/posterize.rb +16 -0
- data/lib/psd/{layer_info → layer/info}/reference_point.rb +4 -2
- data/lib/psd/layer/info/selective_color.rb +32 -0
- data/lib/psd/layer/info/sheet_color.rb +36 -0
- data/lib/psd/layer/info/solid_color.rb +36 -0
- data/lib/psd/layer/info/threshold.rb +16 -0
- data/lib/psd/layer/info/transparency_shapes_layer.rb +16 -0
- data/lib/psd/{layer_info → layer/info}/typetool.rb +27 -13
- data/lib/psd/{layer_info → layer/info}/unicode_name.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/vector_mask.rb +7 -5
- data/lib/psd/layer/info/vector_mask_as_global_mask.rb +16 -0
- data/lib/psd/layer/info/vector_origination.rb +14 -0
- data/lib/psd/{layer_info → layer/info}/vector_stroke.rb +4 -2
- data/lib/psd/{layer_info → layer/info}/vector_stroke_content.rb +4 -2
- data/lib/psd/layer/info/vibrance.rb +22 -0
- data/lib/psd/layer/info.rb +106 -19
- data/lib/psd/layer/position_and_channels.rb +2 -6
- data/lib/psd/layer.rb +13 -16
- data/lib/psd/layer_info.rb +0 -4
- data/lib/psd/layer_mask.rb +61 -50
- data/lib/psd/lazy_execute.rb +5 -1
- data/lib/psd/mask.rb +7 -1
- data/lib/psd/node.rb +142 -49
- data/lib/psd/nodes/ancestry.rb +7 -6
- data/lib/psd/nodes/build_preview.rb +4 -4
- data/lib/psd/nodes/group.rb +36 -0
- data/lib/psd/nodes/layer.rb +45 -0
- data/lib/psd/nodes/layer_comps.rb +93 -0
- data/lib/psd/nodes/locking.rb +36 -0
- data/lib/psd/nodes/root.rb +87 -0
- data/lib/psd/nodes/search.rb +8 -65
- data/lib/psd/path_record.rb +5 -70
- data/lib/psd/renderer/blender.rb +10 -5
- data/lib/psd/renderer/cairo_helpers.rb +46 -0
- data/lib/psd/renderer/canvas.rb +41 -20
- data/lib/psd/renderer/canvas_management.rb +2 -2
- data/lib/psd/renderer/clipping_mask.rb +5 -4
- data/lib/psd/renderer/compose.rb +59 -69
- data/lib/psd/renderer/layer_styles/color_overlay.rb +45 -27
- data/lib/psd/renderer/layer_styles.rb +15 -5
- 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/renderer.rb +18 -7
- data/lib/psd/resource_section.rb +13 -6
- data/lib/psd/resources/base.rb +33 -0
- data/lib/psd/resources/guides.rb +6 -4
- data/lib/psd/resources/layer_comps.rb +6 -4
- data/lib/psd/resources/resolution_info.rb +48 -0
- data/lib/psd/resources/saved_path.rb +19 -0
- data/lib/psd/resources/slices.rb +7 -5
- data/lib/psd/resources/work_path.rb +22 -0
- data/lib/psd/resources/xmp_metadata.rb +46 -0
- data/lib/psd/resources.rb +17 -21
- data/lib/psd/slice.rb +44 -0
- data/lib/psd/slices.rb +13 -0
- data/lib/psd/util.rb +4 -2
- data/lib/psd/version.rb +1 -1
- data/lib/psd.rb +31 -45
- data/psd.gemspec +2 -3
- data/spec/files/alignment_modes.psd +0 -0
- data/spec/files/blendmodes.psd +0 -0
- data/spec/files/example.psb +0 -0
- data/spec/hierarchy_spec.rb +5 -0
- data/spec/locked_spec.rb +8 -8
- data/spec/psb_parsing_spec.rb +57 -0
- data/spec/text_spec.rb +13 -1
- metadata +115 -75
- data/circle.yml +0 -6
- 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
@@ -35,24 +35,18 @@ class PSD
|
|
35
35
|
finish = @file.tell + byte_count
|
36
36
|
|
37
37
|
while @file.tell < finish
|
38
|
-
len = @file.
|
38
|
+
len = @file.read_byte
|
39
39
|
|
40
40
|
if len < 128
|
41
41
|
len += 1
|
42
|
-
|
43
|
-
@channel_data[k] = @file.read(1).bytes.to_a[0]
|
44
|
-
end
|
45
|
-
|
42
|
+
@channel_data.insert @chan_pos, *@file.read(len).bytes.to_a
|
46
43
|
@chan_pos += len
|
47
44
|
elsif len > 128
|
48
45
|
len ^= 0xff
|
49
46
|
len += 2
|
50
47
|
|
51
|
-
val = @file.read(1).bytes.to_a
|
52
|
-
|
53
|
-
@channel_data[k] = val
|
54
|
-
end
|
55
|
-
|
48
|
+
val = @file.read(1).bytes.to_a
|
49
|
+
@channel_data.insert @chan_pos, *(val * len)
|
56
50
|
@chan_pos += len
|
57
51
|
end
|
58
52
|
end
|
data/lib/psd/image_modes/cmyk.rb
CHANGED
@@ -3,17 +3,30 @@ class PSD
|
|
3
3
|
module CMYK
|
4
4
|
private
|
5
5
|
|
6
|
+
def set_cmyk_channels
|
7
|
+
@channels_info = [
|
8
|
+
{ id: 0 },
|
9
|
+
{ id: 1 },
|
10
|
+
{ id: 2 },
|
11
|
+
{ id: 3 }
|
12
|
+
]
|
13
|
+
|
14
|
+
@channels_info << {id: -1} if channels == 5
|
15
|
+
end
|
16
|
+
|
6
17
|
def combine_cmyk_channel
|
18
|
+
cmyk_channels = @channels_info.
|
19
|
+
map { |ch| ch[:id] }.
|
20
|
+
reject { |ch| ch < -1 }
|
21
|
+
|
7
22
|
(0...@num_pixels).step(pixel_step) do |i|
|
8
23
|
c = m = y = k = 0
|
9
24
|
a = 255
|
10
25
|
|
11
|
-
|
12
|
-
next if chan[:id] == -2
|
13
|
-
|
26
|
+
cmyk_channels.each_with_index do |chan, index|
|
14
27
|
val = @channel_data[i + (@channel_length * index)]
|
15
28
|
|
16
|
-
case chan
|
29
|
+
case chan
|
17
30
|
when -1 then a = val
|
18
31
|
when 0 then c = val
|
19
32
|
when 1 then m = val
|
@@ -28,4 +41,4 @@ class PSD
|
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
31
|
-
end
|
44
|
+
end
|
@@ -3,6 +3,11 @@ class PSD
|
|
3
3
|
module Greyscale
|
4
4
|
private
|
5
5
|
|
6
|
+
def set_greyscale_channels
|
7
|
+
@channels_info = [{ id: 0 }]
|
8
|
+
@channels_info << { id: -1 } if channels == 2
|
9
|
+
end
|
10
|
+
|
6
11
|
def combine_greyscale_channel
|
7
12
|
if channels == 2
|
8
13
|
(0...@num_pixels).step(pixel_step) do |i|
|
@@ -19,4 +24,4 @@ class PSD
|
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
22
|
-
end
|
27
|
+
end
|
data/lib/psd/image_modes/rgb.rb
CHANGED
@@ -4,19 +4,31 @@ class PSD
|
|
4
4
|
module RGB
|
5
5
|
private
|
6
6
|
|
7
|
+
def set_rgb_channels
|
8
|
+
@channels_info = [
|
9
|
+
{ id: 0 },
|
10
|
+
{ id: 1 },
|
11
|
+
{ id: 2 }
|
12
|
+
]
|
13
|
+
|
14
|
+
@channels_info << {id: -1} if channels == 4
|
15
|
+
end
|
16
|
+
|
7
17
|
def combine_rgb_channel
|
8
18
|
PSD.logger.debug "Beginning RGB processing"
|
9
19
|
|
20
|
+
rgb_channels = @channels_info.
|
21
|
+
map { |ch| ch[:id] }.
|
22
|
+
reject { |ch| ch < -1 }
|
23
|
+
|
10
24
|
(0...@num_pixels).step(pixel_step) do |i|
|
11
25
|
r = g = b = 0
|
12
26
|
a = 255
|
13
27
|
|
14
|
-
|
15
|
-
next if chan[:id] == -2
|
16
|
-
|
28
|
+
rgb_channels.each_with_index do |chan, index|
|
17
29
|
val = @channel_data[i + (@channel_length * index)]
|
18
30
|
|
19
|
-
case chan
|
31
|
+
case chan
|
20
32
|
when -1 then a = val
|
21
33
|
when 0 then r = val
|
22
34
|
when 1 then g = val
|
@@ -29,4 +41,4 @@ class PSD
|
|
29
41
|
end
|
30
42
|
end
|
31
43
|
end
|
32
|
-
end
|
44
|
+
end
|
@@ -1,30 +1,32 @@
|
|
1
1
|
class PSD
|
2
2
|
class Layer
|
3
3
|
module BlendModes
|
4
|
-
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_reader :blend_mode
|
7
|
+
|
8
|
+
def_delegators :blend_mode, :opacity, :visible, :clipped?
|
9
|
+
alias_method :visible?, :visible
|
10
|
+
|
11
|
+
# Is this layer hidden?
|
12
|
+
def hidden?
|
13
|
+
!visible
|
14
|
+
end
|
5
15
|
|
6
16
|
def blending_mode
|
7
17
|
if !info[:section_divider].nil? && info[:section_divider].blend_mode
|
8
18
|
BlendMode::BLEND_MODES[info[:section_divider].blend_mode.strip.to_sym]
|
9
19
|
else
|
10
|
-
@
|
20
|
+
@blend_mode.mode
|
11
21
|
end
|
12
22
|
end
|
13
23
|
|
14
|
-
# Is the layer below this one a clipping mask?
|
15
|
-
def clipped?
|
16
|
-
@blend_mode.clipping == 1
|
17
|
-
end
|
18
|
-
|
19
24
|
private
|
20
25
|
|
21
26
|
def parse_blend_modes
|
22
|
-
@blend_mode = BlendMode.
|
23
|
-
|
24
|
-
@blending_mode = @blend_mode.mode
|
25
|
-
@opacity = @blend_mode.opacity
|
26
|
-
@visible = @blend_mode.visible
|
27
|
+
@blend_mode = BlendMode.new(@file)
|
28
|
+
@blend_mode.parse!
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
30
|
-
end
|
32
|
+
end
|
data/lib/psd/layer/helpers.rb
CHANGED
@@ -47,16 +47,6 @@ class PSD
|
|
47
47
|
info[:locked].transparency_locked
|
48
48
|
end
|
49
49
|
|
50
|
-
# Is this layer visible?
|
51
|
-
def visible?
|
52
|
-
@visible
|
53
|
-
end
|
54
|
-
|
55
|
-
# Is this layer hidden?
|
56
|
-
def hidden?
|
57
|
-
!@visible
|
58
|
-
end
|
59
|
-
|
60
50
|
# Helper that exports the text data in this layer, if any.
|
61
51
|
def text
|
62
52
|
return nil unless info[:type]
|
@@ -72,6 +62,14 @@ class PSD
|
|
72
62
|
return 255 unless info.has_key?(:fill_opacity)
|
73
63
|
info[:fill_opacity].value
|
74
64
|
end
|
65
|
+
|
66
|
+
def raster_mask?
|
67
|
+
image.has_mask? && image.mask_data.length > 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def vector_mask?
|
71
|
+
!vector_mask.nil?
|
72
|
+
end
|
75
73
|
end
|
76
74
|
end
|
77
75
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class BlackWhite < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'blwh'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :red, :yellow, :green, :cyan, :blue, :magenta
|
10
|
+
attr_reader :tint, :tint_color, :preset_id, :preset_name
|
11
|
+
|
12
|
+
def parse
|
13
|
+
@file.seek 4, IO::SEEK_CUR
|
14
|
+
@data = Descriptor.new(@file).parse
|
15
|
+
|
16
|
+
@red = @data['Rd ']
|
17
|
+
@yellow = @data['Yllw']
|
18
|
+
@green = @data['Grn ']
|
19
|
+
@cyan = @data['Cyn ']
|
20
|
+
@blue = @data['Bl ']
|
21
|
+
@magenta = @data['Mgnt']
|
22
|
+
@tint = @data['useTint']
|
23
|
+
@tint_color = {
|
24
|
+
red: @data['tintColor']['Rd '],
|
25
|
+
green: @data['tintColor']['Grn '],
|
26
|
+
blue: @data['tintColor']['Bl ']
|
27
|
+
}
|
28
|
+
|
29
|
+
@preset_id = @data['bwPresetKind']
|
30
|
+
@preset_name = @data['blackAndWhitePresetFileName']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
# NOTE: this only has the correct values when the "Use Legacy"
|
5
|
+
# checkbox is checked. If the 'CgEd' info key is present, these
|
6
|
+
# values will all be 0. Use that info block instead.
|
7
|
+
class BrightnessContrast < LayerInfo
|
8
|
+
def self.should_parse?(key)
|
9
|
+
key == 'brit'
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :brightness, :contrast, :mean_value, :lab_color
|
13
|
+
|
14
|
+
def parse
|
15
|
+
@brightness = @file.read_short
|
16
|
+
@contrast = @file.read_short
|
17
|
+
@mean_value = @file.read_short
|
18
|
+
@lab_color = @file.read_boolean
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class ChannelBlendingRestrictions < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'brst'
|
7
|
+
end
|
8
|
+
|
9
|
+
MODES = {
|
10
|
+
'RGBColor' => ['R', 'G', 'B'],
|
11
|
+
'CMYKColor' => ['C', 'M', 'Y', 'K']
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :restricted_channels, :restricted_channels_by_letter
|
15
|
+
|
16
|
+
def parse
|
17
|
+
@restricted_channels = []
|
18
|
+
@restricted_channels_by_letter = []
|
19
|
+
|
20
|
+
(@length / 4).times do
|
21
|
+
channel = @file.read_int
|
22
|
+
@restricted_channels << channel
|
23
|
+
@restricted_channels_by_letter << MODES[@layer.header.mode_name][channel]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class ChannelMixer < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'mixr'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :monochrome, :color
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@file.seek 2, IO::SEEK_CUR
|
13
|
+
|
14
|
+
@monochrome = @file.read_short > 0
|
15
|
+
|
16
|
+
@color = 4.times.map do
|
17
|
+
{
|
18
|
+
red_cyan: @file.read_short,
|
19
|
+
green_magenta: @file.read_short,
|
20
|
+
blue_yellow: @file.read_short,
|
21
|
+
black: @file.read_short,
|
22
|
+
constant: @file.read_short
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class ColorBalance < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'blnc'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :shadows, :midtones, :highlights, :preserve_luminosity
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@shadows, @midtones, @highlights = 3.times.map do
|
13
|
+
{
|
14
|
+
cyan_red: @file.read_short,
|
15
|
+
magenta_green: @file.read_short,
|
16
|
+
yellow_blue: @file.read_short
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
@preserve_luminosity = @file.read_short > 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
# This is the new way that Photoshop stores the brightness/contrast
|
5
|
+
# data. If this data is present, DO NOT use the legacy BrightnessContrast
|
6
|
+
# info block.
|
7
|
+
class ContentGenerator < LayerInfo
|
8
|
+
def self.should_parse?(key)
|
9
|
+
key == 'CgEd'
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
# Version
|
14
|
+
@file.seek 4, IO::SEEK_CUR
|
15
|
+
@data = Descriptor.new(@file).parse
|
16
|
+
end
|
17
|
+
|
18
|
+
def brightness
|
19
|
+
@data['Brgh']
|
20
|
+
end
|
21
|
+
|
22
|
+
def contrast
|
23
|
+
@data['Cntr']
|
24
|
+
end
|
25
|
+
|
26
|
+
def mean_value
|
27
|
+
@data['means']
|
28
|
+
end
|
29
|
+
|
30
|
+
def lab_color
|
31
|
+
@data['Lab ']
|
32
|
+
end
|
33
|
+
|
34
|
+
def use_legacy
|
35
|
+
@data['useLegacy']
|
36
|
+
end
|
37
|
+
|
38
|
+
def auto
|
39
|
+
@data['Auto']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
class Curves < LayerInfo
|
5
|
+
def self.should_parse?(key)
|
6
|
+
key == 'curv'
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :curves
|
10
|
+
|
11
|
+
def parse
|
12
|
+
# Padding, spec is wrong. Maybe Photoshop bug?
|
13
|
+
@file.seek 1, IO::SEEK_CUR
|
14
|
+
|
15
|
+
# Version
|
16
|
+
@file.seek 2, IO::SEEK_CUR
|
17
|
+
|
18
|
+
tag = @file.read_int
|
19
|
+
curve_count = 0
|
20
|
+
|
21
|
+
# Legacy data, it looks like there are 32 positions
|
22
|
+
# where you can adjust the curve, and there is a chunk
|
23
|
+
# of 32 bytes that determine whether that chunk is set.
|
24
|
+
32.times do |i|
|
25
|
+
curve_count += 1 if tag & (1 << i) > 0
|
26
|
+
end
|
27
|
+
|
28
|
+
@curves = []
|
29
|
+
curve_count.times do |i|
|
30
|
+
# Before each curve is a channel index
|
31
|
+
count = 0
|
32
|
+
curve = {}
|
33
|
+
32.times do |j|
|
34
|
+
if tag & (1 << j) > 0
|
35
|
+
if count == i
|
36
|
+
curve[:channel_index] = j
|
37
|
+
break
|
38
|
+
end
|
39
|
+
|
40
|
+
count += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
point_count = @file.read_short
|
45
|
+
curve[:points] = point_count.times.map do
|
46
|
+
{
|
47
|
+
output_value: @file.read_short,
|
48
|
+
input_value: @file.read_short
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
@curves << curve
|
53
|
+
end
|
54
|
+
|
55
|
+
if @file.tell < @section_end - 4
|
56
|
+
tag = @file.read_string(4)
|
57
|
+
raise "Extra curves key error: #{tag}" if tag != 'Crv '
|
58
|
+
|
59
|
+
@file.seek 2, IO::SEEK_CUR
|
60
|
+
|
61
|
+
curve_count = @file.read_int
|
62
|
+
curve_count.times do
|
63
|
+
curve = { channel_index: @file.read_short }
|
64
|
+
|
65
|
+
point_count = @file.read_short
|
66
|
+
curve[:points] = point_count.times.map do
|
67
|
+
{
|
68
|
+
output_value: @file.read_short,
|
69
|
+
input_value: @file.read_short
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
@curves << curve
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'psd/layer_info'
|
2
|
+
|
3
|
+
class PSD
|
4
|
+
# The file specification is majorly fucked for this info block. Had to make
|
5
|
+
# lots of tweaks.
|
6
|
+
class EffectsLayer < LayerInfo
|
7
|
+
def self.should_parse?(key)
|
8
|
+
key == 'lrFX'
|
9
|
+
end
|
10
|
+
|
11
|
+
BEVEL_STYLES = [
|
12
|
+
'Outer Bevel',
|
13
|
+
'Inner Bevel',
|
14
|
+
'Emboss',
|
15
|
+
'Pillow Emboss',
|
16
|
+
'Stroke Emboss'
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
def parse
|
20
|
+
version = @file.read_short
|
21
|
+
effects_count = @file.read_short
|
22
|
+
|
23
|
+
@data = {}
|
24
|
+
effects_count.times do
|
25
|
+
sig = @file.read_string(4)
|
26
|
+
if sig != "8BIM"
|
27
|
+
PSD.logger.debug "Effect layer signature was not 8BIM, got #{sig}. Skipping."
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
key = @file.read_string(4)
|
32
|
+
case key
|
33
|
+
when 'cmnS' then common_state
|
34
|
+
when 'dsdw' then shadow(:drop_shadow)
|
35
|
+
when 'isdw' then shadow(:inner_shadow)
|
36
|
+
when 'oglw' then outer_glow
|
37
|
+
when 'iglw' then inner_glow
|
38
|
+
when 'bevl' then bevel
|
39
|
+
when 'sofi' then solid_fill
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# This is the most pointless block of data yet.
|
47
|
+
def common_state
|
48
|
+
@file.seek 11, IO::SEEK_CUR
|
49
|
+
@data[:visible] = true
|
50
|
+
end
|
51
|
+
|
52
|
+
def shadow(key)
|
53
|
+
@data[key] = {}
|
54
|
+
size = @file.read_int
|
55
|
+
|
56
|
+
@data[key][:version] = @file.read_int
|
57
|
+
@data[key][:size] = @file.read_short
|
58
|
+
@data[key][:spread] = @file.read_int
|
59
|
+
@data[key][:angle] = @file.read_int
|
60
|
+
@data[key][:distance] = @file.read_int
|
61
|
+
|
62
|
+
# Not sure if Photoshop or the spec is wrong, but somebody done goofed.
|
63
|
+
@file.seek 2, IO::SEEK_CUR
|
64
|
+
|
65
|
+
@data[key][:color] = @file.read_space_color
|
66
|
+
|
67
|
+
@file.seek 4, IO::SEEK_CUR # blend mode sig
|
68
|
+
@data[key][:blend_mode] = @file.read_string(4)
|
69
|
+
@data[key][:enabled] = @file.read_boolean
|
70
|
+
@data[key][:use_global_light] = @file.read_boolean
|
71
|
+
@data[key][:opacity] = (255.0 / @file.read_byte).round
|
72
|
+
|
73
|
+
if size == 51
|
74
|
+
@data[key][:native_color] = @file.read_space_color
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def outer_glow
|
79
|
+
@data[:outer_glow] = {}
|
80
|
+
size = @file.read_int
|
81
|
+
|
82
|
+
@data[:outer_glow][:version] = @file.read_int
|
83
|
+
@data[:outer_glow][:size] = @file.read_short
|
84
|
+
@data[:outer_glow][:spread] = @file.read_int
|
85
|
+
@file.seek 2, IO::SEEK_CUR
|
86
|
+
|
87
|
+
@data[:outer_glow][:color] = @file.read_space_color
|
88
|
+
|
89
|
+
@file.seek 4, IO::SEEK_CUR # blend sig
|
90
|
+
@data[:outer_glow][:blend_mode] = @file.read_string(4)
|
91
|
+
@data[:outer_glow][:enabled] = @file.read_boolean
|
92
|
+
@data[:outer_glow][:opacity] = (255.0 / @file.read_byte).round
|
93
|
+
|
94
|
+
if size ==42
|
95
|
+
@data[:outer_glow][:native_color] = @file.read_space_color
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def inner_glow
|
100
|
+
@data[:inner_glow] = {}
|
101
|
+
size = @file.read_int
|
102
|
+
|
103
|
+
@data[:inner_glow][:version] = @file.read_int
|
104
|
+
@data[:inner_glow][:size] = @file.read_short
|
105
|
+
@data[:inner_glow][:spread] = @file.read_int
|
106
|
+
@file.seek 2, IO::SEEK_CUR
|
107
|
+
|
108
|
+
@data[:inner_glow][:color] = @file.read_space_color
|
109
|
+
|
110
|
+
@file.seek 4, IO::SEEK_CUR # blend sig
|
111
|
+
@data[:inner_glow][:blend_mode] = @file.read_string(4)
|
112
|
+
@data[:inner_glow][:enabled] = @file.read_boolean
|
113
|
+
@data[:inner_glow][:opacity] = (255.0 / @file.read_byte).round
|
114
|
+
|
115
|
+
if @data[:inner_glow][:version] == 2
|
116
|
+
@data[:inner_glow][:invert] = @file.read_boolean
|
117
|
+
@data[:inner_glow][:native_color] = @file.read_space_color
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def bevel
|
122
|
+
@data[:bevel] = {}
|
123
|
+
size = @file.read_int
|
124
|
+
|
125
|
+
@data[:bevel][:version] = @file.read_int
|
126
|
+
|
127
|
+
# Okay somebody really goofed, this is super weird.
|
128
|
+
@data[:bevel][:angle] = @file.read_short
|
129
|
+
@file.seek 2, IO::SEEK_CUR
|
130
|
+
@data[:bevel][:size] = @file.read_short
|
131
|
+
@file.seek 2, IO::SEEK_CUR
|
132
|
+
@data[:bevel][:soften] = @file.read_short
|
133
|
+
@file.seek 2, IO::SEEK_CUR
|
134
|
+
|
135
|
+
@file.seek 4, IO::SEEK_CUR
|
136
|
+
@data[:bevel][:highlight_blend] = @file.read_string(4)
|
137
|
+
|
138
|
+
@file.seek 4, IO::SEEK_CUR
|
139
|
+
@data[:bevel][:shadow_blend] = @file.read_string(4)
|
140
|
+
|
141
|
+
@data[:bevel][:highlight_color] = @file.read_space_color
|
142
|
+
@data[:bevel][:shadow_color] = @file.read_space_color
|
143
|
+
|
144
|
+
@data[:bevel][:style_id] = @file.read_byte
|
145
|
+
@data[:bevel][:style] = BEVEL_STYLES[@data[:bevel][:style_id]]
|
146
|
+
|
147
|
+
@data[:bevel][:highlight_opacity] = @file.read_byte
|
148
|
+
@data[:bevel][:shadow_opacity] = @file.read_byte
|
149
|
+
@data[:bevel][:enabled] = @file.read_boolean
|
150
|
+
@data[:bevel][:use_global_light] = @file.read_boolean
|
151
|
+
@data[:bevel][:direction] = @file.read_byte == 0 ? 'Up' : 'Down'
|
152
|
+
|
153
|
+
if @data[:bevel][:version] == 2
|
154
|
+
@data[:bevel][:real_highlight_color] = @file.read_space_color
|
155
|
+
@data[:bevel][:real_shadow_color] = @file.read_space_color
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def solid_fill
|
160
|
+
@data[:solid_fill] = {}
|
161
|
+
@file.seek 4, IO::SEEK_CUR
|
162
|
+
|
163
|
+
@data[:solid_fill][:version] = @file.read_int
|
164
|
+
|
165
|
+
@file.seek 4, IO::SEEK_CUR
|
166
|
+
@data[:solid_fill][:blend_mode] = @file.read_string(4)
|
167
|
+
|
168
|
+
@data[:solid_fill][:color] = @file.read_space_color
|
169
|
+
@data[:solid_fill][:opacity] = (255.0 / @file.read_byte).round
|
170
|
+
@data[:solid_fill][:enabled] = @file.read_boolean
|
171
|
+
@data[:solid_fill][:native_color] = @file.read_space_color
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|