This is probably going to be a long messy post so I'll apologize in advance.
I'm getting ready to plot some irregular curves, so I realized I finally have to use something other than setpixel to get acceptable speed.
So I set up a program to test various ways of doing things, and I'm not happy with the results.
What I did was use an area of 500 pixels wide by 100 pixels tall. I tested creating a normal compatible bitmap/dc, and also using CreateDIBSection and selecting it into a dc.
I did my drawing on a memory dc and blitted the final result to a dialog for visual confirmation.
Here is the results of my timing tests-
AuthenticAMD Family 7 Model 10 Stepping 0
AMD Name String: AMD Athlon(tm) XP 3000+
Features: FPU TSC CX8 CMOV FXSR MMX SSE
Method: Min
Iterations: 5
Clocks Description
Test 1 1469 draw with pen
Test 2 2677 draw with pen on DIB
Test 11 134 fill background using FillRect with brush
Test 12 95663 fill background using FillRect with brush on DIB
Test 13 89159 fill background using direct access on DIB
Test 21 133 line using FillRect and Brush
Test 22 2013 line using FillRect and Brush on DIB
Test 31 754300 line using setpixel
Test 32 416543 line using setpixel on DIB
Test 33 1509 line using direct access to bitmap on DIB
Test 41 2091 bitblt from normal memory dc to dialog dc
Test 42 177706 bitblt from DIB dc to dialog dc
While setting each pixel for a line is 499 times faster in my tests, which is exactly what I was trying to accomplish, it seems that filling the area with a color for background is 665 times slower, and the blit from the memory dc to the dialog dc is 85 times slower.
So I'm thinking I must have some incompatibility between my DIB and my dialog.
Here's the parameters I used:
.data
bih BITMAPINFOHEADER <sizeof BITMAPINFOHEADER, \
mwidth, \;biWidth
-mheight, \;biHeight
1, \;biPlanes WORD
32, \;biBitCount WORD
BI_RGB, \;biCompression
0, \;biSizeImage
0, \;biXPelsPerMeter
0, \;biYPelsPerMeter
0, \;biClrUsed
0 > ;biClrImportant
My dialog is TrueColor, which I believe is 32 bit, so is there some other settings for the BITMAPINFOHEADER that will speed this thing up?
My setup for drawing on the compatible memory dc is
Setup1 proc
.if mbuff==0
inv GetProcessHeap
mov hHeap,eax
inv HeapAlloc,hHeap,HEAP_ZERO_MEMORY,2048
mov mbuff,eax
inv CreateFontIndirect,addr deffont ; 10 pt small
mov hfont,eax
.endif
.if mdc
inv DeleteDC,mdc
inv DeleteObject,mbmp
.endif
;inv GetClientRect,hWin,addr crect ; get the size needed for the bitmap
inv GetDC,hWin ; get dc's
mov hdc,eax
inv CreateCompatibleDC,hdc ; create dc for hidden buffer
mov mdc,eax
inv SelectObject,mdc,hfont
inv SetBkMode,mdc,TRANSPARENT
inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
mov mbmp,eax
inv SelectObject, mdc, mbmp
inv DeleteObject,eax ; delete old bitmap
ret
Setup1 endp
and for drawing on the DIB is
Setup2 proc
.if mbuff==0
inv GetProcessHeap
mov hHeap,eax
inv HeapAlloc,hHeap,HEAP_ZERO_MEMORY,2048
mov mbuff,eax
inv CreateFontIndirect,addr deffont ; 10 pt small
mov hfont,eax
.endif
.if mdc
inv DeleteDC,mdc
inv DeleteObject,mbmp
.endif
inv GetDC,hWin ; get dc's
mov hdc,eax
inv CreateCompatibleDC,hdc ; create dc for hidden buffer
mov mdc,eax
inv CreateDIBSection,mdc,addr bih,DIB_RGB_COLORS,addr mbits,0,0
mov mbmp,eax
inv SelectObject,mdc,eax
inv DeleteObject,eax ; delete old bitmap
inv SelectObject,mdc,hfont
inv SetBkMode,mdc,TRANSPARENT
ret
Setup2 endp
example tests of filling the background-
Test11:
Desc 'fill background using FillRect with brush'
call Setup1
inv makbrush,Red
BeginCounter
inv FillRect,mdc,addr rr,hbrush
EndCounter
call Wrapup1
ret
Test12:
Desc 'fill background using FillRect with brush on DIB'
call Setup2
inv makbrush,Blue
BeginCounter
inv FillRect,mdc,addr rr,hbrush
EndCounter
call Wrapup2
ret
Test13:
Desc 'fill background using direct access on DIB'
call Setup2
BeginCounter
mov edi,mbits
mov ecx,mwidth*mheight
mov eax,Yellow
rep stosd
EndCounter
call Wrapup2
ret
etc.
Does anyone see any way to speed these things up?
Is there some way to get the address of the bitmap for the memory dc and write to it directly rather than using a DIB?
[attachment deleted by admin]
I added a few more tests above for text output.
Test51 32120 DrawText
Test52 107594 DrawText on DIB
Test54 958 simple ExtTextOut
Test55 96840 simple ExtTextOut on DIB
Test57 982 simple TextOut
Test58 97263 simple TextOut on DIB
DrawText is only 3.3 times slower, but slowest overall. TextOut is 99 times slower, and ExtTextOut is 101 times slower.
I've also noticed that when I put colors directly using the dib, red comes out as blue, blue comes out as red, yellow comes out as cyan, and cyan comes out as yellow. ??
Anyways, I anxiously await some graphics guru explaining what I'm doing wrong with this DIB to make it so sloooooow.
I added a gdiFlush after each test to try to get better timings. It slowed down the really fast fillrects, and made the ratios not quite so bad, but I'm not really sure if its a valid thing to do for a time test.
Method: Min
Iterations: 100
Clocks Description
Test1 1756 draw with pen
Test2 2910 draw with pen on DIB
Test11 2392 fill background using FillRect with brush
Test12 95879 fill background using FillRect with brush on DIB
Test13 89880 fill background using direct access on DIB
Test21 2406 line using FillRect and Brush
Test22 2138 line using FillRect and Brush on DIB
Test31 689430 line using setpixel
Test32 377303 line using setpixel on DIB
Test33 1812 line using direct access to bitmap on DIB
Test41 2294 bitblt from normal memory dc to dialog dc
Test42 204577 bitblt from DIB dc to dialog dc
Test51 32124 DrawText
Test52 107037 DrawText on DIB
Test54 15243 simple ExtTextOut
Test55 90417 simple ExtTextOut on DIB
Test57 14931 simple TextOut
Test58 90756 simple TextOut on DIB
Ratios, Dib/non-Dib
1.6 draw with pen
40 fill background using FillRect with brush
.88 line using FillRect and Brush
.54 line using setpixel
89 bitblt to dialog dc
3.3 DrawText
5.9 simple ExtTextOut
6. simple TextOut
Of course, you realize that if you pre-define colors in the data section, you must reverse them? Its an Endian thing.
For example:
BgColor dd 0B0FFFFh ; A light yellow [Red=255, Green=255 and Blue=176]
Notice that B0h (for blue) is first.
Paul
Thanks Paul.
Yeah, I just used Red, Blue, Green from windows.inc so I suspected that was it, but the cyan/yellow thing really got me.
Quote from: Jimg on February 16, 2009, 01:47:52 AM
Does anyone see any way to speed these things up?
I'm no GDI expert, but AFAIR you shouldn't call CreateDIBSection with a memory DC, it should be called with a screen DC.
other things which probably can be improved:
- the handle returned by GetDC is to be freed with ReleaseDC().
- when a bitmap is selected in a memory DC, it isn't necessary to delete the default 1x1 bitmap which is returned.
Quote from: japheth on February 16, 2009, 10:22:30 PM
I'm no GDI expert, but AFAIR you shouldn't call CreateDIBSection with a memory DC, it should be called with a screen DC.
Makes sense to me. So I changed it to inv CreateDIBSection,hdc,... but it didn't seem to make any difference
Quote
other things which probably can be improved:
- the handle returned by GetDC is to be freed with ReleaseDC().
Sorry, it was hidden in the wrapup proc called after each test-
Wrapup1 proc
.if mdc
inv BitBlt,hdc,10,10,500,100,mdc,0,0,SRCCOPY
inv DeleteDC,mdc
inv DeleteObject,mbmp
inv ReleaseDC,hWin,hdc
.endif
call delbrush
ret
Wrapup1 endp
Quote
Quote
- when a bitmap is selected in a memory DC, it isn't necessary to delete the default 1x1 bitmap which is returned.
Fixed.
Why use a DIB?
I want to plot some irregular curves in simulated realtime, and setpixel is just too slow.
(and now I'm hoping you'll say "Well just use xxxxapi to access the bytes of the DDB".)
just obtain the start address (the fisrt pixel), you have defined 32 bits colors so you will be easily able to code your own setpixel function...
Quote from: MichaelW on February 17, 2009, 12:57:28 AM
Why use a DIB?
A SetPixel function with a 32 bit DIB section is fairly simple, for execution speed reasons the Set/GetPixel functions require that the width and height of the bitmap are passed to them, this is so that they don't have to waste time calculating them each call.
A couple of functions in GoAsm syntax:
SetDIBPixel FRAME x,y,pDIBits,width,height,color
mov eax,[width]
mov ecx,[height]
cmp [x],eax
jae >.EXIT
cmp [y],ecx
jae >.EXIT
sub ecx,[y]
dec ecx
mul ecx
shl eax,2 ; adjust for DWORD size
mov edx,eax
mov eax,[x]
shl eax,2 ; adjust for DWORD size
add eax,edx
add eax,[pDIBits] ; add the offset to the DIB bit
mov ecx,[color]
mov D[eax],ecx
.EXIT
RET
ENDF
GetDIBPixel FRAME x,y,pDIBits,width,height
mov eax,[width]
mov ecx,[height]
sub ecx,[y]
dec ecx
mul ecx
shl eax,2 ; adjust for DWORD size
push eax ; push the result onto the stack
mov eax,[x]
shl eax,2 ; adjust for DWORD size
pop ecx ; pop the scan line offset off the stack
add eax,ecx
add eax,[pDIBits] ; add the offset to the DIB bit
mov eax,[eax]
RET
ENDF
With these 2 base functions you can do just about anything...
/*
DIBDrawEllipse
Draws an ellipse
Parameters
hDIB = Handle to a 32bit DIB section
centerx = x coordinate of the center point
centery = y coordinate of the center point
a = x radius
b = y radius
color = line color
Returns the original bitmap handle, original is modified
*/
DIBDrawEllipse FRAME hDIB,xc,yc,a,b,color
uses edi,esi,ebx
LOCAL a2,b2 :D
LOCAL crit1,crit2,crit3 :D
LOCAL t,dxt,d2xt,dyt,d2yt :D
LOCAL temp1,temp2,temp3,temp4,temp5,temp6,y :D
LOCAL dibs :BITMAP
; Format the color
mov eax,[color]
bswap eax
shr eax,8
mov [color],eax
invoke GetObjectA,[hDIB],SIZEOF BITMAP,offset dibs
mov ebx, [a]
mov ecx, ebx
imul ecx, ebx
mov esi, [b]
mov edi, esi
mov eax, ecx
imul edi, esi
cdq
and edx, 3
add eax, edx
sar eax, 2
mov D[temp6],0
neg eax
and ebx, -2147483647
mov [y], esi
mov [b2], edi
jns >
dec ebx
or ebx, -2
inc ebx
:
sub eax, ebx
sub eax, edi
mov [crit1], eax
mov eax, edi
cdq
and edx, 3
add eax, edx
sar eax, 2
mov edx, esi
neg eax
and edx, -2147483647
jns >
dec edx
or edx, -2
inc edx
:
sub eax, edx
mov [temp3], eax
sub eax, ecx
mov [crit2], eax
mov eax, ecx
imul eax, esi
mov edi, eax
neg edi
mov ebx, eax
neg ebx
shl edi, 1
test esi, esi
lea edx, [ecx+ecx]
mov D[dxt], 0
mov [d2yt], edx
jl >>.EXIT
mov [temp1], eax
mov eax, [yc]
lea edx, [eax+esi]
neg ecx
sub eax, esi
mov [temp4], edx
mov [temp5], ecx
mov esi, eax
align 4
.WHILELOOP
mov eax,[temp6]
cmp eax, [a]
jg >>.EXIT
mov ecx, [temp4]
mov eax, [xc]
add eax, [temp6]
invoke SetDIBPixel,eax,ecx,[dibs.bmBits],[dibs.bmWidth], \
[dibs.bmHeight],[color]
mov eax,[temp6]
test eax, eax
jne >C0
mov eax, [y]
test eax, eax
je >C1 ;
C0:
mov eax, [xc]
sub eax, [temp6]
mov [temp2],eax
invoke SetDIBPixel,eax,esi,[dibs.bmBits],[dibs.bmWidth], \
[dibs.bmHeight],[color]
mov eax,[temp6]
test eax, eax
je >C1
mov eax, [y]
test eax, eax
je >C1
mov edx, [xc]
mov eax,[temp6]
lea eax, [edx+eax]
invoke SetDIBPixel,eax,esi,[dibs.bmBits],[dibs.bmWidth], \
[dibs.bmHeight],[color]
mov eax, [temp4]
mov ecx, [temp2]
invoke SetDIBPixel,ecx,eax,[dibs.bmBits],[dibs.bmWidth], \
[dibs.bmHeight],[color]
C1:
mov ecx, [b2]
mov eax, [crit1]
mov edx, ecx
imul edx, [temp6]
add edx, ebx
cmp edx, eax
jle >C3
mov eax, [temp1]
lea edx, [eax+ebx]
cmp edx, [temp3]
jle >C3
mov edx, ebx
sub edx, eax
cmp edx, [crit2]
jle >C2
mov ecx, [y]
mov edx, [temp5]
dec ecx
mov [y], ecx
mov ecx,[temp4]
inc esi
dec ecx
mov [temp4], ecx
mov ecx, [d2yt]
add eax, edx
add edi, ecx
mov [temp1], eax
add ebx, edi
jmp >.DONE
C2:
lea edx,[ecx+ecx]
mov ecx,[dxt]
add ecx, edx
mov edx,[y]
inc D[temp6]
dec edx
mov [y], edx
mov edx, [temp4]
inc esi
dec edx
mov [temp4], edx
add eax, [temp5]
add edi, [d2yt]
mov [temp1], eax
mov [dxt], ecx
lea eax, [edi+ecx]
jmp >
C3:
mov eax, [dxt]
add ecx, ecx
inc D[temp6]
add eax, ecx
mov [dxt], eax
:
add ebx, eax
.DONE
mov eax, [y]
test eax, eax
jge <<.WHILELOOP
.EXIT
mov eax,[hDIB]
ret
ENDF
Quote from: NightWare on February 17, 2009, 02:14:05 AM
just obtain the start address (the fisrt pixel), you have defined 32 bits colors so you will be easily able to code your own setpixel function...
Exactly. If I can get the address of the bits in the bitmap, I can do anything in the way of laying down dots to draw whatever. The problem for me, is to get the address of the dang bitmap. As you can see by all my above tests, using a DIB is much slower than using a device dependent bitmap such as the one associated with a dialog. I drew a line on the DIB with no problem, and it's very fast, but all the other functions like filling the whole background with a color is much, much slower than using fillrect on a DDB. And particularly disturbing is how long it takes to blit from the dc with the DIB to the dc of the dialog compared to blitting from a simple memory dc to the dialog.
Quote from: Jimg on February 17, 2009, 03:05:48 AM
The problem for me, is to get the address of the dang bitmap.
LOCAL dibs :BITMAP
invoke GetObjectA,[hDIB],SIZEOF BITMAP,offset dibs
// dibs.bmBits is the address of the bitmap pixels
Never known a DDB to be faster in any way than a DIB, could be the functions you wrote, it should always be faster using direct to memory functions.
Edit: BTW you can use PatBlt to fill a rectangle, much faster than FillRect.
invoke CreateSolidBrush,[Fill]
invoke SelectObject,[memdc],eax
mov [fillbrush],eax
invoke PatBlt,[memdc],[rect.left],[rect.top], \
[rect.right],[rect.bottom],PATCOPY
invoke SelectObject,[memdc],[fillbrush]
invoke DeleteObject,eax
Ok, you threw me by saying dib everywhere. I'll try to get the address of the much faster (see my tests above) ddb.
Quote from: Jimg on February 17, 2009, 03:38:46 AM
Ok, you threw me by saying dib everywhere. I'll try to get the address of the much faster (see my tests above) ddb.
Hi JimG, your tests aside, I have tested this to death as have many others. In cases like Greyscaling the direct to memory approach was nearly 10x faster than GDI or GDI+. There are always exceptions where one usage runs a bit slow but that is a matter of implementation, direct to memory as a rule is always faster when properly done. The only exception to this rule is Blt'ing, because it can make use of the GPU it can be faster if hardware accel. is turned on.
http://www.masm32.com/board/index.php?topic=2640.msg20956#msg20956
Quote from: MSDNWindows 95 and Windows NT 3.5 and later support DIBSections. DIBSections are the fastest and easiest to manipulate, giving the speed of DDBs with direct access to the DIB bits.
Well, getobject didn't work. It returned everything except bmBits which came back zero. sigh.
Hi JimG,
Then you have not passed a valid handle to it, it will not work with a device dependent bitmap only a DIB section created with the CreateDIBSection function.
http://msdn.microsoft.com/en-us/library/aa932477.aspx
QuoteIf hgdiobj identifies a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION structure will contain a pointer to the bitmap's bit values.
If hgdiobj identifies a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap and the pointer to the bitmap's bit values will be NULL. You can only access the bitmap's bits if they are in a device-independent bitmap.
You can use the BITMAP structure for DIB sections as well, the bmBits will contain the pointer to the bit values.
donkey is right, the difference between DDB and DIB is the informations used, DDB use existing infos (essentially BITMAPINFO structure). DIB use his own informations. but here you have defined only 1 serie... there is a problem somewhere with your speed test...
plus, by looking of Setup1 proc, you create a COMPATIBLE BITMAP, so you know where all infos are, coz a bitmap is composed of [BITMAPFILEHEADER]+[BITMAPINFOHEADER]+[RGBQUAD x dup]+[Pixels]
This will create a bitmap that can be used with the GetObject function...
CreateIndependantBitmap FRAME hDC,SizeX,SizeY
LOCAL bmi :BITMAPINFO
LOCAL ppvBits :D
mov D[bmi.bmiHeader.biSize],SIZEOF BITMAPINFOHEADER
mov eax,[SizeX]
mov [bmi.bmiHeader.biWidth],eax
mov eax,[SizeY]
mov D[bmi.bmiHeader.biHeight],eax
mov W[bmi.bmiHeader.biPlanes],1
mov W[bmi.bmiHeader.biBitCount],32
mov D[bmi.bmiHeader.biCompression],BI_RGB
invoke CreateDIBSection,[hDC],ADDR bmi,DIB_RGB_COLORS,ADDR ppvBits,NULL,NULL
push eax
invoke GdiFlush
pop eax
ret
ENDF
If you look at the code I posted, I was already using CreateDibSection to create a bitmap. This is the one I tested against the compatible bitmap. That's how I discovered how much slower the DIB was.
Tried sDraw? Nice inc+lib. Ultrano mentioned it was also available on the asmcommunity forum.
http://www.masm32.com/board/index.php?topic=6514.0
Quote from: NightWare on February 17, 2009, 04:31:52 AM
donkey is right, the difference between DDB and DIB is the informations used, DDB use existing infos (essentially BITMAPINFO structure). DIB use his own informations. but here you have defined only 1 serie... there is a problem somewhere with your speed test...
I don't understand what you mean when you say " here you have defined only 1 serie..."
Quoteplus, by looking of Setup1 proc, you create a COMPATIBLE BITMAP, so you know where all infos are, coz a bitmap is composed of [BITMAPFILEHEADER]+[BITMAPINFOHEADER]+[RGBQUAD x dup]+[Pixels]
When I create a compatible bitmap, all I get back are a handle. If I feed the handle into GETOBJECT, I get back the correct information as to width,height,type,planes, and bitspixel. For bmBits, I get back a zero, so I don't know the starting address.
I've come to the conclusion that this is all a colossal waste of time anyway. If the procedure isn't clearly documented somewhere on MSDN, I don't really want to use it. I'm sure my tests are correct, if not, please point out the mistaken instructions. I apologize for my giant mess of a timing test, but it really helped me see what was actually going on.
Anyway, thanks all for your patience and time, I'll go crawl back in my hole now.
Quote from: Jimg on February 17, 2009, 05:23:50 PM
Quoteplus, by looking of Setup1 proc, you create a COMPATIBLE BITMAP, so you know where all infos are, coz a bitmap is composed of [BITMAPFILEHEADER]+[BITMAPINFOHEADER]+[RGBQUAD x dup]+[Pixels]
When I create a compatible bitmap, all I get back are a handle. If I feed the handle into GETOBJECT, I get back the correct information as to width,height,type,planes, and bitspixel. For bmBits, I get back a zero, so I don't know the starting address.
Yes, you won't get a pointer to the pixels of a DDB. AFAIK this is by design, because this abstraction level allowed GDI to support the old 4bit color-plane modes of the EGA/VGA without API extension.
As far as GDI is concerned there is one option which wasn't mentioned so far: one can use the SetDIBitsToDevice() function instead of CreateDIBSection() + BitBlt(). I've seen several programs using it, so I'm sure it isn't anything exotic, but I have no own experiences.
p.s. Thank you Donkey, PatBlt is absolutely faster than FillRect :thumbu
with GdiFlush after each test
Clocks Description
Test11 2349 fill background using FillRect with brush
Test12 1650 fill background using PatBlt with brush
Test13 96001 fill background using FillRect with brush on DIB
Test14 89930 fill background using direct access on DIB
Test15 95194 fill background using PatBlt with brush on DIB
Test21 2395 line using FillRect and Brush
Test22 1674 line using PatBlt and Brush
Test23 2228 line using FillRect and Brush on DIB
Test24 1586 line using PatBlt and Brush on DIB
without GdiFlush after each test
Clocks Description
Test11 134 fill background using FillRect with brush
Test12 64 fill background using PatBlt with brush
Test13 95720 fill background using FillRect with brush on DIB
Test14 89163 fill background using direct access on DIB
Test15 94868 fill background using PatBlt with brush on DIB
Test21 133 line using FillRect and Brush
Test22 64 line using PatBlt and Brush
Test23 1875 line using FillRect and Brush on DIB
Test24 1263 line using PatBlt and Brush on DIB
Jim,
The reason I asked why use a DIB was that your results show FillRect to be fast with a DDB. How about using a set pixel routine based on FillRect (specifying a one-pixel rectangle) on a DDB? If I assume a worst case of 133 cycles per call, and that the line in your tests is 500 pixels long, the resulting 66500 cycles is more than 11 times faster than using SetPixel on a DDB and more than 6 times faster than using SetPixel on a DIB. And since FillRect runs in 133 cycles for a 500-pixel line it could not have much overhead, so reducing the number of pixels drawn to one should speed it up considerably.
And now that I see your most recent results, read FillRect above as PatBlt.
Quote from: MichaelW on February 17, 2009, 06:30:35 PM
Jim,
The reason I asked why use a DIB was that your results show FillRect to be fast with a DDB. How about using a set pixel routine based on FillRect (specifying a one-pixel rectangle) on a DDB? If I assume a worst case of 133 cycles per call, and that the line in your tests is 500 pixels long, the resulting 66500 cycles is more than 11 times faster than using SetPixel on a DDB and more than 6 times faster than using SetPixel on a DIB. And since FillRect runs in 133 cycles for a 500-pixel line it could not have much overhead, so reducing the number of pixels drawn to one should speed it up considerably.
And now that I see your most recent results, read FillRect above as PatBlt.
If I understood your question correctly, the reason, and the only reason I considered using a DIB was to plot irregular curves. Direct access to a DIB is much, much faster than setpixel in this case. Every other operation seems to be faster on a DDB. Since I will also be "plotting" a bunch of grid lines, I was concerned about how much slower it was on a DIB. In reality, its insignificant if it takes 1800 cycles rather than 134 is for drawing each grid line. I'm still unhappy that it takes 95000 cycles to fill the background on a DIB vs. worse case 2350 on a DDB, but again, in reality, it will never be noticed. And I'm still unhappy that it takes 179000 cycles to blit a 100x500 DIB to the dialog vs. worse case 2300 for a DDB, but still again, it will never be noticed. So what it boils down to, is that I'm letting the raw numbers bother me and not seeing reality. Yes, DIB's are much slower than DDB's, regardless of what others have tried to tell me, but it Just Doesn't Matter. If there is some way to make the DIB's faster, or to be able to put directly to a DDB, I would still like to know, but I'm done losing sleep over it :bg
p.s. I'll run your one pixel test, but I don't have high hopes :wink
pps. And here's the results
Clocks Description
Test31 741509 line using setpixel (on DDB)
Test34 475206 line using PatBlt one pixel at a time in place of setpixel (on DDB)
Test33 2304 line using direct access to bitmap on DIB
So yes, it's good to know that PatBlt is faster than setpixel, thanks.
ppps. I've updated the test program in my first post for all new tests.
I finally ran the tests on my old celeron laptop with minimal memory and graphics card. Unexpectedly, the results were similar to the amd.
cpu1: Mobile Intel(R) Celeron(R) CPU 2.00GHz (Family 15 Model 2 Step 7) SSE2
cpu2: AMD Athlon(tm) XP 3000+ (Family 7 Model 10 Step 0) SSE1
Method: Min
Iterations: 100
Clocks Description
cpu1 cpu2
Test1 6640 1417 draw with pen
Test2 3444 2587 draw with pen on DIB
Test11 208 135 fill background using FillRect with brush
Test12 100 64 fill background using PatBlt with brush
Test13 157412 95606 fill background using FillRect with brush on DIB
Test14 112884 89161 fill background using direct access on DIB
Test15 144776 94870 fill background using PatBlt with brush on DIB
Test21 220 134 line using FillRect and Brush
Test22 100 64 line using PatBlt and Brush
Test23 3500 1864 line using FillRect and Brush on DIB
Test24 1944 1258 line using PatBlt and Brush on DIB
Test31 2298796 702092 line using setpixel
Test32 763076 370507 line using setpixel on DIB
Test33 3992 1510 line using direct access to bitmap on DIB
Test34 1691708 477564 line using PatBlt one pixel at a time in place of setpixel
Test41 5304 1988 bitblt from normal memory dc to dialog dc
Test42 325492 188096 bitblt from DIB dc to dialog dc
Test51 127904 31328 DrawText
Test52 66420 50373 DrawText on DIB
Test54 1264 960 simple ExtTextOut
Test55 41984 34932 simple ExtTextOut on DIB
Test57 1244 950 simple TextOut
Test58 40780 34844 simple TextOut on DIB
Test63 208 134 fill background using FillRect with brush
Test64 132 122 fill background using PatBlt with brush, SelectOBject in timing loop
My apologies to anyone who downloaded the previous version of the program that didn't print results to the results file.
After screwing around for a month and a half, I just discovered this whole thing was a waste of time.
To write directly to a device dependent bitmap, you just get the address and do it. No need for a dib or dibsection.
Just do a GetObject. The address of the bits in in the parameter BITMAP.bmBits
Why didn't anyone tell me this when I was floundering around so badly?
I would be interested in seeing some source to plot curves, Jim
I am new to 32-bit and would like to learn from your efforts
That's actually my next step. So far, anything that could go wrong, has.
Every time I run into a problem, it takes days to find a solution.
95% of my time is taken writing 2% of the code.
And EVERY time, it the windows api that is poorly documented or not documented at all.
The other 98% of the code is easy.
The actual graphing should be pretty easy (unless you want alpha blended smooth (blurry) curves :lol)
I'll certainly post something when I have something worth posting.
And after a little more digging, the answer to my previous questions is-
The documentation says it doesn't return bmBits for a non-dib.
Testing shows otherwise.
Is this yet another case of incorrect windows api documentation?
Quote from: Jimg on April 06, 2009, 06:53:06 PM
After screwing around for a month and a half, I just discovered this whole thing was a waste of time... ...Is this yet another case of incorrect windows api documentation?
it's a bit more complicate, you MUST fill structures of information to allow the use of functions. beside, for 32bits it can appear easy, but for 16/24bits screens or data, it's another story... it's why dib sections are supposed to be used... :wink
Quote from: Jimg on April 06, 2009, 09:55:07 PM
And after a little more digging, the answer to my previous questions is-
The documentation says it doesn't return bmBits for a non-dib.
Testing shows otherwise.
Is this yet another case of incorrect windows api documentation?
IF the documentation states that it should not return a valid value in bmBits member THEN it is possible that this will be the case on any new version or on any new Windows update... and in this case your program will stop working.
It is better to respect the official recommendations even if testing shows otherwise.
I've been trying out some GDI & bitmap related stuff too.
Bitmaps are stored usually as DIBs is that right ?... - so if a file has to be opened & worked on.. - I open it then edit its as a DDB, then save it back again to disk as a DIB. - is this the usual procedure.. ? if not.. what's the usual way of doing this.
thanks
I find the terminology confusing myself. Bitmaps on disk are Bitmaps. There is no dib or ddb, they are just bitmaps in the correct format. The first d stands for device, and there is no device.
Their colors and form may happen to be compatible with the screen, but don't rely on it.
The procedure for editing really depends upon what you want to do. Normally, you are not supposed to edit a ddb directly, you are supposed to only make changes to a ddb by loading it into a device context and using the gdi procedures. As far as writing a bitmap back out of a DC to a file, I haven't done that yet, we will have to get one of the graphics gurus to give us an example.
If you just want to make some simple changes, you can work directly on the bitmap file itself without using gdi.
What kind of things are you trying to do to the bitmap?
wow - all this sounds overly compicated - lol
is this the directX interface ?
thanks for the response. - I get the part about making changes to the DDB via a device context.
jimq wrote..
QuoteWhat kind of things are you trying to do to the bitmap?
maybe just some basic GDI editing/drawing with a pen or something, & then to save it -Just want to get a general idea about that whole part
>> dendave , its in the GDI section in the SDK
- Jimq, About it being stored as a DIB, these are some excerpts from the SDK that give that impression.
QuoteBitmaps should be saved in a file that uses the established bitmap file format and assigned a name with the three-character .bmp extension.
this format seems to be the same as the DIB format with the same structures. - maybe just the BITMAPFILEHEADER, is not mentioned there
QuoteThe DIB file format was designed to ensure that bitmapped graphics created using one application can be loaded and displayed in another application, retaining the same appearance as the original.
if its meant to be used as such, it would brobably be stored with that format too.. or at least in a form taht retains the format, also
QuoteIf you are writing an application that stores a bitmap image in a file, you should use the bitmap file format described in Bitmap Storage.
(which is the same as the DIB format)
jmq wrote
QuoteAs far as writing a bitmap back out of a DC to a file, I haven't done that yet, we will have to get one of the graphics gurus to give us an example.
just glanced, There's an example in the SDK, after a quick look.. it seems they first use CreateFile to create the file then initialize the DIB structures..(including the BITMAPFILEHEADER structure) & pass them to WriteFile .
they say this too,(the example uses the BITMAPINFO structure)
QuoteNote that the BITMAPINFO structure cannot be used with either a BITMAPV4HEADER or a BITMAPV5HEADER structure.
which was kinda confusing
Looks like you're well on your way. Have fun :U
not really, still hazy on the whole topic..& could do with some directions.. : ) & would like to know the usual ways people use for doing this, whether the bitmap after opening is converted into a DDB for GDI operations.
I would like to know also. I know several ways, but most of them are really ugly. From OleLoadPicturePath with the ugly, confusing com syntax, to gdiplus, with ridiculous object nonsense and complication.
I would say the best way, if you are working on an actual .bmp bitmap and not .png or some later format, would be the LoadImage api. It's straightforward, and you can get either a dib or ddb in one shot.
fyi: When I started working on bitmaps, I found CreateDIBSection gives me direct access to the bits. As things have developed I do quite a bit of manipulation of the bits, so this seems to be the easiest approach. I'm not sure of speed. I had some queries on the speed of some of my drawing procedures. Now I do all my manipulation of bits in the background, some are bitmaps some are in hdc's and some are just blocks of memory, depending on the procedure in use. With the amount of manipulation I'm doing I am definitely interested if there is a faster approach.
Good day,
Darrel
After finally listening to NightWare and using a dibsection, I think it is definitely the best way. My only problem with the process is the final step, the SetDIBitsToDevice call to put the results to the screen.
If I'm doing the whole bitmap of the same size as the screen, no problem. If I'm trying to do some random block move, e.g. a 100 pixel square from the middle of the source dib to the upper right of the screen, it take about ten trial and error step before I get it right. I just don't understand the parameters in SetDIBitsToDevice. -
hdc,XDest,YDest are ok, no problems
dwWidth - Specifies the width, in logical units, of the DIB.
dwHeight - Specifies the height, in logical units, of the DIB.
Aren't these values the number of bits wide and high you want to copy, not the "width of the DIB" itself?
XSrc - Specifies the x-coordinate, in logical units, of the lower-left corner of the DIB.
is this one always zero as the description implies or the actual first pixel horzontally you want to copy?
YSrc - Specifies the y-coordinate, in logical units, of the lower-left corner of the DIB.
This one is always a guess. Is it zero? Is it the height, Is it something else?
uStartScan - Specifies the starting scan line in the DIB.
Again, always a trial and error. What is this value?
cScanLines - Specifies the number of DIB scan lines contained in the array pointed to by the lpvBits parameter.
Isn't this values just the height. Isn't it always in the lpbmi BITMAPINFO structure?
Ugh!
Quote from: Jimg on April 17, 2009, 12:49:57 PM
dwWidth/dwHeight
Aren't these values the number of bits wide and high you want to copy, not the "width of the DIB" itself?
it's the number of PIXELS in the dib CONCERNED by the work (can be 8/16/24/32 bits depending of the format), you need to define them (the limits of your WORK).
Quote from: Jimg on April 17, 2009, 12:49:57 PM
XSrc/YSrc
is this one always zero as the description implies or the actual first pixel horzontally you want to copy?
This one is always a guess. Is it zero? Is it the height, Is it something else?
here both represent the start coords (always in pixels, from 0 to (width/height)-1), for the transformation of the dib (your source) to the ddb (display device).
Quote from: Jimg on April 17, 2009, 12:49:57 PM
uStartScan/cScanLines
Again, always a trial and error. What is this value?
Isn't this values just the height. Isn't it always in the lpbmi BITMAPINFO structure?
here it looks like it's simply the height limits for the display, honestly i've never played a lot with this parameter, all i can say is : with half the height of the dib, it display the first half part of the dib (in the second part of the display, so i suppose the ddb consider the last display line as the first scanline...)
so, in brief :
XDest/YDest = DESTINATION start coords
dwWidth/dwHeight = SOURCE width/height TO PROCESS (not necesseraly all the area)
XSrc/YSrc = SOURCE start coords
uStartScan/cScanLines = DESTINATION height limits
i hope it help... :wink
Here's an example from a program I'm working on.
What this is, is a custom cursor, 100 pixels square, white background with antialiased circles and grids in 256 color gray scale, used as an alpha channel to blend with the background.
I blend the cursor with the background (hidden buffer that drew the screen), and use SetDIBitsToDevice to copy to the screen.
On the next call, I copy a cursor sized block from the original full sized hidden buffer to the screen at the location of the cursor.
Note that all dibs were created with bih.biHeight set to the negative of the height wanted.
This copies the cursor/background blend to the screen:
inv GetDC,hWin
mov hdc,eax
mov ecx,100 ; size of the dib
sub ecx,prevmh ; number of scan lines to show, starting from the top
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,0,ecx,0,100,dibmc.bits,addr dibmc.bih,DIB_RGB_COLORS
inv ReleaseDC,hWin,hdc
where:
prevmcx is the left x coordinate of where to put the cursor image
prevmcy is the upper y coordinate for the image
prevmw is the width of the cursor image, usually 100 unless we are at the edge of the screen
prevmh is the height of the cursor image, usually 100 unless overlapping top or bottom.
dibmc is a 100 x 100 dib.
Notice I have to subtract the height wanted from the dib height to get SrcY, which took a long time to figure out because it just doesn't make any sense to me.
To restore the screen back to it's original condition before drawing the cursor elsewhere,
I copy a cursor sized block from the orginal back buffer of size mwidth x mheight.
inv GetDC,hWin ; yes, remove our cursor from the screen
mov hdc,eax
mov esi,dibh1.bits ; source is hidden buffer 1 in this case.
mov edi,offset dibh1.bih
mov ecx,mheight
sub ecx,prevmcy
sub ecx,prevmh
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,prevmcx,ecx,0,mheight,esi,edi,DIB_RGB_COLORS
inv ReleaseDC,hWin,hdc
In this case, uStartScan is zero, and
SrcY is the height of the dib minus upper y-coordinate minus the number of lines to copy.
This is not what I would call intuitively obvious.
for the copy :
>mov ecx,100 ; size of the dib
no, width or height of the dib, if you want, but not the size... plus why do you set this width/height for the Y source start ? should be zero... you copy from 0,0 to 99,99 no ? should be :
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,0,0,0,100,dibmc.bits,addr dibmc.bih,DIB_RGB_COLORS
dito for the restore code, should be :
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,0,0,0,100,dibh1.bits,addr dibh1.bih,DIB_RGB_COLORS
remember that the unique case where you don't have 0,0,0,height, it's when you don't start to copy from 0,0 coords of the source
Quote from: NightWare on April 18, 2009, 03:09:05 AM
for the copy :
>mov ecx,100 ; size of the dib
no, width or height of the dib, if you want, but not the size
yes, I added some quick comments before posting and blew it.
Quote... plus why do you set this width/height for the Y source start ? should be zero... you copy from 0,0 to 99,99 no ? should be :
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,0,0,0,100,dibmc.bits,addr dibmc.bih,DIB_RGB_COLORS
This is exactly what I tried the first time. It works except at the top and bottom.
Quotedito for the restore code, should be :
inv SetDIBitsToDevice,hdc,prevmcx,prevmcy,prevmw,prevmh,0,0,0,100,dibh1.bits,addr dibh1.bih,DIB_RGB_COLORS
Again, exactly what I did the first time. This doesn't work at all. It just always copies the 100 x 100 square from the lower left corner.
Quoteremember that the unique case where you don't have 0,0,0,height, it's when you don't start to copy from 0,0 coords of the source
Yes, again, that's what I do.
I'll try to put together a simple test case later today.
Ok, here's a test program.
It's set up to do what I think you said.
Find method and change method and methodx to a 1 for the way I'm doing it now that works.
[attachment deleted by admin]
wow... you don't use the functions like it should... ok,
first, you shouldn't store the entire client area (only the 100*100 background, where the cursor will be drawn), it means that you MUST store the background JUST BEFORE drawing the cursor. why ? because the client area could change... here you use RECT.right/RECT.bottom of the client area for the width/height... and in the contrary you should use a RECT structure but with RECT.left/RECT.top for the coords and RECT.right should be RECT.left+width(100) and RECT.bottom should be RECT.top+height(100).
second, when you restore you MUST use the COORDS and SIZE of your backup (it means you must store/use them), coz in your example you restore the entire client area (minored by your position coords)... and of course, in a normal use the coords change, so here you can't restore them in a normal use of the function.
i understand better why it took you some time to obtain a good result, if you would have defined a RECT structure for the client area (the limits), AND a RECT structure for the cursor/background (your work) you would have completed your work considerably faster.
Quote from: NightWare on April 19, 2009, 12:41:34 AM
wow... you don't use the functions like it should... ok,
first, you shouldn't store the entire client area (only the 100*100 background, where the cursor will be drawn), it means that you MUST store the background JUST BEFORE drawing the cursor. why ? because the client area could change... here you use RECT.right/RECT.bottom of the client area for the width/height... and in the contrary you should use a RECT structure but with RECT.left/RECT.top for the coords and RECT.right should be RECT.left+width(100) and RECT.bottom should be RECT.top+height(100).
I do all my drawing and such on the full sized hidden buffer dibh1. I copy it to the screen when needed. There are no changes to the client area that I am not in control of. At anytime I need to restore the client area, I can just copy the affected portion from the hidden buffer. That's the whole purpose.
For the 100*100 cursor buffer, it just a temporary scratch dib to create a blend of the cursor with the background. I could use a RECT to store coordinates, but since the api didn't call for it, I saw no reason to create the extra complexity. Perhaps my attempt to make a small test program was confusing.
Quote
second, when you restore you MUST use the COORDS and SIZE of your backup (it means you must store/use them), coz in your example you restore the entire client area (minored by your position coords)... and of course, in a normal use the coords change, so here you can't restore them in a normal use of the function.
Now this is confusing. What do you mean? I'm only restoring a 100*100 area that I put the cursor in, not the whole screen. I'm restoring it from the hidden back buffer, but only the portion needed.
Quote
i understand better why it took you some time to obtain a good result, if you would have defined a RECT structure for the client area (the limits), AND a RECT structure for the cursor/background (your work) you would have completed your work considerably faster.
Clearly I'm not understanding your point. How could storing the values in a RECT structure make any difference. They are just coordinates and sizes. I prefer using using a name that is what is says, the width or height, which is what the api wants, rather than .right and .bottom which is definitely not what the api is asking for. I only used a RECT at all because that's what GetClientRect required. I really think all this confusion is because the documentation for SetDIBitsToDevice is so ambiguous, just like I was trying to say above. I still don't know what they had in mine with all these parameters compared to a simple bitblt, but it certainly is confusing.
dwWidth Specifies the width, in logical units, of the DIB. Is this really the width of the dib, or just the number of pixels you want to transfer?
dwHeight Specifies the height, in logical units, of the DIB. Same here.
XSrc Specifies the x-coordinate, in logical units, of the lower-left corner of the DIB. This is always zero, unless it means the first pixel across the width to use, in which case, it is very poorly written.
YSrc Specifies the y-coordinate, in logical units, of the lower-left corner of the DIB. Why the lower left?
uStartScan Specifies the starting scan line in the DIB. Why would you need that? I just told you the answer in YSrc.
cScanLines Specifies the number of DIB scan lines contained in the array pointed to by the lpvBits parameter. Why ask me this? It that really what it wants?
I've seen some really badly designed and documented apis, but this is near the top of the list.
The bottom line is, it's the only tool available for the job (disregarding directx, etc.), and I'm going to figure out what these parameters really are and how to use them.
ok, i'm going to change your test code, for a normal use of the function, i will post it tomorrow :wink
:wink as promised, the code
in the code, SetDIBitsToDevice is only used once (for the win update), all the others you have included before were totally useless (coz you can't make a copy of the background with this function, it's in the sens Dib To Dc), and that confused me a lot... i've spent an afternoon to understand the logic behind everything... MWAAAAARGGGGG !!!! (i know, it's noisy, but i feel better now... :bg)
i haven't modified the alpha part, (spent enough time... i've just used a hack to simulate an alpha) so you will have to re-work it a bit...
EDIT : oops i've just seen i've forgotten to correct the paths, updated
[attachment deleted by admin]
Okay, you are copying every pixel every time. Certainly I could have done that, but I wanted to update only the portion of the screen that was affected, not the whole screen.
You could see I already knew how to update the whole screen.
The point of my tirade, was that I wanted to know how to update just a portion of the screen as needed, and to do so it was difficult to figure out what the parameters should be.
I just did a time test, and with the 1248*718 area i'm using, it takes over 20000 cycles to update the whole screen but less than 700 to update a 100 x 100 square twice.
hmm... it seams it's complicate when you just include a portion, the problem comme from the height when you don't start at the 0 y coord. coz the DC is vertically reversed. this seams to work for the restore part :
mov edx,ClientArea.bottom
sub edx,BackUpPos.y
sub edx,BackUpSizes.y
inv SetDIBitsToDevice,hDC,BackUpPos.x,BackUpPos.y,BackUpSizes.x,BackUpSizes.y,BackUpPos.x,edx,0,ClientArea.bottom,DibClient.pBits,ADDR DibClient.sBIH,DIB_RGB_COLORS
Hai,
Im using Nighware Blending function and Donkey Graphics Library.
Cool is not it? :green
(http://omploader.org/vMWswaA/test.JPG)
Im uploading it to my FaceBook account as a video and I want to upload it to youtube but got no time. I created that video to impress my friend, and they are really impressed :lol .
where's your video? :bg
It should be here soon http://www.youtube.com/watch?v=gcdpkwgN7hw I hope youtube able to process it.
Dont listen to the audio or translate it, it just me braging how smart I am :green .
I forget to say thank you to donkey and nightware for their kindness and hardwork.
This is where facebook store it but I dont know can it be opened. http://www.facebook.com/v/1094820170800
And this video is about my face recognition test http://www.facebook.com/v/1095210940569
Quote from: NightWare on April 21, 2009, 02:42:36 AM
hmm... it seams it's complicate when you just include a portion, the problem comme from the height when you don't start at the 0 y coord. coz the DC is vertically reversed. this seams to work for the restore part :
mov edx,ClientArea.bottom
sub edx,BackUpPos.y
sub edx,BackUpSizes.y
inv SetDIBitsToDevice,hDC,BackUpPos.x,BackUpPos.y,BackUpSizes.x,BackUpSizes.y,BackUpPos.x,edx,0,ClientArea.bottom,DibClient.pBits,ADDR DibClient.sBIH,DIB_RGB_COLORS
A slightly simpler method:
mov ecx,y ; starting scan line wanted
add ecx,sizey ; number of scan lines from top to last line wanted
inv SetDIBitsToDevice,hdc,x,y,sizex,sizey,x,y,y,ecx,dibbits,addr dibbih,DIB_RGB_COLORS
Quote from: Jimg on April 21, 2009, 04:49:26 PM
A slightly simpler method:
mov ecx,y ; starting scan line wanted
add ecx,sizey ; number of scan lines from top to last line wanted
inv SetDIBitsToDevice,hdc,x,y,sizex,sizey,x,y,y,ecx,dibbits,addr dibbih,DIB_RGB_COLORS
well seen jimg :U
in fact the documentation of windows is correct, coz in "normal" use the first line is the last one (the -1/neg possibillity for the height is just an option, for a more readable/logical representation...). the problem is there is no warning about the possible use of the option.