Jump To …

psdlayermask.coffee

class PSDLayerMask
  constructor: (@file, @header, @options) ->

Array to hold all of the layers

    @layers = []

Does the first alpha channel contain the transparency data?

    @mergedAlpha = false

The global layer mask

    @globalMask = {}

Additional layer information

    @extras = []

Skip over this section and don't parse it

  skip: -> @file.seek @file.readInt()

  parse: ->

Read the size of the entire layers and masks section

    maskSize = @file.readInt()
    endLoc = @file.tell() + maskSize

    Log.debug "Layer mask size is #{maskSize}"

If the mask size is > 0, then parse the section. Otherwise, this section doesn't exist and the whole layers/masks data is 4 bytes (the length we've already read)

    return if maskSize <= 0
    

Size of the layer info section. 4 bytes, rounded up by 2's.

    layerInfoSize = Util.pad2 @file.readInt()

Store the current position in case we need to bail and skip over this section.

    pos = @file.tell()

If the layer info size is > 0, then we have some layers

    if layerInfoSize > 0

Read the number of layers, 2 bytes.

      @numLayers = @file.readShortInt()

If the number of layers is negative, the absolute value is the actual number of layers, and the first alpha channel contains the transparency data for the merged image.

      if @numLayers < 0
        Log.debug "Note: first alpha channel contains transparency data"
        @numLayers = Math.abs @numLayers
        @mergedAlpha = true

      if @numLayers * (18 + 6 * @header.channels) > layerInfoSize
        throw "Unlikely number of #{@numLayers} layers for #{@header['channels']} with #{layerInfoSize} layer info size. Giving up."

      Log.debug "Found #{@numLayers} layer(s)"

      for i in [0...@numLayers]
        layer = new PSDLayer @file
        layer.parse(i)
        @layers.push layer

      for layer in @layers
        if layer.isFolder or layer.isHidden

Layer contains no image data. Skip ahead.

          @file.seek 8
          continue

        layer.image = new PSDChannelImage(@file, @header, layer)

        if @options.layerImages and (
          (@options.onlyVisibleLayers and layer.visible) or
          !@options.onlyVisibleLayers
          )
          layer.image.parse()
        else
          layer.image.skip()

Layers are parsed in reverse order

      @layers.reverse()
      @groupLayers()

In case there are filler zeros

    @file.seek pos + layerInfoSize, false

Parse the global layer mask

    @parseGlobalMask()

Temporarily skip the rest of layers & masks section

    @file.seek endLoc, false
    return

We have more additional info to parse, especially beacuse this is PS >= 4.0 @parseExtraInfo(endLoc) if @file.tell() < endLoc

  parseGlobalMask: ->
    length = @file.readInt()
    return if length is 0

    start = @file.tell()
    end = @file.tell() + length

    Log.debug "Global mask length: #{length}"

Undocumented

    @globalMask.overlayColorSpace = @file.readShortInt()

TODO: parse color space components into actual color.

    @globalMask.colorComponents = []
    for i in [0...4]
      @globalMask.colorComponents.push(@file.readShortInt() >> 8)

0 = transparent, 100 = opaque

    @globalMask.opacity = @file.readShortInt()

0 = color selected; 1 = color protected; 128 = use value per layer

    @globalMask.kind = @file.read(1)[0]

    Log.debug "Global mask:", @globalMask

Filler zeros, seek to end.

    @file.seek end, false

  parseExtraInfo: (end) ->
    while @file.tell() < end

Temporary

      [
        sig,
        key,
        length
      ] = @file.readf ">4s4sI"

      length = Util.pad2 length

      Log.debug "Layer extra:", sig, key, length

      @file.seek length

  groupLayers: ->
    groupLayer = null
    for layer in @layers
      if layer.isFolder
        groupLayer = layer
      else if layer.isHidden
        groupLayer = null
      else
        layer.groupLayer = groupLayer

  toJSON: ->
    data =
      mergedAlpha: @mergedAlpha
      globalMask: @globalMask
      extraInfo: @extras
      numLayers: @numLayers
      layers: []

    for layer in @layers
      data.layers.push layer.toJSON()

    data