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:
P.S. It seems a bit strange that Blitz3D can't save PNG images natively, given that it uses FreeImage itself. :/
; 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. :/