Actually using the amended library turned up several issues. In particular, it required that all the windows you need should be created right at the start of the program, which did not suit me at all. I have, therefore, made a couple of small changes. I have also written a sort of "User Guide", which follows the library and which I hope will be useful to anyone wanting to use the library. (Most of the comments also apply to Richard's original IMGLIB.
Code: Select all
REM Initialise library
DEFPROC_imgInit(dchandles%)
LOCALT%:DIMT%LOCAL15
SYS"GetModuleHandle","GDI32.DLL"TOL@imglib%
SYS"GetProcAddress",L@imglib%,"SetBoundsRect"TO`SetBoundsRect`
SYS"GetProcAddress",L@imglib%,"GetBoundsRect"TO`GetBoundsRect`
SYS"GetModuleHandle","USER32.DLL"TOL@imglib%
SYS"GetProcAddress",L@imglib%,"OffsetRect"TO`OffsetRect`
SYS"GetProcAddress",L@imglib%,"InvalidateRect"TO`InvalidateRect`
SYS"LoadLibrary","GDIPLUS.DLL"TOL@imglib%
IFL@imglib%=0 ERROR100,"Couldn't load GDIPLUS.DLL"
SYS"GetProcAddress",L@imglib%,"GdiplusStartup"TO`GdiplusStartup`
SYS"GetProcAddress",L@imglib%,"GdipLoadImageFromFile"TO`GdipLoadImageFromFile`
SYS"GetProcAddress",L@imglib%,"GdipDrawImageFX"TO`GdipDrawImageFX`
SYS"GetProcAddress",L@imglib%,"GdipCreateMatrix2"TO`GdipCreateMatrix2`
SYS"GetProcAddress",L@imglib%,"GdipRotateMatrix"TO`GdipRotateMatrix`
SYS"GetProcAddress",L@imglib%,"GdipTranslateMatrix"TO`GdipTranslateMatrix`
SYS"GetProcAddress",L@imglib%,"GdipScaleMatrix"TO`GdipScaleMatrix`
SYS"GetProcAddress",L@imglib%,"GdipDeleteMatrix"TO`GdipDeleteMatrix`
SYS"GetProcAddress",L@imglib%,"GdipGetImageHeight"TO`GdipGetImageHeight`
SYS"GetProcAddress",L@imglib%,"GdipGetImageWidth"TO`GdipGetImageWidth`
SYS"GetProcAddress",L@imglib%,"GdipCreateFromHDC"TO`GdipCreateFromHDC`
SYS"GetProcAddress",L@imglib%,"GdipSetInterpolationMode"TO`GdipSetInterpolationMode`
SYS"GetProcAddress",L@imglib%,"GdipDeleteGraphics"TO`GdipDeleteGraphics`
SYS"GetProcAddress",L@imglib%,"GdipDisposeImage"TO`GdipDisposeImage`
SYS"GetProcAddress",L@imglib%,"GdiplusShutdown"TO`GdiplusShutdown`
SYS"GetProcAddress",L@imglib%,"GdipCreateImageAttributes"TO`GdipCreateImageAttributes`
SYS"GetProcAddress",L@imglib%,"GdipDisposeImageAttributes"TO`GdipDisposeImageAttributes`
SYS"GetProcAddress",L@imglib%,"GdipSetImageAttributesColorMatrix"TO`GdipSetImageAttributesColorMatrix`
!T%=1
SYS`GdiplusStartup`,^T@imglib%,T%, 0
DIMdchandle%(dchandles%)
PROCsetdc(0)
ENDPROC
:
DEFPROCsetdc(handle%)
SYS`GdipCreateFromHDC`,@memhdc%,^dchandle%(handle%)
IFdchandle%(handle%)=0ERROR 101,"Couldn't initialise window for GDIP"
SYS`GdipSetInterpolationMode`,dchandle%(handle%),3:REM bilinear
ENDPROC
:
REM Returns 0 if picture failed to load
DEFFN_imgLoad(img$)
LOCALI%,T%:DIMI%22,T%LOCAL513:I%=(I%+3)AND-4
SYS"MultiByteToWideChar",0,0,img$,-1,T%,256
SYS`GdipLoadImageFromFile`,T%,I%
IF!I%=0THEN:=0
SYS`GdipGetImageWidth`,!I%,I%+4
SYS`GdipGetImageHeight`,!I%,I%+8
I%!16=FN_imgPrivate(I%)
=I%
:
REM Multiply the image's R, G, B, A values by the specified factors:
DEFPROC_imgMult(I%,r,g,b,a)
LOCALC%:DIMC%LOCAL99
IFI%!12=0SYS`GdipCreateImageAttributes`,I%+12
C%!0=FN_f4(r):C%!24=FN_f4(g):C%!48=FN_f4(b):C%!72=FN_f4(a)
SYS`GdipSetImageAttributesColorMatrix`,I%!12,1,1,C%,0,0
ENDPROC
:
DEFPROC_imgPlot(I%,x,y,s,t,a,hand%)
LOCALM%,R%:DIMR%LOCAL15
x+=@vdu.o.x%:y+=@vdu.o.y%
SYS`GdipCreateMatrix2`,&3F800000,FALSE,FALSE,&3F800000,FN_f4(x/2),FN_f4(@vdu%!212-y/2),^M%
SYS`GdipRotateMatrix`,M%,FN_f4(a),0
SYS`GdipScaleMatrix`,M%,FN_f4(s),FN_f4(t),0
REM Use the line below to use the centre point of the picture
REMSYS`GdipTranslateMatrix`,M%,FN_f4(-I%!4/2),FN_f4(-I%!8/2),0
REM Use the line below to use the top-left corner of the picture
SYS`GdipTranslateMatrix`,M%,0,0,0
SYS`SetBoundsRect`,@memhdc%,0,5
SYS`GdipDrawImageFX`,dchandle%(hand%),!I%,0,M%,0,I%!12,2
SYS`GetBoundsRect`,@memhdc%,R%,0
SYS`OffsetRect`,R%,-@ox%,-@oy%
SYS`InvalidateRect`,@hwnd%,R%,0
SYS`GdipDeleteMatrix`,M%
ENDPROC
:
DEFFN_imgPrivate(L%)
PRIVATEP%
SWAPL%,P%
=L%
:
REM Convert to 4-byte float:
DEFFN_f4(a#)LOCALp%%:p%%=^a#:IFABSa#<1E-38THEN=FALSE
=p%%!4ANDNOT&7FFFFFFFOR(p%%!4-&38000000<<3OR!p%%>>29AND7)+(!p%%>>28AND1)
:
DEFPROC_imgExit(dchandles%)
LOCALP%
P%=FN_imgPrivate(0)
WHILEP%
SYS`GdipDisposeImage`,!P%
IFP%!12SYS`GdipDisposeImageAttributes`,P%!12
P%=P%!16
ENDWHILE
FORi%=0TOdchandles%
PROCdeleteimghandle(i%)
NEXT
SYS`GdiplusShutdown`,T@imglib%
SYS"FreeLibrary",L@imglib%
ENDPROC
:
DEFPROCdeleteimghandle(i%)
IFdchandle%(i%)>0SYS`GdipDeleteGraphics`,dchandle%(i%):dchandle%(i%)=0
ENDPROC
)
This is the documentation for the IMGLIB library written by Richard Russell and modified by Kendall Down so that it will work with multiple windows. The modified library will also work with a single window. The documentation is the work of Kendall Down and any errors are his responsibility.
PROC_imgInit(dchandles%)
This should be called once at the start of your program but *after* MULTIWIN has been installed and initialised. Here we have ten windows being initialised:
INSTALL @lib$+"MULTIWINMOD"
multiwin%=10
PROC_multiwin(multiwin%)
PROC_imgInit(multiwin%)
Notice, however, that with this initial call to PROC_imgInit an array, dchandles%() is set up with ten elements, but only one window is assigned a DChandle - @hwnd%. As each window must be created *before* it can be assigned a DChandle, it is not always convenient to create all your windows at the start of the program. The routine to assign DChandles is therefore separated from the initialisation and must be called separately for each window as it is created.
Here the first window - window 1 - is created, selected and then a DChandle is assigned.
hw1%=FN_createwin(1,"Presenter window",0,0,cordx%(1)-8,cordy%(1)-60,0,&96C00000,0)
PROC_selectwin(1)
PROCsetdc(1)
================
The corollary is that when you close each window you must delete its associated DChandle.
PROC_selectwin(0)
PROC_closewin(7)
PROCdeleteimghandle(7)
Note that it is always a good idea to select window 0 before you close other windows, for if you try to close a window that is your selected window the results are likely to be unfortunate.
When you close your program, however, you should call PROC_imgExit() once, but with the number of windows as your parameter.
PROC_imgExit(multiwin%)
If any DChandles have not been deleted at this stage, they will be closed and deleted, along with various other bits of housekeeping associated with the library.
================
We now come to the heart of the library, loading and displaying an image as well as subjecting it to various manipulations.
FN_imgLoad(img$)
The parameter is the full file name of the picture you wish to load - for example, "F:\Pictures\Petra\Khazneh01.jpg"
The routine returns a value which *must* be checked. If that value is zero it means that the file name was incorrect or for some other reason the picture could not be loaded. Please note that if you attempt to plot an image with a zero handle BB4W will crash disastrously.
The value is an integer with the convenient attribute that you can find the width and height of the image with two simple operations.
pic%=FN_imgLoad(img$)
width%=pic%!4
height%=pic%!8
You can, of course, use your own variable names. I have merely chosen ones that are meaningful.
To actually display a picture you call PROC_imgPlot(I%,x,y,s,t,a,hand%) with the various parameters
I% is the handle returned by FN_imgLoad
x is the horizontal position of the picture (see below)
y is the vertical position of the picture (see below)
s is the horizontal scale to enlarge or reduce the x-axis
t is the vertical scale to enlarge or reduce the y-axis
a is the rotation in degrees. A positive number rotates clockwise, a negative anti-clockwise
hand% is the multiwin number of the window in which the picture is to be displayed
Richard's original library specified the centre point of the picture and displayed it centred on that point. The jpeg routine given in the Help for BB4W uses the top-left point of the picture and displays it across and down from that point. As my program was already set up in that way I found it more convenient to continue to specify the top-left point. I therefore altered the routine to this:
SYS`GdipTranslateMatrix`,M%,0,0,0
If you prefer to use the centre point - which does have advantages when rotating images - use Richard's original line *instead* of my line.
SYS`GdipTranslateMatrix`,M%,FN_f4(-I%!4/2),FN_f4(-I%!8/2),0
DO NOT attempt to have both lines! I'm not sure what the results would be and I am in no hurry to find out. Note that both lines are in the library and you do not need to retype them - just remove the REM from one and put a REM in front of the other.
The horizontal and vertical scale parameters can be positive or negative. A negative number has the effect of flipping the picture on that axis, so -s will flip on the horizontal axis and -t will flip on the vertical axis. Be aware, however, that if you are using my preference for positioning the picture according to the top-left corner, flipping it in any way will affect the position, whereas using the centre of the picture, its position should remain the same.
If you have - say - three windows and you wish to display your picture in each of those windows, you only load the picture once, but then you would call imgPlot three times like this:
PROC_imgPlot(t%,2*x%+lfx%,-2*y%-sl%+lfy%,s,s,angle,1)
PROC_imgPlot(t%,2*x%+lfx%,-2*y%-sl%+lfy%,s,s,angle,2)
PROC_imgPlot(t%,2*x%+lfx%,-2*y%-sl%+lfy%,s,s,angle,3)
To display the picture in the original window (or if you only have one window) it would be:
PROC_imgPlot(t%,2*x%+lfx%,-2*y%-sl%+lfy%,s,s,angle,0)
Note that the plotting routine is extremely fast and with suitable programming you can grab a corner of a picture and rotate it in real time (though obviously a bigger picture will be slower than a small one!)
================
For most purposes the above is all you need to know. However Richard has written a rather clever routine for manipulating your picture in various ways *before* displaying it.
PROC_imgMult(I%,r,g,b,a)
I% is the image handle returned by FN_imgLoad
r is the red values in the picture
g is the green values in the picture
b is the blue values in the picture
a, somewhat confusingly, is not the angle of the picture but its alpha value
The default values for r, g and b are 1. If you set them all to zero you will get a completely black picture. As you go beyond 1 the colours are increasingly washed out, though, annoyingly, any particularly dark colours will remain visible, even though the colours are horribly distorted. You cannot fade from pure black to pure white just by altering the rgb values.
You can write a simple program which loads an image and then starts off with r g b set to 0 and increase them to some value, then decrease them back to 0 again and the picture will change accordingly, from black to full colour and back to black again.
The default value for a is 1. If you set it to zero you will get a completely white picture, which moves through gray and various washed-out colours to full brightness as a approaches 1. Increasing beyond 1 does not seem to affect the picture in any way.
Annoyingly, starting at 1 and decreasing the value of a does not appear to affect the picture, which continues to be displayed as though a was 1. Alpha controls the transparency (or the opacity) of a colour - in this case, of all the colours simultaneously - so once it is set to fully opaque, plotting a fully or partially transparent picture on top makes no discernable difference! If you clear the screen or otherwise get rid of the picture before re-plotting it, the picture will gradually return to white (or clear or transparent).