The title of this question can also be, "The alpha values in the WIA.Vector
object don't work."
I'm trying to render an ellipse on a transparent background using my own discovered algorithm, and then save the resulting image into a .bmp file. The ellipse is rasterized properly with a black stroke, but my program does not make the background of the image transparent.
As I examined the program, it turned out that when I retrieve an ImageFile
object from the Vector
object, its IsAlphaPixelFormat
property is set to false, indicating that the alpha channel is not available in the output image. Even though I set the alpha value of the background color to zero in the vector, the ImageFile
object generates an opaque white background.
So could you please help me make the background transparent? Here is my VBScript code, which must be run with cscript.exe
.
Note: This program requires Windows Image Acquisition (WIA) library v2.0 in order to create a .bmp image file. So it must be run on Window Vista or higher.
Const width = 500
Const height = 500
color_transparent = GetARGB(0, 255, 255, 255) ' This does not work, renders as opaque white
color_black = GetARGB(255, 0, 0, 0)
PI = 4 * Atn(1)
Dim oVector, oImageFile
Set oVector = NewBlankPage()
rasterizeEllipse oVector, 220, 120, 200, 100, color_black
Set oImageFile = oVector.ImageFile(width, height)
oImageFile.SaveFile "ellipse.bmp"
WScript.StdOut.WriteLine "Done! Press Enter to quit."
WScript.StdIn.SkipLine
Function NewBlankPage()
Dim oVector, i
WScript.StdOut.WriteLine "Creating a new blank page... Please wait..."
Set oVector = CreateObject("WIA.Vector")
For i = 1 To (width * height)
oVector.Add color_transparent
Next
Set NewBlankPage = oVector
End Function
Function getPointOnEllipse(cx, cy, rx, ry, d)
Dim theta
theta = d * Sqr(2 / (rx * rx + ry * ry))
' theta = 2 * PI * d / getEllipsePerimeter(rx, ry)
Dim point(1)
point(0) = Fix(cx + Cos(theta) * rx)
point(1) = Fix(cy - Sin(theta) * ry)
getPointOnEllipse = point
End Function
Function getEllipsePerimeter(rx, ry)
getEllipsePerimeter = Fix(PI * Sqr(2 * (rx * rx + ry * ry)))
End Function
Sub SetPixel(oVector, x, y, color)
x = x + 1
y = y + 1
If x > width Or x < 1 Or y > height Or y < 1 Then
Exit Sub
End If
oVector(x + (y - 1) * width) = color
End Sub
Sub rasterizeEllipse(oVector, cx, cy, rx, ry, color)
Dim perimeter, i
WScript.StdOut.WriteLine "Rendering ellipse..."
perimeter = getEllipsePerimeter(rx, ry)
For i = 0 To (perimeter - 1)
Dim point
point = getPointOnEllipse(cx, cy, rx, ry, i)
SetPixel oVector, point(0), point(1), color
Next
End Sub
' These functions are taken from examples in the documentation
Function Get1ByteHex(val)
Dim s
s = Hex(val)
Do While Len(s) < 2
s = "0" & s
Loop
Get1ByteHex = Right(s, 2)
End Function
Function GetARGB(a, r, g, b)
Dim s
s = "&h" & Get1ByteHex(a) & Get1ByteHex(r) & Get1ByteHex(g) & Get1ByteHex(b)
GetARGB = CLng(s)
End Function
After running the code, you can test the transparency of the output image using this simple HTA:
<html>
<head>
<title>Test</title>
</head>
<body bgcolor="blue">
<img src="ellipse.bmp">
</body>
</html>
And you will see that a white box is displayed behind the ellipse, which indicates non-transparent background.
This can be done by using the ARGB
filter to change all white pixels to transparent and saving the image as a PNG file. Unfortunately, this means iterating through every pixel, so your script will take twice as long to run. I could not find a way to create the initial image with transparency. See here for info regarding the performance issue. Here's the revised script:
Const wiaFormatPNG = "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}"
Const width = 500
Const height = 500
color_white = GetARGB(255, 255, 255, 255)
color_transparent = GetARGB(0, 255, 255, 255)
color_black = GetARGB(255, 0, 0, 0)
PI = 4 * Atn(1)
Dim oVector, oImageFile
Set oVector = NewBlankPage()
rasterizeEllipse oVector, 220, 120, 200, 100, color_black
Set oImageFile = oVector.ImageFile(width, height)
Set IP = CreateObject("WIA.ImageProcess")
IP.Filters.Add IP.FilterInfos("ARGB").FilterID
Set oVector = oImageFile.ARGBData
Wscript.StdOut.WriteLine "Changing white pixels to transparent... Please wait..."
For i = 1 To oVector.Count
If oVector.Item(i) = color_white Then oVector.Item(i) = color_transparent
Next
IP.Filters(1).Properties("ARGBData").Value = oVector
IP.Filters.Add IP.FilterInfos("Convert").FilterID
IP.Filters(2).Properties("FormatID").Value = wiaFormatPNG
Set oImageFile = IP.Apply(oImageFile)
oImageFile.SaveFile "ellipse.png"
Wscript.StdOut.WriteLine "Done! Press Enter to quit."
Wscript.StdIn.SkipLine
Function NewBlankPage()
Dim oVector, i
Wscript.StdOut.WriteLine "Creating a new blank page... Please wait..."
Set oVector = CreateObject("WIA.Vector")
For i = 1 To (width * height)
oVector.Add color_white
Next
Set NewBlankPage = oVector
End Function
Function getPointOnEllipse(cx, cy, rx, ry, d)
Dim theta
theta = d * Sqr(2 / (rx * rx + ry * ry))
' theta = 2 * PI * d / getEllipsePerimeter(rx, ry)
Dim point(1)
point(0) = Fix(cx + Cos(theta) * rx)
point(1) = Fix(cy - Sin(theta) * ry)
getPointOnEllipse = point
End Function
Function getEllipsePerimeter(rx, ry)
getEllipsePerimeter = Fix(PI * Sqr(2 * (rx * rx + ry * ry)))
End Function
Sub SetPixel(oVector, x, y, color)
x = x + 1
y = y + 1
If x > width Or x < 1 Or y > height Or y < 1 Then
Exit Sub
End If
oVector(x + (y - 1) * width) = color
End Sub
Sub rasterizeEllipse(oVector, cx, cy, rx, ry, color)
Dim perimeter, i
Wscript.StdOut.WriteLine "Rendering ellipse..."
perimeter = getEllipsePerimeter(rx, ry)
For i = 0 To (perimeter - 1)
Dim point
point = getPointOnEllipse(cx, cy, rx, ry, i)
SetPixel oVector, point(0), point(1), color
Next
End Sub
' These functions are taken from examples in the documentation
Function Get1ByteHex(val)
Dim s
s = Hex(val)
Do While Len(s) < 2
s = "0" & s
Loop
Get1ByteHex = Right(s, 2)
End Function
Function GetARGB(a, r, g, b)
Dim s
s = "&h" & Get1ByteHex(a) & Get1ByteHex(r) & Get1ByteHex(g) & Get1ByteHex(b)
GetARGB = CLng(s)
End Function
Note: If you only need an ellipse in your HTA, it can be done instantly with CSS:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta http-equiv="X-UA-Compatible" content="IE=9">
<style>
.ellipseDiv
{
height:200px;
width:400px;
border: 1px solid #005;
border-radius:200px / 100px;
}
</style>
</head>
<body bgcolor="blue">
<div class=ellipseDiv>
</div>
</body>
</html>