@@ -29,6 +29,13 @@ End Enum
29
29
#If VBA7 Then
30
30
Private Declare PtrSafe Function GetDC Lib "user32 " (ByVal hWnd As LongPtr ) As LongPtr
31
31
Private Declare PtrSafe Function ReleaseDC Lib "user32 " (ByVal hWnd As LongPtr , ByVal hDC As LongPtr ) As LongPtr
32
+ Private Declare PtrSafe Function CreateCompatibleDC Lib "gdi32 " (ByVal hDC As LongPtr ) As LongPtr
33
+ Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32 " (ByVal hDC As LongPtr , ByVal nWidth As Long , ByVal nHeight As Long ) As LongPtr
34
+ Private Declare PtrSafe Function SelectObject Lib "gdi32 " (ByVal hDC As LongPtr , ByVal hObject As LongPtr ) As LongPtr
35
+ Private Declare PtrSafe Function DeleteObject Lib "gdi32 " (ByVal hObject As LongPtr ) As Long
36
+ Private Declare PtrSafe Function DeleteDC Lib "gdi32 " (ByVal hDC As LongPtr ) As Long
37
+ Private Declare PtrSafe Function BitBlt Lib "gdi32 " (ByVal hDestDC As LongPtr , ByVal x As Long , ByVal y As Long , ByVal nWidth As Long , ByVal nHeight As Long , ByVal hSrcDC As LongPtr , ByVal xSrc As Long , ByVal ySrc As Long , ByVal dwRop As Long ) As Long
38
+ Private Declare PtrSafe Function GetClientRect Lib "user32 " (ByVal hWnd As LongPtr , lpRect As rect ) As Long
32
39
Private Declare PtrSafe Function GdiplusStartup Lib "gdiplus " (ByRef token As LongPtr , ByRef inputbuf As GdiplusStartupInput , ByRef outputbuf As GdiplusStartupOutput ) As Long
33
40
Private Declare PtrSafe Sub GdiplusShutdown Lib "gdiplus " (ByVal token As LongPtr )
34
41
Private Declare PtrSafe Function GdipCreateFromHDC Lib "gdiplus " (ByVal hDC As LongPtr , ByRef graphics As LongPtr ) As Long
@@ -47,6 +54,13 @@ End Enum
47
54
End Enum
48
55
Private Declare Function GetDC Lib "user32 " (ByVal hWnd As LongPtr ) As LongPtr
49
56
Private Declare Function ReleaseDC Lib "user32 " (ByVal hWnd As LongPtr , ByVal hDC As LongPtr ) As LongPtr
57
+ Private Declare Function CreateCompatibleDC Lib "gdi32 " (ByVal hDC As LongPtr ) As LongPtr
58
+ Private Declare Function CreateCompatibleBitmap Lib "gdi32 " (ByVal hDC As LongPtr , ByVal nWidth As Long , ByVal nHeight As Long ) As LongPtr
59
+ Private Declare Function SelectObject Lib "gdi32 " (ByVal hDC As LongPtr , ByVal hObject As LongPtr ) As LongPtr
60
+ Private Declare Function DeleteObject Lib "gdi32 " (ByVal hObject As LongPtr ) As Long
61
+ Private Declare Function DeleteDC Lib "gdi32 " (ByVal hDC As LongPtr ) As Long
62
+ Private Declare Function BitBlt Lib "gdi32 " (ByVal hDestDC As LongPtr , ByVal x As Long , ByVal y As Long , ByVal nWidth As Long , ByVal nHeight As Long , ByVal hSrcDC As LongPtr , ByVal xSrc As Long , ByVal ySrc As Long , ByVal dwRop As Long ) As Long
63
+ Private Declare Function GetClientRect Lib "user32 " (ByVal hWnd As LongPtr , lpRect As rect ) As Long
50
64
Private Declare Function GdiplusStartup Lib "gdiplus " (ByRef token As LongPtr , ByRef inputbuf As GdiplusStartupInput , ByRef outputbuf As GdiplusStartupOutput ) As Long
51
65
Private Declare Sub GdiplusShutdown Lib "gdiplus " (ByVal token As LongPtr )
52
66
Private Declare Function GdipCreateFromHDC Lib "gdiplus " (ByVal hdc As LongPtr , ByRef graphics As LongPtr ) As Long
@@ -61,6 +75,8 @@ End Enum
61
75
Private Declare Function GdipGraphicsClear Lib "gdiplus " (ByVal graphics As LongPtr , ByVal color As Long ) As Long
62
76
#End If
63
77
78
+ Private Const SRCCOPY As Long = &HCC0020
79
+
64
80
Private Type GdiplusStartupInput
65
81
GdiplusVersion As Long
66
82
DebugEventCallback As LongPtr
@@ -115,6 +131,12 @@ Private Type TThis
115
131
gdipToken As LongPtr
116
132
objectsIndex As Long
117
133
objects() As CanvasObject
134
+
135
+ ' Double buffering components
136
+ bufferDC As LongPtr
137
+ bufferBitmap As LongPtr
138
+ bufferWidth As Long
139
+ bufferHeight As Long
118
140
End Type
119
141
Private This As TThis
120
142
@@ -129,6 +151,7 @@ Public Function CreateFromHwnd(ByVal hWnd As LongPtr, Optional ByVal obj As Obje
129
151
Set CreateFromHwnd = New stdCanvas
130
152
Call CreateFromHwnd .protInit (hWnd, obj)
131
153
End Function
154
+
132
155
Public Sub protInit (ByVal hWnd As LongPtr , ByVal obj As Object )
133
156
Set This.obj = obj
134
157
This.hWnd = hWnd
@@ -147,21 +170,39 @@ Public Sub protInit(ByVal hWnd As LongPtr, ByVal obj As Object)
147
170
If GdiplusStartup(This.gdipToken, gdip, gdipo) <> 0 Then
148
171
Err.Raise 5 , "stdCanvas#protInit" , "Failed to start GDI+"
149
172
End If
173
+
174
+ ' Initialize double buffer
175
+ Dim clientRect As rect
176
+ Call GetClientRect (This.hWnd, clientRect)
177
+ This.bufferWidth = clientRect.Right - clientRect.Left
178
+ This.bufferHeight = clientRect.Bottom - clientRect.Top
179
+
180
+ This.bufferDC = CreateCompatibleDC(This.hDC)
181
+ If This.bufferDC <> 0 Then
182
+ This.bufferBitmap = CreateCompatibleBitmap(This.hDC, This.bufferWidth, This.bufferHeight)
183
+ If This.bufferBitmap <> 0 Then
184
+ Call SelectObject (This.bufferDC, This.bufferBitmap)
185
+ End If
186
+ End If
150
187
End Sub
151
188
189
+
190
+
152
191
Private Sub Class_Terminate ()
153
192
'TODO: When sprites/fonts added
154
193
'Dim i As Long
155
194
'For i = 1 To UBound(This.objects)
156
195
' Erase bitmaps/fonts etc.
157
196
'Next
158
197
198
+ ' Cleanup double buffer
199
+ If This.bufferBitmap <> 0 Then Call DeleteObject (This.bufferBitmap)
200
+ If This.bufferDC <> 0 Then Call DeleteDC (This.bufferDC)
201
+
159
202
If This.gdipToken <> 0 Then Call GdiplusShutdown (This.gdipToken)
160
203
If This.hDC <> 0 Then Call ReleaseDC (This.hWnd, This.hDC)
161
204
End Sub
162
205
163
-
164
-
165
206
'Draw a rectangle
166
207
'@param x1 - X Coord of top left corner
167
208
'@param y1 - Y Coord of top left corner
@@ -228,9 +269,77 @@ End Property
228
269
Public Sub Redraw ()
229
270
Const UnitPixel As Long = 2
230
271
272
+ ' Check if buffer needs resizing
273
+ Dim clientRect As rect
274
+ Call GetClientRect (This.hWnd, clientRect)
275
+ Dim newWidth As Long : newWidth = clientRect.Right - clientRect.Left
276
+ Dim newHeight As Long : newHeight = clientRect.Bottom - clientRect.Top
277
+
278
+ If This.bufferBitmap = 0 Or newWidth > This.bufferWidth Or newHeight > This.bufferHeight Then
279
+ ' Recreate buffer if needed
280
+ If This.bufferBitmap <> 0 Then Call DeleteObject (This.bufferBitmap)
281
+ If This.bufferDC <> 0 Then Call DeleteDC (This.bufferDC)
282
+
283
+ This.bufferWidth = newWidth + 50 ' Some padding to reduce frequent resizing
284
+ This.bufferHeight = newHeight + 50
285
+ This.bufferDC = CreateCompatibleDC(This.hDC)
286
+ If This.bufferDC <> 0 Then
287
+ This.bufferBitmap = CreateCompatibleBitmap(This.hDC, This.bufferWidth, This.bufferHeight)
288
+ If This.bufferBitmap <> 0 Then Call SelectObject (This.bufferDC, This.bufferBitmap)
289
+ End If
290
+ End If
291
+
292
+ ' Use double buffering if available, otherwise fall back to direct
293
+ If This.bufferBitmap <> 0 Then
294
+ ' Create GDI+ graphics object from buffer DC
295
+ Dim G As LongPtr
296
+ If GdipCreateFromHDC(This.bufferDC, G) = 0 Then
297
+ ' Clear the buffer
298
+ Call GdipGraphicsClear (G, argb(0 , 255 , 255 , 255 ))
299
+
300
+ ' Draw all objects to the buffer
301
+ Dim i As Long
302
+ For i = 1 To This.objectsIndex
303
+ With This.objects(i)
304
+ Select Case .ObjectType
305
+ Case cvtRectangle
306
+ If .ObjectStyle(CanvasObjectStyles.[_cvsFillVisible]) Then
307
+ Dim fillBrush As LongPtr
308
+ If GdipCreateSolidFill(.ObjectStyle(cvsFillColor), fillBrush) = 0 Then
309
+ Call GdipFillRectangle (G, fillBrush, .ObjectParams(cvfRectLeft), .ObjectParams(cvfRectTop), .ObjectParams(cvfRectRight) - .ObjectParams(cvfRectLeft), .ObjectParams(cvfRectBottom) - .ObjectParams(cvfRectTop))
310
+ Call GdipDeleteBrush (fillBrush)
311
+ End If
312
+ End If
313
+ If .ObjectStyle(CanvasObjectStyles.[_cvsLineVisible]) Then
314
+ Dim strokePen As LongPtr
315
+ If GdipCreatePen1(.ObjectStyle(cvsLineColor), .ObjectStyle(cvsLineWidth), UnitPixel, strokePen) = 0 Then
316
+ Call GdipDrawRectangle (G, strokePen, .ObjectParams(cvfRectLeft), .ObjectParams(cvfRectTop), .ObjectParams(cvfRectRight) - .ObjectParams(cvfRectLeft), .ObjectParams(cvfRectBottom) - .ObjectParams(cvfRectTop))
317
+ Call GdipDeletePen (strokePen)
318
+ End If
319
+ End If
320
+ End Select
321
+ End With
322
+ Next
323
+
324
+ Call GdipDeleteGraphics (G)
325
+
326
+ ' Copy buffer to screen
327
+ Call BitBlt (This.hDC, 0 , 0 , newWidth, newHeight, This.bufferDC, 0 , 0 , SRCCOPY)
328
+ Exit Sub
329
+ End If
330
+ End If
331
+
332
+ ' Fallback to direct rendering
333
+ Call RedrawDirect
334
+ End Sub
335
+
336
+ ' Fallback method for direct rendering (without double buffering)
337
+ Private Sub RedrawDirect ()
338
+ Const UnitPixel As Long = 2
339
+
231
340
Dim G As LongPtr
232
341
If GdipCreateFromHDC(This.hDC, G) <> 0 Then Exit Sub
233
- 'Call GdipGraphicsClear(G, 0)
342
+
234
343
If Not This.obj Is Nothing Then Call This .obj .Repaint
235
344
236
345
Dim i As Long
@@ -259,6 +368,10 @@ Public Sub Redraw()
259
368
Call GdipDeleteGraphics (G)
260
369
End Sub
261
370
371
+ ' Property to check if double buffering is available
372
+ Public Property Get DoubleBuffered() As Boolean
373
+ DoubleBuffered = (This.bufferBitmap <> 0 )
374
+ End Property
262
375
263
376
Private Function getNewObject () As Long
264
377
This.objectsIndex = This.objectsIndex + 1
@@ -269,16 +382,30 @@ Private Function getNewObject() As Long
269
382
getNewObject = This.objectsIndex
270
383
End Function
271
384
272
-
273
-
274
-
385
+ ' Convert RGB to ARGB
386
+ ' @param A - Alpha channel (0-255)
387
+ ' @param R - Red channel (0-255)
388
+ ' @param G - Green channel (0-255)
389
+ ' @param B - Blue channel (0-255)
390
+ ' @returns - ARGB color
275
391
Public Function argb (A As Byte , R As Byte , G As Byte , B As Byte ) As Long
276
- Dim d As Double : d = CDbl(A) * &H1000000 + R * &H10000 + G * &H100 + B
277
- If d > 2147483647 # Then d = d - 4294967296 #
278
- argb = d
392
+ Const AlphaShift As Double = &H1000000
393
+ Const RedShift As Long = &H10000
394
+ Const GreenShift As Long = &H100
395
+
396
+ ' 2^31–1 = max signed 32-bit; 2^32 = wrap modulus
397
+ Const INT32_MAX As Long = 2 ^ 31 - 1
398
+ Const UINT32_MODULO As Double = 2 ^ 32
399
+
400
+ 'Shift the colors without causing an overflow, by shifting in the double domain
401
+ Dim rawValue As Double : rawValue = A * AlphaShift + R * RedShift + G * GreenShift + B
402
+
403
+ ' If rawValue exceeds INT32_MAX, wrap into the signed 32-bit range
404
+ If rawValue > INT32_MAX Then rawValue = rawValue - UINT32_MODULO
405
+ argb = rawValue
279
406
End Function
407
+
280
408
Private Function getAlpha (ByVal u As Double ) As Byte
281
409
If u < 0 Then u = u + 4294967296 #
282
410
getAlpha = CByte(Fix(u / 16777216 #) Mod 256 )
283
- End Function
284
-
411
+ End Function
0 commit comments