Author |
Topic: Bitmap clipper & plotter (BASIC prototypes) (Read 1235 times) |
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Bitmap clipper & plotter (BASIC prototypes)
« Thread started on: Mar 4th, 2012, 3:38pm » |
|
The BASIC prototypes (to be translated into assembly language) for a bitmap clipper and a 'generic' bitmap plotter are contained in the following - hopefully instructive - program which you can copy and paste into the BB4W IDE, and run.
Perhaps someone might like to use these prototypes as a basis for writing their own bitmap plotters. (Hehe, yeah, and pigs might fly.)
http://www.bb4wgames.com/misc/clipbitmap.html
David.
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #1 on: Mar 4th, 2012, 4:56pm » |
|
on Mar 4th, 2012, 3:38pm, David Williams wrote:Perhaps someone might like to use these prototypes as a basis for writing their own bitmap plotters. (Hehe, yeah, and pigs might fly.) |
|
As I mentioned in a recent post, I already have! Mine in fact is a 'sprite' plotter, in that it takes into account the alpha channel of the foreground bitmap (it assumes a pre-multiplied foreground, like all Windows icons do). It also scales (nearest neighbour only, ugh!) and reflects about the x and/or y axis. Most interesting of all, arguably, is that it comes in the form of a position-independent binary module (all of 239 bytes!) that can be loaded anywhere in memory:
http://tech.groups.yahoo.com/group/bb4w/files/Graphics/DrawIconRTR.bin
Here's an adaptation of your program using my plotter:
Code: REM. Sprite plotter and clipper using DrawIconRTR.bin relocatable module
MODE 8 : OFF
ON ERROR OSCLI "REFRESH ON" : ON : CLS : REPORT : PRINT " at line "; ERL : VDU 7 : END
DIM mc% 255
OSCLI "LOAD """ + @lib$ + "DrawIconRTR.bin"" " + STR$~mc%
DrawIconRTR% = mc% + 3
REM. Create then select a 32-bpp ARGB 'DIBSection' (of dimensions
REM. @vdu%!208 by @vdu%!212 pixels) into BB4W's DC:
pBuffer% = FN_createDIBSection( @vdu%!208, @vdu%!212 )
DIM Buffer{T%,W%,H%,B%,P%,M%}
Buffer.W% = @vdu%!208
Buffer.H% = @vdu%!212
Buffer.M% = pBuffer%
REM. Create a 'test' 128x80, 32-bpp ARGB bitmap to be drawn using PROC_drawBitmap
REM. Feel free to experiment with the dimensions
REM. (HIMEM may need to be adjusted if bitmap is too big!)
bmW% = 128
bmH% = 80
bm% = FN_createTestBitmap( bmW%, bmH% )
DIM Bitmap{T%,W%,H%,B%,P%,M%}
Bitmap.W% = bmW%
Bitmap.H% = bmH%
Bitmap.M% = bm%
DIM pt{X%,Y%}
*REFRESH OFF
scale = 1.0
REPEAT
WAIT 2 : CLS
PRINT "Left button to scale up, right button to scale down, middle button to reflect";
SYS "GetCursorPos", pt{}
SYS "ScreenToClient", @hwnd%, pt{}
dx% = bmW%*scale
dy% = bmH%*scale
IF INKEY(-11) dx% = -dx%
IF INKEY(-10) scale *= 1.05
IF INKEY(-12) scale /= 1.05
SYS DrawIconRTR%, Buffer{}, pt.X%-ABS(dx%/2), pt.Y%-ABS(dy%/2), \
\ Bitmap{}, dx%, dy%
SYS "InvalidateRect", @hwnd%, 0, 0
*REFRESH
UNTIL FALSE
END
REM. ------------------------------------------------------------------------
REM. The code contained within this function (FN_createDIBSection) is the
REM. the work of Richard Russell, hereby acknowledged. :)
REM. ------------------------------------------------------------------------
DEF FN_createDIBSection( W%, H% )
LOCAL bits%
DIM BITMAPINFOHEADER{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, \
\ Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, \
\ ClrUsed%, ClrImportant%}
DIM bmi{Header{} = BITMAPINFOHEADER{}}
bmi.Header.Size% = DIM(BITMAPINFOHEADER{})
bmi.Header.Width% = W%
bmi.Header.Height% = H%
bmi.Header.Planes.l& = 1
bmi.Header.BitCount.l& = 32
SYS "CreateDIBSection", @memhdc%, bmi{}, 0, ^bits%, 0, 0 TO hbitmap%
IF hbitmap% = 0 ERROR 100, "Couldn't create DIBSection"
SYS "SelectObject", @memhdc%, hbitmap% TO oldhbm%
SYS "DeleteObject", oldhbm%
CLS
= bits%
DEF FN_createTestBitmap( bmW%, bmH% )
LOCAL bm%, I%, X%, Y%, r%
DIM bm% 4 * bmW%*bmH% +3 : REM. Needs extra 4 bytes (not 3!) for memory alignment
bm% = (bm% + 3) AND -4 : REM. Ensures bitmap base address is DWORD-aligned (divisible by 4)
REM. Fill our bitmap with the colour "SlateBlue" (= &6A5ACD)
FOR I% = bm% TO bm% + (4*bmW%*bmH% - 1) STEP 4
!I% = &6A5ACD
NEXT I%
REM. Give our bitmap a bright yellow border
FOR X% = 0 TO bmW%-1
bm%!(4*X%) = &FFFF00
bm%!(4*(bmW%*(bmH%-1) + X%)) = &FFFF00
NEXT X%
FOR Y% = 0 TO bmH%-1
bm%!(4*Y%*bmW%) = &FFFF00
bm%!(4*(Y%*bmW% + (bmW%-1))) = &FFFF00
NEXT Y%
REM. Draw a sine wave on the bitmap
FOR X% = 0 TO bmW%-1
Y% = bmH%DIV2 + bmH%DIV4*SIN(4*PI * X%/(bmW%-1))
bm%!(4*(Y%*bmW% + X%)) = &FFFFFF
NEXT X%
REM. Draw a circle on our bitmap
FOR I% = 0 TO 359
r% = 0.25 * SQR((bmW%/2)^2 + (bmH%/2)^2)
X% = bmW%/2 + r%*SINRADI%
Y% = bmH%/2 + r%*COSRADI%
IF X% < 0 X% = 0
IF Y% < 0 Y% = 0
IF X% > bmW%-1 X% = bmW%-1
IF Y% > bmH%-1 Y% = bmH%-1
bm%!(4*(Y%*bmW% + X%)) = &00FF00
NEXT I%
= bm% Richard.
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #2 on: Mar 4th, 2012, 6:25pm » |
|
on Mar 4th, 2012, 4:56pm, Richard Russell wrote:As I mentioned in a recent post, I already have! |
|
It is intended that GFXLIB 3's core library will have a set of clipping routines for different types of graphical 'object' -- bitmaps, rectangles, polygons, arbitrary lines, horizontal and vertical lines, circles, etc.
on Mar 4th, 2012, 4:56pm, Richard Russell wrote:Mine in fact is a 'sprite' plotter |
|
Excellent, IMHO. I haven't tested its rendering speed, but it looks fast at this end. It doesn't suffer from a peculiar 'artefact' that GFXLIB's GFXLIB_PlotScale (and related routines) exhibit at very small scale factors -- it's hard to describe, but the image gets a bit scrambled, although it doesn't appear to be dangerous (seems to be stable).
You've mentioned in the past that writing position-independent x86-based code is not straightforward and that such code can incur execution speed penalties (or words to that effect). Is that the case with DrawIconRTR.bin?
Given the completeness of the routine (clipped, scaled, alphablended plotting), 239 bytes is impressively small.
David.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #3 on: Mar 4th, 2012, 9:33pm » |
|
on Mar 4th, 2012, 6:25pm, David Williams wrote:I haven't tested its rendering speed, but it looks fast at this end. |
|
It was designed to be fast (the inner loop is 16 instructions). Something I ought to have mentioned before is that it requires 'integer SSE' (ISSE) support, which means the minimum processor families on which it will run are an Intel Pentium III or an AMD Athlon. As both of those are about ten years old now that's not too limiting, but it will simply crash if run on anything older.
Quote:You've mentioned in the past that writing position-independent x86-based code is not straightforward and that such code can incur execution speed penalties (or words to that effect). Is that the case with DrawIconRTR.bin? |
|
I didn't set out to make it position independent, but when I noticed that it very nearly was anyway, it seemed worth the little extra effort. The main thing you have to avoid is any absolutely-addressed 'variables', so everything which doesn't fit in a register is kept on the stack. Having the alpha byte 'inverted' (00 = opaque, FF = transparent) helped too.
Richard.
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #4 on: Mar 5th, 2012, 03:18am » |
|
Here is a speed test comparing DrawIconRTR and GFXLIB_PlotScaleBlend.
The two routines, admittedly, do slightly different things:
DrawIconRTR alphablends on a pixel-by-pixel basis (using the alpha value of each source bitmap pixel, and pre-multiplied alphas of background pixels).
GFXLIB_PlotScaleBlend uses a single, global alpha (or opacity) value for all pixels, and doesn't read background alphas.
So the two routines do quite similar things.
GFXLIB_PlotScaleBlend doesn't make use of any MMX/SIMD instructions.
DrawIconRTR is certainly faster until larger scale factors are specified, and much clipping needs to be done. Eventually, GFXLIB_PlotScaleBlend overtakes. I think I know why this is, but it's currently 4:12 AM and I'm too tired to explain my thoughts.
GFXLIB is required to run the source code below.
Here's a link to the EXE (+ .BBC source):
http://www.bb4wgames.com/temp/plotspeedtest.zip
Code:
REM. -----------------
REM. PlotSpeedTest.BBC
REM. -----------------
REM.
REM. Compare -- not too scientifically! -- plotting speeds of
REM. DrawIconRTR and GFXLIB_PlotScaleBlend
MODE 8
SOUND 1, 0, 0, 0 : SOUND OFF
ON ERROR OSCLI "REFRESH ON" : ON : CLS : REPORT : PRINT " at line "; ERL : VDU 7 : END
INSTALL @lib$ + "GFXLIB2.BBC" : PROCInitGFXLIB(d{}, 0)
INSTALL @lib$ + "GFXLIB_modules\PlotScaleBlend.BBC" : PROCInitModule
GetTickCount% = FNSYS_NameToAddress( "GetTickCount" )
DIM mc% 255
OSCLI "LOAD """ + @lib$ + "DrawIconRTR.bin"" " + STR$~mc%
DrawIconRTR% = mc% + 3
DIM Buffer{T%,W%,H%,B%,P%,M%}
Buffer.W% = @vdu%!208 : Buffer.H% = @vdu%!212 : Buffer.M% = d.bmBuffAddr%
REM. Create a 'test' 128x80, 32-bpp ARGB bitmap
bmW% = 128 : bmH% = 80 : bm% = FN_createTestBitmap( bmW%, bmH% )
DIM Bitmap{T%,W%,H%,B%,P%,M%}
Bitmap.W% = bmW% : Bitmap.H% = bmH% : Bitmap.M% = bm%
COLOUR 15 : PRINT '" DrawIconRTR vs. GFXLIB_PlotScaleBlend"' : COLOUR 7
INPUT '" Enter a scale factor (1.0) "; scale
INPUT '" How many iterations? (10000) "; N%
IF N% <= 0 THEN N% = 10000
IF scale = 0 THEN scale = 1.0
IF scale < 0 THEN scale *= -1.0
DIM pt{X%,Y%}
dx% = bmW%*scale
dy% = bmH%*scale
pt.X% = 320
pt.Y% = 256
bmW2% = scale * bmW%
bmH2% = scale * bmH%
OFF : WAIT 50
SYS "GetCurrentProcess" TO hprocess%
SYS "SetPriorityClass", hprocess%, &80
REM. Display the 'test bitmap' using DrawIconRTR
SYS DrawIconRTR%, Buffer{}, pt.X%, pt.Y%, Bitmap{}, dx%, dy%
SYS "InvalidateRect", @hwnd%, 0, 0
*REFRESH
REM. Time DrawIconRTR%
PRINT '" Timing DrawIconRTR ("; N%; " calls; scale factor: ";scale;")..." : WAIT 50
*REFRESH OFF
SYS GetTickCount% TO timeA_0%
FOR I% = 1 TO N%
SYS DrawIconRTR%, Buffer{}, pt.X%, pt.Y%, Bitmap{}, dx%, dy%
NEXT
SYS GetTickCount% TO timeA_1%
*REFRESH ON
REM. Display the 'test bitmap' using GFXLIB_PlotScaleBlend
SYS GFXLIB_PlotScaleBlend%, d{}, bm%, bmW%, bmH%, bmW2%, bmH2%, pt.X%, pt.Y%, 254
SYS "InvalidateRect", @hwnd%, 0, 0
*REFRESH
REM. Time GFXLIB_PlotScaleBlend
PRINT '" Timing GFXLIB_PlotScaleBlend ("; N%; " calls; scale factor: ";scale;")..." : WAIT 50
*REFRESH OFF
SYS GetTickCount% TO timeB_0%
FOR I% = 1 TO N%
SYS GFXLIB_PlotScaleBlend%, d{}, bm%, bmW%, bmH%, bmW2%, bmH2%, pt.X%, pt.Y%, 254
NEXT
SYS GetTickCount% TO timeB_1%
*REFRESH ON
SYS "GetCurrentProcess" TO hprocess%
SYS "SetPriorityClass", hprocess%, &20
PRINT '" " + STRING$(40,"-")'
PRINT '" DrawIconRTR took "; (timeA_1% - timeA_0%)/1000; " s."
PRINT '" GFXLIB_PlotScaleBlend took "; (timeB_1% - timeB_0%)/1000; " s."
COLOUR 3 : PRINT ''" Finished." : COLOUR 7
SOUND 1, -10, 220, 1
ON
END
DEF FN_createTestBitmap( bmW%, bmH% )
LOCAL bm%, I%, X%, Y%, r%
DIM bm% 4 * bmW%*bmH% +3 : REM. Needs extra 4 bytes (not 3!) for memory alignment
bm% = (bm% + 3) AND -4 : REM. Ensures bitmap base address is DWORD-aligned (divisible by 4)
REM. Fill our bitmap with the colour "SlateBlue" (= &6A5ACD)
FOR I% = bm% TO bm% + (4*bmW%*bmH% - 1) STEP 4
!I% = &6A5ACD
NEXT I%
REM. Give our bitmap a bright yellow border
FOR X% = 0 TO bmW%-1
bm%!(4*X%) = &FFFF00
bm%!(4*(bmW%*(bmH%-1) + X%)) = &FFFF00
NEXT X%
FOR Y% = 0 TO bmH%-1
bm%!(4*Y%*bmW%) = &FFFF00
bm%!(4*(Y%*bmW% + (bmW%-1))) = &FFFF00
NEXT Y%
REM. Draw a sine wave on the bitmap
FOR X% = 0 TO bmW%-1
Y% = bmH%DIV2 + bmH%DIV4*SIN(4*PI * X%/(bmW%-1))
bm%!(4*(Y%*bmW% + X%)) = &FFFFFF
NEXT X%
REM. Draw a circle on our bitmap
FOR I% = 0 TO 359
r% = 0.25 * SQR((bmW%/2)^2 + (bmH%/2)^2)
X% = bmW%/2 + r%*SINRADI%
Y% = bmH%/2 + r%*COSRADI%
IF X% < 0 X% = 0
IF Y% < 0 Y% = 0
IF X% > bmW%-1 X% = bmW%-1
IF Y% > bmH%-1 Y% = bmH%-1
bm%!(4*(Y%*bmW% + X%)) = &00FF00
NEXT I%
= bm%
David.
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #5 on: Mar 5th, 2012, 4:44pm » |
|
on Mar 5th, 2012, 03:18am, David Williams wrote:DrawIconRTR alphablends on a pixel-by-pixel basis (using the alpha value of each source bitmap pixel, and pre-multiplied alphas of background pixels). |
|
I don't really know what you mean by "pre-multiplied alphas of background pixels" here. In what sense do 'background' pixels have an alpha anyway? I don't guarantee that my routine will do anything sensible with the 'alpha' byte in the background bitmap: it may well corrupt it. I treat it as a 'don't care' byte.
Quote:So the two routines do quite similar things |
|
Does yours have the 'reflect' (about either axis) option? That added a significant degree of complexity, as I recollect.
My routine is, of course, optimised for no clipping, since (at least in my applications) clipping is very much an exceptional case.
Richard.
|
« Last Edit: Mar 5th, 2012, 4:54pm by admin » |
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Bitmap clipper & plotter (BASIC prototypes)
« Reply #6 on: Mar 6th, 2012, 03:21am » |
|
on Mar 5th, 2012, 4:44pm, Richard Russell wrote:I don't really know what you mean by "pre-multiplied alphas of background pixels" here. |
|
Neither do I (lol).
I now know that what I wrote was nonsense.
on Mar 5th, 2012, 4:44pm, Richard Russell wrote:Does yours have the 'reflect' (about either axis) option? That added a significant degree of complexity, as I recollect. |
|
No, mine doesn't.
The only GFXLIB routine that performs any kind of flipping/reflection is GFXLIB_PlotFlip (and a variant of it). This may be about to change, however.
David.
|
|
Logged
|
|
|
|
|