Pourquoi mes textes animés sont-ils flous, puis redeviennent nets avec WPF?

Une chose que l'on remarque souvent lorsqu'on commence à développer avec WPF, c'est que les textes ne s'affichent pas toujours de manière nette. Ceux-ci sont en général un peu flous lors des animations et des transformations, et ce particulièrement une petite police. De plus, une fois l'animation terminée, leur apparence change pour paraître plus sombre et plus précise. Ci-dessous un exemple démontrant ce comportement :

 <Border Width="250">
<Border.RenderTransform>
<ScaleTransform x:Name="scaleTransform" ScaleY="3"/>
</Border.RenderTransform>
<StackPanel>
<TextBlock Background="White" Foreground="Black"
FontSize="8" Padding="10,5,10,5">
Un/Snapped pixel text animation!
      </TextBlock>
         <Button Margin="2,20,2,20">Animer
             <Button.Triggers>
                 <EventTrigger RoutedEvent="Button.Click">
                     <EventTrigger.Actions>
                         <BeginStoryboard>
                             <Storyboard Storyboard.TargetName="scaleTransform">
                                 <DoubleAnimation Storyboard.TargetProperty="ScaleY" To="1"/>
                             </Storyboard>
                         </BeginStoryboard>
                     </EventTrigger.Actions>
                 </EventTrigger>
             </Button.Triggers>
         </Button>
     </StackPanel>
 </Border>

Les résultats sont capturés dans l'image suivante. Les textes marqués Unsnapped sont rendus pendant l'animation, tandis que ceux marqués en snapped sont tel qu'ils sont affichés une seconde après la fin de l'animation: on remarque que les textes snapped sont beaucoup plus lisibles.
image

La raison de ce comportement s'explique par la combinaison de deux facteurs : l'indépendance de résolution de WPF, et le fait que le texte soit de petit taille.

L'indépendance de résolution permet aux applications d'avoir une même taille physique quelque soit la résolution du moniteur utilisée. Le moteur WPF gère cette fonctionnalité en effectuant ses rendus "dans les coulisses", puis en les affichant à travers la grille de pixels qu'est l'écran de l'utilisateur. Plus cette grille est fine (ie. plus le dpi est élevé) plus l'affichage sera net. Afin d'éviter les effets de crénelage on utilise des techniques d'anti-crénelage (anti-aliasing ou AA) : on donne à un pixel physique les couleurs pondérées des pixels logiques sous-jacents. La contrepartie de cette technique est un léger effet de flou. A ces techniques s'ajoutent également d'autres processus tels le subpixel rendering qui dépassent la portée de cette article.

Ces techniques d'optimisation font des merveilles sur la quasi totalité des éléments affichables, mais les textes ne font pas partis du lot : ceux-ci nécessitent une grande netteté pour rester lisible, et le flou résultant du traitement AA est dans ce cas contre productif. C'est la raison pour laquelle les UIElements disposent d'une propriété SnapsToDevicePixels permettant de préciser au moteur WPF s'il doit aligner les bords d'un élément aux pixels physiques de l'écran. Dans le cas des éléments textuels statiques, cette propriété est forcée à la valeur true, améliorant ainsi la lisibilité. Notons que le développeur ne peut pas changer la valeur de cette propriété pour les textes statiques.

En quoi cela répond-il au problème posé au départ? La réponse vient du fait que les animations paraissent plus fluides lorsque le rendu se fait avec de l'AA, en particulier lorsqu'il s'agit d'éléments précis et de petite taille ... comme du texte avec une petite police. Une animation de petits éléments sans AA paraîtra saccadée, en particulier dans le cas du texte où les espacements entre les caractères varieront au fur et à mesure de l'animation puisque les caractères s'attacheront au pixels physiques les plus proches. Afin d'éviter ces artéfacts visuels, l'équipe WPF a décidé de désactiver SnapsToDevicePixels pour les éléments texte lors des animations, et de les réactiver lorsque l'animation s'achève, afin de rétablir un niveau de lisibilité satisfaisant.

Le délai d'une seconde avant que le texte ne soit snappé - défini arbitrairement au niveau du framework WPF - sera normalement réduit dans la version 3.5 SP1 du framework .NET à paraître cet été. A l'heure actuelle, ce comportement ne peut pas être modifié et est un compromis technique entre fluidité d'animation et lisibilité du texte. Une solution permettant au développeur de préciser une vitesse de transition fluidifiant le passage d'un rendu unsnapped à un rendu snapped avait été étudiée, mais n'a pu être implémentée dans la version actuelle du framework. Le futur nous dira si elle pourra l'être dans une version ultérieure!

 

Liens:

Alignement des pixels dans les applications WPF (MSDN), SnapsToDevicePixels (MSDN)

Post du blog de Seema Ramchandani, décrivant ce comportement

Excellent article sur le subpixel rendering, une technique d'anti crénelage ciblant les écrans LCD

Comment ClearType améliore la lisibilité du texte, cette technologie disponible sous Windows XP et Vista utilise, entre autres, le pixel snapping pour afficher du texte de manière optimale sur écran LCD