From: Mike Williams on
"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
"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
"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
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
"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