LazPaint LZP directory structure

From Lazarus wiki
Revision as of 16:40, 4 December 2020 by Circular (talk | contribs) (Tree: originals)

The directory structure contains additional information about the layered image, in particular originals used to render the layers.

Global header

Offset  Size  Content
------  ----  -------------------------------------------------------
0       16    Contains the string 'TMemDirectory' followed by the bytes 1A 00 00
16      8     Root 64-bit offset R0
24      8     Root 64-bit size S0
R0      S0    Root entry

The headers thus locates the root of the directory structure.

Directory

The root directory and the subdirectory have the same format.

Offset  Size  Content
------  ----  -------------------------------------------------------
0       4     Number of entries in the directory
4             First entry if any

All entries are one after another.

Entry

An entry has a variable length depending on its content.

Offset  Size  Content
------  ----  -------------------------------------------------------
0       2     Flags of the entry
2       2     Filename size in bytes FS
4       8     Offset to the content (relative to the global header)
12      8     Compressed size CS (optional)
20      8     Uncompressed size UC (optional)
?             Filename (not null terminated)

Possible flags:

  • 1: the entry is a directory
  • 2: the entry is compressed
  • $8000: the entry is small (content and name each 255 bytes or less)
Small entry

When the entry is small, the size fields are not supplied, so the entry header size without filename is 12 bytes. The content size is extracted from the filename size field. The content cannot be compressed.

CS = UC = FS shr 8
FS = FS and 255

The entry header can also be summarized as:

Offset  Size  Content
------  ----  -------------------------------------------------------
0       2     Flags of the entry
2       1     Filename size in bytes FS
3       1     Content size
4       8     Offset to the content (relative to the global header)
12      FS    Filename (not null terminated)
Non small entry

When the entry is not small, the entry header size without filename is 28 bytes. The content can be compressed using the adequate flag.

The filename is not null terminated and encoded as UTF8. Its size is supplied in bytes and not in characters.

Entry content

The entry content offset is relative to the start of the global header of the directory structure. If the flag directory flag is set, then it is to be interpreted as a directory like the root directory. Otherwise, it is to be interpreted as a file.

The root is the path '/'. An entry in the root and called 'hello' has the path '/hello'.

When directories are nested, the resulting path is concatenated with a '/' character as in Unix paths. For example, if there is a subdirectory called 'container' in the root directory and that it contains a file called 'readme.txt', then its path is '/container/readme.txt'.

A file that does not contain '.' has thus no extension and is considered to be an attribute. An attribute is a value associated to the object that the directory represents, like a number or a list of numbers.

If a file is not an attribute, it is similar to file that would be saved on a disk, like an image in PNG format.

Attributes

An attribute can have various types, but those are not saved explicitly. The reader needs to know what kind of value it expects from an attribute. Attributes are stored in ASCII unless specified.

Here is the list of possible types:

  • integer: an optional sign and a sequence of digits forming a signed 32-bit integer.
  • float: a single precision floating point number written using '.' decimal separator and that can have an exponent E.
  • boolean: either 'true' or 'false'
  • string: any sequence of characters (quotes are not added)
  • color: can be a VGA or CSS color name, an RGB value with or without hashtag using one or two digit per channel with or without the alpha channel (example: red = #f00 = ff0000ff), an RGB value expressed in CSS format (exemple: rgb(255,0,0) = rgba(100%,0,0,1))
  • list of float: float values (or 'none' to specify undefined) separated by ','
  • list of colors: color values separated by ','
  • affine matrix: an affine matrix stored as binary so a sequence of 6 single precision floats (each 4 bytes in little endian) representing ux, uy, vx, vy, tx and tx

Note:

  • a 2D coordinate is a list of 2 values (x and y), a 2D rectangle is a list of 4 values (left, top, right and bottom).
  • the affine matrix is stored as binary to ensure exact equality when comparing matrices

Tree

Here as an example of tree structure of a LazPaint file, with 3 layers and 2 originals.

The first two layers has some registry entries, for example if layer1 is an effect generated for layer2.

The second layer contains render backup, for example because it contains text that might not be rendered the same way on a different computer.

There are two originals for example because layer2 is a vector layer and layer3 is a rotated image.

root
  ├─ layers
  │   ├─ layer1
  │   │    └─ registry
  │   ├─ layer2
  │   │    ├─ registry
  │   │    └─ render
  │   └─ layer3
  ├─ originals
  │   ├─ {7F5D5954-8EAC-D992-6D0A-9CD24929EBEB}
  │   │    ├─ shape1
  │   │    ├─ shape2
  │   │    └─ shape3
  │   └─ {61BD152C-9F86-1325-B256-B50D877389E8}
  └─ registry

Registry

A registry is a series of attributes containing any kind of value. The identifier can only contain the following characters: 'A'..'Z','a'..'z','0'..'9','_','-'

The registry at the root does not have predefined attributes. It can be used by scripts to store values like the last parameters used.

Layers

The layers represent the layer stack. The first layer is at the bottom and the last is on top.

Each layer can have its own registry. The attribute 'guid' can define a Guid identifier for the layer, for example {5A8F1094-8386-E527-39E9-6F843771EDC6}. Script can also store values like the parameters of an effect.

Each layer can have its own render directory. This directory contains at least a 'last-matrix' attribute containing the last affine matrix used to render the layer. If it is equal to the current affine matrix of the layer, then the render backup can be used to draw the layer. A part from that, each original is free to use the render directory as it wants.

Originals

The originals are graphical elements that can be transformed according to an affine matrix.

Each original has a Guid and a directory named after its Guid.

In each original directory, there is a 'class' attribute that identifies the type of original. The reader can know from this classname whether it can understand its structure. Apart from that, the content of the original directory is completely up to the class.

The following classnames are defined in LazPaint:

  • image: a bitmap (PNG or JPEG)
  • gradient: a gradient that fills the whole layer
  • vector: a vectorial layer (containing vector objects of LazPaint)
  • svg: an SVG file

Using an image original is useful when an image is transformed as it allows to keep the original bitmap and thus avoid loss of information after multiple transformations.