Results 1 to 5 of 5

Thread: PlgBlt weirdness... or a bug?

  1. #1

    Thread Starter
    Join Date
    Feb 2006

    PlgBlt weirdness... or a bug?

    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:

    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
            PlgBlt.Render Picture1, 0, 0, 300, 300, Needle, vbGreen, Rotation, 1
            .AutoRedraw = False
        End With
        Rotation = (Rotation + 1) Mod 360
    End Sub
    Name:  sshot1.png
Views: 266
Size:  1.8 KB

    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:

    Name:  sshot2.png
Views: 220
Size:  1.5 KB

    Here is the 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)
        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
        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.

    Any ideas?
    Attached Files Attached Files

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Southern Tier NY

    Re: PlgBlt weirdness... or a bug?

    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.

  3. #3
    Join Date
    Jun 2015

    Re: PlgBlt weirdness... or a bug?

    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.


  4. #4

    Thread Starter
    Join Date
    Feb 2006

    Re: PlgBlt weirdness... or a bug?

    Thanks, that's probably it. It is even beginning to sound familiar so it was probably discussed a lot in days gone by.

  5. #5

    Thread Starter
    Join Date
    Feb 2006

    Re: PlgBlt weirdness... or a bug?

    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.

    Name:  sshot.png
Views: 154
Size:  7.6 KB

    The cars just run around the crudely drawn "track" here.


    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.
    Attached Files Attached Files
    Last edited by dilettante; Jul 1st, 2018 at 03:35 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

Click Here to Expand Forum to Full Width