Answered Strange blurring issue when using shader effect

  • Wednesday, February 25, 2009 12:33 AM
     
      Has Code
    I noticed a strange blurring issue that occurs when using shader effects. It seems to apply to anything that has the effect applied to it, unless there is an element that is clipped somewhere in the visual tree. Here is a snippet that demonstrates the issue:


    <Window x:Class="WpfBlurryTextWithEffect.Window1" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="233" Width="285">  
        <StackPanel TextBlock.FontSize="14">  
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Grid> 
                    <Grid.ColumnDefinitions> 
                        <ColumnDefinition Width="*"/>  
                        <ColumnDefinition Width="16"/>  
                    </Grid.ColumnDefinitions> 
     
                    <TextBlock Text="Without Effect" Margin="0.5" Background="LightGoldenrodYellow"/>  
     
                    <Rectangle Grid.Column="1" Width="8" Height="8" HorizontalAlignment="Right" Fill="Black" Margin="0,0,4,0"/>  
                </Grid> 
            </Border> 
              
              
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <Grid.ColumnDefinitions> 
                        <ColumnDefinition Width="*"/>  
                        <ColumnDefinition Width="16"/>  
                    </Grid.ColumnDefinitions> 
     
                    <TextBlock Text="With Effect" Margin="0.5" Background="LightGoldenrodYellow"/>  
     
                    <Rectangle Grid.Column="1" Width="8" Height="8" HorizontalAlignment="Right" Fill="Black" Margin="0,0,4,0"/>  
                </Grid> 
            </Border> 
              
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <Grid.ColumnDefinitions> 
                        <ColumnDefinition Width="*"/>  
                        <ColumnDefinition Width="16"/>  
                    </Grid.ColumnDefinitions> 
     
                    <TextBlock Text="With Effect, Clipped" Margin="0.5" Background="LightGoldenrodYellow"/>  
     
                    <Rectangle Grid.Column="1" Width="8" Height="8" HorizontalAlignment="Right" Fill="Black" Margin="0,0,12,0"/>  
                </Grid> 
            </Border> 
     
     
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
                  
                <Grid> 
                <Grid.ColumnDefinitions> 
                    <ColumnDefinition Width="*"/>  
                    <ColumnDefinition Width="16"/>  
                </Grid.ColumnDefinitions> 
                  
                <TextBlock Text="With Effect, Animation" Margin="0.5" Background="LightGoldenrodYellow"/>  
                      
                <Rectangle Grid.Column="1" Name="Dot" Width="8" Height="8" HorizontalAlignment="Right" Fill="Black" Margin="0,0,4,0"/>  
                      
                <Grid.Triggers> 
                    <EventTrigger RoutedEvent="FrameworkElement.Loaded">  
                        <BeginStoryboard> 
                            <Storyboard RepeatBehavior="Forever">  
                                <ThicknessAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Margin" From="0,0,4,0" To="0,0,12,0" Duration="0:0:1"/>  
                                <ThicknessAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Margin" From="0,0,12,0" To="0,0,4,0" BeginTime="0:0:1" Duration="0:0:1"/>  
                            </Storyboard> 
                        </BeginStoryboard> 
                    </EventTrigger> 
                </Grid.Triggers> 
            </Grid> 
          </Border> 
        </StackPanel> 
    </Window> 
     

    If you copy this into the WPF application template and run it you'll see 4 boxes:
    1. No effect applied - sharp text and border
    2. Effect applied - blurred text and border
    3. Effect applied, with clipped element - sharp again
    4. Effect applied, with animated element - gets blurry and sharp depending on position of animated element

    Also note that all 4 boxes look fine in the WPF designer.

    Unfortunately, this bug is preventing me from using effects in our app because the text becomes difficult to read. I guess I could add a clipped element to the control template to work around it...but that seems like such an ugly hack.

    Jonathan

    My info:
    Visual Studio 2008 Professional SP1
    .NET Framework 3.5 SP1
    Windows Vista Business 32-bit SP1
    nVidia Quadro FX 1600M

    Also verified to occur on:
    .NET Framework 3.5 SP1
    Windows XP Professional SP3
    Intel 915GM

Answers

  • Wednesday, February 25, 2009 4:33 AM
     
     Answered
    I can verify that the behavior of the items is as you described.

    There are known issuess with text blurring in Effects.  I cannot remember the details however, something to do with  algorithm handling subpixel placement if I recall.

    The fourth example is really wierd. 





    ** Free WPF shader utiliy **
    ** edit HLSL
    ** auto generates your C#/VB shader classes **
    ** auto generates test pages **

    Shazzam - Open source pixel shader utility


    Walt | http://wpfwonderland.wordpress.com
    • Marked As Answer by Tao Liang Monday, March 02, 2009 9:14 AM
    •  
  • Wednesday, February 25, 2009 6:18 PM
     
     Answered
    Funny, I just blogged about this. It seems to be on NVIDIA cards.

    I came up with a workaround for my flavor (which seems to be very much like yours, if not the same) of the issue (see blog entry).
    • Edited by Cory Plotts Wednesday, February 25, 2009 6:36 PM
    • Proposed As Answer by Cory Plotts Wednesday, February 25, 2009 10:32 PM
    • Marked As Answer by Tao Liang Monday, March 02, 2009 9:14 AM
    •  
  • Thursday, February 26, 2009 7:19 PM
     
     Answered
    Jonathan L Marston said:

    The jagged rectangle issue also occurs on my Intel chip. It looks like it is rendering the rectangle without the rotation to a texture, and then drawing the texture at an angle with bilinear filtering turned off (notice how the corners are still antialiased - sort of).


    I answered the question in Cory's thread but your intuition is exactly right.  The "loss of anti-aliasing" issue has to do with setting the DirectX sampling mode incorrectly - depending on what other content you're rendering inside the Effect the state might wind up being set correctly most of the time, which is why we hadn't caught the issue previously.  It only affects BlurEffect and DropShadowEffect when rendering in hardware, not the custom ShaderEffects.  It's been fixed for the next version.
  • Thursday, February 26, 2009 7:45 PM
     
     Answered Has Code

    The blurriness caused by Effects has to do with a number of things.  First, when you're interested in text clarity it's important to note that you'll lose ClearType inside an Effect (same with OpacityMask, non-rectilinear Clips, LayeredWindows, etc).  This can decrease text readability somewhat and should factor into your decision about where to use Effects in your application.  If all you're looking for is a simple drop shadow behind a text box, it's much better to create one geometrically with another shape than it is to put an Effect on your text box, both in terms of clarity and (most likely) performance.

    More to the point, though, there are some complexities with layout interaction with the Effect feature.  Running a shader on some content produces a bitmap, much like an Image element, and as soon as you start using Effects you have to take position into account in the same way you need to for any other bitmap in WPF.  See this blog post for more specific details:
    http://blogs.msdn.com/dwayneneed/archive/2007/10/05/blurry-bitmaps.aspx

    The summary is that you want to ensure you place your content at integer positions, and ensure that its size (width and height) are both whole numbers as well, or else we'll have to conceptually resample from one texture into another, since the pixel grids won't align, and this will cause blurriness.  This is exactly what you're seeing above - your Border is centering the Grid inside it, and TextBlocks are notorious for giving out non-integer sizes since the size of text can vary quite a bit - this is causing the text to be drawn at a fractional offset of a pixel.  When you clip the size of the Grid you're forcing it to be a whole number of pixels tall, which happens to allow it to align correctly. 

    Here is a more simple repro app - as you resize the Window you can see the text become sharp when the Button is layed-out to an integral postion (when the Window is an even height/width), and blurry in-between (when the Window is an odd height/width).

    <Window  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="300" Width="300">      
            <Grid> 
                <Button Height="200" Width="200">  
                <Button.Effect>    
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>     
                </Button.Effect>    
                    Here's some text.  
                </Button> 
            </Grid>    
    </Window 

    The only way to guarantee that you won't get blurring is, just like Images, to ensure you position your content in a way to align it correctly.  Here's an example from a Silverlight lead for one way to accomplish it.  We have plans for the next framework release to make this easier as well.
  • Thursday, February 26, 2009 9:13 PM
     
     Answered Has Code
    I believe you're right, that specific example might not have been explained completely correctly.  The Border itself is inside a StackPanel which futher contributes to its positioning.  I'm betting that all the Borders are going to wind up with non-integral Height since it's being based partially off of the TextBlock - since they are being stacked vertically this can ofset all the other Borders after the first by a sub-pixel amount.  I haven't debugged into it to verify (and I'm not very familiar with how Border works) but I'm guessing the alignment of the text inside one of the Borders just happens to work out if you apply the Clip, and happens not to otherwise.  This illustrates why it becomes necessary to round all your positions / sizes in the parent chain to ensure that things are layed-out correctly to align to screen pixels.  Here's a simpler example to illustrate the problem, note the difference in Canvas.Top:

    <Window  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="300" Width="300">  
        <Canvas TextBlock.FontSize="14">  
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Grid>                 
                    <TextBlock Text="Without Effect" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
     
     
            <Border Canvas.Top="30" Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <TextBlock Text="With Effect, Aligned" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
     
            <Border Canvas.Top="60.5" Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <TextBlock Text="With Effect, Misaligned" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
        </Canvas> 
     
    </Window> 

All Replies

  • Wednesday, February 25, 2009 4:33 AM
     
     Answered
    I can verify that the behavior of the items is as you described.

    There are known issuess with text blurring in Effects.  I cannot remember the details however, something to do with  algorithm handling subpixel placement if I recall.

    The fourth example is really wierd. 





    ** Free WPF shader utiliy **
    ** edit HLSL
    ** auto generates your C#/VB shader classes **
    ** auto generates test pages **

    Shazzam - Open source pixel shader utility


    Walt | http://wpfwonderland.wordpress.com
    • Marked As Answer by Tao Liang Monday, March 02, 2009 9:14 AM
    •  
  • Wednesday, February 25, 2009 2:39 PM
     
     
    Does anyone know of a better way to work around the issue than to add a clipped element to my XAML (like on the 2nd box)?
  • Wednesday, February 25, 2009 6:18 PM
     
     Answered
    Funny, I just blogged about this. It seems to be on NVIDIA cards.

    I came up with a workaround for my flavor (which seems to be very much like yours, if not the same) of the issue (see blog entry).
    • Edited by Cory Plotts Wednesday, February 25, 2009 6:36 PM
    • Proposed As Answer by Cory Plotts Wednesday, February 25, 2009 10:32 PM
    • Marked As Answer by Tao Liang Monday, March 02, 2009 9:14 AM
    •  
  • Wednesday, February 25, 2009 6:20 PM
     
     
    Here is another, seemingly related issue.

    Walt: are you suggesting that this is an known issue ... that Microsoft thinks it can solve on its end?
  • Wednesday, February 25, 2009 6:35 PM
     
     
    Walt: do you have any links to the reasons behind this issue? I would love to read up on it.

    I agree, that fourth example is just plain weird.

    Finally, on an off-topic note: how did you create such a cool looking avatar?
  • Wednesday, February 25, 2009 9:30 PM
     
     
    Cory Plotts said:

    Funny, I just blogged about this. It seems to be on NVIDIA cards.

    I came up with a workaround for my flavor (which seems to be very much like yours, if not the same) of the issue (see blog entry).


    It's not only nVidia cards, because it is also happening on my laptop with an integrated Intel 915GM chip. Unfortunately I don't have access to an ATI machine to test it.

    Frustrating how shader effects supposedly replaced bitmap effects so we can finally use them in our apps - and then I run into this.
  • Wednesday, February 25, 2009 10:31 PM
     
     
    Yes, another fellow also saw it on an ATI card ... so, it seems I was mistaken about that ... which makes me feel better, actually. I would rather have it be a Microsoft problem that they can correct, versus relying on a driver manufacturer to get their act together.

    I think the intermittency of this issue at times ... confused me into thinking it was an NVIDIA problem.

    I would also be interested to see if you also see the other issue above on your Intel integrated solution. However, we've also seen other odd issues with Intel integrated chipsets.
  • Thursday, February 26, 2009 4:30 PM
     
     
    The jagged rectangle issue also occurs on my Intel chip. It looks like it is rendering the rectangle without the rotation to a texture, and then drawing the texture at an angle with bilinear filtering turned off (notice how the corners are still antialiased - sort of).
  • Thursday, February 26, 2009 7:19 PM
     
     Answered
    Jonathan L Marston said:

    The jagged rectangle issue also occurs on my Intel chip. It looks like it is rendering the rectangle without the rotation to a texture, and then drawing the texture at an angle with bilinear filtering turned off (notice how the corners are still antialiased - sort of).


    I answered the question in Cory's thread but your intuition is exactly right.  The "loss of anti-aliasing" issue has to do with setting the DirectX sampling mode incorrectly - depending on what other content you're rendering inside the Effect the state might wind up being set correctly most of the time, which is why we hadn't caught the issue previously.  It only affects BlurEffect and DropShadowEffect when rendering in hardware, not the custom ShaderEffects.  It's been fixed for the next version.
  • Thursday, February 26, 2009 7:27 PM
     
     

    Thanks for the explanation Brendan.

    Is the original problem I brought up in this thread (blurry rendering with effect) caused by the same issue?

  • Thursday, February 26, 2009 7:45 PM
     
     Answered Has Code

    The blurriness caused by Effects has to do with a number of things.  First, when you're interested in text clarity it's important to note that you'll lose ClearType inside an Effect (same with OpacityMask, non-rectilinear Clips, LayeredWindows, etc).  This can decrease text readability somewhat and should factor into your decision about where to use Effects in your application.  If all you're looking for is a simple drop shadow behind a text box, it's much better to create one geometrically with another shape than it is to put an Effect on your text box, both in terms of clarity and (most likely) performance.

    More to the point, though, there are some complexities with layout interaction with the Effect feature.  Running a shader on some content produces a bitmap, much like an Image element, and as soon as you start using Effects you have to take position into account in the same way you need to for any other bitmap in WPF.  See this blog post for more specific details:
    http://blogs.msdn.com/dwayneneed/archive/2007/10/05/blurry-bitmaps.aspx

    The summary is that you want to ensure you place your content at integer positions, and ensure that its size (width and height) are both whole numbers as well, or else we'll have to conceptually resample from one texture into another, since the pixel grids won't align, and this will cause blurriness.  This is exactly what you're seeing above - your Border is centering the Grid inside it, and TextBlocks are notorious for giving out non-integer sizes since the size of text can vary quite a bit - this is causing the text to be drawn at a fractional offset of a pixel.  When you clip the size of the Grid you're forcing it to be a whole number of pixels tall, which happens to allow it to align correctly. 

    Here is a more simple repro app - as you resize the Window you can see the text become sharp when the Button is layed-out to an integral postion (when the Window is an even height/width), and blurry in-between (when the Window is an odd height/width).

    <Window  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="300" Width="300">      
            <Grid> 
                <Button Height="200" Width="200">  
                <Button.Effect>    
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>     
                </Button.Effect>    
                    Here's some text.  
                </Button> 
            </Grid>    
    </Window 

    The only way to guarantee that you won't get blurring is, just like Images, to ensure you position your content in a way to align it correctly.  Here's an example from a Silverlight lead for one way to accomplish it.  We have plans for the next framework release to make this easier as well.
  • Thursday, February 26, 2009 8:13 PM
     
     
    Again, thanks for the detailed explanation. I understand that things become more complicated when using an effect because you have to render the element to a texture before rendering it to the final scene, but I'm still confused by what is happening in the example I posted.

    For instance, take the second Border (the one that seems to be most affected by the blurriness due to its vertical position), and change the margin of the black Rectangle to "0,0,12,0", run the sample again and the bluriness is gone. Does the fact that the Rectangle is being clipped by the Grid's column width really affect the sub-pixel positioning of the parent Border?
  • Thursday, February 26, 2009 9:13 PM
     
     Answered Has Code
    I believe you're right, that specific example might not have been explained completely correctly.  The Border itself is inside a StackPanel which futher contributes to its positioning.  I'm betting that all the Borders are going to wind up with non-integral Height since it's being based partially off of the TextBlock - since they are being stacked vertically this can ofset all the other Borders after the first by a sub-pixel amount.  I haven't debugged into it to verify (and I'm not very familiar with how Border works) but I'm guessing the alignment of the text inside one of the Borders just happens to work out if you apply the Clip, and happens not to otherwise.  This illustrates why it becomes necessary to round all your positions / sizes in the parent chain to ensure that things are layed-out correctly to align to screen pixels.  Here's a simpler example to illustrate the problem, note the difference in Canvas.Top:

    <Window  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="300" Width="300">  
        <Canvas TextBlock.FontSize="14">  
            <Border Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Grid>                 
                    <TextBlock Text="Without Effect" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
     
     
            <Border Canvas.Top="30" Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <TextBlock Text="With Effect, Aligned" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
     
            <Border Canvas.Top="60.5" Margin="4" Background="White" BorderBrush="Black" BorderThickness="1">  
                <Border.Effect> 
                    <DropShadowEffect Color="Black" ShadowDepth="4" BlurRadius="8" Opacity="0.5"/>  
                </Border.Effect> 
     
                <Grid> 
                    <TextBlock Text="With Effect, Misaligned" Margin="0.5" Background="LightGoldenrodYellow"/>     
                </Grid> 
            </Border> 
        </Canvas> 
     
    </Window> 
  • Thursday, February 26, 2009 9:38 PM
     
     
    Nice explanation Brendan (as usual).


    Walt | http://wpfwonderland.wordpress.com
  • Thursday, February 26, 2009 9:40 PM
     
     
    Cory Plotts said:

    Walt: do you have any links to the reasons behind this issue? I would love to read up on it.

    I agree, that fourth example is just plain weird.

    Finally, on an off-topic note: how did you create such a cool looking avatar?


    Cory:  Off Topic

    That's my XBOX 360 avatar.  I copied the image from the XBOX website.


    Walt | http://wpfwonderland.wordpress.com
  • Tuesday, November 10, 2009 3:45 PM
     
     
    I have found a workaround in Cory Plott's blog, that although also being sort of a hack at least is more intuitive. Read it here .