# Visual Basic > Visual Basic 6 and Earlier >  [RESOLVED] Desperately need some BMP DC graphics functions

## ISAWHIM

I have been going crazy trying to find some decent, fast and somewhat simple graphics functions. Things that BitBlt could do, if it did them correctly, but it does not.

The biggest thing, which I can't fathom why there isn't a "fast" way to do it. A simple "darken" and "lighten" function for BMP. I do realize that BMP is a rather demanding format, but it is virtually the core of everything VB6 related.

Almost every graphical program has a "fast" ability to "fade-out" and "fade-in" and "blow-out" (fade to white). GDI sort-of has something similar that should work, but due to whatever shortcuts they took, the functions don't actually work as expected.

The math is sort-of simple, in a "cheap" way. VB6 just isn't, by itself, capable of the speeds needed to do this internally, for a gaming-sense.

The math I am looking for is just a simple RGB(+/-1,+/-1,+/-1) with the obvious "limit" of 255 and 0. A matrix-transform is too universal/modular to be fast, so this is something that has to be done at a root-level.

SSE2, MMX, some kind of hijacked "string masking logical operator"... something

Ultimately, the full list of functions I am seeking, besides "darken" and "lighten", which need to be bitblt and GDIplus safe, are the following things.

The same function, but only on an "Alpha" channel, which would assist with GDIplus mostly.

A dual-mask version of the simple darken and lighten code. However, instead of feeding it a value of +/-1 to +/-255 to apply globally to RGB, it simply performs XOR, NOT, AND, NOR, OR, NAND, while also possibly taking those same +/-1 to +/-255 values as a "strength" to apply the two provided handles to a "darkness mask" and a "lightness mask". This could give a sense of "lighting depth" or "lighting effect" to the image. (Possibly, for speed, a fast-interpolation that uses an image that is 1/2 the size as the mask, or just a less "accurate" brightness and darkness factor, or the same mask is both brightness and darkness, like a "depth-map".)

The most exotic thing I wanted, was something you would only expect to find in Direct-X, but honestly, it should have its own external function too. The more complex "Normal map", which uses RGB values to act as a "directional Normal-facing", like a shadow-map or a depth-map, but only the colors associated with the "direction of lighting and darkness", are being bumped-up or down more. It would require the same +/-1 to +/-255, as well as some fast value for the directional position of the "modified strength" (The angle of the light, as one or two values. 2 bytes or a single byte that holds 2x 128 position values.)

The last "missing" function that I can't really find is one that can swap 256 color pallets, or manipulate them by shifting or changing value ranges, but return the BitBlt safe or GDIplus safe, 24-bit result back. (BMP's get the pallet stripped in the DC process to display as a 24-bit image, for manipulation. However, the colors are still the same and could be quickly compared for equality to the pallet, if it wasn't stripped. If it has an alpha channel, the pallet index for each RGB could be stored there, since BMP doesn't really use the Alpha channel. Or that could just be a restriction for using these "special" RGBA types of images.)

----------


## wqweto

Amen!

I wish there were such procedures built in the language too :-))

----------


## Schmidt

Regarding the first request (Blitting/Cropping with Darkening/Lightening),
Cairo has enough BlendOperators built-in, to accomplish that (on Image-Surfaces):

The Cairo-wrapper is freeware - and can be retrieved from vbRichClient.com...

Into a Form (with a Project-Reference to RC6):


```
Option Explicit
 
Private Sub Form_Load()
  Visible = True
  
  Dim Src As cCairoSurface, LumFac As Double
  Set Src = Cairo.ImageList.AddImage("", "c:\temp\tulips.jpg")
  
  LumFac = 0.25 'lighten it up by 25%
  Set Picture = BlitWithLuminance(Src, 0, 0, Src.Width / 2, Src.Height / 2, LumFac).Picture
  MsgBox "Half-cropped Result with LumFactor: " & LumFac
  
  LumFac = -0.5 'a negative factor will darken it (here by 50%)
  Set Picture = BlitWithLuminance(Src, 0, 0, Src.Width / 2, Src.Height / 2, LumFac).Picture
  MsgBox "Half-cropped Result with LumFactor: " & LumFac
  
  Set Picture = Src.Picture
  MsgBox "Original Src-Image"
End Sub
 
Function BlitWithLuminance(Src As cCairoSurface, x, y, dx, dy, ByVal LumFac#) As cCairoSurface
  Set BlitWithLuminance = Src.CropSurface(x, y, dx, dy) 'make a cropped copy of the Src
  
  LumFac = 127 + 127 * LumFac
  
  Dim CC As cCairoContext
  Set CC = BlitWithLuminance.CreateContext 'derive a drawing-context from that Surface
      CC.Operator = CAIRO_OPERATOR_HARD_LIGHT
      CC.Paint 1, Cairo.CreateSolidPatternLng(RGB(LumFac, LumFac, LumFac))
End Function
```

Your second request could be fulfilled by the Cairo-wrapper "only half-ways", meaning that:
- the conversion of a "256-Colors paletted BitmapFile" to 32Bit-Alpha ImageSurfaces will work
- as well as the following Darkening/Lightening-Operation
- though for the last part (back-conversion to the Paletted BMP) there's nothing directly built-in

HTH

Olaf

----------


## baka

one very easy way to do is using gdialphablend
copy a 1 pixel black or 1 pixel white into the whole rect of the bitmap using the opacity preferred.

----------


## ISAWHIM

I'm looking at that exact function, as you typed that reply... Originally, when I saw that API, I ASSUMED, it was part of GDIplus, as it starts with GDI...

I am setting-up a time-demo now, to test how fast it is. That MAY the "missing link" I was crying about NOT being in there. (Naturally, I would have expected that function to be BitBltAlphaBlend.)

Will be back with results, hopefully.

Also looking into Cairo-wrapper

----------


## ISAWHIM

First test... I found "AlphaBlend()", sister to "GdiAlphaBlend()"...

Initial test results.
Provisions: Needed to create BLENDFUNCTION type, which didn't like being used directly. Have to do a CopyMemory() to stuff the CUSTOM TYPE into an actual "LONG" value. Though, since only one value changes, I made a little hack...

There is a nice function called RGB(), which takes three byte-values and spits-out a LONG. Normally this equates to R,G,B,A but without the ability to set the "A" (alpha) value. To make a long story short, all values are normally 0, except the first and third value. The first, for this blend-mode is 0 and the third value is the blend percent, as a byte...

So, I used...
Dim AlphaBF as Long
AlphaBF = RGB(0,0,i)

Where (i) was the inner-loop value of 1 to 254. The result of RGB(), as a long, expressed as bytes is this, (0,0,i,0), which equates to the expected BLENDFUNCTION format. If I wanted to use the images alpha, I could use 255 for i and/or (1,0,255,0), but the image has to be a 32-bpp, with an alpha, I think?

Test Setup was a full color spectrum image in a 240x640px (144,000 total px). The quick loop test was 10 passes doing a fade from 1 to 254, because 0 is 100% transparent {use none} and 255 is Alpha {Use Alpha value in image}, and the image source was a picture-box setup with a bitblt of vbBlackness, which had the "image" set to "picture" after blitting. The BG of the picture-box also was black, just to be sure too. :P

Results of 100 passes doing fades from 1 to 254 was, 15.69 seconds, in the GUI. (1 / 15.69 sec * 25400 = ~1619 fades per second)

However, when compiled, AlphaBlend() seems to crash after 35 loops or 8890 calls, or 5.5 seconds. Sad too, because the function uses my GPU 3D processing. Running in compatibility mode, for Win 8 or lower, it runs fine. Faster in Win 95!

Short solution was to put some breaks on the time-demo test. I forced a 1ms delay/release where the loop could not continue until gettickcount was > +1 of the old value. That stopped the crashing, but reduced it down to only 60 loops, from 100, in the same time-period.

Overall, I am THRILLED with the results.

Now I just need to find some of those "other" odd things. Cairo, here I come!

----------


## baka

I dont use GDI anymore for my needs, I use Direct2d nowadays.
when I did use it I did like this:



```
Private Declare Function GdiAlphaBlend Lib "gdi32" (ByVal hdcDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal WidthDest As Long, ByVal HeightDest As Long, ByVal hdcSrc As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal WidthSrc As Long, ByVal HeightSrc As Long, ByVal Blendfunc As Long) As Long
```

Opacity is 0-255

Transparency = 25100288 + Opacity * 32768

or if u want to use 0.0 to 1.0 (this is how Direct2D works)

Transparency = 25100288 + CLng(Opacity * 256) * 32768

in Direct2D I just type 0.0-1.0 where 1.0 is full opacity and 0.0 is full transparency

and if u want speed, Direct2D is much better. and vsync as well.

----------


## georgekar

@ISAWHIM
I am sure that the crashing happen because you get each time fresh handler and you didn't release it.
Another aspect is the HDC. You have to take HDC each time to do something with a picturebox, else you hold something which will be invalid some time. VB6 is very fast, for what you want to do. So if you can post your code I can fix it.

----------


## Schmidt

> Cairo, here I come!...


Well, since this seems performance-related (in a kind of animation) -
the higher quality BlendOperator in my first example: CAIRO_OPERATOR_HARD_LIGHT
...seems a bit "overkill" for this task.

So, when you want to check this with the simpler AlphaBlend-Operator of Cairo 
(CAIRO_OPERATOR_OVER ... which is the default) -
you probably want something like this: 
(in an empty Form-Project with a reference to "RC6").



```
Private Sub Form_Click()
  Dim Src As cCairoSurface
  Set Src = Cairo.ImageList.AddImage("", "c:\temp\tulips.jpg", 240, 640)
 
  New_c.Timing True
    Dim CC As cCairoContext 'create a render-context in the same size as the Source
    Set CC = Cairo.CreateSurface(Src.Width, Src.Height).CreateContext
    
    Const OpCount = 2540
    Dim i As Long
    For i = 1 To OpCount
        CC.Paint 1, Src.CreateSurfacePattern 'draw the Src-Pixels
        CC.Paint i / OpCount, Cairo.CreateSolidPatternLng(vbBlack) 'and blend over with Black
        CC.Surface.DrawToDC hDC 'now render the result from the intermediate Surface to the Form-hDC
    Next
  Caption = New_c.Timing
End Sub
```

HTH

Olaf

----------


## dilettante

GDI also offers SetColorAdjustment() which can do a lot of things.



```
Option Explicit

Private Const WIN32_FALSE As Long = 0

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Declare Function AdjustWindowRect Lib "user32" ( _
    ByRef RECT As RECT, _
    ByVal dsStyle As Long, _
    ByVal bMenu As Long) As Long

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long

Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long

Private Const GWL_STYLE As Long = -16

Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongW" ( _
    ByVal hWnd As Long, _
    ByVal nIndex As Long) As Long

Private Declare Function SelectObject Lib "gdi32" ( _
    ByVal hDC As Long, _
    ByVal hObject As Long) As Long

Private Const CA_NULL = 0
Private Const CA_NEGATIVE = 1   'Specifies that the negative of the original image
                                'should be displayed.
Private Const CA_LOG_FILTER = 2 'Specifies that a logarithmic function should be
                                'applied to the final density of the output colors.
                                'This will increase the color contrast when the
                                'luminance is low.

Private Const ILLUMINANT_DEVICE_DEFAULT = 0 'Device's default. Standard used by output
                                            'devices.
Private Const ILLUMINANT_A = 1              'Tungsten lamp.
Private Const ILLUMINANT_B = 2              'Noon sunlight.
Private Const ILLUMINANT_C = 3              'NTSC daylight.
Private Const ILLUMINANT_D50 = 4            'Normal print.
Private Const ILLUMINANT_D55 = 5            'Bond paper print.
Private Const ILLUMINANT_D65 = 6            'Standard daylight. Standard for CRTs and
                                            'pictures.
Private Const ILLUMINANT_D75 = 7            'Northern daylight.
Private Const ILLUMINANT_F2 = 8             'Cool white lamp.
Private Const ILLUMINANT_TUNGSTEN = ILLUMINANT_A
Private Const ILLUMINANT_DAYLIGHT = ILLUMINANT_C
Private Const ILLUMINANT_FLUORESCENT = ILLUMINANT_F2
Private Const ILLUMINANT_NTSC = ILLUMINANT_C

'The COLORADJUSTMENT structure defines the color adjustment values used by the StretchBlt
'and StretchDIBits functions when the stretch mode is HALFTONE.
Private Type COLORADJUSTMENT
    caSize As Integer               'Specifies the size, in bytes, of the structure.
    caFlags As Integer              'Specifies how the output image should be prepared.
    caIlluminantIndex As Integer    'Specifies the type of standard light source under
                                    'which the image is viewed.

    'These gamma values are tricky: 16-bit unsigned values (WORD), not really Integers.
    caRedGamma As Integer           'Specifies the nth power gamma-correction value for
                                    'the red primary of the source colors. The value must
                                    'be in the range from 2500 to 65,000. A value of
                                    '10,000 means no gamma correction.
    caGreenGamma As Integer         'Specifies the nth power gamma-correction value for
                                    'the green primary of the source colors.
    caBlueGamma As Integer          'Specifies the nth power gamma-correction value for
                                    'the blue primary of the source colors
    caReferenceBlack As Integer     'Specifies the black reference for the source colors.
                                    'Any colors that are darker than this are treated as
                                    'black. The value must be in the range from 0 to 4000.
    caReferenceWhite As Integer     'Specifies the white reference for the source colors.
                                    'Any colors that are lighter than this are treated as
                                    'white. The value must be in the range from 6000 to
                                    '10,000.
    caContrast As Integer           'Specifies the amount of contrast to be applied to
                                    'the source object. The value must be in the range
                                    'from 100 to 100. A value of 0 means no contrast
                                    'adjustment.
    caBrightness As Integer         'Specifies the amount of brightness to be applied to
                                    'the source object. The value must be in the range
                                    'from 100 to 100. A value of 0 means no brightness
                                    'adjustment.
    caColorfulness As Integer       'Specifies the amount of colorfulness to be applied
                                    'to the source object. The value must be in the range
                                    'from 100 to 100. A value of 0 means no colorfulness
                                    'adjustment.
    caRedGreenTint As Integer       'Specifies the amount of red or green tint adjustment
                                    'to be applied to the source object. The value must be
                                    'in the range from 100 to 100. Positive numbers
                                    'adjust towards red and negative numbers adjust
                                    'towards green. Zero means no tint adjustment.
End Type

Private Declare Function GetColorAdjustment Lib "gdi32" ( _
    ByVal hDC As Long, _
    ByRef COLORADJUSTMENT As COLORADJUSTMENT) As Long

Private Declare Function SetColorAdjustment Lib "gdi32" ( _
    ByVal hDC As Long, _
    ByRef COLORADJUSTMENT As COLORADJUSTMENT) As Long

Private Enum StretchBltModes
    [_SBMFAILED] = 0
    BLACKONWHITE = 1
    WHITEONBLACK = 2
    COLORONCOLOR = 3
    HALFTONE = 4
End Enum

Private Declare Function SetStretchBltMode Lib "gdi32" ( _
    ByVal hDC As Long, _
    ByVal StretchMode As StretchBltModes) As StretchBltModes

Private Declare Function StretchBlt Lib "gdi32" ( _
    ByVal hDCDest As Long, _
    ByVal nXOriginDest As Long, _
    ByVal nYOriginDest As Long, _
    ByVal nWidthDest As Long, _
    ByVal nHeightDest As Long, _
    ByVal hdcSrc As Long, _
    ByVal nXOriginSrc As Long, _
    ByVal nYOriginSrc As Long, _
    ByVal nWidthSrc As Long, _
    ByVal nHeightSrc As Long, _
    Optional ByVal dwRop As RasterOpConstants = vbSrcCopy) As Long

Private Pic As IPicture
Private PicW As Long
Private PicH As Long
Private hDCPic As Long
Private OrigCA As COLORADJUSTMENT
Private Fade As Integer
Private FadeDelta As Integer

Private Sub Form_Load()
    Dim RECT As RECT

    Set Pic = LoadPicture("sample.jpg")
    With Pic
        PicW = ScaleX(Pic.Width, vbHimetric, vbPixels)
        PicH = ScaleY(Pic.Height, vbHimetric, vbPixels)
        'We'll render into Form1, so resize it to fit Pic:
        With RECT
            .Right = PicW
            .Bottom = PicH
            AdjustWindowRect RECT, _
                             GetWindowLong(hWnd, GWL_STYLE), _
                             WIN32_FALSE
            Width = ScaleX(.Right, vbPixels, vbTwips)
            Height = ScaleY(.Bottom, vbPixels, vbTwips)
        End With
        hDCPic = CreateCompatibleDC(hDC)
        SelectObject hDCPic, .Handle
    End With
    'We don't have to save and restore DC settings for this degenerate case where
    'Form1 has nothing but the images we render:
    SetStretchBltMode hDC, HALFTONE
    GetColorAdjustment hDC, OrigCA
    FadeDelta = 2
    Timer1.Enabled = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
    DeleteDC hDCPic
End Sub

Private Sub Timer1_Timer()
    Dim CA As COLORADJUSTMENT

    CA = OrigCA
    CA.caBrightness = Fade
    CA.caContrast = Fade
    SetColorAdjustment hDC, CA
    StretchBlt hDC, 0, 0, PicW, PicH, hDCPic, 0, 0, PicW, PicH

    Fade = Fade + FadeDelta
    If Abs(Fade) >= 100 Then FadeDelta = -FadeDelta
End Sub
```

----------


## ISAWHIM

Here is the "heavily commented code"...

Remember: This ONLY crashes in the compiled EXE, not while running in the VB6 programming IDE.

Well, here is an interesting observation. I was running the "trimmed code" and it didn't crash. Thought I may have fixed it. Then I opened-up "Task Manager", to see the GPU use, and the running demo instantly crashed.

Guessing here... "Task Manager" may be fighting with the same API calls, or something in the "Task Manager" "Monitoring", is causing the crash. (Could be both. I see a LOT of transparent icons in there. This is, after all, a "core API" that the system is also fighting for use with.) Possibly also the "status polling" for the GPU is interrupting the 3D acceleration of the data being processed. I'd guess windows didn't code the "pause" or "interrupts" for the new alphablend() functions correctly, or at all, to gain speed.

The line crashing the time-demo is this one...


```
Call AlphaBlend(pScreen.hdc, 0, 0, pScreen.Width, pScreen.Height, pBuff.hdc, 0, 0, pBuff.Width, pBuff.Height, AlphaBF)
```

All screens are the same size and "bpp" format, created by the system, so there should be no "conversion" going on. "pScreen, pBuff, pScreenCopy" are just renamed "PictureBox" controls. (AutoRedraw = True, ScaleMode = 3-Pixel), ScreenCopy and Buff are (Visibility = False)



```
Private Sub Command1_Click()
    Dim i As Long
    Dim x As Long
    Dim y As Long
    Dim oldT As Long
    'Dim BF As BLENDFUNCTION
    'With BF
    '    .BlendOp = AC_SRC_OVER ' Value is always 0 here
    '    .BlendFlags = 0
    '    .SourceConstantAlpha = 1
    '    .AlphaFormat = 0
    'End With
    
    oldT = GetTickCount()
    
    ' Copies the "original image" to a "copy" for restoring the original, when needed
    Call BitBlt(pScreenCopy.hdc, 0, 0, pScreen.Width, pScreen.Height, pScreen.hdc, 0, 0, vbSrcCopy)
    ' Had to refresh, because bitblt doesn't trigger an actual paint event
    pScreenCopy.Refresh
    ' This just creates the "Solid black surface", used to darken the screen, placed in a buffer, because it was there.
    Call BitBlt(pBuff.hdc, 0, 0, pScreen.Width, pScreen.Height, pScreenCopy.hdc, 0, 0, vbBlackness)
    ' Sets the actual "result" into the picturebox's picture, since this was a bitblt creation
    pBuff.Picture = pBuff.Image
    
    y = oldT + 1
    ' Change this to 60, if you uncomment the "DO LOOP"
    For x = 1 To 100 '1000001
        For i = 1 To 254
            '##################################
            ' Forced delay to "stall" the loop
            'Do Until GetTickCount > y
            '    i = i + 1 - 1
            '    DoEvents
            'Loop
            'y = y + 1
            '##################################
            ' Original "custom type" conversion into an actual LONG
            'BF.SourceConstantAlpha = i
            'CopyMemory AlphaBF, BF, 4
            ' Faster method to convert needed values into a LONG
            AlphaBF = RGB(0, 0, i)
            ' Changed to CALL, in hope that it "disposed" of the return values faster
            ' Applies the "original image COPY" to the visible screen. (Not working in the buffer here, for time-demo)
            Call BitBlt(pScreen.hdc, 0, 0, pScreen.Width, pScreen.Height, pScreenCopy.hdc, 0, 0, vbSrcCopy)
            ' Does the "blend" of the black image in the BUFFER, onto the "original" image in the display
            ' This is the source of the "crashing". I can do a billion normal bitblt calls without crashing
            ' Something in the alphablend or gdialphablend is causing the crash
            ' I didn't use gdialphablend, because it returns a LONG, which holds specific info about "failures".
            ' However, this "crash" doesn't return anything, it just dies silently in the DLL, I assume.
            ' Hoped that returning less info would let it live longer, but it doesn't and it's not any faster or slower
            Call AlphaBlend(pScreen.hdc, 0, 0, pScreen.Width, pScreen.Height, pBuff.hdc, 0, 0, pBuff.Width, pBuff.Height, AlphaBF)
            ' Only needed in the time-demo, to "see" the results on the "original" image
            pScreen.Refresh
        Next i
        ' Just restores the original picture after the "time-demo" is done, for a second run
        Call BitBlt(pScreen.hdc, 0, 0, pScreen.Width, pScreen.Height, pScreenCopy.hdc, 0, 0, vbSrcCopy)
        pScreen.Refresh
    Next x
    
    Form1.Caption = FormatNumber(CDbl(GetTickCount - oldT) / 1000, 2) & " sec"
End Sub
```

Shorter version: (Comments and "fat" removed)



```
Private Sub Command1_Click()
    Dim i As Long
    Dim x As Long
    Call BitBlt(pCopy.hdc, 0, 0, pScrn.Width, pScrn.Height, pScrn.hdc, 0, 0, vbSrcCopy)
    pCopy.Refresh
    Call BitBlt(pBlk.hdc, 0, 0, pScrn.Width, pScrn.Height, pCopy.hdc, 0, 0, vbBlackness)
    pBlk.Picture = pBlk.Image
    For x = 1 To 100
        For i = 1 To 254
            AlphaBF = RGB(0, 0, i)
            Call BitBlt(pScrn.hdc, 0, 0, pScrn.Width, pScrn.Height, pCopy.hdc, 0, 0, vbSrcCopy)
            Call AlphaBlend(pScrn.hdc, 0, 0, pScrn.Width, pScrn.Height, pBlk.hdc, 0, 0, pBlk.Width, pBlk.Height, AlphaBF)
            pScrn.Refresh
        Next i
        Call BitBlt(pScrn.hdc, 0, 0, pScrn.Width, pScrn.Height, pCopy.hdc, 0, 0, vbSrcCopy)
        pScrn.Refresh
    Next x
End Sub
```

The actual VB6 project: Test Alpha Fade.zip

P.S. The i = i + 1 - 1 is just a time-killer in the loop, so it is not a "dead loop of nothingness". No "doevents" is actually needed, because the loop is only, at the longest, 1ms long. I could have added a more "demanding time-killer", like the following...


```
If i > 0 AND "frog" & "cow" <> "tree" & "sneaker" AND Sqr(999.72832323) <> 4374.33434^3232 Then
    i = i + 1 - 1
Else
    Exit Do
End If
```

----------


## ISAWHIM

Still struggling to get DirectX to work correctly on my system. Hates going full-screen (dedicated), and going to a small-screen, just draws a tiny desktop in my 4K screen, not even attempting to do a real "full screen". (Just imagine those black-bars you see in wide-screen movies, but all around my 4K screen, with a TINY 640x480 "desktop", showing a movable "fake full-screen" image where the real "full-screen" DX stuff should be...) Annoys me because it literally resizes my SCREEN to 640x480, messing with my icons and any running programs that force themselves to 'resize" to the tiny window in the middle of this giant 4K 65" screen, which is basically a large ICON at that size.

I have played with the official DX7/DX8 stuff, but it's a bit of a task to get those actually setup on peoples computers correctly, since they no longer include or support dx8vb and dx7vb OCX/DLLs. (Playing with "The Tricks" dx8/dx9 "TBL" files has had some similar limitations, as well as a LOT of bloated overhead to do such simple things, like "drawing pictures on a screen", or just "getting a screen to draw on". Since everything is all 3D now.)

I'd love to get Direct2D working. It did exactly what most people needed for 2D games, and not much more.

----------


## georgekar

@ISAWHIM,
Before you get assumptions on task manager, lets think what you wish to do and what you get so far. Whatever "change" in a "view", like a picturebox, has to do with a rate suitable for human eyes. So a rate of 1000/50 (where 1000 is a delay of 1000 milliseconds and we divide this to 50 parts of small delays), means a 50hz, to refresh the picturebox is very good.
So if you need to display a series of bitmaps (perhaps same bitmap changing the lightness), you have to find a way to produce each frame at intervals of 1000/50 milliseconds.

Your program show that you have a control, a command button, and all the task happen in the click event. This is the first fault. Events supposed to finish as quick as possible, otherwise system will think your program not respond to events (all events have a timestamp at the generation and OS watch them like children if we serve them, and if not at a "logic" time, Os do appropriate actions to halt the program).

So how we work to get a loop with a delay without do this in an event response function. Timers do this job. A timer event is the one which execute the body of loop. So if I want to start a loop in a command button, I have to enable the timer. In timer event I can disable it for a while, without actual disabled it, using a static variable named once as boolean, and setting to true when we start the body of loop, and setting this to false at the end of the body of loop. So if we don't do the job in the desired interval, the next call to event function for the timer (if somewhere we have a doevents, can happen) just skip the code (this is like skipping frames, or lost a frame). From the command button, we can disable the button, and enable it from the timer function, when the timer has to be disabled. 

Lets see another thing, the refresh method. We call refresh in a logical rate (not in 1μs rate).  So your code which is not in VB6 logic, using properly event handling functions, send refresh to system at a high rate. System has to perform the refresh, which for an AutoRedraw True, the double buffer system, write the hidden buffer to final buffer (or swap them), and the OS has to show as this change. So you send thousands of refresh (which are costly), and you talk about a "time killer" the i = i + 1 - 1. The actual OS killer is the refresh method. And you are an amazing "OS killer" by placing not one but many refresh, each for every call to bitblit.

So lets rethink your task, and change to a time based loop, using a "compound" refresh for all controls (from parent), and then we can see the results.

----------


## ISAWHIM

I'm not using the loop for testing. It was only a "temporary" resolution to stop the crashing. (Clearly you can see it commented out.)

The "refresh" has to be called, to "see" the changes, or the screen never changes. Bitblt doesn't trigger a "WM_PAINT" event, you HAVE to manually refresh it, to see the changes. No "back buffer" is needed for an event that paints the whole screen, in one pass, before triggering a refresh. Yes, in a "Normal" operation, this would be something managed in a back-buffer, but not for a time-demo where I am testing (ONE refresh) + (The back-buffer drawing). It removes the redundant, pointless, middle step... The only other alternative to trigger a WM_PAINT event is to insert the picture1.image into the picture1.picture, which is a BIG waste since the image is about to be disposed of, a microsecond later.

AutoRedraw is your FRIEND... if it is OFF then any paint event triggers a "refresh" of the whole screen. Being ON, it is yielding to other processes and only refreshes when appropriate. In this case, is is after a collection of paint events reaches the "screen refresh rate" of 60Hz.

Redundant...

Draw Original screen to back buffer
Draw AlphaBlend to back buffer
Draw back buffer to screen
Refresh
Repeat...

In production, it will just be...

Draw AlphaBlend to Screen
Refresh

However, since I was testing the "fade" all 254 values... I NEEDED to restore the original after every call.

Draw Original to Screen
Draw AlphaBlend to screen
Refresh
Repeat...

There is no "flicker", because until there is a "refresh", the pictures own buffer is not flipped. Again, This is for time-demo purposes, to get the maximum possible potential, from a "visual potential". Screens have refresh rates up to 240Hz (240FPS). Mine just happens to be 60Hz, so calling refresh is actually "ignored", beyond that refresh rate of the monitor. "Refresh" is a "suggestion", not a forced action.

Timers resolution is 15ms by the way... on windows. That is, at best, a forced limit of 60 frames per second and that fails every other time in windows now. It goes 15, 0, 15, 0, 15, 0, 15, 0, 15, 15, 15, 0. Feel free to time it yourself. All code "in a timer" is actually running in a timer-thread, which has an additional delay, just as "calling" a function adds a delay. You can set a lower time on a timer, like 1ms, but it's NOT rolling-out every 1ms with a new pass of code. It will not even respond to any interrupts, until after 15ms, about 28ms last time I checked. Also, that defeats the purpose of a time-demo, and offers zero resolution to 1ms delays.

FYI, VB6 can't kill CPU's with "addition" and "comparison" and most formulations, because it has few threads it actually runs in. Yes, a "time demo" will stress a CPU, that is the whole point of time-demo testing. Why would doing something-else, as opposed to potential "crashy windows functions", matter? It would have been doing the crashy function anyways. I am trying to find out WHY it's crashy. (No assumptions on taskmanager, other then the "specific cause". It's factually, crashing, every time, I open taskmanager. No other things open, not even the VB6 GUI.)

Being "in a button" is irrelevant. That is just a trigger for the code, it's not actually "IN a button". I have run entire games and massive loops that run for weeks "in a button". If you make a CALL to a function, outside the button, it's still "in a button", until the function is finished, then it returns back into the button to finish it, before exiting the "button". If fired from a timer, that is a horrible method. Timer events have limits and being in a "windows managed thread", out of your control, all actions to "stop it" are subject to the same "VB6 is busy" issue, or "timer is busy". That's why it is bad to put loops and doevents in timers, and calls to gettickcount or high-resolutiontimers from within a "callback timer function".

----------


## ISAWHIM

To elaborate as to WHY I am doing "as many calls as possible"... (AKA: Time-Demo)

When this IS in a game, and IN a BACKBUFFER, it will be doing more than the FEW calls to blit the results to the screen. If it is failing after 3000 fast calls, when compiled, then it is going to DEMAND some delays, so it does not go past that, when it's drawing blindly to the back-buffer which would normally be 10,000+ calls to lighten or darken specific things in the final render to the screen.

I've ruled out memory leaks, because RAM never changes use, nor does GPU VRAM and processes clean-up from 3D GPU just fine. But if there is another thing I can do, to stop it from crashing, I'll try it. (Going to try running it in the IDE and compiled, without task manager being open, see how long it runs for, before crashing, if it even crashes. Otherwise I'll just have to detect if task manager is opened and pause the program or make it use slower code when that happens. :P)

At the moment, it's dyeing at 3000 SLOW calls, when compiled, while bitblt can do millions in that time.

It doesn't matter if I "See" the results or not, if I remove the refresh, it crashes even faster. I can do it all in the buffer, unseen, and it crashes even faster than that. (But the count of around 3000 remains the same.)

Again, if I am using this as a fade-out, no back buffer is needed, it would just directly fade the screen 1 level every 1/60th of a second, until it is black. (That would be the pointless thing, doing it in the back buffer then blitting the back buffer to the screen, when the action itself, is a blit. That would slow it down by one blit that didn't need to be done.)

As pointless as people playing games and trying to get 200 FPS on a screen with only a 60Hz refresh, wasting GPU power drawing 140 screens they NEVER SEE. Instead of limiting it to 60 and giving the GPU time to rest and run cooler, doing the SAME THING YOU SEE, and not more, to get that 0.1ms 60hz timing release, instead of a slight hiccup of 1-4ms between 100 frames. Even with adaptive refresh, you still end-up throwing about half of the frames away as it bounces from 30-60-90-120Hz.

----------


## ISAWHIM

Okay, so I figured-out what was happening...

When you open task-manager, windows starts doing a BUNCH of extra "application monitoring", because it assumes you opened it for the purpose of, well, monitoring tasks. (Duh)

Well, the time-demo is running blindly, without pause, which isn't NORMALLY an issue. You expect the program to be unresponsive until it finishes seeing how fast it can do a million operations or more. However, because it is now being "heavily monitored", windows flags it as "possibly dead" or "frozen", and that process breaks the program, as windows is trying to force the "title-bar" to say "Whatever your title says & {Not Responding}".

It is at that exact moment that the alphablend dies, but bitblt keeps going, as well as the rest of the program. The link to the DLL just gets unresponsive.

So, a simple doevents, forced after 2500 alphablend() calls, stops that from happening. (Doesn't alter the time-demo, unless I try to drop that into the individual calls, then it kills the time-demo results.)

Problem solved, on to the next one. Oh wait, there was none after that.

Have it screaming at around 2,540,000 calls in 47.25 seconds now. (I reduced the draw size to 64x64 to see if speeding-up the calls was the issue.) Bend it until it breaks! That is more "real world" to how it will be used in the program anyways. When it isn't darkening the whole screen for a fade-out or fade-in.

----------


## georgekar

You can use this function to not having problems with monitoring from task manager. Just call it in the sub main once.



```
Private Declare Sub DisableProcessWindowsGhosting Lib "user32" ()
```

----------


## ISAWHIM

Interesting...

I didn't even realize that was a thing.

----------

