aboutsummaryrefslogtreecommitdiffstats
path: root/src/layerdata/layereffect.coffee
blob: 7255358a6a144d29c1baf2e8138bc6b45532e435 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# libpsd has effect layer parsing 
# see https://github.com/alco/psdump/blob/master/libpsd-0.9

class PSDEffectsInfo
  constructor: (@layer, @length) ->
    @file = @layer.file

  parse: ->
    effectsVersion = @file.readInt()
    assert effectsVersion is 0

    version = @file.readInt()
    assert version is 16

    (new PSDEffectsDescriptor(@file)).parse()

  parseLegacy: ->
    effects = []

    [
        v, # always 0
        count
    ] = @file.readf ">HH"

    while count-- > 0
      [
        signature,
        type
      ] = @file.readf ">4s4s"

      [size] = @file.readf ">i"

      pos = @file.tell()

      Log.debug("Parsing effect layer with type #{type} and size #{size}")

      effect =    
        switch type
          when "cmnS" then new PSDLayerEffectCommonStateInfo @file
          when "dsdw" then new PSDDropDownLayerEffect @file     
          when "isdw" then new PSDDropDownLayerEffect @file, true # inner drop shadow

      data = effect?.parse()

      left = (pos + size) - @file.tell()
      if left != 0
       Log.debug("Failed to parse effect layer with type #{type}")
       @file.seek left 
      else
        effects.push(data) unless type == "cmnS" # ignore commons state info

    legacy: true
    effects: effects

# This is ridiculous. The effects layer does not follow the standard
# descriptor structure. Have to do a little hacking here.
class PSDEffectsDescriptor extends PSDDescriptor
  parseItem: (id) ->
    type = @file.readString(4)

    data = switch id
      when "patternFill" then @parseLayerPatternOverlay()
      else super(id, type)

    data

class PSDLayerEffect

  constructor: (@file) ->
  
  parse: ->
    # these are common to all effects
    [@version] = @file.readf ">i"

  getSpaceColor: ->
    @file.read(2) # 2 bytes for space
    @file.readf ">HHHH" # 4 * 2 byte color component - r, g, b, a
   
class PSDLayerEffectCommonStateInfo extends PSDLayerEffect

  parse: -> 
    super()
    # always true
    @visible = @file.readBoolean()
    # unused
    @file.read(2)

    {visible: @visible}

# Based on https://github.com/alco/psdump/blob/master/libpsd-0.9/src/drop_shadow.c
class PSDDropDownLayerEffect extends PSDLayerEffect

  constructor: (file, @inner = false) -> 
    super(file)

    #defaults 
    @blendMode = "mul"
    @color = @nativeColor = [0,0,0,0]
    @opacity = 191
    @angle = 120
    @useGlobalLight = true
    @distance = 5

    # v2
    @spread = 0
    @size = 5
    @antiAliased = false
    @knocksOut = false

  parse: ->
    super()

    [
      @blur,      # This seems to be wrong in the specification! - see libpsd
      @intensity,
      @angle,
      @distance
    ] = @file.readf ">hiii"


    @file.read(2) # extra 2 bytes for space. The spec doesn't mention this!

    @color = @getSpaceColor()

    [ 
      @signature,
      @blendMode
    ] =  @file.readf ">4s4s"

    @enabled = @file.readBoolean()
    @useAngleInAllFX = @file.readBoolean()
    
    [@opacity] = @file.read(1) 
    
    @nativeColor = @getSpaceColor() if @version == 2

    data = {}
    for own key, val of @
      continue if key is "file"
      data[key] = val

    data