-
Jan 28th, 2023, 09:17 AM
#1
Thread Starter
Addicted Member
UpdateLayeredWindow and elliptic region (Jagged edges)
Hi,
I have a form and I want to make the form look circular .
1- I use CreateEllipticRgn to create the circular region.
2- I use CreateCompatibleBitmap (Also tried CreateDIBSection) and CreateCompatibleDC to create a memory DC.
3- I use FillRect to draw some brush color (ex: RED) onto the memry DC.
4- I use SelectClipRgn to select the above circular region onto the memory DC.
5- I use UpdateLayeredWindow (ULW_COLORKEY) - This takes away the memory DC black background.
This works and displays the form as a RED circular shape BUT the circle outline looks with jagged edges.
Does the UpdateLayeredWindow API along with ULW_ALPHA requires the bitmap to have an Alpha channel in order to draw smooth borders ?
Any thoughts ? Thank you.
-
Jan 28th, 2023, 10:03 AM
#2
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Shot in the dark here but it sounds like you need some form of anti-aliasing to smooth out the jagged edges. You should probably look into that. My GDI is a bit rusty so I can't recall if there are options for anti-aliasing.
-
Jan 28th, 2023, 10:04 AM
#3
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
Does the UpdateLayeredWindow API along with ULW_ALPHA requires the bitmap to have an Alpha channel in order to draw smooth borders ?
Yes.
Olaf
-
Jan 28th, 2023, 10:16 AM
#4
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
Hi,
Does the UpdateLayeredWindow API along with ULW_ALPHA requires the bitmap to have an Alpha channel in order to draw smooth borders ?
.
yes and you would have to use "GDI+" since it allows SmoothingModeAntiAlias
anyway i want to share this, but it's only for windows 10, and you can also get the gdi+ routine
add this code to a form with BorderStyle = 0
Code:
Option Explicit
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare Function SetWindowCompositionAttribute Lib "user32.dll" (ByVal hWnd As Long, ByRef Data As WindowsCompostionAttributeData) As Long
Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hdc As Long, ByRef graphics As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal graphics As Long) As Long
Private Declare Function GdiplusStartup Lib "gdiplus" (ByRef token As Long, ByRef lpInput As GDIPlusStartupInput, Optional ByRef lpOutput As Any) As Long
Private Declare Function GdiplusShutdown Lib "gdiplus" (ByVal token As Long) As Long
Private Declare Function GdipSetSmoothingMode Lib "GdiPlus.dll" (ByVal mGraphics As Long, ByVal mSmoothingMode As Long) As Long
Private Declare Function GdipDeleteBrush Lib "GdiPlus.dll" (ByVal mBrush As Long) As Long
Private Declare Function GdipFillEllipseI Lib "GdiPlus.dll" (ByVal mGraphics As Long, ByVal mBrush As Long, ByVal mX As Long, ByVal mY As Long, ByVal mWidth As Long, ByVal mHeight As Long) As Long
Private Declare Function GdipCreateSolidFill Lib "GdiPlus.dll" (ByVal mColor As Long, ByRef mBrush As Long) As Long
Private Declare Function CreateEllipticRgn Lib "gdi32.dll" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function SetWindowRgn Lib "user32.dll" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
Private Type GDIPlusStartupInput
GdiPlusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Const SmoothingModeAntiAlias As Long = &H4
Private Const WM_SYSCOMMAND As Long = &H112&
Private Const MOUSE_MOVE As Long = &HF012&
Private Type WindowsCompostionAttributeData
Attribute As Long
Data As Long
SizeOfData As Long
End Type
Private Enum AccentState
ACCENT_DISABLED = 0
ACCENT_ENABLE_GRADIENT = 1
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2
ACCENT_ENABLE_BLURBEHIND = 3
ACCENT_INVALID_STATE = 4
End Enum
Const WCA_ACCENT_POLICY = 19
Private Type AccentPolicy
AccentState As AccentState
AccentFlags As Long
GradientColor As Long
AnimationId As Long
End Type
Private Sub Form_Load()
Dim Data As WindowsCompostionAttributeData
Dim accent As AccentPolicy
Me.ScaleMode = vbPixels
Me.AutoRedraw = True
Me.BackColor = vbBlack
Me.Width = 5000
Me.Height = 5000
accent.AccentState = 6
accent.AnimationId = 5
Data.Attribute = WCA_ACCENT_POLICY
Data.SizeOfData = Len(accent)
Data.Data = VarPtr(accent)
Call SetWindowCompositionAttribute(hWnd, Data)
Dim hRgn As Long
hRgn = CreateEllipticRgn(-5, -5, Me.ScaleWidth + 5, Me.ScaleHeight + 5)
SetWindowRgn hWnd, hRgn, True
Dim GdipToken As Long
Dim GdipStartupInput As GDIPlusStartupInput
Dim hGraphics As Long, hBrush As Long
GdipStartupInput.GdiPlusVersion = 1&
Call GdiplusStartup(GdipToken, GdipStartupInput, ByVal 0)
If GdipCreateFromHDC(hdc, hGraphics) = 0 Then
Call GdipSetSmoothingMode(hGraphics, SmoothingModeAntiAlias)
GdipCreateSolidFill &HFFFF0000, hBrush
GdipFillEllipseI hGraphics, hBrush, 0, 0, Me.ScaleWidth - 1, Me.ScaleHeight - 1
GdipDeleteBrush hBrush
GdipDeleteGraphics hGraphics
End If
Call GdiplusShutdown(GdipToken)
End Sub
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
ReleaseCapture
SendMessage hWnd, WM_SYSCOMMAND, MOUSE_MOVE, 0
End Sub
Private Sub Form_DblClick()
Unload Me
End Sub
-
Jan 28th, 2023, 10:28 AM
#5
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
another alternative is to create the circular picture in paintshop. save it as .png
and have it load during startup.
-
Jan 28th, 2023, 10:53 AM
#6
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
-
Jan 30th, 2023, 01:24 AM
#7
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Thanks everyone.
I like LeandroA's workaround. It makes coding this a lot easier. However, it won't work prior to Win10 so I can't use it in my project.
baka suggested the use of a png image. I had already thought about that.
I am thinking If I use a png file representing a solid colored circle image with transparent background I could then use UpdateLayeredWindow along with ULW_ALPHA to reshape the form as the circle image hence producing a round form with smooth edges. I can subsequently still draw whatever I want onto the form's DC.
I am not sure if this is the way to go.
-
Jan 30th, 2023, 02:51 AM
#8
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
I have just implemented UpdateLayeredWindow on the form based on a PNG file (PNG with transparent background).
The form was reshaped successfully as the PNG image BUT the form edges still look jagged !!
I thought that UpdateLayeredWindow was supposed to produce smoother images\edges than SetLayeredWindowAttributes... Is that true ? It doesn't seem to be the case though.
BTW, I have tried both: ULW_ALPHA and ULW_COLORKEY when calling UpdateLayeredWindow just in case but no difference.
This is how the reshaped form looks based on the PNG image and using the UpdateLayeredWindow api function. I see no edges smoothness.
I see no difference in quality over using SetWindowRgn
Am I missing something ? Any ideas ?
EDIT:
Also, I am using GdipSetSmoothingMode(hGraphics, SmoothingModeAntiAlias) on the memory dc but makes no difference.
Last edited by AngelV; Jan 30th, 2023 at 03:07 AM.
-
Jan 30th, 2023, 03:21 AM
#9
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
the PNG image itself need to have smooth edges. its a 32bit PNG picture where u can see the edges of the circle with different opacity levels (so it kind of fade away) that will create the effect of smoothness since opacity will let the background show depending on the opacity level.
-
Jan 30th, 2023, 04:04 AM
#10
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by baka
the PNG image itself need to have smooth edges. its a 32bit PNG picture where u can see the edges of the circle with different opacity levels (so it kind of fade away) that will create the effect of smoothness since opacity will let the background show depending on the opacity level.
Thanks baka.
I just double checked the png image file (along with two other png images) and it is a 32bit PNG
-
Jan 30th, 2023, 04:08 AM
#11
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
the circle should be like this
https://i.stack.imgur.com/poE8A.png
but with transparent background.
Im not home so I cant create one. should be easy with any paint tool.
-
Jan 30th, 2023, 05:14 AM
#12
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by baka
Thanks baka.
Finally, I seem to have managed to make a decent transparent round PNG image online (https://kleki.com/) for testing and just as you suggested, it worked ! The edges of the resulting re-shaped form are now very smooth.
Question: Do you think I could achieve the same result with other image formats other than PNG so long as they have an alpha channel ?
-
Jan 30th, 2023, 08:26 AM
#13
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
Question: Do you think I could achieve the same result with other image formats other than PNG so long as they have an alpha channel ?
Yes. In Windows all image formats are eventually converted to an hBitmap.
-
Jan 30th, 2023, 12:54 PM
#14
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
You can even draw/generate the circle/ellipse in memory so no need to ship a separate PNG file for this reason only.
If your transparency needs are more complex than a basic shape then a special purpose PNG might be justified shipping.
cheers,
</wqw>
-
Jan 30th, 2023, 05:45 PM
#15
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by wqweto
You can even draw/generate the circle/ellipse in memory so no need to ship a separate PNG file for this reason only.
If your transparency needs are more complex than a basic shape then a special purpose PNG might be justified shipping.
cheers,
</wqw>
Hi wqweto.
Yes. Creating the circle in memory is what I had in mind and is what I was thinking to do next. This would give me much more control over the size, color etc of the image PLUS t will serve my project portability w\o the need to ship a separate image file.
And since it is always going to be a basic circle with a transparent background (only difference will be in the circle size), it will be ideal to create the image in memory.
I know how to create a 32bpp bitmap in memory but I am not sure how I would go about the transparency layer (alpha channel). I have never done this before.
Would it be easier to create the 32bpp transparent memory bitmap with legacy GDI, GDIPlus or WIA ?
-
Jan 30th, 2023, 08:23 PM
#16
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
if u want to be able to change color, maybe inverted circle.
so the center is transparent.
and if u want to be able to resize, maybe the inverted circle as big possible
and resize using interpolation bilinear/bicubic.
or if u want to use GDI32, u can use halftone (SetStretchBltMode) should be good enough for this purpose.
the png can be stored as resource. so no need to have it as an external picture u load.
also, who cares 2022 about a .exe that is 1MB in size?
-
Jan 31st, 2023, 12:58 AM
#17
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
Would it be easier to create the 32bpp transparent memory bitmap with legacy GDI, GDIPlus or WIA ?
Easiest is using GDI+ the way LeandroA used Call GdipSetSmoothingMode(hGraphics, SmoothingModeAntiAlias)
It's possible with only GDI too but first post your non-working GDI code which produces jagged results (preferrably a single Form1 with nothing extra) to illustrate the issue and let someone fix it for you.
Working with existing code is so much simpler for us and so much better to figure out the root issue. That's why programming forums are very convenient for getting help back on your specific *code*.
cheers,
</wqw>
-
Jan 31st, 2023, 03:40 AM
#18
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by wqweto
Easiest is using GDI+ ...
Nahh, to be fair here, easiest is to use the Cairo-Wrapper and the Alpha-capable RC6-FormEngine:
(code below only needs a *.bas-Module in a Project which starts from Sub Main and has an RC6-reference)
Code:
Option Explicit
Sub Main()
Const W = 640, H = 480
Dim CC As cCairoContext
Set CC = Cairo.CreateSurface(W, H).CreateContext
CC.SetLineWidth 6
CC.Ellipse W / 2, H / 2, W - 1, H - 1, True
CC.Stroke , Cairo.CreateSolidPatternLng(vbRed)
Cairo.ImageList.AddSurface "BG", CC.Surface
Const AlphaWithTaskbarEntry = 6, AlphaNoTaskbarEntry = 7
Dim fMain As cWidgetForm
Set fMain = Cairo.WidgetForms.Create(AlphaWithTaskbarEntry, "MyCaption", , W, H)
fMain.WidgetRoot.ImageKey = "BG"
fMain.Show vbModal
End Sub
HTH
Olaf
Last edited by Schmidt; Jan 31st, 2023 at 04:03 AM.
-
Jan 31st, 2023, 05:48 AM
#19
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Place this into a standard module:
Code:
'--- Module1.bas
Option Explicit
DefObj A-Z
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleW" (ByVal lpModuleName 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 Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongW" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongW" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, lpRect As Any) As Long
Private Declare Function UpdateLayeredWindow Lib "user32" (ByVal hWnd As Long, ByVal hdcDest As Long, ptDst As Any, pSize As Any, ByVal hdcSrc As Long, ptSrc As Any, ByVal crKey As Long, pBlend As Any, ByVal dwFlags As Long) As Long
'--- GDI+
Private Declare Function GdiplusStartup Lib "gdiplus" (hToken As Long, pInputBuf As Any, Optional ByVal pOutputBuf As Long = 0) As Long
Private Declare Function GdipCreateBitmapFromScan0 Lib "gdiplus" (ByVal lWidth As Long, ByVal lHeight As Long, ByVal lStride As Long, ByVal lPixelFormat As Long, ByVal Scan0 As Long, hBitmap As Long) As Long
Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal hImage As Long) As Long
Private Declare Function GdipGetImageGraphicsContext Lib "gdiplus" (ByVal hImage As Long, hGraphics As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal hGraphics As Long) As Long
Private Declare Function GdipSetSmoothingMode Lib "gdiplus" (ByVal hGraphics As Long, ByVal lSmoothingMd As Long) As Long
Private Declare Function GdipCreateSolidFill Lib "gdiplus" (ByVal argb As Long, hBrush As Long) As Long
Private Declare Function GdipDeleteBrush Lib "gdiplus" (ByVal hBrush As Long) As Long
Private Declare Function GdipFillEllipseI Lib "gdiplus" (ByVal hGraphics As Long, ByVal hBrush As Long, ByVal lX As Long, ByVal lY As Long, ByVal lWidth As Long, ByVal lHeight As Long) As Long
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "gdiplus" (ByVal hBitmap As Long, hbmReturn As Long, ByVal clrBackground As Long) As Long
Private Type POINTAPI
X As Long
Y As Long
End Type
Public Function GdipCreateEllipseBitmap(ByVal lWidth As Long, ByVal lHeight As Long, ByVal lColor As Long, Optional ByVal SmoothingMode As Long) As Long
Const PixelFormat32bppPARGB As Long = &HE200B
Dim aInput(0 To 3) As Long
Dim hBitmap As Long
Dim hGraphics As Long
Dim hBrush As Long
If GetModuleHandle(StrPtr("gdiplus")) = 0 Then
aInput(0) = 1
Call GdiplusStartup(0, aInput(0))
End If
If GdipCreateBitmapFromScan0(lWidth, lHeight, 4 * lWidth, PixelFormat32bppPARGB, 0, hBitmap) <> 0 Then
GoTo QH
End If
If GdipGetImageGraphicsContext(hBitmap, hGraphics) <> 0 Then
GoTo QH
End If
If SmoothingMode <> 0 Then
If GdipSetSmoothingMode(hGraphics, SmoothingMode) <> 0 Then
GoTo QH
End If
End If
If GdipCreateSolidFill(lColor, hBrush) <> 0 Then
GoTo QH
End If
If GdipFillEllipseI(hGraphics, hBrush, 0, 0, lWidth - 1, lHeight - 1) <> 0 Then
GoTo QH
End If
'--- success
GdipCreateEllipseBitmap = hBitmap
hBitmap = 0
QH:
If hBrush <> 0 Then
Call GdipDeleteBrush(hBrush)
End If
If hGraphics <> 0 Then
Call GdipDeleteGraphics(hGraphics)
End If
If hBitmap <> 0 Then
Call GdipDisposeImage(hBitmap)
End If
End Function
Public Function GdipUpdateLayeredWindow(ByVal hWnd As Long, ByVal hBitmap As Long, Optional ByVal Opacity As Single = 1) As Boolean
Const GWL_EXSTYLE As Long = -20
Const WS_EX_LAYERED As Long = &H80000
Const ULW_ALPHA As Long = 2
Const AC_SRC_ALPHA As Long = &H1000000
Dim lStyle As Long
Dim hScreenDC As Long
Dim hMemDC As Long
Dim hBmp As Long
Dim hPrevBmp As Long
Dim uRect(0 To 1) As POINTAPI
Dim uPtSrc As POINTAPI
lStyle = GetWindowLong(hWnd, GWL_EXSTYLE)
If (lStyle And WS_EX_LAYERED) = 0 Then
Call SetWindowLong(hWnd, GWL_EXSTYLE, lStyle Or WS_EX_LAYERED)
End If
hScreenDC = GetDC(0)
If hScreenDC = 0 Then
GoTo QH
End If
hMemDC = CreateCompatibleDC(hScreenDC)
If hMemDC = 0 Then
GoTo QH
End If
If GdipCreateHBITMAPFromBitmap(hBitmap, hBmp, 0) <> 0 Then
GoTo QH
End If
hPrevBmp = SelectObject(hMemDC, hBmp)
If hPrevBmp = 0 Then
GoTo QH
End If
Call GetWindowRect(hWnd, uRect(0))
With uRect(1)
.X = .X - uRect(0).X
.Y = .Y - uRect(0).Y
End With
Call UpdateLayeredWindow(hWnd, hScreenDC, uRect(0), uRect(1), hMemDC, uPtSrc, 0, _
AC_SRC_ALPHA Or CByte(255 * Opacity) * &H10000, ULW_ALPHA)
'--- success
GdipUpdateLayeredWindow = True
QH:
If hBmp <> 0 Then
If hPrevBmp <> 0 Then
Call SelectObject(hMemDC, hPrevBmp)
End If
Call DeleteObject(hBmp)
End If
If hMemDC <> 0 Then
Call DeleteDC(hMemDC)
End If
If hScreenDC <> 0 Then
Call ReleaseDC(0, hScreenDC)
End If
End Function
You can use GdipCreateEllipseBitmap and GdipUpdateLayeredWindow from a form like this
Code:
'--- Form1.frm
Option Explicit
DefObj A-Z
Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal hImage As Long) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Sub Form_Load()
Const SmoothingModeAntiAlias As Long = 4
Dim bInIde As Boolean: Debug.Assert pvSetTrue(bInIde)
Dim hBitmap As Long
If bInIde Then
Visible = True
End If
hBitmap = GdipCreateEllipseBitmap(Width / Screen.TwipsPerPixelX, Height / Screen.TwipsPerPixelY, &HFFFF0000, SmoothingModeAntiAlias)
If hBitmap = 0 Then
MsgBox "Cannot create elliptical bitmap", vbExclamation
GoTo QH
End If
If Not GdipUpdateLayeredWindow(hWnd, hBitmap, 0.5) Then
MsgBox "Layered window failed", vbExclamation
GoTo QH
End If
QH:
If hBitmap <> 0 Then
Call GdipDisposeImage(hBitmap)
End If
End Sub
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Const WM_SYSCOMMAND As Long = &H112
Const SC_MOVE As Long = &HF010&
Call ReleaseCapture
Call SendMessage(hWnd, WM_SYSCOMMAND, SC_MOVE, 0)
End Sub
Private Function pvSetTrue(bValue As Boolean) As Boolean
bValue = True
pvSetTrue = True
End Function
This is not a quick hack (has proper error handling) but the code is made with reusability in mind (not brevity).
There is an optional SmoothingMode parameter of GdipCreateEllipseBitmap where you can try different modes of anti-aliasing the ellipse.
There is an optional Opacity parameter of GdipUpdateLayeredWindow.
The final form is in the shape of a red ellipse *and* is 50% transparent because it's easy w/ the API call.
cheers,
</wqw>
-
Jan 31st, 2023, 07:17 AM
#20
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Thanks wqweto,
I have adapted your code to x64bit vba and worked beautifully!
I was already half way into making it work but was stuck when trying to resize the image to fit the exact size of the form.
I am still studying the code so that I better uderstand it.
BTW, reading the MS documentation on the blend argument of the UpdateLayeredWindow api, it says that the pblend is a pointer to the BLENDFUNCTION structure. Therefore, I initially proceeded like this :
Code:
Const AC_SRC_OVER = &H0
Dim tBF As BLENDFUNCTION, pBF As Long
With tBF
.BlendOp = AC_SRC_OVER
.BlendFlags = 0
.SourceConstantAlpha = 255
.AlphaFormat = 0
End With
Call CopyMemory(pBF, tBF, 4)
Call UpdateLayeredWindow(hwnd, hScreenDC, uRect(0), uRect(1), hMemDC, uPtSrc, 0, pBF, ULW_ALPHA)
I was expecting the above to work but it didn't.
Whereas if I pass the following, in the pblend argument :
AC_SRC_ALPHA Or CByte(255 * Opacity) * &H10000
It works. Just like you have done in your code.
I would be grateful if you could explain this bit ?
Thanks
-
Jan 31st, 2023, 09:59 AM
#21
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
I would be grateful if you could explain this bit ?
It should work both ways, I've seen the UDT being passed both ByVal and ByRef.
BLENDFUNCTION is a 4-byte UDT and in AlphaBlend API it's passed ByVal (this is possible for UDTs less than 8 bytes long).
Current SDK definition of UpdateLayeredWindow uses a pointer to BLENDFUNCTION i.e. the UDT should be passed ByRef but for some reason it works even if the UDT is passed ByVal like in AlphaBlend case.
No idea why this is possible, probably too many people erred or some prior version (XP?) of the SDK had this parameter declared ByVal.
I'm not sure this is such a good idea on x64 though where pointers are larger than the small 4 byte UDT, so keep passing the parameter ByRef to be in sync with the latest SDK.
cheers,
</wqw>
-
Jan 31st, 2023, 10:33 AM
#22
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by wqweto
It should work both ways, I've seen the UDT being passed both ByVal and ByRef.
BLENDFUNCTION is a 4-byte UDT and in AlphaBlend API it's passed ByVal (this is possible for UDTs less than 8 bytes long).
Current SDK definition of UpdateLayeredWindow uses a pointer to BLENDFUNCTION i.e. the UDT should be passed ByRef but for some reason it works even if the UDT is passed ByVal like in AlphaBlend case.
No idea why this is possible, probably too many people erred or some prior version (XP?) of the SDK had this parameter declared ByVal.
I'm not sure this is such a good idea on x64 though where pointers are larger than the small 4 byte UDT, so keep passing the parameter ByRef to be in sync with the latest SDK.
cheers,
</wqw>
Thanks.
I will take another look at that.
I am still not clear as to how the following actually works : (The OR and * Opeartors as well as the &H10000 confuse me) :
AC_SRC_ALPHA Or CByte(255 * Opacity) * &H10000
Trying to visualise the above bitwise.
Thanks
-
Jan 31st, 2023, 11:08 AM
#23
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
The OR and * Opeartors as well as the &H10000 confuse me) :
AC_SRC_ALPHA Or CByte(255 * Opacity) * &H10000
Multiplication (*) takes precedence over bitwise "Or" so first you calculate the multiplications which just seem to set some bits. &H10000 is decimal 65536 which is 2^16 so an integer with the 17th bit set to 1 and the rest to 0. I don't know the value of "Opacity" but if it's "1" for example then multiplication result would be "1111 1111 0000 0000 0000 0000" in binary. If you need to "visualize" the bits then you could use the Windows Calculator in "programmer mode" since it shows you the same value in Hex, Decimal and Binary for easy comparisons
-
Jan 31st, 2023, 02:46 PM
#24
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by wqweto
It should work both ways, I've seen the UDT being passed both ByVal and ByRef...
Originally Posted by wqweto
No idea why this is possible, probably too many people erred or some prior version (XP?) of the SDK had this parameter declared ByVal.
I'm skeptical that it does work both ways. Besides, I've never seen a Win32 API function that expects a structure to be passed by value. Even if they do exist, these would be exceptions. The rule with Win32 functions seems to be to pass pointers when passing structures.
I'd wager that these code that passes them by value either don't actually work or if they do, it's only a fluke.
The only other possibility I can think of is that the function can detect an invalid pointer and when it does, it treats the pointer as the value itself. It's certainly possible but I can't imagine what the point of writing an interface like that would be.
-
Jan 31st, 2023, 02:53 PM
#25
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by Niya
I'm skeptical that it does work both ways. Besides, I've never seen a Win32 API function that expects a structure to be passed by value. Even if they do exist, these would be exceptions. The rule with Win32 functions seems to be to pass pointers when passing structures.
POINT, LARGE_INTEGER structures are often passed by value. BTW the last example i encountered yesterday:
https://learn.microsoft.com/en-us/wi...calingcallback
Code:
Private Function MagCallback( _
ByVal hWnd As OLE_HANDLE, ByVal pSrc As PTR, _
ByVal lSrcHdr_Width As Long, ByVal lSrcHdr_Height As Long, _
ByVal lSrcHdr_cFormat1 As Currency, ByVal lSrcHdr_cFormat2 As Currency, _
ByVal lSrcHdr_lStride As Long, ByVal lSrcHdr_lOffset As Long, _
ByVal lSrcHdr_cbSize As Long, ByVal pDst As PTR, _
ByVal lDsrHdr_Width As Long, ByVal lDsrHdr_Height As Long, _
ByVal lDsrHdr_cFormat1 As Currency, ByVal lDsrHdr_cFormat2 As Currency, _
ByVal lDsrHdr_lStride As Long, ByVal lDsrHdr_lOffset As Long, _
ByVal lDsrHdr_cbSize As Long, ByVal lUnclippedLeft As Long, _
ByVal lUnclippedTop As Long, ByVal lUnclippedRight As Long, _
ByVal lUnclippedBottom As Long, ByVal lClippedLeft As Long, _
ByVal lClippedTop As Long, ByVal lClippedRight As Long, _
ByVal lClippedBottom As Long, ByVal hDirty As OLE_HANDLE) As Long
-
Jan 31st, 2023, 03:36 PM
#26
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by The trick
POINT, LARGE_INTEGER structures are often passed by value. BTW the last example i encountered yesterday:
Yea, POINT and RECT structures do sometimes tend to get passed by value. Another example is WindowFromPoint. Interestingly though, people often just pass the members as individual parameters.
-
Jan 31st, 2023, 04:14 PM
#27
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Yes, you can split an UDT of two Longs and pass both members separately due to matching stack layout but this hack fails fast under x64 because the calling convention there uses registers for the first several parameters and you have to revert back to “packing” your 8 byte ByVal UDT into a ByVal Currency parameter to work both x86 and x64.
-
Jan 31st, 2023, 10:45 PM
#28
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by VanGoghGaming
Multiplication (*) takes precedence over bitwise "Or" so first you calculate the multiplications which just seem to set some bits. &H10000 is decimal 65536 which is 2^16 so an integer with the 17th bit set to 1 and the rest to 0. I don't know the value of "Opacity" but if it's "1" for example then multiplication result would be "1111 1111 0000 0000 0000 0000" in binary. If you need to "visualize" the bits then you could use the Windows Calculator in "programmer mode" since it shows you the same value in Hex, Decimal and Binary for easy comparisons
Thanks VanGoghGaming
I understand that. What I don't understand is how AC_SRC_ALPHA Or CByte(255 * Opacity) * &H10000 evaluates to the memory address of the BLENDFUNCTION UDT !!
-
Jan 31st, 2023, 11:32 PM
#29
Thread Starter
Addicted Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
@wqweto
Couple of observations on your working code:
* I noticed that you filled only the first member (GdiplusVersion) of the GdiplusStartupInput UDT and never obtained a token. Also you didn't call GdiplusShutdown when done.
Any particular reason ? and is it safe not to call GdiplusShutdown in the end ?
Code:
aInput(0) = 1
Call GdiplusStartup(0, aInput(0))
* You used WM_SYSCOMMAND and SC_MOVE . This keeps the mouse jumping to the top when moving the image. Is there a reason for doing this ?
Code:
Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal Y As Single)
Const WM_SYSCOMMAND = &H112
Const SC_MOVE = &HF010&
Call ReleaseCapture
Call SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE, 0)
End Sub
Shouldn't it be like this :
Code:
Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal Y As Single)
Const WM_NCLBUTTONDOWN As Long = &HA1
Const HTCAPTION As Long = 2&
Call ReleaseCapture
SendMessage hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0
End Sub
* The Scan0 argument in the GdipCreateBitmapFromScan0 api expects a pointer to an array of bytes that contains the pixel data. Here, we passed a null pointer (0).
Would it then be fair to say that if we were to pass a pointer to a byte array filled with pixel data in the Scan0 arg, it would load the image (based on the pixel data) and hence it would then be similar to using the GdipLoadImageFromStream GDIPlus function ? I can't seem to find much info\examples on GDIPlus.
Also, would I be correct to say that when we pass a null pointer in the Scan0 argument (just like you did in your code), we are essentially just creating a *blank* GDI+ bitmap object (like a blank canvas) on which we can then draw whatever we want?
Regards.
Late edit:
In the GdipUpdateLayeredWindow routine, you declared Const AC_SRC_ALPHA As Long = &H1000000 but I already had AC_SRC_ALPHA =1 declared elswhere in my code (I had already looked up the value of the AC_SRC_ALPHA constant on the web)... I had to change it when testing out\filling the BLENDFUNCTION UDT.
Last edited by AngelV; Jan 31st, 2023 at 11:53 PM.
-
Feb 1st, 2023, 12:15 PM
#30
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by AngelV
* I noticed that you filled only the first member (GdiplusVersion) of the GdiplusStartupInput UDT and never obtained a token. Also you didn't call GdiplusShutdown when done.
Any particular reason ? and is it safe not to call GdiplusShutdown in the end ?
You don't shutdown original GDI, do you? Anyone seen trying to unload base DLLs like gdi32.dll or crypt32.dll?
Other languages have GDI+ loaded by the runtime (i.e. before hitting Sub Main) so you never have to worry when to start/shut it down
I was begging LaVolpe and everyone else here to stop shutting GDI+ at all and stop fiddling with any of its startup tokens.
No one else in this world besides VB6 devs try to unload gdiplus.dll.
Originally Posted by AngelV
* You used WM_SYSCOMMAND and SC_MOVE . This keeps the mouse jumping to the top when moving the image. Is there a reason for doing this ?
I copied this from Leandro's snippet here and never gave it a thought. Saw the jumping but deemed it a minor nuisance.
You are not planning on using exactly this hack in production, do you? Please, don't!
Originally Posted by AngelV
* The Scan0 argument in the GdipCreateBitmapFromScan0 api expects a pointer to an array of bytes that contains the pixel data. Here, we passed a null pointer (0).
Would it then be fair to say that if we were to pass a pointer to a byte array filled with pixel data in the Scan0 arg, it would load the image (based on the pixel data) and hence it would then be similar to using the GdipLoadImageFromStream GDIPlus function ? I can't seem to find much info\examples on GDIPlus.
Also, would I be correct to say that when we pass a null pointer in the Scan0 argument (just like you did in your code), we are essentially just creating a *blank* GDI+ bitmap object (like a blank canvas) on which we can then draw whatever we want?
Right! Yes, it's a hack. This creates an empty bitmap (all pixels = 0) with desired format, it's RGB w/ precomputed alpha to be compatible w/ GDI and particularly w/ UpdateLayeredWindow API call. The format of the GDI+ bitmap is the most important parameter here.
Didn't want to risk it creating halo effect w/ non-precomputed (straight) alpha here although GdipCreateHBITMAPFromBitmap call would probably fix such format mismatch between GDI+ bitmap format and GDI's only supported pre-computed alpha.
Originally Posted by AngelV
Late edit:
In the GdipUpdateLayeredWindow routine, you declared Const AC_SRC_ALPHA As Long = &H1000000 but I already had AC_SRC_ALPHA =1 declared elswhere in my code (I had already looked up the value of the AC_SRC_ALPHA constant on the web)... I had to change it when testing out\filling the BLENDFUNCTION UDT.
Yes, now you know why declaring consts local to a procedure and local to a module is very handy -- one can change their values! :-))
Here I just precomputed the offset for 3-rd byte (&H1000000) into the value (&H1) and got &H1000000 for AC_SRC_ALPHA which is exactly what I needed the source code to convey here -- to deliver the intent which comes with the name of the constant. Otherwise I would have simply slapped &H1000000 as an anonymous literal and everyone reading this would wonder what's going on here.
cheers,
</wqw>
-
Feb 1st, 2023, 02:50 PM
#31
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Right! Yes, it's a hack. This creates an empty bitmap (all pixels = 0) with desired format, it's RGB w/ precomputed alpha to be compatible w/ GDI and particularly w/ UpdateLayeredWindow API call. The format of the GDI+ bitmap is the most important parameter here.
When you specify Scan0 parameter it uses your array as pixels data abd you can change it at any time. When you specify null GDI+ allocates and manages this data.
-
Feb 1st, 2023, 03:13 PM
#32
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by The trick
When you specify Scan0 parameter it uses your array as pixels data abd you can change it at any time. When you specify null GDI+ allocates and manages this data.
Well, I wasn't sure of this but if true this would be very cool.
First I started by creating a DIB and passing its lpBits returned to GdipCreateBitmapFromScan0 so that I can use GDI+ to draw anti-aliased ellipse right inside the DIB but I wasn't sure if GDI+ is making a copy of the bits and operate on this copy for speed and didn't have time to investigate.
This would be a very cool trick to mingle GDI and GDI+ with simple DIBs if it's working.
cheers,
</wqw>
-
Feb 1st, 2023, 05:52 PM
#33
Hyperactive Member
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
For reference only.
Like I always said, I don't use PictureBox to store the file image, because (1) the image may be 32-BPP with alpha values in the 4th bytes and (2) if I use DIB instead, the image can thus be saved back to file as 32 BPP, subsequent to an edit if any. I invariably use DIBs only (may be of 32, 24, 8 &/or 1 BPP ones).
When I want to draw an Oval, an unfilled or filled one, I use the code below.
Remarks: (A) As said, I store the image in DIB only, hence when I call GdipCreateBitmapFromScan0() I just pass the DIB's bytes pointer "BitsPtr"; and (B) After calling OvalGDIplus(), DIB may be "rendered" (or "BitBlt/StretchBlt" if without alpha) to whatever the display medium. AlphaBlend is dispensed with (see "Edited" below) **.
Code:
Public Sub OvalGDIplus(inDIB As clsDIB, ByVal inX1 As Long, ByVal inY1 As Long, ByVal inX2 As Long, _
ByVal inY2 As Long, ByVal inBrushSize As Long, ByVal inColor As Long, _
Optional ByVal inAlpha As Integer = 255, Optional inFilled As Boolean = False, _
Optional inPremultiplied As Boolean = False)
Dim hBitmap As Long
Dim hImg As Long
Dim hPath As Long
Dim hPen As Long
Dim w As Long, h As Long
Dim WW As Long, HH As Long
Dim mPenSize As Single
Dim hBrush As Long
Dim tmpX As Long, tmpY As Long
If inBrushSize < 1 Then inBrushSize = 1
mPenSize = CSng(inBrushSize)
If inX1 > inX2 Then
tmpX = inX2
inX2 = inX1
inX1 = tmpX
End If
If inY1 > inY2 Then
tmpY = inY2
inY2 = inY1
inY1 = tmpY
End If
w = inX2 - inX1 + 1
h = inY2 - inY1 + 1
WW = inDIB.Width
HH = inDIB.Height
If inPremultiplied = False Then
inDIB.RotateDIB inDIB, 1 'Adjust our bottom up DIB
GdipCreateBitmapFromScan0 WW, HH, WW * 4, PixelFormat32bppARGB, ByVal inDIB.BitsPtr, hImg
If hImg = 0& Then
inDIB.RotateDIB inDIB, 1 'Restore
Exit Sub
End If
Call GdipGetImageGraphicsContext(hImg, hBitmap)
'Remember to call "nDIB.RotateDIB inDIB, 1" to restore DIB orientation before Exit Sub
Else
If GdipCreateFromHDC(inDIB.hDC, hBitmap) <> [ok] Then
Exit Sub
End If
End If
Call GdipSetSmoothingMode(hBitmap, SmoothingModeAntiAlias)
Call GdipSetInterpolationMode(hBitmap, [InterpolationModeHighQualityBicubic])
If inFilled = False Then
Call GdipCreatePen1(ColorValue(inColor, inAlpha), mPenSize, UnitPixel, hPen)
Call GdipDrawEllipseI(hBitmap, hPen, inX1, inY1, w, h)
Else
Call GdipCreateSolidFill(ColorValue(inColor, inAlpha), hBrush)
Call GdipFillEllipseI(hBitmap, hBrush, inX1, inY1, (inX2 - inX1), (inY2 - inY1))
End If
If inFilled = False Then
Call GdipDeletePen(hPen)
Else
If hPen <> 0 Then
Call GdipDeletePen(hPen)
End If
If hPath <> 0 Then
Call GdipDeletePath(hPath)
End If
Call GdipDeleteBrush(hBrush)
End If
Call GdipDeleteGraphics(hBitmap)
If hImg <> 0 Then
Call GdipDisposeImage(hImg)
End If
If inPremultiplied = False Then
inDIB.RotateDIB inDIB, 1 'Restore
End If
End Sub
Private Function ColorValue(inColor As Long, inA As Integer) As Long
Dim arrByte(0 To 3) As Byte
Dim mColor As Long
If inA < 5 Then inA = 5
If inA > 255 Then inA = 255
arrByte(0) = (inColor \ &H10000) And &HFF
arrByte(1) = (inColor \ &H100) And &HFF
arrByte(2) = inColor And &HFF
arrByte(3) = CByte(inA)
CopyMemory mColor, arrByte(0), 4&
ColorValue = mColor
End Function
** Edited: Regarding earlier said "AlphaBlend is dispensed with"
-- Here we are to draw (an oval) only.
-- When AlphaBlend is deployed, per MS rules R,G & B bytes must be premultiplied first, both destination and source DIBs.
Last edited by Brenker; Feb 1st, 2023 at 09:07 PM.
-
Feb 2nd, 2023, 03:59 AM
#34
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by wqweto
Well, I wasn't sure of this but if true this would be very cool.
First I started by creating a DIB and passing its lpBits returned to GdipCreateBitmapFromScan0 so that I can use GDI+ to draw anti-aliased ellipse right inside the DIB but I wasn't sure if GDI+ is making a copy of the bits and operate on this copy for speed and didn't have time to investigate.
This would be a very cool trick to mingle GDI and GDI+ with simple DIBs if it's working.
cheers,
</wqw>
Yes, GDI+ doesn't make a copy you could use an array or a DIB as well for pixel data. Long ago when I started to study GDI+ i was very surprised why garbage appears on my bitmaps and the program crashes sometimes (it was becasue i used local arrays for pixel initialization because i thought GDI+ makes copy too like GDI )
-
Feb 2nd, 2023, 04:33 AM
#35
Re: UpdateLayeredWindow and elliptic region (Jagged edges)
Originally Posted by The trick
Yes, GDI+ doesn't make a copy you could use an array or a DIB as well for pixel data. Long ago when I started to study GDI+ i was very surprised why garbage appears on my bitmaps and the program crashes sometimes (it was becasue i used local arrays for pixel initialization because i thought GDI+ makes copy too like GDI )
We have similar problems in .Net with GDI+. For example:-
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Open a JPG file using a FileStream
Dim s As FileStream = File.Open("d:\a.jpg", FileMode.Open)
'Create a GDI+ Bitmap from the FileStream.
Dim b As New Bitmap(s)
'Close the FileStream
s.Close()
'This throws an error because GDI+ doesn't actually make a copy of the image
'from the Filestream. It needs the FileStream to remain open and valid
Me.BackgroundImage = b
End Sub
End Class
Fortunately for us the Bitmap class has a constructor that takes another instance of a Bitmap which it copies. To fix the above we just need to change this:-
Code:
Dim b As New Bitmap(s)
To this:-
Code:
Dim b As New Bitmap(New Bitmap(s))
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|