Is there an API function in the lines of BitBlt or StretchBlt for rotating
a bitmap by 90º? In other words, it must transpose the matrix of pixel
values and thereafter do a horizontal or vertical flip, depending on
whether the rotation is CW or CCW.
Last edited by krtxmrtz; May 11th, 2005 at 05:22 AM.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
I know this thread, I used NoteMe's code when I had to implement rotations by any degree. For 90º rotations however I hoped there was a much faster function, as the one notquitehere188 has suggested.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
It does just nothing. Can it be used with the same source and destination?
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
You can flip the picture using the paintpicture method by supplying a negative value for either the destination height or width
Flipping is not the problem, it's transposing, i.e. from an original matrix A where pixel values are stored with NRow rows and NCol columns, a new matrix B must be created with NCol rows and NRow columns such that:
B(i,j) = A(j,i)
for i=1, 2, ..., NRows and j=1, 2, ..., NCols
If that can be done with a single call to PlgBlt rather than using loops then a 90º rotation is quite straighforward (and I would imagine faster) as it is a combination of a flip followed by a transposition. The problem is I can't get PlgBlt working.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
All right, I'm ready to do it with 2 nested loops. But for large images, even with GetPixel and SetPixel this could be kind of slow, so I thought I'd do it with a VC++ DLL, as some of you folks have recently taught me the basic steps.
The one question I need an answer to before I get down to business is, how can I pass a picturebox as an argument to the procedure in the DLL?
Last edited by krtxmrtz; May 11th, 2005 at 05:27 AM.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
The one question I need an answer to before I get down to business is, how can I pass a picturebox as an argument to the procedure in the DLL?
OK, I've solved that. I've made my sub in a C dll and it works nicely. However I've used SetPixel and GetPixel and this doesn't make it fast enough for large pictureboxes.
My 2 questions to the C experts:
- Is there really anything to be gained by doing 2 nested loops with SetPixels & GetPixels in VC++ rather than in VB?
- Is it worth using DIB functions in the VC++ DLL to speed it up?
Last edited by krtxmrtz; May 11th, 2005 at 10:26 AM.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
Rotation by 90 degree should be easy to do in C++, I can do it for yu but right now i'm at work (just starting).
SetPixel and GetPixel are VERY, VERY.... VERY slow... even though it's API...
The fastest way to do it is to get the actual bitmap data, and work with the bytes, but you have to understand the bitmap structure for that, it's actually easy...
Actually, if you do it this way, even in VB will be a significant improvement compared to SetPixel and GetPixel.
Well, in about 10 hours I'll probably be in bed counting bytes to catch some sleep... so I remind you now...
My app reads a large file (ca. 10 Mb) and then plots the contents in a bitmap. I've implemented a part of the code in VC++ to make it faster -and it really is.
The actual plotting is handled by SetDIBitsToDevice, which is perhaps what you had in mind. However, this part is in VB (I'm using somene else's code that I found in the forum) so,
(1) I'd like to know if it can improve in terms of speed by converting it to VC++ and
(2) I'm not sure I'm able to transpose (rotation = flip + transposition) the array of bytes:
MyByteArray(NBytes)
where the overall number of bytes is
NBytes = Rows * (3 * Cols + DBytes)
DBytes is the number of dummy bytes at the end of each row (so all rows are divisible by 4) like
RGBR GBRG B000
RGBR GBRG B000
RGBR GBRG B000
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
I downloaded this from my home computer...
You have to put it in a module.
Use the GetBitmapData to get the bitmap as an array, so you can manipulate the data, and SetBitmapData to put the data back in the PictureBox.
For the SetBitmapData, the value param is a long, so you will have to use the VarPtr(YourArray(0)) function to get the array pointer.
The functions works in 24 bits, so it does not matter how many bits per pixel your screen resolution has, it will always convert to 24 bits. And SetBitmapData function it's expecting that you pass a 24 bit bitmap data to it.
You will need these functions even when I'll make the C++ rotation, cuz you need to pass the pointer to the data to the DLL.
VB Code:
Option Explicit
Public Const BI_RGB = 0&
Public Const BI_RLE4 = 2&
Public Const BI_RLE8 = 1&
Public Const DIB_RGB_COLORS = 0 ' color table in RGBs
Public Const DIB_PAL_COLORS = 1 ' color table in palette indices
Public Const DIB_PAL_INDICES = 2 ' No color table indices into surf palette
Public Const DIB_PAL_PHYSINDICES = 2 ' No color table indices into surf palette
Public Const DIB_PAL_LOGINDICES = 4 ' No color table indices into DC palette
Public Const SRCCOPY = &HCC0020 ' (DWORD) dest = source
Public Type SAFEARRAYBOUND
cElements As Long
lLbound As Long
End Type
Public Type SAFEARRAY2D
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
Bounds(0 To 1) As SAFEARRAYBOUND
End Type
Public Type BITMAPINFOHEADER '40 bytes
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Public Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As Long ' RGBQUAD
End Type
Public Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Public Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Public Declare Function CreateDIBSection Lib "gdi32" (ByVal hdc As Long, pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long
Public Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Public Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Public Declare Function GetCurrentObject Lib "gdi32" (ByVal hdc As Long, ByVal uObjectType As Long) As Long
Public Declare Function GetObjectType Lib "gdi32" (ByVal hgdiobj As Long) As Long
Public Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Public Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Public Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Public Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpbi As BITMAPINFO, ByVal wUsage As Long) As Long
Public Declare Function SetDIBits Lib "gdi32" (ByVal hdc As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpbi As BITMAPINFO, ByVal wUsage As Long) As Long
Public Declare Function SetDIBitsToDevice Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFO, ByVal wUsage As Long) As Long
Public Declare Function GetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As Long
Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Public Declare Function StretchBlt Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
Public Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Function GetBitmapData(hdc As Long, Width As Long, Height As Long, value() As Byte, Optional ByVal ReSize As Double = 1) As Boolean
Dim bi As BITMAPINFO, mhDC As Long, bitsPtr As Long, hDIB As Long
Dim bDibFrom() As Byte, Size As Long, old_bmp As Long, Ret As Long
'Clear the temporary array descriptor, This is necessary under NT4.
CopyMemory ByVal VarPtrArray(bDibFrom), 0&, 4
End If
DeleteObject hDIB
SelectObject mhDC, old_bmp
DeleteDC mhDC
GetBitmapData = True
End Function
Public Function SetBitmapData(ByVal hdc As Long, ByVal Width As Long, ByVal Height As Long, ByVal value As Long, Optional ByVal ReSize As Double = 1) As Boolean
Dim bi As BITMAPINFO, mhDC As Long, bitsPtr As Long, hDIB As Long
Dim bDibFrom() As Byte, old_bmp As Long, Ret As Long
Using the functions above, I managed to write the following code, but It does not seem very fast (when it comes to big pictures).
Is this code any faster than what you have now ?
VB Code:
Option Explicit
Private Declare Function GetTickCount Lib "kernel32" () As Long
Using the functions above, I managed to write the following code, but It does not seem very fast (when it comes to big pictures).
Is this code any faster than what you have now ?
Well, it's quite acceptable, certainly much faster than my Set/Get-Pixels. Now I'll have to study your code in detail and see how it can be adapted into my app. Probably I'm gonna need more help so stand by!!!
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
It could just be the math thats making it slow. Porting it to C++ might add that little bit of speed you are looking for.
I will, but first I've got to check my old C books as I've sort of forgotten all about structures and I anticipate I'll have to use some.
Last edited by krtxmrtz; May 12th, 2005 at 08:51 AM.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
You don't have to use any structures, just use a pointer of char array. Everything is pointers in C/C++ ... If you learn to master pointers in C/C++, you can do pretty cool stuff.
I did not have time to convert it to C++ yesterday, I hope I will have time today after work...
It should not be that difficult. Actually that's why I did it in VB first, cuz I can test easier in VB, and then when it's working properly, I was planning to convert it, but did not get to it.
Also, can you tell me what is the difference in time, like how long did your function take, and how long did mine. Then when I finish the C++ one, to see the final time, and the total difference (just for reference, to see how long each method took)
Last edited by CVMichael; May 12th, 2005 at 10:08 AM.
You don't have to use any structures, just use a pointer of char array. Everything is pointers in C/C++ ... If you learn to master pointers in C/C++, you can do pretty cool stuff.
How about the BITMAPINFO type and all that stuff? Is that implicitly declared in windows.h or any other header file?
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
Don't convert that, it's too difficult, and it won't save much time.
GetBitmapData + SetBitmapData, together they take 30 ms on my computer, if you do it C++ it won't save much time, just convert the 2 for loops in the RotatePic function, that's it...
Loops are slow in VB, that's what you have to convert. There are no loops in GetBitmapData and SetBitmapData ,only API functions, that's why you won't get it much faster by converting to C++.
Don't convert that, it's too difficult, and it won't save much time.
All right, then that'll be much much easier.
I'll let you know about the timing, hopefully tomorrow. Thanks a lot.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
Now I come to think of it, your code makes use of a few DIB API functions. I understand these result (sometimes?) in vertically inverted images.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
Yea, you are right. I think a bitmap is saved inverted in the bmp file, but I think in memory is stored properly (i.e, pos x = 0, y = 0, is pos 0 in the buffer). I did not do any serious bitmap programming for more than a year, I might be wrong...
Hehe, what a stupid mistake... I forgot to add "Step 3" to the For Loop, line:
VB Code:
For X = 0 To (FromPic.ScaleWidth - 1) * 3 [b]Step 3[/b]
Now it takes half the time in VB...
I also edited the previous post and added "Step 3" to that line...
[edit] Is it rotating properly ??
It seems that it rotates and flips at the same time, I don't think it should do that ?
Before I was testing with a picture that looked almost the same even if you rotate, but I just tested with a picture wich is more obvious how it rotates... (WINNT.BMP picture in the windows directory )
Last edited by CVMichael; May 12th, 2005 at 05:21 PM.
For the WINNT.BMP, in VB it took ~ 250 ms, now it takes 20 ms !
Here's the C++ code:
Code:
_declspec(dllexport) long _stdcall BytesPerScanLine(long width, short bit_count)
{
long ret = width * bit_count;
if((ret % 32) > 0) ret += 32 - (ret % 32);
return ret >> 3;
}
_declspec(dllexport) void _stdcall RotateBMPData_CW(char *from_bmp, long width, long height, char *to_bmp)
{
long x, y, h_add;
long bpsl_from = BytesPerScanLine(width, 24);
long bpsl_to = BytesPerScanLine(height, 24);
char *from_pos, *to_pos;
for(y = 0; y < height; y++)
{
from_pos = from_bmp + y * bpsl_from;
h_add = y * 3;
for(x = 0; x < width; x++)
{
to_pos = to_bmp + ((width - 1) - x) * bpsl_to + h_add;
*(to_pos++) = *(from_pos++);
*(to_pos++) = *(from_pos++);
*(to_pos++) = *(from_pos++);
}
}
}
_declspec(dllexport) void _stdcall RotateBMPData_CCW(char *from_bmp, long width, long height, char *to_bmp)
{
long x, y, h_add;
long bpsl_from = BytesPerScanLine(width, 24);
long bpsl_to = BytesPerScanLine(height, 24);
char *from_pos, *to_pos;
for(y = 0; y < height; y++)
{
from_pos = from_bmp + y * bpsl_from;
h_add = ((height - 1) - y) * 3;
for(x = 0; x < width; x++)
{
to_pos = to_bmp + x * bpsl_to + h_add;
*(to_pos++) = *(from_pos++);
*(to_pos++) = *(from_pos++);
*(to_pos++) = *(from_pos++);
}
}
}
And the Visual Basic code & declarations:
VB Code:
Option Explicit
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function BytesPerScanLine2 Lib "BitmapStuff.dll" Alias "BytesPerScanLine" (ByVal Width As Long, ByVal BitCount As Integer) As Long
Private Declare Function RotateBMPData_CW Lib "BitmapStuff.dll" (ByVal ptrFromData As Long, ByVal Width As Long, ByVal Height As Long, ByVal ptrToData As Long) As Long
Private Declare Function RotateBMPData_CCW Lib "BitmapStuff.dll" (ByVal ptrFromData As Long, ByVal Width As Long, ByVal Height As Long, ByVal ptrToData As Long) As Long
I also attached the C++ project with the DLL (In case you don't have the compiler handy)
I made the C++ project in Visual C++ 6.0, this way you can open it even in newer Visual Studio versions
Tell me if you need more help...
Last edited by CVMichael; May 12th, 2005 at 07:51 PM.
Nice bit of code
I played around with the VB version and have a few suggestions
1. The line you changed to fix the flipping problem slowed it down on my computer by a factor of 3 so I went back to the original code and flipped the final image with a paintpicture. You have to paint the picture anyway if you want VB to recognize it.
2. It may speed things up if you use a copymemory API call in the loop
3. I also added a line to resize the ToPic
Not bat moeur, the VB code does it in 80 ms now... (on the same winnt.bmp picture)
Thanks...
[edit]Since there are so many posts in this thread, I won't make a new one..
Anyways, i tested the C++ compared to VB, For a picture of Width=2000, Height=3008, and ran the code 2 times for each...
In VB (with moeur's addition):
Time: 9894
Time: 9804
In C++
Time: 2614
Time: 2524
Last edited by CVMichael; May 12th, 2005 at 11:25 PM.
Hehe, what a stupid mistake... I forgot to add "Step 3" to the For Loop, line:
VB Code:
For X = 0 To (FromPic.ScaleWidth - 1) * 3 [b]Step 3[/b]
Now it takes half the time in VB...
Good morning. The lack of that "step 3" also resulted in slightly wrong rotated images though I hadn't as yet noticed because I was first trying with grayscale images.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
I've been testing it on an old computer (one that really sucks) for 2 bitmaps:
1. (651 rows x 621 columns)
With Setpixels: 1950 ms
With your code in VB: 750 ms
With your code in VC++: 208 ms
2. (1189 rows x 1405 columns)
With Setpixels: 9532 ms
With your code in VB: 5699 ms
With your code in VC++: 1001 ms
I was going to rate you up but I couldn't... because I already did
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)