Saving a PNG?

Blitz3D Forums/Blitz3D Programming/Saving a PNG?

Yeah, I use FreeImage with Paul Snart's Blitz3D wrapper. I cut down the wrapper to my own requirements and fixed a few minor bugs. It only saves images in 24-bit, though. Here's the code, in case it's of any use:

; FreeImage Open-source Userlib Include
; Writen By Paul Snart
; Copyright Pacific Software
;
; Cut-down version with bug fixes by Chris Chadwick 13/7/2005.
;
; This Userlib is free to use in commercial and non-commercial programs
; Please also follow the guidlines set by the FreeImage Development team
; with regards to a GPL License (Included)
;
; #### Main Include Library ####
;
; Version: 1.3
;


	Global FIImage.ImageList
	Global BiHeader = CreateBank(18)
	Global FIB_RedMask[4],FIB_GreenMask[4],FIB_BlueMask[4],FIB_BitDepth[4]
	Global Int32 = CreateBank(4)
	Global FI_Word = CreateBank(2)
	
	FIB_BitDepth[1]=16
	FIB_RedMask[1]=$1F
	FIB_GreenMask[1]=$7E0
	FIB_BlueMask[1]=$F800
	
	FIB_BitDepth[2]=16
	FIB_RedMask[2]=$1F
	FIB_GreenMask[2]=$3E0
	FIB_BlueMask[2]=$7C00
	
	FIB_BitDepth[3]=24
	FIB_RedMask[3]=$FF0000
	FIB_GreenMask[3]=$FF00
	FIB_BlueMask[3]=$FF
	
	FIB_BitDepth[4]=32
	FIB_RedMask[4]=$FF0000
	FIB_GreenMask[4]=$FF00
	FIB_BlueMask[4]=$FF
	
	Dim CRC_Table(256)
	Dim FI_FileEXT$(24,1)
	Dim FI_SaveList(24)

	ComputeCRC()

	Restore LoadExtentions
	Read FI_FIFType
	Repeat
	
		Read FI_FileEXT(FI_FIFType,0)
		FI_FileEXT(FI_FIFType,1) = FI_GetFIFDescription(FI_FIFType) 
		If FI_FIFSupportsWriting(FI_FIFType) = 1 Then FI_SaveList(FI_FIFType) = True
		Read FI_FIFType
	
	Until FI_FIFType = -1

	Type ImageList
	
		; Original/New File Info
		Field FileName$
		Field FileDirectory$
		Field ImageFormat%
		Field ImageFormatDescript$
		Field File_Size%
		Field Format%
		Field LoadFlags%
		Field SaveFlags%
		Field FIBITMAP ; If Used
		
		; Blitz Image Variables
		Field Image%
		Field Width%
		Field Height%
		Field DisplayPitch%
		Field ImageSize%
		Field Depth%
		Field RedMask%
		Field GreenMask%
		Field BlueMask%
		
		; Alpha & Palette Storage
		Field Palette%
		Field PixMap%
		Field AlphaMap%
		Field AlphaPitch%
		
		; FIBITMAP Variables
		Field DIBSize%
		Field Pitch%
		Field Planes%
		Field BitCount%
		Field Flags% 
		Field SizeImage%
		Field XPelsPerMeter% 
		Field YPelsPerMeter% 
		Field ClrUsed%
		Field ClrImportant%
		Field FIRedMask%
		Field FIGreenMask%
		Field FIBlueMask%
	
	End Type

	;
	; #### Constant Declarations ####
	;
	
	; **** Image Format Constants
	Const FIF_UNKNOWN=-1
	Const FIF_BMP=0
	Const FIF_ICO=1
	Const FIF_JPEG=2
	Const FIF_JNG=3
	Const FIF_KOALA=4
	Const FIF_LBM=5
	Const FIF_MNG=6
	Const FIF_PBM=7
	Const FIF_PBMRAW=8
	Const FIF_PCD=9
	Const FIF_PCX=10
	Const FIF_PGM=11
	Const FIF_PGMRAW=12
	Const FIF_PNG=13
	Const FIF_PPM=14
	Const FIF_PPMRAW=15
	Const FIF_RAS=16
	Const FIF_TARGA=17
	Const FIF_TIFF=18
	Const FIF_WBMP=19
	Const FIF_PSD=20
	Const FIF_CUT=21
	Const FIF_IFF=FIF_LBM
	Const FIF_XBM=22
	Const FIF_XPM=23
	Const FIF_DDS=24

	.LoadExtentions
	Data FIF_BMP,"BMP"
	Data FIF_ICO,"ICO"
	Data FIF_JPEG,"JPG"
	Data FIF_JNG,"JNG"
	Data FIF_KOALA,"KOA"
	Data FIF_LBM,"IFF"
	Data FIF_MNG,"MNG"
	Data FIF_PBM,"PBM"
	Data FIF_PBMRAW,"PBX"
	Data FIF_PCD,"PCD"
	Data FIF_PCX,"PCX"
	Data FIF_PGM,"PGM"
	Data FIF_PGMRAW,"PGX"
	Data FIF_PNG,"PNG"
	Data FIF_PPM,"PPM"
	Data FIF_PPMRAW,"PPX"
	Data FIF_RAS,"RAS"
	Data FIF_TARGA,"TGA"
	Data FIF_TIFF,"TIFF"
	Data FIF_WBMP,"WBMP"
	Data FIF_PSD,"PSD"
	Data FIF_CUT,"CUT"
	Data FIF_XBM,"XBM"
	Data FIF_XPM,"XPM"
	Data FIF_DDS,"DDS"
	Data -1

	; **** Image Types
	Const FIT_UNKNOWN=0			; unknown Type
	Const FIT_BITMAP=1			; standard image			: 1-, 4-, 8-, 16-, 24-, 32-bit
	Const FIT_UINT16=2			; array of unsigned short	: unsigned 16-bit
	Const FIT_INT16=3			; array of short			: signed 16-bit
	Const FIT_UINT32=4			; array of unsigned long	: unsigned 32-bit
	Const FIT_INT32=5			; array of long				: signed 32-bit
	Const FIT_FLOAT=6			; array of Float			: 32-bit IEEE floating point
	Const FIT_DOUBLE=7			; array of double			: 64-bit IEEE floating point
	Const FIT_COMPLEX=8			; array of FICOMPLEX		: 2 x 64-bit IEEE floating point
	
	; **** Image Colour Type Constants
	Const FIC_MINISWHITE=0		; min value is white
	Const FIC_MINISBLACK=1		; min value is black
	Const FIC_RGB=2				; RGB Color model
	Const FIC_PALETTE=3			; Color map indexed
	Const FIC_RGBALPHA=4		; RGB Color model with alpha channel
	Const FIC_CMYK=5			; CMYK Color model
	
	; **** Colour Channel Constants
	Const FICC_RGB=0			; Use red, green And blue channels
	Const FICC_RED=1			; Use red channel
	Const FICC_GREEN=2			; Use green channel
	Const FICC_BLUE=3			; Use blue channel
	Const FICC_ALPHA=4			; Use alpha channel
	Const FICC_BLACK=5			; Use black channel
	Const FICC_REAL=6			; Complex images: use real part
	Const FICC_IMAG=7			; Complex images: use imaginary part
	Const FICC_MAG=8			; Complex images: use magnitude
	Const FICC_PHASE=9		
	
	; **** Colour Quantize Constants
	Const FIQ_WUQUANT=0			; Xiaolin Wu Color quantization algorithm
	Const FIQ_NNQUANT=1			; NeuQuant neural-net quantization algorithm by Anthony Dekker
	
	; **** Dither Constants
	Const FID_FS=0				; Floyd & Steinberg error diffusion
	Const FID_BAYER4x4=1		; Bayer ordered dispersed dot dithering (order 2 dithering matrix)
	Const FID_BAYER8x8=2		; Bayer ordered dispersed dot dithering (order 3 dithering matrix)
	Const FID_CLUSTER6x6=3		; Ordered clustered dot dithering (order 3 - 6x6 matrix)
	Const FID_CLUSTER8x8=4		; Ordered clustered dot dithering (order 4 - 8x8 matrix)
	Const FID_CLUSTER16x16=5	; Ordered clustered dot dithering (order 8 - 16x16 matrix)
	
	; **** Resample Consrants
	Const FILTER_BOX=0			; Box, pulse, Fourier window, 1st order (constant) b-spline
	Const FILTER_BICUBIC=1		; Mitchell & Netravali's two-param cubic filter
	Const FILTER_BILINEAR=2		; Bilinear filter
	Const FILTER_BSPLINE=3		; 4th order (cubic) b-spline
	Const FILTER_CATMULLROM=4	; Catmull-Rom spline, Overhauser spline
	Const FILTER_LANCZOS3=5		; Lanczos3 filter
	
	; **** Loading/Saving Flag Constants 
	Const BMP_DEFAULT=0
	Const BMP_SAVE_RLE=1
	Const CUT_DEFAULT=0
	Const DDS_DEFAULT=0
	Const ICO_DEFAULT=0
	Const IFF_DEFAULT=0
	Const JPEG_DEFAULT=0
	Const JPEG_FAST=1
	Const JPEG_ACCURATE=2
	Const JPEG_QUALITYSUPERB=$80
	Const JPEG_QUALITYGOOD=$100
	Const JPEG_QUALITYNORMAL=$200
	Const JPEG_QUALITYAVERAGE=$400
	Const JPEG_QUALITYBAD=$800
	Const KOALA_DEFAULT=0
	Const LBM_DEFAULT=0
	Const MNG_DEFAULT=0
	Const PCD_DEFAULT=0
	Const PCD_BASE=1				; load the bitmap sized 768 x 512
	Const PCD_BASEDIV4=2			; load the bitmap sized 384 x 256
	Const PCD_BASEDIV16=3			; load the bitmap sized 192 x 128
	Const PCX_DEFAULT=0
	Const PNG_DEFAULT=0
	Const PNG_IGNOREGAMMA=1			; avoid gamma correction
	Const PNM_DEFAULT=0
	Const PNM_SAVE_RAW=0			; If set the writer saves in RAW format (i.e. P4, P5 Or P6)
	Const PNM_SAVE_ASCII=1			; If set the writer saves in ASCII format (i.e. P1, P2 Or P3)
	Const PSD_DEFAULT=0
	Const RAS_DEFAULT=0
	Const TARGA_DEFAULT=0
	Const TARGA_LOAD_RGB888=1       ; If set the loader converts RGB555 And ARGB8888 -> RGB888.
	Const TIFF_DEFAULT=0
	Const TIFF_CMYK=$1				; reads/stores tags For separated CMYK (use | To combine with compression flags)
	Const TIFF_PACKBITS=$100  		; save using PACKBITS compression
	Const TIFF_DEFLATE=$200 	 	; save using DEFLATE compression
	Const TIFF_ADOBE_DEFLATE=$400	; save using ADOBE DEFLATE compression
	Const TIFF_NONE=$800  			; save without any compression
	Const WBMP_DEFAULT=0
	Const XBM_DEFAULT=0
	Const XPM_DEFAULT=0


;
; Fast Save JPG Images (24Bit Only).
;
;Function SaveJPG(Image, File$, Compression%=JPEG_QUALITYSUPERB)
;
;	; Compression:
;	; JPEG_QUALITYSUPERB 	= Superb Quality, Low Compression (100:1)
;	; JPEG_QUALITYGOOD 		= Good Quality, Low-Medium Compression (75:1) (DEFAULT)
;	; JPEG_QUALITYNORMAL 	= Normal Quality, Normal Compression (50:1)
;	; JPEG_QUALITYAVERAGE	= Average Quality, Normal-High Compression (25:1)
;	; JPEG_QUALITYBAD			= Poor Quality, High Compression (10:1)
;
;	If Image
;		FIBITMAP = FIQSend(Image)
;		Suc = FI_Save(FIF_JPEG,FIBITMAP,File$,Compression)
;		FI_Unload FIBITMAP
;		If Not Suc Then RuntimeError "JPG Save Failed" + Chr(10) + File$
;	Else
;		RuntimeError "Image Instance Not Found (JPG Save)"
;	EndIf
;
;End Function


;
; Fast Save TGA Images (24Bit Only).
;
Function SaveTGA(Image, File$)

	If Image
		FIBITMAP = FIQSend(Image)
		Suc = FI_Save(FIF_TARGA,FIBITMAP,File$,0)
		FI_Unload FIBITMAP
		Return Suc
	Else
		RuntimeError "Image Instance Not Found (TGA Save)" 
	EndIf

End Function

;
; Fast Save TIFF Images (24Bit Only).
;
;Function SaveTIFF(Image, File$, Compression%=TIFF_NONE)
;
;	; Compression
;	; TIFF_PACKBITS 			= save using PACKBITS compression
;	; TIFF_DEFLATE		 		= save using DEFLATE compression
;	; TIFF_ADOBE_DEFLATE	= save using ADOBE DEFLATE compression
;	; TIFF_NONE		  			= save without any compression (DEFAULT)
;
;	If Image
;		FIBITMAP = FIQSend(Image)
;		Suc = FI_Save(FIF_TIFF,FIBITMAP,File$,Compression)
;		FI_Unload FIBITMAP
;		If Not Suc Then RuntimeError "TIFF Save Failed" + Chr(10) + File$
;	Else
;		RuntimeError "Image Instance Not Found (TIFF Save)" 
;	EndIf	
;
;End Function


;
; Fast Save BMP Images (24Bit Only).
;
Function SaveBMP(Image, File$)
		
	If Image
		FIBITMAP = FIQSend(Image)
		Suc = FI_Save(FIF_BMP,FIBITMAP,File$,0)
		FI_Unload FIBITMAP
		Return Suc
	Else
		RuntimeError "Image Instance Not Found (BMP Save)" 
	EndIf

End Function


;
; Fast Save PNG Images (24Bit Only).
;
Function SavePNG(Image, File$)
	
	If Image
		FIBITMAP = FIQSend(Image)		
		Suc = FI_Save(FIF_PNG,FIBITMAP,File,0)
		FI_Unload FIBITMAP
		Return Suc
	Else
		RuntimeError "Image Instance Not Found (PNG Save)" 
	EndIf

End Function


;
; Load image file.
;
Function FILoadImage(File$)

	Local FI_FIFType%, FIBITMAP%
	Local FI_n%, ImType$, ImageBank
	Local FIF_FLAG%
	
	FI_FIFType = FI_GetFileType(File$,0)
	If FI_FIFType = -1
		FI_FIFType = FI_GetFIFFromFilename(File$)
		If FI_FIFType = -1 Then Return 0 ; ** FAIL
	EndIf
	
	If FI_FIFType = 0 Then FIF_FLAG = PNG_AVOIDGAMMA
	
	FIBITMAP = FI_Load(FI_FIFType,File$,FIF_FLAG)
	If FIBITMAP = 0 Then Return 0 ; ** FAIL

	FIImage = New ImageList
	
	; Get file type (extension), name and directory.
	For FI_n = Len(File$) To 1 Step -1
		If Mid(File$,FI_n,1) = "."
			ImType$ = Upper(Right(File$,Len(File$)-FI_n))
		EndIf
		
		If Mid(File$,FI_n,1) = ""
			FIImage\FileName = Right(File$,Len(File$)-FI_n)
			FIImage\FileDirectory = Left(File$,FI_n)
			Exit
		EndIf
	Next
	
	If FIImage\FileName = ""
		FIImage\FileName = File$
		FIImage\FileDirectory = CurrentDir()
	EndIf

	PopulateFIType FIImage,FIBITMAP
	FIImage\File_Size = FileSize(File$)
	FIImage\ImageFormatDescript = FI_GetFIFDescription(FI_FIFType)
	FIImage\ImageFormat = FI_FIFType
	FIImage\Image = CreateImage(FIImage\Width,FIImage\Height)
	
	iBuffer = ImageBuffer(FIImage\Image)
	LockBuffer iBuffer
	ImageBank = FI_PeekIntEx(iBuffer,72)
	FIImage\DisplayPitch = FI_PeekIntEx(iBuffer,56)
	FIImage\ImageSize = FIImage\Height*FIImage\Pitch
	FIImage\Depth = FI_PeekIntEx(iBuffer,100)
	FIImage\RedMask = FI_PeekIntEx(iBuffer,112)
	FIImage\GreenMask = FI_PeekIntEx(iBuffer,116)
	FIImage\BlueMask = FI_PeekIntEx(iBuffer,120)
	FI_ConvertToRawBits3D ImageBank,FIBITMAP,FIImage\DisplayPitch,FIImage\Depth,FIImage\RedMask,FIImage\GreenMask,FIImage\BlueMask,0
	UnlockBuffer iBuffer

	Image = FIImage\Image
	FI_Unload FIBITMAP
	Delete FIImage
	Return Image

End Function


;
;
;
Function FI_PeekIntEx(iBuffer, Offset)

	RtlMoveMemory_From Int32,iBuffer+Offset,4
	Return PeekInt(Int32,0)

End Function

;
;
;
Function FI_PeekWordEx(iBuffer, Offset)

	RtlMoveMemory_From FI_Word,iBuffer+Offset,2
	Return PeekShort(FI_Word,0)

End Function


;
; Quickly Sends a Blitz Image to an FIBITMAP Instance.
;
Function FIQSend(Image)

	Local iBuffer%, iPitch%, iFormat%
	Local FIBITMAP%, nFIBITMAP%, iBank%

	iBuffer = ImageBuffer(Image)
	LockBuffer iBuffer
	iBank = FI_PeekIntEx(iBuffer,72)
	iPitch = FI_PeekIntEx(iBuffer,56)
	FIBITMAP = FI_ConvertFromRawBits3D(iBank,ImageWidth(Image),ImageHeight(Image),iPitch,FI_PeekIntEx(iBuffer,100),FI_PeekIntEx(iBuffer,112),FI_PeekIntEx(iBuffer,116),FI_PeekIntEx(iBuffer,120),0)
	UnlockBuffer iBuffer
		
	If FI_GetBPP(FIBITMAP) <> 24
		nFIBITMAP = FI_ConvertTo24Bits(FIBITMAP)
		If nFIBITMAP
			FI_Unload FIBITMAP
			FIBITMAP = nFIBITMAP
		EndIf
	EndIf
	
	Return FIBITMAP

End Function

;
; Quickly Returns an FIBITMAP Instance to a BlitzImage.
;
Function FIQGrab(Image, FIBITMAP)

	Local iBuffer%, iPitch%, iFormat%
	Local iBank%

	iBuffer = ImageBuffer(Image)
	LockBuffer iBuffer
	iBank = FI_PeekIntEx(iBuffer,72)
	iPitch = FI_PeekIntEx(iBuffer,56)
	iFormat = FI_PeekIntEx(iBuffer,104)
	FI_ConvertToRawBits3D iBank,FIBITMAP,iPitch,FIB_BitDepth[iFormat],FIB_RedMask[iFormat],FIB_GreenMask[iFormat],FIB_BlueMask[iFormat],0
	UnlockBuffer iBuffer

End Function


;
; Populate the FreeImage Type Instance (.ImageList).
;
Function PopulateFIType(FI.ImageList, FIBITMAP)

	FI\Width				= FI_GetWidth(FIBITMAP)	
	FI\Height				= FI_GetHeight(FIBITMAP)
	FI\BitCount			= FI_GetBPP(FIBITMAP)
	FI\Pitch				= FI_GetPitch(FIBITMAP)
	FI\ClrUsed			= FI_GetColorsUsed(FIBITMAP)
	FI\FIRedMask		= FI_GetRedMask(FIBITMAP)
	FI\FIGreenMask	= FI_GetGreenMask(FIBITMAP)
	FI\FIBlueMask		= FI_GetBlueMask(FIBITMAP)
	FI\DIBSize			= FI_GetDIBSize(FIBITMAP)

End Function


;
;
;
Function ComputeCRC()

	c = CreateBank(4)
	
	For n=0 To 255
		PokeInt c,0,n
		
		For k = 0 To 7
			If (PeekInt(c,0) And 1)
				PokeInt c,0,($EDB88320 Xor (PeekInt(c,0) Shr 1))
			Else
				PokeInt c,0,(PeekInt(c,0) Shr 1)
			EndIf
		Next
		
		CRC_Table(n) = CreateBank(4)
		PokeInt CRC_Table(n),0,PeekInt(c,0)
	Next
	
	FreeBank c

End Function


P.S. It seems a bit strange that Blitz3D can't save PNG images natively, given that it uses FreeImage itself. :/