From: Mike Williams on 14 May 2010 11:15 "Robert" <noname(a)noserver.com> wrote in message news:132qu5lb6ts7gt2vf5i36tlkfsj9gmbqtd(a)4ax.com... > The code in the link is blitting the three corner images and > then tiling the right and bottom edge images using the GDI+ > texturebrush. Obviously to convert it to GDI one would have > to tile the edge images using multiple blits rather than > rectangle (unless patternbrush supports transparency, > which I doubt) No, you would not need to perform multiple GDI blits to do the job, because you can use the GDI AlphaBlend function to stretch a small per-pixel translucency DIB, which will produce the same output as the GDI+ code at that link is producing. Have another look at what I said in the message I posted a couple of days ago, specifically at what I said regarding the capabilities of the GDI32 AlphaBlend function which you can use from VB6. I know that you have (apparently) decided to go with my previous suggestion of pointing a SAFEARRAY at the bitmap data, and it will almost certainly be fast enough for your needs, but there are other alternatives and it is definitely worth giving them a go. If you look at the output of the C++ / GDI+ code at the link you posted and at the multiple translucency .png images associated with it you will see that (for example) the bottom dropshadow is a small 6 x 5 pixel .png with each of the five "6 pixel wide" horizontal lines being a different translucency, and as far as the main part of the shadow is concerned it is drawing that small variable translucency .png along the full desired width of the dropshadow, producing five long translucent lines, with each of the five lines having a different translucency. That's pretty much what your original "GetPixel and SetPixel" VB6 code was doing. It is possible to produce exactly the same output in VB6 using AlphaBlend. Just stretch a small multiple translucencvy DIB to the horizontal line in one call to AlphaBlend and stretch a different DIB for the vertical line in another call to AlphaBlend and then drop in the three corners, in the same fasion that the C++ / GDI+ code is doing. Here is some code to perform the first part of that job. It looks more than it is simply because most of the declarations are for the code in the Form Load event, which creates the required small 6 x 5 pixel multiple translucency DIB for the main part of the horiziontal dropshadow line. In the example, once that DIB has been created and assigned to a DC then that DC remains available for the rest of your code until the main Form closes, and so drawing any desired length of horizontal dropshadow then becomes a simple "one-liner" call to the AlphaBlend function, which will work okay over all backgrounds. You will need to add only a small amount of code to create the small DIB for the vertical dropshadow and the three corner DIBs. Anyway, here's what I've got so far. Paste the code intom a VB Form containing a Command Button. When you run the project the Form Load event will create the required small multiple translucency DIB and when you click the Command Button the original size DIB will be drawn to the Form, and five other dropshadow lines of different lengths (using exactly the same DIB) will be drawn as well. Each drawn dropshadow line is a simple "one-liner" call to AlphaBlend. Mike Option Explicit Private Declare Function CreateCompatibleDC Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function CreateDIBSection Lib "gdi32" _ (ByVal hdc As Long, pBitmapInfo As BITMAPINFO, _ ByVal un As Long, ByVal lplpVoid As Long, _ ByVal handle As Long, ByVal dw 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 DeleteDC Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Sub CopyMemory Lib "kernel32" _ Alias "RtlMoveMemory" (Destination As Any, Source As Any, _ ByVal Length As Long) Private Declare Function VarPtrArray Lib "msvbvm60.dll" _ Alias "VarPtr" (Ptr() As Any) As Long Private Declare Function AlphaBlend Lib "msimg32.dll" _ (ByVal hdc As Long, ByVal lInt As Long, _ ByVal lInt As Long, ByVal lInt As Long, _ ByVal lInt As Long, ByVal hdc As Long, _ ByVal lInt As Long, ByVal lInt As Long, _ ByVal lInt As Long, ByVal lInt As Long, _ ByVal BLENDFUNCT As Long) As Long Private Type BLENDFUNCTION BlendOp As Byte BlendFlags As Byte SourceConstantAlpha As Byte AlphaFormat As Byte End Type Private Const AC_SRC_OVER As Long = &H0 Private Const AC_SRC_ALPHA As Long = &H1 Private Type SAFEARRAYBOUND cElements As Long lLbound As Long End Type Private Type SAFEARRAY2D cDims As Integer fFeatures As Integer cbElements As Long cLocks As Long pvData As Long Bounds(0 To 1) As SAFEARRAYBOUND End Type Private Type BITMAPINFOHEADER biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type Private Type BITMAPINFO bmiHeader As BITMAPINFOHEADER End Type Private Const DIB_RGB_COLORS = 0 Private Const BI_RGB = 0& Private BF1 As BLENDFUNCTION, TransBlend As Long Private HorzDIB As Long, horzDC As Long Private horzWide As Long, horzHigh As Long Private Sub Form_Load() Dim x As Long, y As Long Dim DIBAddress As Long Dim bi32BitInfo As BITMAPINFO, myLongs() As Long Dim SA1 As SAFEARRAY2D horzWide = 6 horzHigh = 5 With bi32BitInfo.bmiHeader .biBitCount = 32 .biCompression = BI_RGB .biPlanes = 1 .biSize = Len(bi32BitInfo.bmiHeader) .biWidth = horzWide .biHeight = horzHigh End With horzDC = CreateCompatibleDC(Me.hdc) HorzDIB = CreateDIBSection(horzDC, bi32BitInfo, _ DIB_RGB_COLORS, VarPtr(DIBAddress), ByVal 0&, ByVal 0&) SA1.cDims = 2 SA1.cbElements = 4 SA1.pvData = DIBAddress SA1.Bounds(0).cElements = horzHigh SA1.Bounds(1).cElements = horzWide ' point array to DIB data and draw the pixels CopyMemory ByVal VarPtrArray(myLongs), VarPtr(SA1), 4& For x = 0 To horzWide - 1 myLongs(x, 0) = &H6000000 myLongs(x, 1) = &H10000000 myLongs(x, 2) = &H21000000 myLongs(x, 3) = &H34000000 myLongs(x, 4) = &H46000000 Next x ' undo the pointer CopyMemory ByVal VarPtrArray(myLongs), 0&, 4& SelectObject horzDC, HorzDIB ' set up the blend function With BF1 .BlendOp = AC_SRC_OVER .BlendFlags = 0 .SourceConstantAlpha = 255 .AlphaFormat = AC_SRC_ALPHA End With CopyMemory TransBlend, BF1, 4 Me.BackColor = vbWhite ' or whatever End Sub Private Sub Form_Unload(Cancel As Integer) DeleteObject HorzDIB DeleteDC horzDC End Sub Private Sub Command1_Click() ' draw an original 6 x 5 pixel size horizontal ' dropshadow near the top left of the Form AlphaBlend Me.hdc, 10, 10, horzWide, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend ' draw a three pixel section just to show any width is okay AlphaBlend Me.hdc, 15, 20, 3, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend ' draw another wider one AlphaBlend Me.hdc, 20, 30, 123, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend ' and another AlphaBlend Me.hdc, 25, 40, 234, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend ' and another AlphaBlend Me.hdc, 30, 50, 345, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend ' and another AlphaBlend Me.hdc, 35, 60, 456, horzHigh, _ horzDC, 0, 0, horzWide, horzHigh, TransBlend End Sub
From: Mike Williams on 14 May 2010 13:31 "Mike Williams" <Mike(a)WhiskeyAndCoke.com> wrote in message news:O3hO8g38KHA.4604(a)TK2MSFTNGP04.phx.gbl... > Anyway, here's what I've got so far. Paste the code into > a VB Form containing a Command Button . . I've just carried out some high resolution timing of the AlphaBlend multiple translucency DIB code I posted earlier and on this machine it takes about 120 microseconds to draw a 320 pixel width bottom dropshadow, 65 microseconds for a 160 pixel dropshadow and about 12 microseconds each for a "corner sized" dropshadow So, the full dropshadow for a 320 x 160 pixel rectangle (a similar size to the one in the C++ / GDI+ example) should take about 220 microseconds (about one fifth of a millisecond), which is pretty fast for per-pixel translucency stuff and which I think will definitely give you the kind of speed you are looking for. Mike
From: Mike Williams on 15 May 2010 16:46 "Mike Williams" <Mike(a)WhiskeyAndCoke.com> wrote in message news:O9rq8s48KHA.4600(a)TK2MSFTNGP02.phx.gbl... > I've just carried out some high resolution timing of the AlphaBlend > multiple translucency DIB code I posted earlier and on this machine > it takes about 120 microseconds to draw a 320 pixel width bottom > dropshadow [stretching a small ready prepared DIB) . . . As a final note (since this thread has by now been done to death), for virtually all sizes of dropshadow bars it is actually faster to create and populate and draw a per-pixel translucency DIB of the exact required length "on the fly" than it is to stretch a ready prepared and populated differently sized per-pixel translucency DIB, so that is the method I would advise if you want the best speed (although it is considerably faster to use ready prepared DIBs for the fixed size corners). Note that I've carried out all timings on my Vista machine, which of course does not use the GDI hardware acceleration that is present on many graphics cards, so it should be faster on many XP machines (and presumably on many Win7 machines if M$ have reverted to using the GDI accelerated hardware which Vista nobbled). Mike
From: Robert on 16 May 2010 06:01 Hi Mike I'm going to try building a drop shadow in sections (as described in your last post) next but in the meantime I've converted the code I had been using to use AlphaBlend and a Array pointing to a DIB (see below). Thanks again for your help on this. Rob Option Explicit Private Declare Function Rectangle Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal X1 As Long, _ ByVal Y1 As Long, _ ByVal X2 As Long, _ ByVal Y2 As Long _ ) As Long Private Declare Function CreateCompatibleDC Lib "gdi32" ( _ ByVal hdc As Long _ ) As Long Private Declare Function CreateDIBSection Lib "gdi32" ( _ ByVal hdc As Long, _ ByRef pBitmapInfo As BITMAPINFOHEADER, _ ByVal un As Long, _ ByRef lplpVoid As Long, _ ByVal handle As Long, _ ByVal dw As Long _ ) As Long Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" ( _ Ptr() As Any _ ) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ Destination As Any, _ Source As Any, _ ByVal Length As Long _ ) Private Declare Function DeleteObject Lib "gdi32" ( _ ByVal hObject As Long _ ) As Long Private Declare Function DeleteDC Lib "gdi32" ( _ ByVal hdc As Long _ ) As Long Private Declare Function AlphaBlend Lib "msimg32" ( _ ByVal hDestDC As Long, _ ByVal x As Long, _ ByVal y As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long, _ ByVal hSrcDC As Long, _ ByVal xSrc As Long, _ ByVal ySrc As Long, _ ByVal widthSrc As Long, _ ByVal heightSrc As Long, _ ByVal blendFunct As Long _ ) As Boolean Private Declare Function SelectObject Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal hObject As Long _ ) As Long Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" ( _ ByRef Destination As Any, _ ByVal Length As Long _ ) Private Type SAFEARRAYBOUND cElements As Long lLbound As Long End Type Private Type SAFEARRAY2D cDims As Integer fFeatures As Integer cbElements As Long cLocks As Long pvData As Long Bounds(0 To 1) As SAFEARRAYBOUND End Type Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Type BITMAPINFOHEADER '40 bytes biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type Private Type BLENDFUNCTION BlendOp As Byte BlendFlags As Byte SourceConstantAlpha As Byte AlphaFormat As Byte End Type Private Type RGBQUAD b As Byte g As Byte r As Byte a As Byte End Type Private Const DIB_RGB_COLORS = 0 ' color table in RGBs Private Const BI_RGB = 0& Private Const AC_SRC_OVER = &H0 Private Const AC_SRC_ALPHA = &H1 Private Sub DrawShadow(ByVal hdc As Long, ByRef rec As RECT, Optional ByVal nOpacity As Long = 6) Dim TempDC As Long Dim TempBMP As Long Dim OldBMP As Long Dim bmInfo As BITMAPINFOHEADER Dim TempRect As RECT Dim pvBits As Long Dim DIBData() As RGBQUAD Dim arrInf As SAFEARRAY2D Dim x As Long Dim y As Long Dim bf As BLENDFUNCTION Dim lngBlend As Long TempRect.Right = rec.Right - rec.Left + 4 TempRect.Bottom = rec.Bottom - rec.Top + 4 With bmInfo .biSize = Len(bmInfo) .biWidth = TempRect.Right .biHeight = TempRect.Bottom .biBitCount = 32 .biPlanes = 1 .biClrImportant = 0 .biClrUsed = 0 .biCompression = BI_RGB .biSizeImage = .biWidth * .biHeight .biXPelsPerMeter = 0 .biYPelsPerMeter = 0 End With TempDC = CreateCompatibleDC(hdc) TempBMP = CreateDIBSection(TempDC, bmInfo, DIB_RGB_COLORS, pvBits, 0, 0) OldBMP = SelectObject(TempDC, TempBMP) With arrInf .cbElements = 4 .cDims = 2 .Bounds(0).cElements = TempRect.Bottom .Bounds(1).cElements = TempRect.Right ' * 4 .pvData = pvBits End With Call CopyMemory(ByVal VarPtrArray(DIBData()), VarPtr(arrInf), 4) ' Call ZeroMemory(ByVal VarPtr(DIBData(0, 0)), TempRect.Right * TempRect.Bottom) For x = 1 To 4 For y = 1 To 4 DIBData(TempRect.Right - x, TempRect.Top + y).a = nOpacity * x * (y - 0) Next y For y = 5 To TempRect.Bottom - 5 DIBData(TempRect.Right - x, y).a = (nOpacity * 5) * x Next y For y = TempRect.Bottom - 4 To TempRect.Bottom - 1 DIBData(TempRect.Right - x, TempRect.Top + y).a = nOpacity * x * -(y - TempRect.Bottom) Next y Next x For y = 1 To 4 For x = 1 To 4 DIBData(TempRect.Left + x, y).a = nOpacity * (x - 0) * y Next x For x = 5 To TempRect.Right - 5 DIBData(TempRect.Left + x, y).a = (nOpacity * 5) * y Next x Next y Call CopyMemory(ByVal VarPtrArray(DIBData()), 0&, 4) bf.BlendOp = AC_SRC_OVER bf.BlendFlags = 0 bf.AlphaFormat = AC_SRC_ALPHA ' Use source alpha bf.SourceConstantAlpha = &HFF ' Use 100% constant alpha Call CopyMemory(lngBlend, bf, 4) Call AlphaBlend(hdc, rec.Left, rec.Top + 1, _ TempRect.Right, TempRect.Bottom, _ TempDC, 0, 0, TempRect.Right, TempRect.Bottom, lngBlend) Call DeleteObject(SelectObject(TempDC, OldBMP)) Call DeleteDC(TempDC) End Sub Private Sub Form_Paint() Dim rec As RECT Me.Cls With rec .Left = 10 .Top = 10 .Right = 160 .Bottom = 210 End With Rectangle hdc, rec.Left, rec.Top, rec.Right, rec.Bottom DrawShadow Me.hdc, rec End Sub
From: Mike Williams on 16 May 2010 07:38
"Robert" <noname(a)noserver.com> wrote in message news:8a2vu5lngg8ot2ilir842sro003udmtd6t(a)4ax.com... > Hi Mike. I'm going to try building a drop shadow in sections (as > described in your last post) next but in the meantime I've converted > the code I had been using to use AlphaBlend and a Array pointing > to a DIB (see below). Thanks again for your help on this. Actually I think you've got pretty much as good as it gets already, now that you've eliminated the reading of the individual destination data pixels and got rid of GetPixel and SetPixel and moved over to pointing a SafeArray at a translucent DIBSection. I think those things pretty much cover all the bases. I've tried your code at this end and it runs quite quickly, and I don't think there's a great deal of further optimization you can do to it, especially since the loop code itself is not now taking up a large percentage of the overall time. Regarding building the dropshadow in sections, for the rectangle sizes that you appear to be dealing with, I think the fastest way of doing it might be to pre-build the three corner DIBs and to build the vertical and horizontal DIBs "on the fly" (rather than pre-building all five). I haven't actually written my own code to do exactly that (to save time at this end I wrote just enough to check out the speed of the basic building blocks), but extrapolating my test results so far to determine the likely speed of the method I have just outlined I reckon that the speed of the two methods will most likely be much the same. Your existing method is dealing with a much larger size overall DIB, but that is compensated for by the fact that because most of it is totally transparent the AlphaBlend function only actually needs to "set destination pixels" for a small portion of it, and by the fact that the five smaller DIBs required by the other method, although a smaller total overall size, will require five calls to AlphaBlend. So, at a guess I reckon you've got it just about right already, although a comaparison of the actual speed of the two methods on your own machine will of course be worth doing, even if it is only out of curiosity. Mike |