Code archives/Graphics/GIMP file format exploration
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| Just exploring The GIMP's XCF file format. This program outputs various details about any given GIMP image file, such as width/height, layer/channel names, layer editing status, etc. Tested on a total of 4 .xcf files, so don't be too surprised if it fails for you... Example output: -- Filename: boing.xcf File format version: 0 Image size: 256 x 256 (RGB color) Image properties: ----> RLE encoded ----> Resolution: 299.923187 x 299.923187 DPI ----> Tattoo (unique ID for drawable element): 13 ----> Units: Millimetres ----> Parasite name: gimp-image-grid Properties for channel "Background copy" (256 x 256) ----> This is the active layer Properties for channel "New Layer" (256 x 256) ----> Opacity: 255 out of 255 ----> Layer visible ----> Layer not linked ----> Layer transparency not preserved ----> Layer mask not applied ----> Layer mask is not being edited ----> Layer mask is not visible ----> Layer offsets from top-left of canvas: x = 0, y = 0 ----> Layer mode: Normal ----> Tattoo (unique ID for drawable element): 4 | |||||
' -----------------------------------------------------------------------------
' GIMP file format exploration...
' -----------------------------------------------------------------------------
SuperStrict
' -----------------------------------------------------------------------------
' GIMP XCF file format specs taken from...
' -----------------------------------------------------------------------------
' http://svn.gnome.org/viewvc/gimp/trunk/devel-docs/xcf.txt?view=markup
' -----------------------------------------------------------------------------
' -----------------------------------------------------------------------------
' Test calls...
' -----------------------------------------------------------------------------
GIMPInfo "lagunafixed.xcf"
GIMPInfo "lagunaclaycanvas.xcf"
GIMPInfo "test.xcf"
GIMPInfo "boing.xcf"
' -----------------------------------------------------------------------------
' Constants...
' -----------------------------------------------------------------------------
Const PROP_COLORMAP:Int = 1
Const PROP_ACTIVE_LAYER:Int = 2
Const PROP_OPACITY:Int = 6
Const PROP_MODE:Int = 7
Const PROP_VISIBLE:Int = 8
Const PROP_LINKED:Int = 9
Const PROP_PRESERVE_TRANSPARENCY:Int = 10
Const PROP_APPLY_MASK:Int = 11
Const PROP_EDIT_MASK:Int = 12
Const PROP_SHOW_MASK:Int = 13
Const PROP_OFFSETS:Int = 15
Const PROP_COMPRESSION:Int = 17
Const PROP_RESOLUTION:Int = 19
Const PROP_TATTOO:Int = 20
Const PROP_PARASITES:Int = 21
Const PROP_UNIT:Int = 22 ' Incorrectly defined as 21 in above document!
' -----------------------------------------------------------------------------
' GIMP file reader...
' -----------------------------------------------------------------------------
Function GIMPInfo (filename:String)
' ------------------------------------------------------------------------
' Local function for reading zero-terminated strings...
' ------------------------------------------------------------------------
Function ReadZeroString:String (file:TStream)
Local sbyte:Byte
Local zstring:String
Repeat
sbyte = ReadByte (file)
If sbyte Then zstring = zstring + Chr (sbyte)
Until sbyte = 0
Return zstring
End Function
' ------------------------------------------------------------------------
' Local function for reading GIMP 'property' values...
' ------------------------------------------------------------------------
Function ReadProperties:Int (file:TStream)
Local property_type:Int
Local payload_length:Int
' To support more properties, see line 546, entitled "Layer properties",
' in the document linked at the top of this file.
' General method: Copy the PROP_TEMPLATE code below, define the Const value of
' the property as per the document, change xxx to the number of bytes in the
' payload (as per the document), then just read and interpret the data that
' follows, eg.
' PROP_SHOW_MASK (editing state)
' uint32 13 The type number for PROP_APPLY_MASK is 13 ' This is the Const value to declare
' uint32 4 Four bytes of payload ' This is how many bytes to read
' uint32 b 1 if the layer mask is visible, 0 if not ' The is the data (one integer in this case)
' ... becomes:
Rem
Const PROP_SHOW_MASK:Int = 13 ' Add to top of this file!
...
Case PROP_SHOW_MASK
payload_length = ReadInt (file)
If payload_length = 4
Local mask_visible:Int = ReadInt (file)
If mask_visible Then Print "Mask visible" Else Print "Mask not visible"
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
End Rem
Repeat
property_type = ReadInt (file)
Select property_type
Rem
Case PROP_TEMPLATE
payload_length = ReadInt (file)
If payload_length = xxx
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
End Rem
Case 0
' Do nothing!
Case PROP_COLORMAP
payload_length = ReadInt (file)
Local num_colors:Int = ReadInt (file)
Local red:Byte [num_colors], green:Byte [num_colors], blue:Byte [num_colors]
Print "~tReading color map into RGB arrays..."
For Local loop:Int = 0 Until num_colors
red [loop] = ReadByte (file)
green [loop] = ReadByte (file)
blue [loop] = ReadByte (file)
Next
Case PROP_ACTIVE_LAYER
' No payload to read...
Print "~tThis is the active layer"
Case PROP_OPACITY
payload_length = ReadInt (file)
If payload_length = 4
Local opacity:Int = ReadInt (file)
Print "~tOpacity: " + opacity + " out of 255"
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_MODE
payload_length = ReadInt (file)
If payload_length = 4
Local layer_mode:String
Select ReadInt (file)
Case 0
layer_mode = "Normal"
Case 1
layer_mode = "Dissolve"
Case 2
layer_mode = "Behind"
Case 3
layer_mode = "Multiply"
Case 4
layer_mode = "Screen"
Case 5
layer_mode = "Overlay"
Case 6
layer_mode = "Difference"
Case 7
layer_mode = "Addition"
Case 8
layer_mode = "Subtract"
Case 9
layer_mode = "Darken Only"
Case 10
layer_mode = "Lighten Only"
Case 11
layer_mode = "Hue"
Case 12
layer_mode = "Saturation"
Case 13
layer_mode = "Color"
Case 14
layer_mode = "Value"
Case 15
layer_mode = "Divide"
Case 16
layer_mode = "Dodge"
Case 17
layer_mode = "Burn"
Case 18
layer_mode = "Hard Light"
Case 19
layer_mode = "Soft Light"
Case 20
layer_mode = "Grain Extract"
Case 21
layer_mode = "Grain Merge"
Default
layer_mode = "Unknown"
End Select
Print "~tLayer mode: " + layer_mode
Else
Print "Payload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_VISIBLE
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer visible"
Else
Print "~tLayer not visible"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_LINKED
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer linked"
Else
Print "~tLayer not linked"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_PRESERVE_TRANSPARENCY
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer transparency preserved"
Else
Print "~tLayer transparency not preserved"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_APPLY_MASK
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer mask applied"
Else
Print "~tLayer mask not applied"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_EDIT_MASK
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer mask is being edited"
Else
Print "~tLayer mask is not being edited"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_SHOW_MASK
payload_length = ReadInt (file)
If payload_length = 4
If ReadInt (file) = 1
Print "~tLayer mask is visible"
Else
Print "~tLayer mask is not visible"
EndIf
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_OFFSETS
payload_length = ReadInt (file)
If payload_length = 8
Local dx:Int = ReadInt (file)
Local dy:Int = ReadInt (file)
Print "~tLayer offsets from top-left of canvas: x = " + dx + ", y = " + dy
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_COMPRESSION
payload_length = ReadInt (file)
If payload_length = 1 ' Expected length for property
Local compression:Byte = ReadByte (file)
Select compression
Case 0
Print "~tNo compression"
Case 1
Print "~tRLE encoded"
Case 2
Print "~tzlib compression"
Case 3
Print "~tFractal compression"
End Select
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_RESOLUTION
payload_length = ReadInt (file)
If payload_length = 8
Local x_res:Float = ReadFloat (file)
Local y_res:Float = ReadFloat (file)
Print "~tResolution: " + x_res + " x " + y_res + " DPI"
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_TATTOO
payload_length = ReadInt (file)
If payload_length = 4
Print "~tTattoo (unique ID for drawable element): " + ReadInt (file)
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Case PROP_PARASITES
payload_length = ReadInt (file)
Local string_len:Int = ReadInt (file)
Print "~tParasite name: " + ReadZeroString (file)
ReadString file, payload_length - (string_len + 4) ' Subtract length of string and 4 bytes of string_len from payload_length
Case PROP_UNIT
payload_length = ReadInt (file)
If payload_length = 4
Local unit:String
Select ReadInt (file)
Case 0
unit = "Inches"
Case 1
unit = "Millimetres"
Case 2
unit = "Points"
Case 3
unit = "Picas"
Default
unit = "Unknown unit size"
End Select
Print "~tUnits: " + unit
Else
Print "~tPayload length unexpected! Contains: " + ReadString (file, payload_length)
EndIf
Default
Print "~tUnhandled property type: " + property_type
payload_length = ReadInt (file)
ReadString file, payload_length
End Select
Until property_type = 0
End Function
' ------------------------------------------------------------------------
' Main function code...
' ------------------------------------------------------------------------
Local file:TStream = BigEndianStream (ReadFile (filename))
If file
Print ""
Print "--"
Print ""
Print "Filename: " + filename
' Read expected XCF string...
If ReadString (file, 9) = "gimp xcf "
Local version:String = ReadString (file, 4)
Local v:Int
' Version string may be "file" or "v001", "v002", etc...
If version = "file"
v = 0
Else
If Left (version, 1) = "v"
v = Int (Right (version, 3)) ' Read xxx from "vxxx", eg. "v002"
Else
v = -1
EndIf
EndIf
' WTF? Should probably abort here...
If v = -1
version = "Unknown XCF format!"
Else
version = v
EndIf
Print "File format version: " + version
ReadByte file ' String's 0-byte
' Width, height and pixel format...
Local width:Int = ReadInt (file)
Local height:Int = ReadInt (file)
Local pixels:Int = ReadInt (file)
Local pixel_format:String
Select pixels
Case 0
pixel_format = "RGB color"
Case 1
pixel_format = "Grayscale"
Case 2
pixel_format = "Indexed color"
Default
pixel_format = "Unknown depth format"
End Select
Print "Image size: " + width + " x " + height + " (" + pixel_format + ")"
Print ""
Print "Image properties:"
Print ""
ReadProperties file ' Call local ReadProperties function to get master image properties...
' Read layers...
Local layer:Int
Repeat
layer = ReadInt (file)
If layer
Print "Layer pointer: " + layer ' Doh... I was meant to deal with this! Should be handled much like channels, below...
EndIf
Until layer = 0
' Read channels...
Local channel:Int
Repeat
channel = ReadInt (file)
If channel
Local channel_pos:Int = StreamPos (file) ' Store current position in file...
' Read channel information...
SeekStream file, channel
Local channel_width:Int = ReadInt (file)
Local channel_height:Int = ReadInt (file)
ReadInt (file) ' Undocumented integer!
Local channel_string:String
Local channel_string_length:Int = ReadInt (file)
If channel_string_length
channel_string = ReadString (file, channel_string_length - 1)
ReadByte (file) ' String's 0-byte
EndIf
Print ""
Print "Properties for channel ~q" + channel_string + "~q (" + channel_width + " x " + channel_height + ")"
Print ""
' Read channel properties via local ReadProperties function...
ReadProperties file
Local pixel_data:Int = ReadInt (file) ' Pointer to pixel data
SeekStream file, channel_pos ' Returned to stored position in file...
EndIf
Until channel = 0
Else
Print "Not a GIMP file"
EndIf
CloseFile file
EndIf
End Function |
Comments
None.
Code Archives Forum