Code archives/Graphics/GIMP file format exploration

This code has been declared by its author to be Public Domain code.

Download source code

GIMP file format exploration by BlitzSupport2009
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