Partager via


Filtres d’images SkiaSharp

Les filtres d’image sont des effets qui fonctionnent sur tous les bits de couleur de pixels qui composent une image. Ils sont plus polyvalents que les filtres de masque, qui fonctionnent uniquement sur le canal alpha comme décrit dans l’article Filtres de masque SkiaSharp. Pour utiliser un filtre d’image, définissez la ImageFilter propriété d’un SKPaint objet de type SKImageFilter que vous avez créé en appelant l’une des méthodes statiques de la classe.

La meilleure façon de se familiariser avec les filtres de masque consiste à expérimenter ces méthodes statiques. Vous pouvez utiliser un filtre de masque pour flouter une image bitmap entière :

Exemple de flou

Cet article montre également l’utilisation d’un filtre d’image pour créer une ombre goutte-à-goutte, et pour les effets d’embossage et de gravure.

Flou des graphiques vectoriels et des bitmaps

L’effet de flou créé par la SKImageFilter.CreateBlur méthode statique présente un avantage significatif sur les méthodes floues de la SKMaskFilter classe : le filtre d’image peut flouter une image bitmap entière. La méthode a la syntaxe suivante :

public static SkiaSharp.SKImageFilter CreateBlur (float sigmaX, float sigmaY,
                                                  SKImageFilter input = null,
                                                  SKImageFilter.CropRect cropRect = null);

La méthode a deux valeurs sigma : la première pour l’étendue floue dans la direction horizontale et la seconde pour la direction verticale. Vous pouvez appliquer des filtres d’images en cascade en spécifiant un autre filtre d’image comme troisième argument facultatif. Un rectangle de rognage peut également être spécifié.

La page Image Blur Experiment dans l’exemple comprend deux Slider vues qui vous permettent d’expérimenter la définition de différents niveaux de flou :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.ImageBlurExperimentPage"
             Title="Image Blur Experiment">

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="sigmaXSlider"
                Maximum="10"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaXSlider},
                              Path=Value,
                              StringFormat='Sigma X = {0:F1}'}"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="sigmaYSlider"
                Maximum="10"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaYSlider},
                              Path=Value,
                              StringFormat='Sigma Y = {0:F1}'}"
               HorizontalTextAlignment="Center" />
    </StackLayout>
</ContentPage>

Le fichier code-behind utilise les deux Slider valeurs à appeler SKImageFilter.CreateBlur pour l’objet utilisé pour afficher à la fois du SKPaint texte et une bitmap :

public partial class ImageBlurExperimentPage : ContentPage
{
    const string TEXT = "Blur My Text";

    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
                            typeof(MaskBlurExperimentPage),
                            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public ImageBlurExperimentPage ()
    {
        InitializeComponent ();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear(SKColors.Pink);

        // Get values from sliders
        float sigmaX = (float)sigmaXSlider.Value;
        float sigmaY = (float)sigmaYSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            // Set SKPaint properties
            paint.TextSize = (info.Width - 100) / (TEXT.Length / 2);
            paint.ImageFilter = SKImageFilter.CreateBlur(sigmaX, sigmaY);

            // Get text bounds and calculate display rectangle
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);
            SKRect textRect = new SKRect(0, 0, info.Width, textBounds.Height + 50);

            // Center the text in the display rectangle
            float xText = textRect.Width / 2 - textBounds.MidX;
            float yText = textRect.Height / 2 - textBounds.MidY;

            canvas.DrawText(TEXT, xText, yText, paint);

            // Calculate rectangle for bitmap
            SKRect bitmapRect = new SKRect(0, textRect.Bottom, info.Width, info.Height);
            bitmapRect.Inflate(-50, -50);

            canvas.DrawBitmap(bitmap, bitmapRect, BitmapStretch.Uniform, paint: paint);
        }
    }
}

Les trois captures d’écran montrent différents paramètres pour les sigmaX paramètres et sigmaY les paramètres :

Expérience de flou d’image

Pour maintenir la cohérence du flou entre différentes tailles d’affichage et résolutions, définissez sigmaX et définissez des sigmaY valeurs proportionnelles à la taille de pixel rendue de l’image à laquelle le flou est appliqué.

Ombre portée

La SKImageFilter.CreateDropShadow méthode statique crée un SKImageFilter objet pour une ombre portée :

public static SKImageFilter CreateDropShadow (float dx, float dy,
                                              float sigmaX, float sigmaY,
                                              SKColor color,
                                              SKDropShadowImageFilterShadowMode shadowMode,
                                              SKImageFilter input = null,
                                              SKImageFilter.CropRect cropRect = null);

Définissez cet objet sur la ImageFilter propriété d’un SKPaint objet, et tout ce que vous dessinez avec cet objet aura une ombre portée derrière lui.

Les dx paramètres indiquent dy les décalages horizontaux et verticaux de l’ombre en pixels de l’objet graphique. La convention dans les graphiques 2D consiste à supposer une source de lumière provenant du coin supérieur gauche, ce qui implique que ces deux arguments doivent être positifs pour positionner l’ombre en dessous et à droite de l’objet graphique.

Les sigmaX paramètres et sigmaY les paramètres sont des facteurs flous pour l’ombre portée.

Le color paramètre est la couleur de l’ombre portée. Cette SKColor valeur peut inclure la transparence. Une possibilité est la valeur SKColors.Black.WithAlpha(0x80) de couleur pour assombrir n’importe quel arrière-plan de couleur.

Les deux derniers paramètres sont facultatifs.

Le programme Drop Shadow Experiment vous permet d’expérimenter des valeurs de , dy, sigmaXet sigmaY d’afficher une chaîne de dxtexte avec une ombre portée. Le fichier XAML instancie quatre Slider vues pour définir ces valeurs :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.DropShadowExperimentPage"
             Title="Drop Shadow Experiment">
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="Margin" Value="10, 0" />
        </Style>

        <Style TargetType="Label">
            <Setter Property="HorizontalTextAlignment" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="dxSlider"
                Minimum="-20"
                Maximum="20"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference dxSlider},
                              Path=Value,
                              StringFormat='Horizontal offset = {0:F1}'}" />

        <Slider x:Name="dySlider"
                Minimum="-20"
                Maximum="20"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference dySlider},
                              Path=Value,
                              StringFormat='Vertical offset = {0:F1}'}" />

        <Slider x:Name="sigmaXSlider"
                Maximum="10"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaXSlider},
                              Path=Value,
                              StringFormat='Sigma X = {0:F1}'}" />

        <Slider x:Name="sigmaYSlider"
                Maximum="10"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaYSlider},
                              Path=Value,
                              StringFormat='Sigma Y = {0:F1}'}" />
    </StackLayout>
</ContentPage>

Le fichier code-behind utilise ces valeurs pour créer une ombre de dépôt rouge sur une chaîne de texte bleue :

public partial class DropShadowExperimentPage : ContentPage
{
    const string TEXT = "Drop Shadow";

    public DropShadowExperimentPage ()
    {
        InitializeComponent ();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Get values from sliders
        float dx = (float)dxSlider.Value;
        float dy = (float)dySlider.Value;
        float sigmaX = (float)sigmaXSlider.Value;
        float sigmaY = (float)sigmaYSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            // Set SKPaint properties
            paint.TextSize = info.Width / 7;
            paint.Color = SKColors.Blue;
            paint.ImageFilter = SKImageFilter.CreateDropShadow(
                                    dx,
                                    dy,
                                    sigmaX,
                                    sigmaY,
                                    SKColors.Red,
                                    SKDropShadowImageFilterShadowMode.DrawShadowAndForeground);

            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Center the text in the display rectangle
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Voici le programme en cours d’exécution :

Drop Shadow Experiment

Les valeurs de décalage négatives dans la capture d’écran plateforme Windows universelle à l’extrême droite entraînent l’apparition de l’ombre au-dessus et à gauche du texte. Cela suggère une source de lumière en bas à droite, ce qui n’est pas la convention pour les graphiques informatiques. Mais il ne semble pas mal d’une manière quelconque, peut-être parce que l’ombre est également très floue et semble plus ornementale que la plupart des ombres goutte.

Effets d’éclairage

La SKImageFilter classe définit six méthodes qui ont des noms et des paramètres similaires, répertoriées ici dans l’ordre de complexité croissante :

Ces méthodes créent des filtres d’images qui imitent l’effet de différents types de lumière sur des surfaces tridimensionnelles. Le filtre d’image résultant illumine les objets à deux dimensions comme s’ils existaient dans l’espace 3D, ce qui peut entraîner l’apparition de ces objets élevés ou en retrait, ou avec une mise en surbrillance spéculaire.

Les Distant méthodes de lumière supposent que la lumière provient d’une distance éloignée. Dans le but d’éclairer des objets, la lumière est supposée pointer dans une direction cohérente dans l’espace 3D, comme le Soleil sur une petite zone de la Terre. Les Point méthodes de lumière imitent une ampoule positionnée dans l’espace 3D qui émet la lumière dans toutes les directions. La Spot lumière a à la fois une position et une direction, beaucoup comme une lampe de poche.

Les emplacements et les directions dans l’espace 3D sont tous deux spécifiés avec des valeurs de la SKPoint3 structure, qui est similaire à SKPoint mais avec trois propriétés nommées X, Yet Z.

Le nombre et la complexité des paramètres de ces méthodes rendent l’expérimentation difficile. Pour commencer, la page Expérience légère distante vous permet d’expérimenter des paramètres dans la CreateDistantLightDiffuse méthode :

public static SKImageFilter CreateDistantLitDiffuse (SKPoint3 direction,
                                                     SKColor lightColor,
                                                     float surfaceScale,
                                                     float kd,
                                                     SKImageFilter input = null,
                                                     SKImageFilter.CropRect cropRect = null);

La page n’utilise pas les deux derniers paramètres facultatifs.

Trois Slider vues dans le fichier XAML vous permettent de sélectionner la Z coordonnée de la SKPoint3 valeur, du surfaceScale paramètre et du kd paramètre, qui est défini dans la documentation de l’API comme « constante d’éclairage diffuse » :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaLightExperiment.MainPage"
             Title="Distant Light Experiment">

    <StackLayout>

        <skia:SKCanvasView x:Name="canvasView"
                           PaintSurface="OnCanvasViewPaintSurface"
                           VerticalOptions="FillAndExpand" />

        <Slider x:Name="zSlider"
                Minimum="-10"
                Maximum="10"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference zSlider},
                              Path=Value,
                              StringFormat='Z = {0:F0}'}"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="surfaceScaleSlider"
                Minimum="-1"
                Maximum="1"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference surfaceScaleSlider},
                              Path=Value,
                              StringFormat='Surface Scale = {0:F1}'}"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="lightConstantSlider"
                Minimum="-1"
                Maximum="1"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference lightConstantSlider},
                              Path=Value,
                              StringFormat='Light Constant = {0:F1}'}"
               HorizontalTextAlignment="Center" />
    </StackLayout>
</ContentPage>

Le fichier code-behind obtient ces trois valeurs et les utilise pour créer un filtre d’image pour afficher une chaîne de texte :

public partial class DistantLightExperimentPage : ContentPage
{
    const string TEXT = "Lighting";

    public DistantLightExperimentPage()
    {
        InitializeComponent();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        float z = (float)zSlider.Value;
        float surfaceScale = (float)surfaceScaleSlider.Value;
        float lightConstant = (float)lightConstantSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            paint.IsAntialias = true;

            // Size text to 90% of canvas width
            paint.TextSize = 100;
            float textWidth = paint.MeasureText(TEXT);
            paint.TextSize *= 0.9f * info.Width / textWidth;

            // Find coordinates to center text
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            float xText = info.Rect.MidX - textBounds.MidX;
            float yText = info.Rect.MidY - textBounds.MidY;

            // Create distant light image filter
            paint.ImageFilter = SKImageFilter.CreateDistantLitDiffuse(
                                    new SKPoint3(2, 3, z),
                                    SKColors.White,
                                    surfaceScale,
                                    lightConstant);

            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Le premier argument est SKImageFilter.CreateDistantLitDiffuse la direction de la lumière. Les coordonnées X et Y positives indiquent que la lumière est pointée vers la droite et vers le bas. Les coordonnées Z positives pointent vers l’écran. Le fichier XAML vous permet de sélectionner des valeurs Z négatives, mais c’est seulement pour voir ce qui se passe : conceptuellement, les coordonnées Z négatives entraînent la lumière à pointer hors de l’écran. Pour quoi que ce soit d’autre puis de petites valeurs négatives, l’effet d’éclairage cesse de fonctionner.

L’argument surfaceScale peut aller de -1 à 1. (Les valeurs supérieures ou inférieures n’ont aucun effet supplémentaire.) Il s’agit de valeurs relatives dans l’axe Z qui indiquent le déplacement de l’objet graphique (dans ce cas, la chaîne de texte) de l’aire de canevas. Utilisez des valeurs négatives pour élever la chaîne de texte au-dessus de la surface du canevas et les valeurs positives pour la décompresser dans le canevas.

La lightConstant valeur doit être positive. (Le programme autorise les valeurs négatives afin que vous puissiez voir qu’elles provoquent l’arrêt du travail.) Les valeurs plus élevées provoquent une lumière plus intense.

Ces facteurs peuvent être équilibrés pour obtenir un effet embossé lorsqu’il surfaceScale est négatif (comme avec les captures d’écran iOS et Android) et un effet gravé lorsqu’il surfaceScale est positif, comme avec la capture d’écran UWP à droite :

Expérience de lumière distante

La capture d’écran Android a une valeur Z de 0, ce qui signifie que la lumière pointe vers le bas et vers la droite. L’arrière-plan n’est pas éclairé et la surface de la chaîne de texte n’est pas éclairée non plus. La lumière n’affecte que le bord du texte pour un effet très subtil.

Une autre approche du texte embossé et gravé a été illustrée dans l’article The Translate Transform : The Translate String is afficher deux fois avec des couleurs différentes qui sont décalées légèrement les unes des autres.