Previously we explored encoding pixels from color components. This works perfectly if our alpha value is 255, and in most cases it will be. But, what if we need transparency?
Pixel Format (again)
WriteableBitmap Pixel Format in Silverlight
When assigning colors to pixels in your bitmap, use pre-multiplied colors. The format used by the Silverlight WriteableBitmap is ARGB32 (premultiplied RGB). The format becomes relevant if you are populating the integer values in the Pixels array.
The initial pixel values in the dimensioned array are 0, which will render as black if left unaltered.
I want to clarify one thing. The documentation says the pixel values are set to 0 by default (which is correct) and that it will render as black if left unaltered (which is incorrect). It will render as transparent because the alpha values are also 0. Don’t believe me? Try it.
Transparency
Why would we ever want transparency when we are processing images? Perhaps we want to highlight a point of interest. You could leave the original image intact and just place a semi-transparent image over the top of it and let Silverlight blend them for you. Regardless of the need, here’s how you do it.
If we were to create an image with a blue background covered with a semi-transparent (say, alpha = 128) white square, this is the output we would expect:
If you remember the MSDN documentation referenced in the last lesson it noted that the pixel format was premultiplied ARGB32. This means that the RGB value has already been multiplied before storing the pixel.
The Code
Now we are going to try to recreate the blue color above using two different techniques. The one will be straight RGB (i.e. not premultiplied) and the other will be premultiplied alpha.
1.) First, let’s set up a couple of blue rectangles to test.
<Grid x:Name="LayoutRoot"
Background="White">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle
Width="300"
Height="300"
Fill="Blue" />
<Rectangle
Grid.Row="1"
Width="300"
Height="300"
Fill="Blue" />
</Grid>
Running this simple program should produce:
2.) Now we want to create a swatch for comparison. Add a New Item, select Windows Phone User Control and call it “Swatch” (for comparing the techniques):
3.) Replace Swatch’s default <Grid> with this:
<Canvas Width="100" Height="100">
<Rectangle
Fill="Blue"
Width="100"
Height="100"
Stroke="Black"
StrokeThickness="2" />
<Rectangle
Fill="White"
Opacity=".5"
Width="100"
Height="100"
Stroke="Black"
StrokeThickness="2" />
</Canvas>
All we are doing is overlaying a blue rectangle with a semi-transparent (Opacity=.5) white rectangle.
4.) Add Swatch to the MainPage.xaml for comparison:
<Grid x:Name="LayoutRoot"
Background="White">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle
Width="300"
Height="300"
Fill="Blue" />
<Rectangle
Grid.Row="1"
Width="300"
Height="300"
Fill="Blue" />
<local:Swatch x:Name="SwatchControl"
Margin="190,0,190,-50"
VerticalAlignment="Bottom" />
</Grid>
This output should look something like this:
4.) Add the two overlay images:
<Grid x:Name="LayoutRoot"
Background="White">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle
Width="300"
Height="300"
Fill="Blue" />
<Image x:Name="StraightRGB"
Grid.Row="0"
Width="300"
Height="300" />
<Rectangle
Grid.Row="1"
Width="300"
Height="300"
Fill="Blue" />
<Image x:Name="PremultipliedAlpha"
Grid.Row="1"
Width="300"
Height="300"/>
<local:Swatch x:Name="SwatchControl"
Margin="190,0,190,-50"
VerticalAlignment="Bottom" />
</Grid>
The output will be the same as above because we haven’t actually set the source for these images.
4.) Finally, the code behind:
private void AcquireImageButton_Click(object sender, EventArgs e)
{
WriteableBitmap straightRGBImage =
new WriteableBitmap(300, 300);
WriteableBitmap premultipliedAlphaImage =
new WriteableBitmap(300, 300);
for (int pixelIndex = 0; pixelIndex < 90000; pixelIndex++ )
{
byte alpha = 128;
byte red = 255;
byte green = 255;
byte blue = 255;
double scaleAlpha = alpha / 255.0;
// we are not using scaleAlpha here
straightRGBImage.Pixels[pixelIndex] =
(alpha << 24)
| (red << 16)
| (green << 8 )
| blue;
// notice the alpha value is NOT scaled
// it’s also very important to scale BEFORE
// shifting the values
premultipliedAlphaImage.Pixels[pixelIndex] =
(alpha << 24)
| ((byte)(red * scaleAlpha) << 16)
| ((byte)(green * scaleAlpha) << 8 )
| (byte)(blue * scaleAlpha);
}
StraightRGB.Source = straightRGBImage;
PremultipliedAlpha.Source = premultipliedAlphaImage;
}
Output
Launch the program and tap the acquire button. I’ll let you guess which one is right.
Summary
Premultiplied alpha is pretty straightforward, but it will cost performance in tight loops. In most cases we won’t have to worry about this because the images we typically deal with don’t have transparencies.
Download Code
Up next: A Brief History of Color