Layer and Mask Information Structure#

struct LayerAndMaskInformation : public FileSection#

The LayerAndMaskInformation section stores the layer structure as well as any image data associated with it.

The section is split up into two fixed components and one optional section. It always contains a LayerInfo section as well as a GlobalLayerMaskInfo section and optionally can contain an AdditionalLayerInfo section which e.g. in 16- and 32-bit mode stores the LayerInfo struct as a Tagged Block.

This appears to be more of a band aid fix when they introduced 16-bit colours in 1992 and again when 32-bit colours were introduced in 2005 although it is not quite clear why this was done rather than directly integrating it into the LayerInfo section as the information and structure is identical.

Public Functions

LayerAndMaskInformation() = default#
inline LayerAndMaskInformation(LayerInfo &layerInfo, GlobalLayerMaskInfo globalLayerMaskInfo, std::optional<AdditionalLayerInfo> additionalLayerInfo)#
void read(File &document, const FileHeader &header, ProgressCallback &callback, const uint64_t offset)#

Read and Initialize the struct from disk using the given offset.

void write(File &document, const FileHeader &header, ProgressCallback &callback)#

Write the section to disk in a Photoshop compliant way.

inline void initialize(size_t offset, size_t size) noexcept#

Initialize the file section with a size and offset. If re-initialization is needed this function may be called again

template<typename T = size_t>
inline T size() const#

Get the size of the FileSection as the given integral type checking internally if this access would overflow the template argument T. This function is primarily intended for structures such as a PascalString which may only be a max of uint8_t in size.

inline void size(size_t size) noexcept#

Set the size of the file section.

inline void addSize(size_t increment)#

Add to the size of the FileSection.

inline size_t offset() const noexcept#

Public Members

LayerInfo m_LayerInfo#

This struct holds the documents layers and channel image data.

GlobalLayerMaskInfo m_GlobalLayerMaskInfo#

This section is undocumented and gets skipped.

std::optional<AdditionalLayerInfo> m_AdditionalLayerInfo#

If present, this holds a list of tagged blocks which add some document specific layer data.

Layer Information Structure#

struct LayerInfo : public FileSection#

The LayerInfo section holds the layer structure as well as the image data for the layers.

Internally these are stored as two vectors of LayerRecord as well as ChannelImageData with the same size as the order is the exact same. E.g. a Layer Record at index 5 would correlate to the Image Data at index 5.

If the file is 16- or 32-bit this section exists twice, once in its regular spot in the LayerAndMaskInformation section and again in a ‘Lr16’ or ‘Lr32’ TaggedBlock. If this is the case the section in the LayerAndMaskInformation must be empty!

Public Functions

LayerInfo() = default#
inline LayerInfo(std::vector<LayerRecord> layerRecords, std::vector<ChannelImageData> imageData)#
void read(File &document, const FileHeader &header, ProgressCallback &callback, const uint64_t offset, const bool isFromAdditionalLayerInfo = false, std::optional<uint64_t> sectionSize = std::nullopt)#

Read and Initialize the struct from disk using the given offset

Parameters:
  • document – The file handle

  • header – The file header (preparsed)

  • callback – Read callback

  • offset – The offset in the file handle this section starts at

  • isFromAdditionalLayerInfo – If true the section is parsed without a size marker as it is already stored on the tagged block

  • sectionSize – This parameter must be present when isFromAdditionalLayerInfo = true

void write(File &document, const FileHeader &header, ProgressCallback &callback)#

Write the layer info section to file with the given padding.

int getLayerIndex(const std::string &layerName)#

Find the index to a layer based on a layer name that is given

If no layer with the name is found, return -1. In the case of multiple name matches the last in the photoshop document is returned (due to photoshop storing layers in reverse). This can also be used to get an index into the ChannelImageData vector as the indices are identical

inline void initialize(size_t offset, size_t size) noexcept#

Initialize the file section with a size and offset. If re-initialization is needed this function may be called again

template<typename T = size_t>
inline T size() const#

Get the size of the FileSection as the given integral type checking internally if this access would overflow the template argument T. This function is primarily intended for structures such as a PascalString which may only be a max of uint8_t in size.

inline void size(size_t size) noexcept#

Set the size of the file section.

inline void addSize(size_t increment)#

Add to the size of the FileSection.

inline size_t offset() const noexcept#

Public Members

std::vector<LayerRecord> m_LayerRecords#
std::vector<ChannelImageData> m_ChannelImageData#

Layer Records and Channel Image Data Structures#

Both of these sections exist as std::vector on the LayerInfo struct and they are intrinsically linked. A LayerRecord always needs a ChannelImageData struct to go with it.

Layer Record#

struct LayerRecord : public FileSection#

A layer record describes a single layer in a photoshop document and may include up to 56 channels.

Public Functions

LayerRecord(PascalString layerName, int32_t top, int32_t left, int32_t bottom, int32_t right, uint16_t channelCount, std::vector<LayerRecords::ChannelInformation> channelInfo, Enum::BlendMode blendMode, uint8_t opacity, uint8_t clipping, LayerRecords::BitFlags bitFlags, std::optional<LayerRecords::LayerMaskData> layerMaskData, LayerRecords::LayerBlendingRanges layerBlendingRanges, std::optional<AdditionalLayerInfo> additionalLayerInfo)#

Construct a layer record with literal values, useful when we know all the data beforehand, i.e. for round tripping.

void read(File &document, const FileHeader &header, ProgressCallback &callback, const uint64_t offset)#

Read and Initialize the struct from disk using the given offset.

void write(File &document, const FileHeader &header, ProgressCallback &callback, const std::vector<LayerRecords::ChannelInformation> channelInfos) const#

Write the layer record to disk, requires the Image data to be compressed already and the size to be known.

uint32_t getWidth() const noexcept#

Extract the absolute width of the layer.

uint32_t getHeight() const noexcept#

Extract the absolute height of the layer.

inline void initialize(size_t offset, size_t size) noexcept#

Initialize the file section with a size and offset. If re-initialization is needed this function may be called again

template<typename T = size_t>
inline T size() const#

Get the size of the FileSection as the given integral type checking internally if this access would overflow the template argument T. This function is primarily intended for structures such as a PascalString which may only be a max of uint8_t in size.

inline void size(size_t size) noexcept#

Set the size of the file section.

inline void addSize(size_t increment)#

Add to the size of the FileSection.

Public Members

PascalString m_LayerName = {}#

The name of the Layer as pascal string, has a maximum length of 255.

int32_t m_Top = {}#

The top edge of the layer bounding box.

int32_t m_Left = {}#

The left edge of the layer bounding box.

int32_t m_Bottom = {}#

The bottom edge of the layer bounding box.

int32_t m_Right = {}#

The right edge of the layer bounding box.

uint16_t m_ChannelCount = {}#

The absolute amount of channels stored in the layer.

std::vector<LayerRecords::ChannelInformation> m_ChannelInformation = {}#

The channel information telling us the channel ID as well as the size of the channel for when we read the ChannelImageData

uint8_t m_Opacity = 255#

0 - 255

uint8_t m_Clipping = 0#

0 or 1

LayerRecords::BitFlags m_BitFlags = {}#

Bit flags which control certain information such as visibility.

std::optional<LayerRecords::LayerMaskData> m_LayerMaskData = std::nullopt#

If one or both of the layer masks has some special data on it (feather or blur) it will be stored in this structure.

LayerRecords::LayerBlendingRanges m_LayerBlendingRanges = {}#

The channel blending ranges for all the default channels (r, g and b in rgb color mode). Photoshop appears to always write out the maximum possible channels (5) as the section size is trivial. We match this behaviour

std::optional<AdditionalLayerInfo> m_AdditionalLayerInfo = std::nullopt#

An optional series of TaggedBlocks. This is where, e.g. SmartObjects or Adjustment layers would store their data.

Channel Image Data#

struct ChannelImageData : public FileSection#

Channel Image Data for a single layer, there is at most 56 channels in a given layer.

Public Functions

ChannelImageData() = default#
inline ChannelImageData(std::vector<std::unique_ptr<ImageChannel>> data)#
template<typename T>
uint64_t estimateSize(const FileHeader &header, const uint16_t numSamples = 16u)#

Estimate the size the of compressed data by compressing n amount of chunks from the data and averaging the compression ratio The chunks are chosen at random and have the size of m_ChunkSize in the ImageChannels. numSamples controls how many random chunks we choose

template<typename T>
std::vector<std::vector<uint8_t>> compressData(const FileHeader &header, std::vector<LayerRecords::ChannelInformation> &lrChannelInfo, std::vector<Enum::Compression> &lrCompression, size_t numThreads)#

Compress the data for the current layer and return the individual channels, invalidating the data as we go. This function must be called before writing the data for the LayerRecord as it reveals the size of the data required to write them. We fill out the lrChannelInfo and lrCompression vector as it goes.

void read(ByteStream &stream, const FileHeader &header, const uint64_t offset, const LayerRecord &layerRecord)#

Read a single layer instance from a pre-allocated bytestream.

void write(File &document, std::vector<std::vector<uint8_t>> &compressedChannelData, const std::vector<Enum::Compression> &channelCompression)#

Write a single layer to disk, there is no need to write to a preallocated buffer here as we compress ahead of time.

inline int getChannelIndex(Enum::ChannelID channelID) const#

Get an index to a specific channel based on the identifier returns -1 if no matching channel is found

inline int getChannelIndex(Enum::ChannelIDInfo channelIDInfo) const#

Get an index to a specific channel based on the identifier returns -1 if no matching channel is found

template<typename T>
inline std::vector<T> extractImageData(int index)#

Extract a channel from the given index and take ownership of the data. After this function is called the index will point to nullptr If the channel has already been extracted we return an empty array of T and raise a warning about accessing elements that have already had their data removed

template<typename T>
inline std::vector<T> extractImageData(Enum::ChannelID channelID)#

Extract a channel from the given ChannelID and take ownership of the data. After this function is called the index will point to nullptr If the channel has already been extracted we return an empty array of T and raise a warning about accessing elements that have already had their data removed

inline std::unique_ptr<ImageChannel> extractImagePtr(Enum::ChannelIDInfo channelIDInfo)#

Extract a channels pointer from our channel vector and invalidate the index. If the channel is already a nullptr we just return that silently and leave it up to the caller to check for this

inline std::vector<std::tuple<uint64_t, uint64_t>> getChannelOffsetsAndSizes() const noexcept#

Get the offsets and sizes for each of the channels, the order being the same as m_ImageData. Therefore indices gotten through e.g. getChannelIndex() are valid here as well. The offsets include the compression marker (2 bytes) so the actual data starts at offset + 2

inline Enum::Compression getChannelCompression(int index) const noexcept#

Get the compression of a channel by logical index acquired by e.g. getChannelIndex.

inline void initialize(size_t offset, size_t size) noexcept#

Initialize the file section with a size and offset. If re-initialization is needed this function may be called again

template<typename T = size_t>
inline T size() const#

Get the size of the FileSection as the given integral type checking internally if this access would overflow the template argument T. This function is primarily intended for structures such as a PascalString which may only be a max of uint8_t in size.

inline void size(size_t size) noexcept#

Set the size of the file section.

inline void addSize(size_t increment)#

Add to the size of the FileSection.

inline size_t offset() const noexcept#