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
|