Skip to content
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
162 lines (126 sloc) 7.26 KB
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
Attribute VB_Name = "pdPSDLayerInfo"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'PhotoDemon PSD (PhotoShop Image) Layer Info Container and Parser
'Copyright 2019-2019 by Tanner Helland
'Created: 15/January/19
'Last updated: 24/January/19
'Last update: split layer info storage into a separate class, as we need to use it from multiple places
'This class contains layer-subdata pulled from a PSD file. It is populated by two possible places: a parent
' pdPSD instance, or a parent pdPSDLayer instance. It has no purpose outside of a PSD parsing context;
' for layer handling inside PhotoDemon, refer to the pdLayer class.
'All code in this class is my original work. It is based off the "official" Adobe spec at this URL
' (link good as of January 2019):
'All source code in this file is licensed under a modified BSD license. This means you may use the code in your own
' projects IF you provide attribution. For more information, please visit
Option Explicit
'PSD files contain a *lot* of information. To aid debugging, you can activate "verbose" output; this will
' dump all kinds of diagnostic information to the debug log. (Note that other PSD classes have their own
' version of this constant.)
Private Const PSD_DEBUG_VERBOSE As Boolean = False
'Layers can include additional optional data. This comes in a variety of shapes and sizes,
' and while we can successfully parse it all, we don't make use of every possible descriptor.
Private Type PSD_AdditionalInfo
aiSignature As String * 4
aiKey As String * 4
aiDataLength As Long
aiDataBytes() As Byte
End Type
Private m_AdditionalInfo() As PSD_AdditionalInfo
Private m_AdditionalInfoCount As Long
Friend Function DoesKeyExist(ByVal srcKey As String) As Boolean
DoesKeyExist = (GetIndexOfKey(srcKey) >= 0)
End Function
Friend Function GetInfoCount() As Long
GetInfoCount = m_AdditionalInfoCount
End Function
Private Function GetIndexOfKey(ByRef srcKey As String) As Long
GetIndexOfKey = -1
If (m_AdditionalInfoCount <= 0) Then Exit Function
Dim i As Long
For i = 0 To m_AdditionalInfoCount - 1
If (m_AdditionalInfo(i).aiKey = srcKey) Then
GetIndexOfKey = i
Exit Function
End If
Next i
End Function
Friend Function GetStreamForKey(ByVal srcKey As String) As pdStream
Dim kIndex As Long
kIndex = GetIndexOfKey(srcKey)
If (kIndex >= 0) Then
Set GetStreamForKey = New pdStream
With m_AdditionalInfo(kIndex)
GetStreamForKey.StartStream PD_SM_ExternalPtrBacked, PD_SA_ReadOnly, , .aiDataLength, VarPtr(.aiDataBytes(0))
End With
Exit Function
End If
End Function
'NOTE: the passed stream *must* point at a valid block within the PSD file, or this function will fail.
Friend Function ParseAdditionalLayerInfo(ByRef srcStream As pdStream, ByRef warningStack As pdStringStack, ByVal imageIsPSB As Boolean, ByVal finalPointerPos As Long, Optional ByVal infoIsInGlobalArea As Boolean = False) As PD_PSDResult
ParseAdditionalLayerInfo = psd_Success
'Reset internal storage
ReDim m_AdditionalInfo(0 To 3) As PSD_AdditionalInfo
m_AdditionalInfoCount = 0
Dim sigCheck As String
Do While (srcStream.GetPosition() < finalPointerPos)
'Verify the signature
sigCheck = srcStream.ReadString_ASCII(4)
If (sigCheck <> "8BIM") And (sigCheck <> "8B64") Then
warningStack.AddString "ParseAdditionalLayerInfo found an unknown additional segment signature: " & sigCheck
ParseAdditionalLayerInfo = psd_Warning
If (m_AdditionalInfoCount > UBound(m_AdditionalInfo)) Then ReDim Preserve m_AdditionalInfo(0 To m_AdditionalInfoCount * 2 - 1) As PSD_AdditionalInfo
With m_AdditionalInfo(m_AdditionalInfoCount)
.aiKey = srcStream.ReadString_ASCII(4)
.aiDataLength = srcStream.ReadLong_BE()
'PSB has another 4 bytes of length here BUT ONLY FOR CERTAIN KEYS
If imageIsPSB Then
'Per the spec, in a PSB file "...the following keys have a length count of 8 bytes:
' LMsk, Lr16, Lr32, Layr, Mt16, Mt32, Mtrn, Alph, FMsk, lnk2, FEid, FXid, PxSD.
If (.aiKey = "LMsk") Or (.aiKey = "Lr16") Or (.aiKey = "Lr32") Or (.aiKey = "Layr") Then .aiDataLength = srcStream.ReadLong_BE()
If (.aiKey = "Mt16") Or (.aiKey = "Mt32") Or (.aiKey = "Mtrn") Or (.aiKey = "Alph") Then .aiDataLength = srcStream.ReadLong_BE()
If (.aiKey = "FMsk") Or (.aiKey = "lnk2") Or (.aiKey = "FEid") Or (.aiKey = "FXid") Then .aiDataLength = srcStream.ReadLong_BE()
If (.aiKey = "PxSD") Then .aiDataLength = srcStream.ReadLong_BE()
End If
If PSD_DEBUG_VERBOSE Then PDDebug.LogAction "Additional layer info found: " & .aiKey & " (" & .aiDataLength & " bytes)"
If (.aiDataLength > 0) Then srcStream.ReadBytes .aiDataBytes, .aiDataLength, True
'Per the spec, length data should be "rounded up to an even byte count." Unfortunately, like many
' things in the spec, this does not appear to be true in practice. Quoting from the developers
' of Paint.NET's PSD plugin (
'// Documentation states that the length is even-padded. Actually:
'// 1. Most keys have 4-padded lengths.
'// 2. However, some keys (LMsk) have even-padded lengths.
'// 3. Other keys (Txt2, Lr16, Lr32) have unpadded lengths.
'// Photoshop writes data that is always 4-padded, even when the stated
'// length is not a multiple of 4. The length mismatch seems to occur
'// only on global layer info. We do not read extra padding in other
'// cases because third-party programs are likely to follow the spec.
'Many thanks to those developers for cracking this problem and sharing their solution.
If infoIsInGlobalArea Then
If ((.aiDataLength Mod 4) <> 0) Then srcStream.SetPosition 4 - (.aiDataLength Mod 4), FILE_CURRENT
End If
End With
m_AdditionalInfoCount = m_AdditionalInfoCount + 1
End If
If PSD_DEBUG_VERBOSE Then PDDebug.LogAction "pdPSDLayerInfo found " & m_AdditionalInfoCount & " additional info records"
End Function
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.