=====Saving a JPEG image=====
//by Richard Russell, December 2006//\\ \\ The main BBC BASIC for Windows documentation explains how to [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwine.html#gifjpeg|display a JPEG image]] and elsewhere you can find out how to [[/Displaying%20a%20JPEG%20or%20GIF%20in%20a%20picture%20box|load a JPEG image]] for use in your program. This article describes how to **save** an image as a JPEG file. It relies on the presence of the //GDI Plus// library so will work only on Windows XP (or later) or if you have specifically installed **GDIPLUS.DLL** on the target computer. Microsoft permits you to redistribute this file so you can include it with your program if necessary.\\ \\ To begin with you need to have a **handle** to the bitmap you want to save (see below if instead the bitmap is in the form of a **DIB**). One way of obtaining a handle is to load the image from a file (e.g. a BMP file):
bmpfile$ = "\Windows\Soap Bubbles.bmp"
SYS "LoadImage", 0, bmpfile$, 0, 0, 0, 16 TO hbitmap%
Here the image is loaded at its original size. You can alternatively scale the image to different dimensions:
bmpfile$ = "\Windows\Soap Bubbles.bmp"
SYS "LoadImage", 0, bmpfile$, 0, dx%, dy%, 16 TO hbitmap%
Where **dx%** and **dy%** are the wanted width and height of the image respectively (the scaling quality is not particularly good so for best results you might prefer to scale the image using a third-party program).\\ \\ There are a number of other ways in which you might obtain a bitmap handle, which are outside the scope of this article. You can easily obtain a handle to whatever is displayed in your program's output window:
SYS "GetCurrentObject", @memhdc%, 7 TO hbitmap%
but this ordinarily returns the //entire// 1920 x 1440 bitmap which is probably not what you want. To save just a region of your program's output window the easiest way is probably to save it first as a BMP file (using ***GSAVE**) then load it using LoadImage as shown above.\\ \\ Once you've got a handle to the bitmap you simply save it as a JPEG file as follows:
PROCsavejpeg(hbitmap%, filename$, quality%)
Here **filename$** is the name of the JPEG file to create and **quality%** is a measure of relative quality from 1 (bad) to 100 (good). The better the quality the larger the file that will be created.\\ \\ One you've saved the file you should delete the bitmap handle:
SYS "DeleteObject", hbitmap%
Finally, here's the code for **PROCsavejpeg** itself:
DEF PROCsavejpeg(hbitmap%, filename$, quality%)
LOCAL gdiplus%, ole32%
SYS "LoadLibrary", "GDIPLUS.DLL" TO gdiplus%
IF gdiplus% = 0 ERROR 100, "Couldn't load GDIPLUS.DLL"
SYS "GetProcAddress", gdiplus%, "GdiplusStartup" TO `GdiplusStartup`
SYS "GetProcAddress", gdiplus%, "GdiplusShutdown" TO `GdiplusShutdown`
SYS "GetProcAddress", gdiplus%, "GdipCreateBitmapFromHBITMAP" TO `GdipCreateBitmapFromHBITMAP`
SYS "GetProcAddress", gdiplus%, "GdipDisposeImage" TO `GdipDisposeImage`
SYS "GetProcAddress", gdiplus%, "GdipSaveImageToFile" TO `GdipSaveImageToFile`
SYS "LoadLibrary", "OLE32.DLL" TO ole32%
SYS "GetProcAddress", ole32%, "CLSIDFromString" TO `CLSIDFromString`
LOCAL tSI{}, tParams{}, lRes%, lGDIP%, lBitmap%, tJpgEncoder%, guid%, filename%
DIM tJpgEncoder% LOCAL 15, guid% LOCAL 79, filename% LOCAL 2*LEN(filename$)+1
DIM tParams{Count%, Guid&(15), NumberOfValues%, Type%, Value%}
DIM tSI{GdiplusVersion%, DebugEventCallback%, \
\ SuppressBackgroundThread%, SuppressExternalCodecs%}
REM Initialize GDI+
tSI.GdiplusVersion% = 1
SYS `GdiplusStartup`, ^lGDIP%, tSI{}, 0 TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Create the GDI+ bitmap from the image handle
SYS `GdipCreateBitmapFromHBITMAP`, hbitmap%, 0, ^lBitmap% TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Initialize the encoder GUID
SYS "MultiByteToWideChar", 0, 0, "{557CF401-1A04-11D3-9A73-0000F81EF32E}", -1, guid%, 40
SYS `CLSIDFromString`, guid%, tJpgEncoder%
REM Initialize the encoder parameters
tParams.Count% = 1
SYS "MultiByteToWideChar", 0, 0, "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}", -1, guid%, 40
SYS `CLSIDFromString`, guid%, ^tParams.Guid&(0)
tParams.NumberOfValues% = 1
tParams.Type% = 4
tParams.Value% = ^quality%
REM Save the image
SYS "MultiByteToWideChar", 0, 0, filename$, -1, filename%, LEN(filename$)+1
SYS `GdipSaveImageToFile`, lBitmap%, filename%, tJpgEncoder%, tParams{} TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Destroy the bitmap
SYS `GdipDisposeImage`, lBitmap%
REM Shutdown GDI+
SYS `GdiplusShutdown`, lGDIP%
SYS "FreeLibrary", gdiplus%
ENDPROC
If, instead of a bitmap **handle**, you have a bitmap (DIB) stored in memory you can use this alternative routine:
DEF PROCsavejpegdib(dib%, bmi%, filename$, quality%)
LOCAL gdiplus%, ole32%
SYS "LoadLibrary", "GDIPLUS.DLL" TO gdiplus%
IF gdiplus% = 0 ERROR 100, "Couldn't load GDIPLUS.DLL"
SYS "GetProcAddress", gdiplus%, "GdiplusStartup" TO `GdiplusStartup`
SYS "GetProcAddress", gdiplus%, "GdiplusShutdown" TO `GdiplusShutdown`
SYS "GetProcAddress", gdiplus%, "GdipCreateBitmapFromGdiDib" TO `GdipCreateBitmapFromGdiDib`
SYS "GetProcAddress", gdiplus%, "GdipDisposeImage" TO `GdipDisposeImage`
SYS "GetProcAddress", gdiplus%, "GdipSaveImageToFile" TO `GdipSaveImageToFile`
SYS "LoadLibrary", "OLE32.DLL" TO ole32%
SYS "GetProcAddress", ole32%, "CLSIDFromString" TO `CLSIDFromString`
LOCAL tSI{}, tParams{}, lRes%, lGDIP%, lBitmap%, tJpgEncoder%, guid%, filename%
DIM tJpgEncoder% LOCAL 15, guid% LOCAL 79, filename% LOCAL 2*LEN(filename$)+1
DIM tParams{Count%, Guid&(15), NumberOfValues%, Type%, Value%}
DIM tSI{GdiplusVersion%, DebugEventCallback%, \
\ SuppressBackgroundThread%, SuppressExternalCodecs%}
REM Initialize GDI+
tSI.GdiplusVersion% = 1
SYS `GdiplusStartup`, ^lGDIP%, tSI{}, 0 TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Create the GDI+ bitmap from the DIB
SYS `GdipCreateBitmapFromGdiDib`, bmi%, dib%, ^lBitmap% TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Initialize the encoder GUID
SYS "MultiByteToWideChar", 0, 0, "{557CF401-1A04-11D3-9A73-0000F81EF32E}", -1, guid%, 40
SYS `CLSIDFromString`, guid%, tJpgEncoder%
REM Initialize the encoder parameters
tParams.Count% = 1
SYS "MultiByteToWideChar", 0, 0, "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}", -1, guid%, 40
SYS `CLSIDFromString`, guid%, ^tParams.Guid&(0)
tParams.NumberOfValues% = 1
tParams.Type% = 4
tParams.Value% = ^quality%
REM Save the image
SYS "MultiByteToWideChar", 0, 0, filename$, -1, filename%, LEN(filename$)+1
SYS `GdipSaveImageToFile`, lBitmap%, filename%, tJpgEncoder%, tParams{} TO lRes%
IF lRes% ERROR 100, "GDI+ error "+STR$(lRes%)
REM Destroy the bitmap
SYS `GdipDisposeImage`, lBitmap%
REM Shutdown GDI+
SYS `GdiplusShutdown`, lGDIP%
SYS "FreeLibrary", gdiplus%
ENDPROC
You would call it as follows:
PROCsavejpegdib(dibits%, bmi{}, filename$, quality%)
where **dibits%** is the address of the bitmap data and **bmi{}** is a BITMAPINFO structure containing the dimensions etc. and (optionally) colour palette for the bitmap.