Home
Products
Community
Manuals
Contact
Login or Signup

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 BlitzSupport(Posted 1+ years ago)
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