Phone Vision 04 – Premultiplied Alpha

13 01 2011

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)

From MSDN:

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:

image_thumb_thumb

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:

 

image

 

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):

image

 

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:

image

 

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.

image

 

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

http://cid-88e82fb27d609ced.office.live.com/embedicon.aspx/Blog%20Files/PhoneVision/PhoneVision%2004%20-%20PremultipliedAlpha.zip

Up next: A Brief History of Color