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.
Files changed (130) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +2 -0
  3. data/README.md +60 -20
  4. data/lib/psd/blend_mode.rb +46 -38
  5. data/lib/psd/channel_image.rb +11 -5
  6. data/lib/psd/color.rb +14 -5
  7. data/lib/psd/descriptor.rb +39 -16
  8. data/lib/psd/file.rb +9 -6
  9. data/lib/psd/header.rb +38 -33
  10. data/lib/psd/helpers.rb +2 -2
  11. data/lib/psd/image.rb +21 -16
  12. data/lib/psd/image_formats/layer_rle.rb +2 -2
  13. data/lib/psd/image_formats/rle.rb +4 -10
  14. data/lib/psd/image_modes/cmyk.rb +18 -5
  15. data/lib/psd/image_modes/greyscale.rb +6 -1
  16. data/lib/psd/image_modes/rgb.rb +17 -5
  17. data/lib/psd/layer/blend_modes.rb +15 -13
  18. data/lib/psd/layer/helpers.rb +8 -10
  19. data/lib/psd/layer/info/black_white.rb +33 -0
  20. data/lib/psd/{layer_info → layer/info}/blend_clipping_elements.rb +4 -2
  21. data/lib/psd/{layer_info → layer/info}/blend_interior_elements.rb +4 -2
  22. data/lib/psd/layer/info/brightness_contrast.rb +21 -0
  23. data/lib/psd/layer/info/channel_blending_restrictions.rb +27 -0
  24. data/lib/psd/layer/info/channel_mixer.rb +27 -0
  25. data/lib/psd/layer/info/color_balance.rb +23 -0
  26. data/lib/psd/layer/info/color_lookup.rb +14 -0
  27. data/lib/psd/layer/info/content_generator.rb +42 -0
  28. data/lib/psd/layer/info/curves.rb +78 -0
  29. data/lib/psd/layer/info/effects_layer.rb +174 -0
  30. data/lib/psd/layer/info/exposure.rb +20 -0
  31. data/lib/psd/{layer_info → layer/info}/fill_opacity.rb +4 -2
  32. data/lib/psd/layer/info/gradient_fill.rb +14 -0
  33. data/lib/psd/layer/info/gradient_map.rb +69 -0
  34. data/lib/psd/layer/info/hue_saturation.rb +41 -0
  35. data/lib/psd/layer/info/invert.rb +17 -0
  36. data/lib/psd/layer/info/knockout.rb +22 -0
  37. data/lib/psd/layer/info/layer_effects.rb +16 -0
  38. data/lib/psd/{layer_info → layer/info}/layer_group.rb +4 -2
  39. data/lib/psd/{layer_info → layer/info}/layer_id.rb +4 -2
  40. data/lib/psd/layer/info/layer_mask_as_global_mask.rb +16 -0
  41. data/lib/psd/{layer_info → layer/info}/layer_name_source.rb +4 -2
  42. data/lib/psd/{layer_info → layer/info}/layer_section_divider.rb +4 -2
  43. data/lib/psd/{layer_info → layer/info}/legacy_typetool.rb +6 -4
  44. data/lib/psd/layer/info/levels.rb +48 -0
  45. data/lib/psd/{layer_info → layer/info}/locked.rb +4 -2
  46. data/lib/psd/{layer_info → layer/info}/metadata_setting.rb +5 -3
  47. data/lib/psd/{layer_info → layer/info}/object_effects.rb +5 -5
  48. data/lib/psd/layer/info/pattern.rb +14 -0
  49. data/lib/psd/layer/info/pattern_fill.rb +15 -0
  50. data/lib/psd/layer/info/photo_filter.rb +44 -0
  51. data/lib/psd/{layer_info → layer/info}/placed_layer.rb +4 -2
  52. data/lib/psd/layer/info/posterize.rb +16 -0
  53. data/lib/psd/{layer_info → layer/info}/reference_point.rb +4 -2
  54. data/lib/psd/layer/info/selective_color.rb +32 -0
  55. data/lib/psd/layer/info/sheet_color.rb +36 -0
  56. data/lib/psd/layer/info/solid_color.rb +36 -0
  57. data/lib/psd/layer/info/threshold.rb +16 -0
  58. data/lib/psd/layer/info/transparency_shapes_layer.rb +16 -0
  59. data/lib/psd/{layer_info → layer/info}/typetool.rb +27 -13
  60. data/lib/psd/{layer_info → layer/info}/unicode_name.rb +4 -2
  61. data/lib/psd/{layer_info → layer/info}/vector_mask.rb +7 -5
  62. data/lib/psd/layer/info/vector_mask_as_global_mask.rb +16 -0
  63. data/lib/psd/layer/info/vector_origination.rb +14 -0
  64. data/lib/psd/{layer_info → layer/info}/vector_stroke.rb +4 -2
  65. data/lib/psd/{layer_info → layer/info}/vector_stroke_content.rb +4 -2
  66. data/lib/psd/layer/info/vibrance.rb +22 -0
  67. data/lib/psd/layer/info.rb +106 -19
  68. data/lib/psd/layer/position_and_channels.rb +2 -6
  69. data/lib/psd/layer.rb +13 -16
  70. data/lib/psd/layer_info.rb +0 -4
  71. data/lib/psd/layer_mask.rb +61 -50
  72. data/lib/psd/lazy_execute.rb +5 -1
  73. data/lib/psd/mask.rb +7 -1
  74. data/lib/psd/node.rb +142 -49
  75. data/lib/psd/nodes/ancestry.rb +7 -6
  76. data/lib/psd/nodes/build_preview.rb +4 -4
  77. data/lib/psd/nodes/group.rb +36 -0
  78. data/lib/psd/nodes/layer.rb +45 -0
  79. data/lib/psd/nodes/layer_comps.rb +93 -0
  80. data/lib/psd/nodes/locking.rb +36 -0
  81. data/lib/psd/nodes/root.rb +87 -0
  82. data/lib/psd/nodes/search.rb +8 -65
  83. data/lib/psd/path_record.rb +5 -70
  84. data/lib/psd/renderer/blender.rb +10 -5
  85. data/lib/psd/renderer/cairo_helpers.rb +46 -0
  86. data/lib/psd/renderer/canvas.rb +41 -20
  87. data/lib/psd/renderer/canvas_management.rb +2 -2
  88. data/lib/psd/renderer/clipping_mask.rb +5 -4
  89. data/lib/psd/renderer/compose.rb +59 -69
  90. data/lib/psd/renderer/layer_styles/color_overlay.rb +45 -27
  91. data/lib/psd/renderer/layer_styles.rb +15 -5
  92. data/lib/psd/renderer/mask.rb +26 -22
  93. data/lib/psd/renderer/mask_canvas.rb +12 -0
  94. data/lib/psd/renderer/vector_shape.rb +239 -0
  95. data/lib/psd/renderer.rb +18 -7
  96. data/lib/psd/resource_section.rb +13 -6
  97. data/lib/psd/resources/base.rb +33 -0
  98. data/lib/psd/resources/guides.rb +6 -4
  99. data/lib/psd/resources/layer_comps.rb +6 -4
  100. data/lib/psd/resources/resolution_info.rb +48 -0
  101. data/lib/psd/resources/saved_path.rb +19 -0
  102. data/lib/psd/resources/slices.rb +7 -5
  103. data/lib/psd/resources/work_path.rb +22 -0
  104. data/lib/psd/resources/xmp_metadata.rb +46 -0
  105. data/lib/psd/resources.rb +17 -21
  106. data/lib/psd/slice.rb +44 -0
  107. data/lib/psd/slices.rb +13 -0
  108. data/lib/psd/util.rb +4 -2
  109. data/lib/psd/version.rb +1 -1
  110. data/lib/psd.rb +31 -45
  111. data/psd.gemspec +2 -3
  112. data/spec/files/alignment_modes.psd +0 -0
  113. data/spec/files/blendmodes.psd +0 -0
  114. data/spec/files/example.psb +0 -0
  115. data/spec/hierarchy_spec.rb +5 -0
  116. data/spec/locked_spec.rb +8 -8
  117. data/spec/psb_parsing_spec.rb +57 -0
  118. data/spec/text_spec.rb +13 -1
  119. metadata +115 -75
  120. data/circle.yml +0 -6
  121. data/lib/psd/layer_info/vector_mask_2.rb +0 -10
  122. data/lib/psd/node_exporting.rb +0 -20
  123. data/lib/psd/node_group.rb +0 -86
  124. data/lib/psd/node_layer.rb +0 -81
  125. data/lib/psd/node_root.rb +0 -93
  126. data/lib/psd/nodes/has_children.rb +0 -13
  127. data/lib/psd/nodes/lock_to_origin.rb +0 -7
  128. data/lib/psd/nodes/parse_layers.rb +0 -18
  129. data/lib/psd/renderer/layer_styles/drop_shadow.rb +0 -75
  130. 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.read(1).bytes.to_a[0]
38
+ len = @file.read_byte
39
39
 
40
40
  if len < 128
41
41
  len += 1
42
- (@chan_pos...@chan_pos+len).each do |k|
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[0]
52
- (@chan_pos...@chan_pos+len).each do |k|
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
@@ -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
- @channels_info.each_with_index do |chan, index|
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[:id]
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
@@ -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
- @channels_info.each_with_index do |chan, index|
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[:id]
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
- attr_reader :blend_mode, :opacity
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
- @blending_mode
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.read(@file)
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
@@ -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
@@ -1,8 +1,10 @@
1
- require_relative '../layer_info'
1
+ require 'psd/layer_info'
2
2
 
3
3
  class PSD
4
4
  class BlendClippingElements < LayerInfo
5
- @key = 'clbl'
5
+ def self.should_parse?(key)
6
+ key == 'clbl'
7
+ end
6
8
 
7
9
  attr_reader :enabled
8
10
  def parse
@@ -1,8 +1,10 @@
1
- require_relative '../layer_info'
1
+ require 'psd/layer_info'
2
2
 
3
3
  class PSD
4
4
  class BlendInteriorElements < LayerInfo
5
- @key = 'infx'
5
+ def self.should_parse?(key)
6
+ key == 'infx'
7
+ end
6
8
 
7
9
  attr_reader :enabled
8
10
  def parse
@@ -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,14 @@
1
+ require 'psd/layer_info'
2
+
3
+ class PSD
4
+ class ColorLookup < LayerInfo
5
+ def self.should_parse?(key)
6
+ key == 'clrL'
7
+ end
8
+
9
+ def parse
10
+ @file.seek 6, IO::SEEK_CUR
11
+ @data = Descriptor.new(@file).parse
12
+ end
13
+ end
14
+ 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