Loading+a+PNG+or+TIFF+image

//by Richard Russell, October 2008, revised November 2010 and January 2012//

The article Loading a GIF or JPEG image lists a replacement for the **LoadImage** API which works for JPEG, GIF, ICO, EMF and WMF images as well as for BMP images. However two common still-image formats not supported by that function are **PNG** and **TIFF**.

The two functions listed below each load PNG and TIFF images in addition to the others (since there's a large variety of different TIFF formats it may load only the more common variants). They require **GDI+** to be installed on the target PC.

Method 1
This version is the simpler of the two, but can't cope with very large images and, when scaling is required, the quality is not as good as it might be: code format="bb4w" DEF FNloadimagegdip(file$, RETURN cx%, RETURN cy%, par%) LOCAL tSI{}, bmp{}, ix%, iy%, hbm%, bmp%, hcopy%, gdip%, text%, res%, lGDIP% DIM text% LOCAL 513 SYS "MultiByteToWideChar", 0, 0, file$, -1, text%, 256 SYS "LoadLibrary", "GDIPLUS.DLL" TO gdip% IF gdip% = 0 ERROR 100, "Couldn't load GDIPLUS.DLL" SYS "GetProcAddress", gdip%, "GdiplusStartup" TO `GdiplusStartup` SYS "GetProcAddress", gdip%, "GdipCreateBitmapFromFile" TO `GdipCreateBitmapFromFile` SYS "GetProcAddress", gdip%, "GdipCreateHBITMAPFromBitmap" TO `GdipCreateHBITMAPFromBitmap` SYS "GetProcAddress", gdip%, "GdipDisposeImage" TO `GdipDisposeImage` SYS "GetProcAddress", gdip%, "GdiplusShutdown" TO `GdiplusShutdown` DIM tSI{GdiplusVersion%, DebugEventCallback%, \ \      SuppressBackgroundThread%, SuppressExternalCodecs%} tSI.GdiplusVersion% = 1 SYS `GdiplusStartup`, ^lGDIP%, tSI{}, 0 SYS `GdipCreateBitmapFromFile`, text%, ^bmp% TO res% IF res% THEN SYS `GdiplusShutdown`, lGDIP% SYS "FreeLibrary", gdip% =0     ENDIF SYS `GdipCreateHBITMAPFromBitmap`, bmp%, ^hbm% TO res% IF res% THEN SYS `GdipDisposeImage`, bmp% SYS `GdiplusShutdown`, lGDIP% SYS "FreeLibrary", gdip% =0     ENDIF DIM bmp{bmType%,bmWidth%,bmHeight%,bmWidthBytes%,bmPlanes{l&,h&}, \ \      bmBitsPixel{l&,h&}, bmBits%} SYS "GetObject", hbm%, DIM(bmp{}), bmp{} ix% = bmp.bmWidth% iy% = bmp.bmHeight% IF cx%=0 cx% = ix% IF cy%=0 cy% = iy% IF par% THEN IF cx%/cy%>ix%/iy% cx% = cy%*ix%/iy% ELSE cy% = cx%*iy%/ix% ENDIF SYS "CopyImage", hbm%, 0, cx%, cy%, 0 TO hcopy% SYS "DeleteObject", hbm% SYS `GdipDisposeImage`, bmp% SYS `GdiplusShutdown`, lGDIP% SYS "FreeLibrary", gdip% = hcopy% code

Method 2
This version is a little more complicated, but can cope with very large images (potentially greater than 10000 x 10000 pixels) and scales the image, when required, with a higher quality: code format="bb4w" DEF FNloadimagegdip2(file$, RETURN cx%, RETURN cy%, par%) LOCAL tSI{}, bmi{}, text%, gdip%, image%, ix%, iy%, lGDIP% LOCAL hdc%, hdib%, old%, gfx%, bits% DIM text% LOCAL 513 SYS "MultiByteToWideChar", 0, 0, file$, -1, text%, 256 SYS "LoadLibrary", "GDIPLUS.DLL" TO gdip% IF gdip% = 0 ERROR 100, "Couldn't load GDIPLUS.DLL" SYS "GetProcAddress", gdip%, "GdiplusStartup" TO `GdiplusStartup` SYS "GetProcAddress", gdip%, "GdipLoadImageFromFile" TO `GdipLoadImageFromFile` SYS "GetProcAddress", gdip%, "GdipDrawImageRectRectI" TO `GdipDrawImageRectRectI` SYS "GetProcAddress", gdip%, "GdipGetImageHeight" TO `GdipGetImageHeight` SYS "GetProcAddress", gdip%, "GdipGetImageWidth" TO `GdipGetImageWidth` SYS "GetProcAddress", gdip%, "GdipCreateFromHDC" TO `GdipCreateFromHDC` SYS "GetProcAddress", gdip%, "GdipSetSmoothingMode" TO `GdipSetSmoothingMode` SYS "GetProcAddress", gdip%, "GdipDeleteGraphics" TO `GdipDeleteGraphics` SYS "GetProcAddress", gdip%, "GdipDisposeImage" TO `GdipDisposeImage` SYS "GetProcAddress", gdip%, "GdiplusShutdown" TO `GdiplusShutdown` DIM tSI{GdiplusVersion%, DebugEventCallback%, \ \      SuppressBackgroundThread%, SuppressExternalCodecs%} DIM bmi{biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \ \      biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \ \      biClrUsed%, biClrImportant%} tSI.GdiplusVersion% = 1 SYS `GdiplusStartup`, ^lGDIP%, tSI{}, 0 SYS `GdipLoadImageFromFile`, text%, ^image% IF image% = 0 THEN SYS `GdiplusShutdown`, lGDIP% SYS "FreeLibrary", gdip% =0     ENDIF SYS `GdipGetImageWidth`, image%, ^ix% SYS `GdipGetImageHeight`, image%, ^iy% IF cx% = 0 THEN cx% = ix% IF cy% = 0 THEN cy% = iy% IF par% THEN IF cx%/cy%>ix%/iy% cx% = cy%*ix%/iy% ELSE cy% = cx%*iy%/ix% ENDIF bmi.biSize% = DIM(bmi{}) bmi.biWidth% = cx% bmi.biHeight% = -cy% bmi.biPlanes.l& = 1 bmi.biBitCount.l& = 24 SYS "CreateCompatibleDC", 0 TO hdc% IF hdc% = 0 ERROR 92, "CreateCompatibleDC failed" SYS "CreateDIBSection", hdc%, bmi{}, 0, ^bits%, 0, 0 TO hdib% IF hdib%=0 ERROR 91, "CreateDIBSection failed" SYS "SelectObject", hdc%, hdib% TO old% SYS "PatBlt", hdc%, 0, 0, cx%, cy%, &FF0062 : REM WHITENESS SYS `GdipCreateFromHDC`, hdc%, ^gfx% IF gfx%=0 ERROR 90, "GdipCreateFromHDC failed" SYS `GdipSetSmoothingMode`, gfx%, 2 SYS `GdipDrawImageRectRectI`,gfx%,image%,0,0,cx%,cy%,0,0,ix%,iy%,2,0,0,0 SYS "SelectObject", hdc%, old% SYS "DeleteDC", hdc% SYS `GdipDeleteGraphics`, gfx% SYS `GdipDisposeImage`, image% SYS `GdiplusShutdown`, lGDIP% SYS "FreeLibrary", gdip% = hdib% code

How to use
The parameters to the functions are the filename of the image, the required width and height (in pixels) and a flag to indicate whether you want the **aspect ratio** to be preserved or not. The returned value is a **handle** to the image, or zero if the image cannot be found or opened.

If the required width and/or height values are supplied as zero then the actual width and/or height of the original image are used. Thus if a non-zero value is supplied for the width and zero is supplied for the height, the image will be scaled horizontally (if required) but not vertically; however if the **preserve aspect ratio** flag is set to TRUE, the then image //will// be scaled vertically to preserve the correct shape.

The actual dimensions of the (possibly scaled) image are returned in the second and third parameters, therefore they must be supplied as **variables** rather than as **constants**. Thus if you don't want to scale the image call the function as follows

code format="bb4w" cx% = 0 cy% = 0 himage% = FNloadimagegdip2(filename$, cx%, cy%, FALSE) code Alternatively if you are not interested in knowing the actual dimensions you can modify the function definition by removing the **RETURN** qualifiers:

code format="bb4w" DEF FNloadimagegdip2(file$, cx%, cy%, par%) code With this modification you can supply constant values (e.g. **0**) as parameters.

When you have completely finished with the image you should delete the handle as follows:

code format="bb4w" SYS "DeleteObject", himage% code To use the **FNloadimagegdip** function to display an image in a **static control** see this article.

To access the data of the image it is better to use **Method 2** (in which the bitmap is stored in a **DIB Section**). The local variable **bits%** points to the bitmap data, so that could either be changed to a global or returned from the function as another RETURN parameter. The data is in 'top-down' format as RGB triplets, although a different format could be used by modifying the contents of the **bmi{}** structure.