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
|
# PSD files also store merged image data for each individual layer.
# Unfortunately, parsing this image data is a bit different than parsing
# the overall merged image data at the end of the file. The main difference
# is that each image channel has independent compression, and the channel
# order is defined individually for each layer. Also, the layer size is often
# not the same size as the full image.
class PSDChannelImage extends PSDImage
constructor: (file, header, @layer) ->
@width = @layer.cols
@height = @layer.rows
@channelsInfo = @layer.channelsInfo
super file, header
skip: ->
Log.debug "Skipping channel image data. Layer = #{@layer.name}"
for channel in @channelsInfo
@file.seek channel.length
getImageWidth: -> @width
getImageHeight: -> @height
getImageChannels: -> @layer.channels
# Since we're working on a per-channel basis now, we only read the byte counts
# for the current channel only.
getByteCounts: ->
byteCounts = []
for i in [0...@getImageHeight()]
byteCounts.push @file.readShortInt()
byteCounts
parse: ->
Log.debug "\nLayer: #{@layer.name}, image size: #{@length} (#{@getImageWidth()}x#{@getImageHeight()})"
# We must keep track of the current channel data position global to this object
# now, since we parse a single channel at a time.
@chanPos = 0
# Loop through each image channel and parse each one like a full image.
for i in [0...@getImageChannels()]
@chInfo = @layer.channelsInfo[i]
if @chInfo.length <= 0
@parseCompression()
continue
# If the ID of this current channel is -2, then we assume the dimensions
# of the layer mask.
if @chInfo.id is -2
@width = @layer.mask.width
@height = @layer.mask.height
else
@width = @layer.cols
@height = @layer.rows
start = @file.tell()
Log.debug "Channel ##{@chInfo.id}: length=#{@chInfo.length}"
@parseImageData()
end = @file.tell()
# Sanity check
if end isnt start + @chInfo.length
Log.debug "ERROR: read incorrect number of bytes for channel ##{@chInfo.id}. Layer=#{@layer.name}, Expected = #{start + @chInfo.length}, Actual: #{end}"
@file.seek start + @chInfo.length, false
# Futher sanity checks
if @channelData.length isnt @length
Log.debug "ERROR: #{@channelData.length} read; expected #{@length}"
@processImageData()
#@parseUserMask()
if exports?
memusage = process.memoryUsage()
used = Math.round memusage.heapUsed / 1024 / 1024
total = Math.round memusage.heapTotal / 1024 / 1024
Log.debug "\nMemory usage: #{used}MB / #{total}MB"
# Since we're parsing on a per-channel basis, we need to modify the behavior
# of the RAW encoding parser a bit. This version is aware of the current
# channel data position, since layers that have RAW encoding often use RLE
# encoded alpha channels.
parseRaw: ->
Log.debug "Attempting to parse RAW encoded channel..."
data = @file.read(@chInfo.length - 2)
dataIndex = 0
for i in [@chanPos...@chanPos+@chInfo.length - 2]
@channelData[i] = data[dataIndex++]
@chanPos += @chInfo.length - 2
# Compression is stored on a per-channel basis, not a per-image basis for layers
parseImageData: ->
@compression = @parseCompression()
switch @compression
when 0 then @parseRaw()
when 1 then @parseRLE()
when 2, 3 then @parseZip()
else
Log.debug "Unknown image compression. Attempting to skip."
return @file.seek @endPos, false
# Parse a single channel instead of every image channel
parseChannelData: ->
lineIndex = 0
Log.debug "Parsing layer channel ##{@chInfo.id}, Start = #{@file.tell()}"
[@chanPos, lineIndex] = @decodeRLEChannel(@chanPos, lineIndex)
#parseUserMask: ->
# if @getImageDepth() is 8
# @parseUserMask8()
|