Hello!
I am stuck while trying to cut out the object of a dropshadow.
What I am trying to achieve is this (photoshopped version):
Attachment 193519
But actually, it looks like this:
Attachment 193520
I don't find the flaw in my code.
Perhaps the flaw lays in the masking part, but I am not sure.
Thank you if anybody can lend a hand.
This is the main function:
Attachment 193521Code:Public Function CreateDropShadow(ByRef uSourceSurface As cCairoSurface, ByVal uRadius As Long, ByVal uColor As Long, ByVal uAlpha As Double) As cCairoSurface
' Convert the specified alpha to a byte value
Dim btAlpha As Byte
btAlpha = pConvertAlpha(uAlpha)
' Create a copy of the source surface where non-transparent pixels are made black and opaque
Dim nGrey As cCairoSurface
Set nGrey = uSourceSurface.CreateSimilar(CAIRO_CONTENT_COLOR_ALPHA, uSourceSurface.Width, uSourceSurface.Height, True)
modCairo.SetBlackIfPixelIsOpaque nGrey
' Create a mask surface where non-transparent pixels from the original are black and the rest are transparent
Dim nObjectIsOpaque_RestIsTransparent As cCairoSurface
Set nObjectIsOpaque_RestIsTransparent = uSourceSurface.CreateSimilar(CAIRO_CONTENT_COLOR_ALPHA, uSourceSurface.Width, uSourceSurface.Height, True)
modCairo.SetBlackIfPixelIsOpaque nObjectIsOpaque_RestIsTransparent
nObjectIsOpaque_RestIsTransparent.WriteContentToPngFile "d:\temp\OpaquePixelsAreNowBlack.png"
modCairo.SetWhiteIfPixelIsTransparent nObjectIsOpaque_RestIsTransparent
nObjectIsOpaque_RestIsTransparent.WriteContentToPngFile "d:\temp\TransparentPixelsAreNowWhite.png"
nObjectIsOpaque_RestIsTransparent.WriteContentToPngFile "d:\temp\ObjectBlackRestWhite.png"
modCairo.MakeBlackPixelsTransparent nObjectIsOpaque_RestIsTransparent
nObjectIsOpaque_RestIsTransparent.WriteContentToPngFile "d:\temp\ObjectIsNowOpaque.png"
' Apply Gaussian Blur to the nGrey surface
Dim nGreyWithBlur As cCairoSurface
Set nGreyWithBlur = nGrey.CreateSimilar(CAIRO_CONTENT_COLOR_ALPHA, nGrey.Width, nGrey.Height, True)
Dim nGreyWithBlurC As cCairoContext
Set nGreyWithBlurC = nGreyWithBlur.CreateContext
Set nGreyWithBlur = nGreyWithBlur.GaussianBlur(uRadius / 2, uRadius, False)
nGreyWithBlur.WriteContentToPngFile "D:\temp\shadow.png"
' Create a mask for subtracting the object from the shadow
Dim nMask As cCairoSurface
Set nMask = nGrey.CreateSimilar(CAIRO_CONTENT_COLOR_ALPHA, nGrey.Width, nGrey.Height, True)
Dim nMaskC As cCairoContext
Set nMaskC = nMask.CreateContext
nMaskC.SetSourceRGBA 0, 0, 0, 1
nMaskC.Paint
nMaskC.SetSourceSurface nObjectIsOpaque_RestIsTransparent, 0, 0
nMaskC.Operator = CAIRO_OPERATOR_DEST_OUT
nMaskC.Paint
' Create the final shadow surface, excluding the object
Dim nShadowOnly As cCairoSurface
Set nShadowOnly = nGreyWithBlur.CreateSimilar(CAIRO_CONTENT_COLOR_ALPHA, nGreyWithBlur.Width, nGreyWithBlur.Height, True)
Dim nShadowOnlyC As cCairoContext
Set nShadowOnlyC = nShadowOnly.CreateContext
nShadowOnlyC.SetSourceSurface nGreyWithBlur, 0, 0
nShadowOnlyC.MaskSurface nMask, 0, 0
nShadowOnlyC.Paint
' Save the shadow image to a file
Dim sFilepathShadowOnly As String
sFilepathShadowOnly = "d:\temp\shadowonly.png"
modIO.DeleteFile sFilepathShadowOnly
nShadowOnly.WriteContentToPngFile sFilepathShadowOnly
Set CreateDropShadow = nShadowOnly
End Function
These are the helper functions that I use:
Code:Public Sub MakeSemitransparentFullyOpaque(Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
Srf.BindToArray b
Dim lLimit&
lLimit = 5
For Y = 0 To UBound(b, 2): For X = 0 To UBound(b, 1) Step 4
If b(X + 3, Y) > 0 Then 'And b(X + 3, y) > 0 Then 'when semi-transparent (in a certain range)...
' b(X + 0, y) = 0
' b(X + 1, y) = 0
' b(X + 2, y) = 0
b(X + 3, Y) = 255 ' 255 'ShadowAlpha '255 = completely black/opaque 0=completely transparent, 124=halftransparent
End If
Next X, Y
Srf.ReleaseArray b
End Sub
Public Sub MakeWhitePixelsOpaque(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
' Bind the surface to a byte array for direct pixel manipulation
Srf.BindToArray b
' Loop through each pixel in the byte array
For Y = 0 To UBound(b, 2) ' Iterate over each row
For X = 0 To UBound(b, 1) Step 4 ' Iterate over each pixel, skipping every 4 bytes (RGBA)
' Check if the pixel is black, meaning all RGB values are at minimum (0)
If b(X + 0, Y) = 255 And b(X + 1, Y) = 255 And b(X + 2, Y) = 255 Then
' Set alpha component to 255 to make the pixel fully opaque
b(X + 3, Y) = 255
End If
Next X
Next Y
' Release the array back to the surface, applying the changes
Srf.ReleaseArray b
End Sub
Public Sub MakeBlackPixelsOpaque(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
' Bind the surface to a byte array for direct pixel manipulation
Srf.BindToArray b
' Loop through each pixel in the byte array
For Y = 0 To UBound(b, 2) ' Iterate over each row
For X = 0 To UBound(b, 1) Step 4 ' Iterate over each pixel, skipping every 4 bytes (RGBA)
' Check if the pixel is black, meaning all RGB values are at minimum (0)
If b(X + 0, Y) = 0 And b(X + 1, Y) = 0 And b(X + 2, Y) = 0 Then
' Set alpha component to 255 to make the pixel fully opaque
b(X + 3, Y) = 255
End If
Next X
Next Y
' Release the array back to the surface, applying the changes
Srf.ReleaseArray b
End Sub
Public Sub MakeBlackPixelsTransparent(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
' Bind the surface to a byte array for direct pixel manipulation
Srf.BindToArray b
' Loop through each pixel in the byte array
For Y = 0 To UBound(b, 2) ' Iterate over each row
For X = 0 To UBound(b, 1) Step 4 ' Iterate over each pixel, skipping every 4 bytes (RGBA)
' Check if the pixel is white, meaning all RGB values are at maximum (255)
If b(X + 0, Y) = 0 And b(X + 1, Y) = 0 And b(X + 2, Y) = 0 Then
' Set alpha component to 0 to make the pixel fully transparent
b(X + 3, Y) = 0
End If
Next X
Next Y
' Release the array back to the surface, applying the changes
Srf.ReleaseArray b
End Sub
Public Sub MakeWhitePixelsTransparent(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
' Bind the surface to a byte array for direct pixel manipulation
Srf.BindToArray b
' Loop through each pixel in the byte array
For Y = 0 To UBound(b, 2) ' Iterate over each row
For X = 0 To UBound(b, 1) Step 4 ' Iterate over each pixel, skipping every 4 bytes (RGBA)
' Check if the pixel is white, meaning all RGB values are at maximum (255)
If b(X + 0, Y) = 255 And b(X + 1, Y) = 255 And b(X + 2, Y) = 255 Then
' Set alpha component to 0 to make the pixel fully transparent
b(X + 3, Y) = 0
End If
Next X
Next Y
' Release the array back to the surface, applying the changes
Srf.ReleaseArray b
End Sub
Public Sub SetWhiteIfPixelIsTransparent(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
Srf.BindToArray b
Dim lLimit&
lLimit = 5
For Y = 0 To UBound(b, 2): For X = 0 To UBound(b, 1) Step 4
If b(X + 3, Y) < lLimit Then
b(X + 0, Y) = 255
b(X + 1, Y) = 255
b(X + 2, Y) = 255
'... and makeit fully opaque
b(X + 3, Y) = 255 '= completely black/opaque 0=completely transparent, 124=halftransparent
End If
Next X, Y
Srf.ReleaseArray b
End Sub
Public Sub SetWhiteIfPixelIsOpaque(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
Srf.BindToArray b
Dim lLimit&
lLimit = 5
For Y = 0 To UBound(b, 2): For X = 0 To UBound(b, 1) Step 4
If b(X + 3, Y) > lLimit Then 'And b(X + 3, y) > lLimit Then 'when semi-transparent (in a certain range)...
b(X + 0, Y) = 255
b(X + 1, Y) = 255
b(X + 2, Y) = 255
'... and makeit fully opaque
b(X + 3, Y) = 255 '= completely black/opaque 0=completely transparent, 124=halftransparent
End If
Next X, Y
Srf.ReleaseArray b
End Sub
Public Sub SetBlackIfPixelIsOpaque(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
Srf.BindToArray b
Dim lLimit&
lLimit = 5
For Y = 0 To UBound(b, 2): For X = 0 To UBound(b, 1) Step 4
If b(X + 3, Y) > lLimit Then
'make pixel black
b(X + 0, Y) = 0
b(X + 1, Y) = 0
b(X + 2, Y) = 0
'... and fully opaque
b(X + 3, Y) = 255 ' '255 = completely black/opaque 0=completely transparent, 124=halftransparent
End If
Next X, Y
Srf.ReleaseArray b
End Sub
Public Sub SetBlackIfPixelIsTransparent(ByRef Srf As cCairoSurface)
Dim X As Long, Y As Long, b() As Byte
Srf.BindToArray b
Dim lLimit&
lLimit = 5
For Y = 0 To UBound(b, 2): For X = 0 To UBound(b, 1) Step 4
If b(X + 3, Y) < lLimit Then 'And b(X + 3, y) < lLimit Then 'when semi-transparent (in a certain range)...
'make pixel black
b(X + 0, Y) = 0
b(X + 1, Y) = 0
b(X + 2, Y) = 0
'... and fully opaque
b(X + 3, Y) = 255 ' '255 = completely black/opaque 0=completely transparent, 124=halftransparent
End If
Next X, Y
Srf.ReleaseArray b
End Sub
Private Function pConvertAlpha(alpha As Double) As Integer
If alpha < 0 Then alpha = 0
If alpha > 1 Then alpha = 1
pConvertAlpha = Int(alpha * 255 + 0.5)
End Function
