So I was playing around and ran into something odd.
This code uses PlgBlt() to draw a "compass needle" and spin it on a Timer:
Code:
Option Explicit
Private Needle As StdPicture
Private Rotation As Long
Private Sub Form_Load()
Set Needle = LoadResPicture("NEEDLE", vbResBitmap)
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
With Picture1
.AutoRedraw = True
.Cls
PlgBlt.Render Picture1, 0, 0, 300, 300, Needle, vbGreen, Rotation, 1
.AutoRedraw = False
End With
Rotation = (Rotation + 1) Mod 360
End Sub
For the most part it works just fine, but at two rotation angles (95 deg. and about 180 deg. past that) the dot in the center stretches strangely:
Here is the code:
Code:
Public Sub Render( _
ByVal Dest As Object, _
ByVal DestX As Long, _
ByVal DestY As Long, _
ByVal DestWidth As Long, _
ByVal DestHeight As Long, _
ByVal Source As StdPicture, _
ByVal MaskColor As Long, _
Optional ByVal Rotation As Double, _
Optional ByVal SourceScale As Double = 1, _
Optional ByVal SourceX As Long, _
Optional ByVal SourceY As Long, _
Optional ByVal SourceWidth As Long, _
Optional ByVal SourceHeight As Long)
'Dest is a Form, UserControl with an hDC, PictureBox, etc. or the Printer object for
'a printer that supports this operation (PlgBlt).
'
'Source is a bitmap type StdPicture.
'
'MaskColor is the pixel color within Source to be treated as transparent.
'
'Rotation is a clockwise angle in degrees.
'
'SourceScale is a scale factor, 1.0 = 100%.
'
'When SourceWidth and/or SourceHeight are 0 we will use the full size of Source.
Dim BITMAP As BITMAP
Dim hbmMask As Long
Dim hdcMemColor As Long
Dim hbmColorOrig As Long
Dim bkColorOrig As Long
Dim hDCMemMask As Long
Dim hbmMaskOrig As Long
Dim Points(2) As POINTAPI
Dim I As Long
Dim RotatedPoints(2) As POINTAPI
Dim StretchBltModeOrig As StretchBltModes
'Set up memory DCs and create mask bitmap:
GetBitmap Source.Handle, LenB(BITMAP), BITMAP
With BITMAP
If SourceWidth < 1 Then SourceWidth = .bmWidth
If SourceHeight < 1 Then SourceHeight = .bmHeight
hbmMask = CreateBitmap(.bmWidth, .bmHeight, 1, 1, WIN32_NULL)
End With
hdcMemColor = CreateCompatibleDC(Dest.hDC)
hbmColorOrig = SelectObject(hdcMemColor, Source.Handle)
bkColorOrig = SetBkColor(hdcMemColor, MaskColor)
hDCMemMask = CreateCompatibleDC(Dest.hDC)
hbmMaskOrig = SelectObject(hDCMemMask, hbmMask)
BitBlt hDCMemMask, 0, 0, SourceWidth, SourceHeight, hdcMemColor, 0, 0, vbNotSrcCopy
SetBkColor hdcMemColor, bkColorOrig
'Origins at center (0, 0) of the dest area:
Points(0).x = Int(SourceScale * SourceWidth + 0.5) \ 2 - Int(SourceScale * SourceWidth + 0.5)
Points(0).y = Int(SourceScale * SourceHeight + 0.5) \ 2 - Int(SourceScale * SourceHeight + 0.5)
Points(1).x = Points(0).x + Int(SourceScale * SourceWidth + 0.5)
Points(1).y = Points(0).y
Points(2).x = Points(0).x
Points(2).y = Points(0).y + Int(SourceScale * SourceHeight + 0.5)
'Degrees to radians:
Rotation = Rotation * PI / 180
'Rotate points and adjust origins:
For I = 0 To 2
RotatedPoints(I).x = (DestX + DestWidth) \ 2 _
+ Int(Points(I).x * Cos(Rotation) - Points(I).y * Sin(Rotation) + 0.5)
RotatedPoints(I).y = (DestY + DestHeight) \ 2 _
+ Int(Points(I).x * Sin(Rotation) + Points(I).y * Cos(Rotation) + 0.5)
Next
StretchBltModeOrig = SetStretchBltMode(Dest.hDC, sbmHalftone)
If PlgBlt(Dest.hDC, _
RotatedPoints(0), _
hdcMemColor, _
SourceX, _
SourceY, _
SourceWidth, _
SourceHeight, _
hbmMask, _
SourceX, _
SourceY) = 0 Then
Err.Raise &H8004C700, TypeName(Me), "PlgBlt error " & CStr(Err.LastDllError)
End If
SetStretchBltMode Dest.hDC, StretchBltModeOrig
'Cleanup:
SelectObject hDCMemMask, hbmMaskOrig
DeleteDC hDCMemMask
SelectObject hdcMemColor, hbmColorOrig
DeleteDC hdcMemColor
End Sub
I can't tell whether I have a bug, some sort of rounding error, or a flaw in PlgBlt() working with my video adapter or its driver.
I haven't tried to use this GDI call in a very long time, so I may have just screwed it up. The rotation logic may be more convoluted than necessary too, it is just "air code" with a few tweaks until it seemed to work.
No, I think you're seeing a known bug. I've used plgblt a bit in the past, and I know that I had to check to insure I didn't use an exact 180 degree rotation, always added a slight offset like .001 to avoid the issue.
I'm not 100% sure that is what you're seeing, but I know there is a bug. Since your rotation is around 95 degrees and 180 off of that, it might be a variation of the bug.
I changed from using plgblt to using the gdi matrix call to implement rotation later on (actually a bit simpler, I think), and I don't know if it has the same issues or not.
I have to go to a long meeting, and have a number of meetings today so don't have time to dig around and see if I can fine some examples of where I've used plgblt vs matrix rotation for comparison. Maybe, this evening.
Last edited by passel; Jun 12th, 2018 at 06:54 AM.
Hello NW90,
yes, that's a bug. The somewhat inelegant solution is to offset
the coordinates of
a vertex by one pixel at a rotation angle of 180 ° . This
does not create an exact rectangle and the error does
not occur. The clean, but rather elaborate, solution
is to
execute the rotation with StretchBlt (negative width and height) in
another bitmap at an angle of rotation of 180 ° and then
to copy it into the target bitmap with TransparentBlt.
In any case it seems good enough for some purposes.
Here is another rude and crude demo. It uses a bitmap image as a source to "clip" stitched subimages from, assuming they're arranged in a grid layout of rows of columns. Not fully tested, I only used a single-row source bitmap here.
A lot of the artifacting here comes from the source bitmap itself. i had grabbed a sample PNG with transparency and hurriedly flattened it to a GIF for use. Fix up some of those magenta-ish pixels at the edges and it would look better.
The cars just run around the crudely drawn "track" here.
Edit:
Replaced the attachment. Cleaned up the image fringing, added a 2nd row to test that. Also added click to stop/resume animation. No changes to the important code, only to demo Form1.
Last edited by dilettante; Jul 1st, 2018 at 03:35 PM.