Partager via


Modes de fusion non séparables

Comme vous l’avez vu dans l’article SkiaSharp, les modes de fusion séparables effectuent des opérations sur les canaux bleus, rouges, verts et bleus séparément. Les modes de fusion non séparables ne le font pas. En exploitant les niveaux de couleur Hue, Saturation et Luminosity, les modes de fusion non séparables peuvent modifier les couleurs de manière intéressante :

Exemple non séparable

Le modèle Hue-Saturation-Luminosity

Pour comprendre les modes de fusion non séparables, il est nécessaire de traiter la destination et les pixels sources comme des couleurs dans le modèle Hue-Saturation-Luminosity. (La luminosité est également appelée Lumière.)

Le modèle de couleur HSL a été abordé dans l’article Intégration avec Xamarin.Forms et un exemple de programme dans cet article permet l’expérimentation avec des couleurs HSL. Vous pouvez créer une SKColor valeur à l’aide de valeurs Hue, Saturation et Luminosity avec la méthode statique SKColor.FromHsl .

La teinte représente la longueur d’onde dominante de la couleur. Les valeurs hue sont comprises entre 0 et 360 et parcourent les primaires additifs et soustractives : rouge est la valeur 0, jaune est 60, vert est 120, cyan est 180, bleu est 240, magenta est 300, et le cycle revient en rouge à 360.

S’il n’y a pas de couleur dominante ( par exemple, la couleur est blanche ou noire ou une nuance grise), la teinte n’est pas définie et est généralement définie sur 0.

Les valeurs de saturation peuvent aller de 0 à 100 et indiquer la pureté de la couleur. Une valeur de saturation de 100 est la couleur la plus pure, tandis que les valeurs inférieures à 100 provoquent la couleur devenant plus grisâtre. Une valeur de saturation de 0 entraîne une nuance de gris.

La valeur Luminosity (ou Lightness) indique la luminosité de la couleur. Une valeur de luminosité de 0 est noire indépendamment des autres paramètres. De même, une valeur de luminosité de 100 est blanche.

La valeur HSL (0, 100, 50) est la valeur RVB (FF, 00, 00), qui est rouge pur. La valeur HSL (180, 100, 50) est la valeur RVB (00, FF, FF), cyan pur. À mesure que la saturation est réduite, le composant de couleur dominant est diminué et les autres composants sont augmentés. À un niveau de saturation de 0, tous les composants sont identiques et la couleur est une nuance grise. Diminuer la luminosité pour aller en noir ; augmentez la luminosité pour aller au blanc.

Modes de fusion en détail

Comme les autres modes de fusion, les quatre modes de fusion non séparables impliquent une destination (qui est souvent une image bitmap) et une source, qui est souvent une couleur unique ou un dégradé. Les modes de fusion combinent des valeurs Hue, Saturation et Luminosity à partir de la destination et de la source :

Blend Mode Composants provenant de la source Composants de la destination
Hue Teinte Saturation et luminosité
Saturation Saturation Hue et Luminosity
Color Teinte et saturation Luminosité
Luminosity Luminosité Teinte et saturation

Consultez la spécification W3C Compositing and Blending Level 1 pour les algorithmes.

La page Modes de fusion non séparables contient l’un Picker de ces modes de fusion et trois Slider vues pour sélectionner une couleur HSL :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
             xmlns:skiaviews="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.NonSeparableBlendModesPage"
             Title="Non-Separable Blend Modes">

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

        <Picker x:Name="blendModePicker"
                Title="Blend Mode"
                Margin="10, 0"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKBlendMode}">
                    <x:Static Member="skia:SKBlendMode.Hue" />
                    <x:Static Member="skia:SKBlendMode.Saturation" />
                    <x:Static Member="skia:SKBlendMode.Color" />
                    <x:Static Member="skia:SKBlendMode.Luminosity" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

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

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

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

        <StackLayout Orientation="Horizontal">
            <Label x:Name="hslLabel"
                   HorizontalOptions="CenterAndExpand" />

            <Label x:Name="rgbLabel"
                   HorizontalOptions="CenterAndExpand" />

        </StackLayout>
    </StackLayout>
</ContentPage>

Pour économiser de l’espace, les trois Slider vues ne sont pas identifiées dans l’interface utilisateur du programme. Vous devrez vous rappeler que l’ordre est Hue, Saturation et Luminosity. Deux Label vues situées en bas de la page affichent les valeurs de couleur HSL et RVB.

Le fichier code-behind charge l’une des ressources bitmap, affiche que le plus grand possible sur le canevas, puis couvre le canevas avec un rectangle. La couleur du rectangle est basée sur les trois Slider vues et le mode de fusion est celui sélectionné dans les Pickeréléments suivants :

public partial class NonSeparableBlendModesPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
                        typeof(NonSeparableBlendModesPage),
                        "SkiaSharpFormsDemos.Media.Banana.jpg");
    SKColor color;

    public NonSeparableBlendModesPage()
    {
        InitializeComponent();
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
    {
        // Calculate new color based on sliders
        color = SKColor.FromHsl((float)hueSlider.Value,
                                (float)satSlider.Value,
                                (float)lumSlider.Value);

        // Use labels to display HSL and RGB color values
        color.ToHsl(out float hue, out float sat, out float lum);

        hslLabel.Text = String.Format("HSL = {0:F0} {1:F0} {2:F0}",
                                      hue, sat, lum);

        rgbLabel.Text = String.Format("RGB = {0:X2} {1:X2} {2:X2}",
                                      color.Red, color.Green, color.Blue);

        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();
        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);

        // Get blend mode from Picker
        SKBlendMode blendMode =
            (SKBlendMode)(blendModePicker.SelectedIndex == -1 ?
                                        0 : blendModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Color = color;
            paint.BlendMode = blendMode;
            canvas.DrawRect(info.Rect, paint);
        }
    }
}

Notez que le programme n’affiche pas la valeur de couleur HSL sélectionnée par les trois curseurs. Au lieu de cela, il crée une valeur de couleur à partir de ces curseurs, puis utilise la ToHsl méthode pour obtenir les valeurs Hue, Saturation et Luminosity. Cela est dû au fait que la FromHsl méthode convertit une couleur HSL en couleur RVB, qui est stockée en interne dans la SKColor structure. La ToHsl méthode convertit de RVB en HSL, mais le résultat ne sera pas toujours la valeur d’origine.

Par exemple, FromHsl convertit la valeur HSL (180, 50, 0) en couleur RVB (0, 0, 0), car la Luminosity valeur est égale à zéro. La ToHsl méthode convertit la couleur RVB (0, 0, 0) en couleur HSL (0, 0, 0), car les valeurs hue et saturation ne sont pas pertinentes. Lorsque vous utilisez ce programme, il est préférable de voir la représentation de la couleur HSL que le programme utilise plutôt que celle que vous avez spécifiée avec les curseurs.

Le SKBlendModes.Hue mode de fusion utilise le niveau Hue de la source tout en conservant les niveaux saturation et luminosité de la destination. Lorsque vous testez ce mode de fusion, les curseurs de saturation et de luminosité doivent être définis sur une valeur autre que 0 ou 100, car dans ces cas, la hue n’est pas définie de manière unique.

Modes de fusion non séparables - Hue

Lorsque vous utilisez définir le curseur sur 0 (comme avec la capture d’écran iOS à gauche), tout devient rouge. Mais cela ne signifie pas que l’image est entièrement absente du vert et du bleu. Évidemment, il y a encore des nuances grises présentes dans le résultat. Par exemple, la couleur RVB (40, 40, C0) équivaut à la couleur HSL (240, 50, 50). La teinte est bleue, mais la valeur de saturation de 50 indique qu’il y a également des composants rouges et verts. Si la teinte est définie sur 0 avec SKBlendModes.Hue, la couleur HSL est (0, 50, 50), qui est la couleur RVB (C0, 40, 40). Il y a encore des composants bleus et verts, mais maintenant le composant dominant est rouge.

Le SKBlendModes.Saturation mode de fusion combine le niveau de saturation de la source avec la teinte et la luminosité de la destination. Comme la teinte, la saturation n’est pas bien définie si la luminosité est 0 ou 100. En théorie, tout paramètre de luminosité entre ces deux extrêmes devrait fonctionner. Toutefois, le paramètre Luminosity semble affecter le résultat plus qu’il ne le devrait. Définissez la luminosité sur 50, et vous pouvez voir comment définir le niveau de saturation de l’image :

Modes de fusion non séparables - Saturation

Vous pouvez utiliser ce mode de fusion pour augmenter la saturation de couleur d’une image terne, ou vous pouvez réduire la saturation jusqu’à zéro (comme dans la capture d’écran iOS à gauche) pour une image résultante composée uniquement de nuances grises.

Le SKBlendModes.Color mode de fusion conserve la luminosité de la destination, mais utilise la teinte et la saturation de la source. Là encore, cela implique que tout paramètre du curseur Luminosity quelque part entre les extrêmes doit fonctionner.

Modes de fusion non séparables - Couleur

Vous verrez une application de ce mode de fusion sous peu.

Enfin, le SKBlendModes.Luminosity mode blend est l’opposé de SKBlendModes.Color. Il conserve la teinte et la saturation de la destination, mais utilise la luminosité de la source. Le Luminosity mode de fusion est le plus mystérieux du lot : les curseurs Hue et Saturation affectent l’image, mais même à la luminosité moyenne, l’image n’est pas distincte :

Modes de fusion non séparables - Luminosité

En théorie, l’augmentation ou la diminution de la luminosité d’une image devrait la rendre plus claire ou plus sombre. Cet exemple de propriété Luminosity ou cette définition S Ko lendMode Enum peut être intéressante.

Il n’est généralement pas le cas que vous souhaitez utiliser l’un des modes de fusion non séparables avec une source qui se compose d’une seule couleur appliquée à l’image de destination entière. L’effet est juste trop grand. Vous souhaiterez limiter l’effet à une partie de l’image. Dans ce cas, la source incorporera probablement la transparance, ou peut-être la source sera limitée à un graphique plus petit.

Un mat pour un mode séparable

Voici l’une des bitmaps incluses en tant que ressource dans l’exemple. Le nom de fichier est Banana.jpg :

Banana Monkey

Il est possible de créer un mat qui englobe uniquement la banane. Il s’agit également d’une ressource dans l’exemple. Le nom de fichier est BananaMatte.png :

Banane Mat

Outre la forme de banane noire, le reste de la bitmap est transparent.

La page Banane bleue utilise ce mat pour modifier la teinte et saturation de la banane que le singe tient, mais pour ne rien changer d’autre dans l’image.

Dans la classe suivante BlueBananaPage , la bitmap Banana.jpg est chargée en tant que champ. Le constructeur charge la bitmap BananaMatte.png en tant qu’objet matteBitmap , mais il ne conserve pas cet objet au-delà du constructeur. Au lieu de cela, une troisième bitmap nommée blueBananaBitmap est créée. Le matteBitmap dessin est dessiné sur blueBananaBitmap un avec SKPaint son Color ensemble en bleu et son BlendMode ensemble sur SKBlendMode.SrcIn. Les blueBananaBitmap restes principalement transparents mais avec une image bleue pure solide de la banane :

public class BlueBananaPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
        typeof(BlueBananaPage),
        "SkiaSharpFormsDemos.Media.Banana.jpg");

    SKBitmap blueBananaBitmap;

    public BlueBananaPage()
    {
        Title = "Blue Banana";

        // Load banana matte bitmap (black on transparent)
        SKBitmap matteBitmap = BitmapExtensions.LoadBitmapResource(
            typeof(BlueBananaPage),
            "SkiaSharpFormsDemos.Media.BananaMatte.png");

        // Create a bitmap with a solid blue banana and transparent otherwise
        blueBananaBitmap = new SKBitmap(matteBitmap.Width, matteBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(blueBananaBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(matteBitmap, new SKPoint(0, 0));

            using (SKPaint paint = new SKPaint())
            {
                paint.Color = SKColors.Blue;
                paint.BlendMode = SKBlendMode.SrcIn;
                canvas.DrawPaint(paint);
            }
        }

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

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

        canvas.Clear();

        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);

        using (SKPaint paint = new SKPaint())
        {
            paint.BlendMode = SKBlendMode.Color;
            canvas.DrawBitmap(blueBananaBitmap,
                              info.Rect,
                              BitmapStretch.Uniform,
                              paint: paint);
        }
    }
}

Le PaintSurface gestionnaire dessine la bitmap avec le singe tenant la banane. Ce code est suivi de l’affichage avec blueBananaBitmapSKBlendMode.Color. Sur la surface de la banane, la teinte et la saturation de chaque pixel sont remplacées par le bleu unie, qui correspond à une valeur de teinte de 240 et une valeur de saturation de 100. La luminosité reste cependant la même, ce qui signifie que la banane continue d’avoir une texture réaliste malgré sa nouvelle couleur :

Banane bleue

Essayez de modifier le mode de fusion en SKBlendMode.Saturation. La banane reste jaune, mais c’est un jaune plus intense. Dans une application réelle, il peut s’agir d’un effet plus souhaitable que de tourner le bleu banane.