Thursday, May 14, 2009

Understanding OpacityMask and its usage

OpacityMask as it’s name implies a mask to the opacity.ie a mask which is given to the visibility of a visual element.So the places which are not covered by the mask will be transparent.
Here comes the first doubt.How can we specify this opacitymask ? Mask here refers to a definition of Brush.Brush is the abstract base class for a variety of brushes like SolidColorBrush,LinearGradientBrush,VisualBrush etc…So we can apply mask in so many ways.

Need for OpacityMask

The main application of OpacityMask is to define some transparent areas in visual elements.Let’s consider the scenario below.
We have a Rectangle filled with Yellow and and an Ellipse filled with Red on top of it.So naturally the red ellipse will be visible over rectangle.Below is the xaml and image in blend.

<Grid x:Name="LayoutRoot">
<Rectangle Fill="#FFFFFF00" />
<Ellipse Stroke="#FF000000" Fill="#FFFF0000"/>
</Grid>



Qn : We need to show the yellow rectangle over the ellipse what we do?

Ans : Just set the Alpha value of the Fill property of the ellipse to 00.Its fully transparent and we can view the rectangle.

Qn : We need to make the bottom half of the ellipse transparent.What will we do?

Ans : We will make the Fill brush of ellipse to a LinearGradientBrush and set the bottom part transparent by setting alpha to 0.Xaml and screenshot is below.



<Grid x:Name="LayoutRoot">
<Rectangle Fill="#FFFFFF00" />
<Ellipse Stroke="#FF000000">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00000000" Offset="0.571"/>
<GradientStop Color="#FFFF0000" Offset="0.531"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>



Qn : How can we make 3/4 th of ellipse transparent?

Ans : According to my WPF knowledge there is no way to do this by using only one brush.Here comes the importance of OpacityMask.OpacityMask here defines another area which is transparent by using another LinearGradientBrush.Code and screenshot is below.



<Grid x:Name="LayoutRoot">
<Rectangle Fill="#FFFFFF00" />
<Ellipse Stroke="#FF000000">
<Ellipse.OpacityMask>
<LinearGradientBrush EndPoint="0.05,0.56" StartPoint="0.943,0.568">
<GradientStop Color="#FF000000" Offset="0.487"/>
<GradientStop Color="#00FFFFFF" Offset="0.527"/>
</LinearGradientBrush>
</Ellipse.OpacityMask>
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00000000" Offset="0.571"/>
<GradientStop Color="#FFFF0000" Offset="0.531"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>



I have used a LinearGradientBrush with Brush transform to implement this.When we are specifying OpacityMask,it won’t consider the color of the brush.Because here the intention is to define an area which is transparent or opaque.But it cares the alpha value very seriously.According to the alpha value only it defines the amount of transparency or opacity.

This is the basic idea of OpacityMask.I will get confused often while working extensively with OpacityMask.So if anybody reading this, have more knowledge about OpacityMask please share with me.

OpacityMask is very much useful in giving awesome look and feel to your application like glass effects transparency etc…To understand better about the opacitymask I will be writing another post which talks about creating a magic transparent glass.


More links about OpacityMask

http://geekswithblogs.net/Silverlight2/archive/2008/10/22/working-with-opacity-masks.aspx

http://www.vbdotnetheaven.com/UploadFile/dbeniwal321/OpacityMaskWPF01302009011520AM/OpacityMaskWPF.aspx

11 comments:

  1. Excellent post. Liked the Q&A approach. One of the nicest posts you have made.

    ReplyDelete
  2. Hi, i need a help ...i want to know how can we mask a customized textbox with the SSN format(111-11-1111) it needs to append hyphens on its own when the user enters the number

    ReplyDelete
  3. Great post, it helped me! But you confused me when you said there is no way to make 3/4 of the ellipse transparent without more than one brush. Of course you can! In the LinearGradientBrush, put 0.75 and 0.25 instead of 0.5 and 0.5, and there you have it. But I understood what you meant after reading below it.

    ReplyDelete
  4. Hi Anony,
    You need to handle this type of formats you need to inherit the TextBox and the override the KeyDown method.
    Stephen,
    Happy to hear that your confusion is over :)

    ReplyDelete
  5. Augustine Mathew [MSFT]February 16, 2012 at 2:22 PM

    Hey Joy,

    There is a way to do this using a single brush. Use an opacity mask with a DrawingBrush containing a rectangle with Height = 1/2 of the vertical radius of the ellipse and Width=1/2 of horizontal radius of the ellipse.

    Here is an example of a 64x64 image with only top right quadrant displayed















    Thanks,
    Augustine Mathew [MSFT]

    ReplyDelete
    Replies
    1. Augustine Mathew [MSFT]February 16, 2012 at 2:28 PM

      Sorry I had to replace < and > to get this thing pasted. Please consider fixing the comment section.

      Here is the snippet:

      <Image HorizontalAlignment="Left" Source="Images/Scanner.png" Stretch="Fill" Width="64">
      <Image.OpacityMask>
      <DrawingBrush AlignmentX="Right" AlignmentY="Top" Stretch="None" >
      <DrawingBrush.Drawing>
      <GeometryDrawing Brush="Black">
      <GeometryDrawing.Geometry>
      <RectangleGeometry Rect="0,0,32,32" />
      </GeometryDrawing.Geometry>
      </GeometryDrawing>
      </DrawingBrush.Drawing>
      </DrawingBrush>
      </Image.OpacityMask>
      </Image>

      Delete
  6. Thanks Augustine,
    It gives me extra energy to post more on this blog as I got a comment from [MSFT] guy..

    Is it possible without opacity mask?

    ReplyDelete
    Replies
    1. Augustine Mathew [MSFT]February 17, 2012 at 9:16 AM

      Thanks for your kind words.

      Yes it is certainly possible do it without an opacity mask. I suggest one use clip bounds for inclusion and exclusion of visible regions and opacity for a "fade" effect inside those regions.

      I had posted an elaborate lengthy reply a moment ago but it has disappeared since. So I'm going to keep it short. The above solution uses 5 visual tree elements inside an image to achieve what you were trying to do. However if clipping, i.e. inclusion and exclusion of visible regions, is all what you are interested in then you can do it with 2 elements. This is what you were trying to do in your post.

      As a general rule of thumb, i.e. not always, the lesser number elements in your visual tree = faster the rendering time which equates to better perf leading to happier customers :).

      Here is an example of an ellipse with only its top right quadrant being visible.

      <Ellipse HorizontalAlignment="Left" StrokeThickness="2" Fill="Black" Height="200" Width="400" Stroke="#FFE02020">
      <Ellipse.Clip>
      <RectangleGeometry Rect="200,0,200,100" />
      </Ellipse.Clip>
      </Ellipse>

      Delete
  7. Thank u so much... You saved my butt..

    ReplyDelete