Partager via


Modes de fusion séparables

Comme vous l’avez vu dans l’article SkiaSharp Porter-Duff modes de fusion, les modes de fusion Porter-Duff effectuent généralement des opérations de découpage. Les modes de fusion séparables sont différents. Les modes séparables modifient les composants de couleur rouge, vert et bleu d’une image. Les modes de mélange séparables peuvent mélanger la couleur pour démontrer que la combinaison rouge, verte et bleue est en effet blanche :

Couleurs primaires

Éclaircir et assombrir deux façons

Il est courant d’avoir une bitmap qui est un peu trop sombre ou trop clair. Vous pouvez utiliser des modes de fusion séparables pour éclaircir ou assombrir l’image. En effet, deux des modes de fusion séparables dans l’énumération SKBlendMode sont nommés Lighten et Darken.

Ces deux modes sont illustrés dans la page Lighten et Darken . Le fichier XAML instancie deux SKCanvasView objets et deux Slider vues :

<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.LightenAndDarkenPage"
             Title="Lighten and Darken">
    <StackLayout>
        <skia:SKCanvasView x:Name="lightenCanvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="lightenSlider"
                Margin="10"
                ValueChanged="OnSliderValueChanged" />

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

        <Slider x:Name="darkenSlider"
                Margin="10"
                ValueChanged="OnSliderValueChanged" />
    </StackLayout>
</ContentPage>

La première SKCanvasView et Slider la SKBlendMode.Lighten deuxième paire illustrent SKBlendMode.Darken. Les deux Slider vues partagent le même ValueChanged gestionnaire et les deux SKCanvasView partagent le même PaintSurface gestionnaire. Les deux gestionnaires d’événements case activée quel objet déclenche l’événement :

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

    public LightenAndDarkenPage ()
    {
        InitializeComponent ();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        if ((Slider)sender == lightenSlider)
        {
            lightenCanvasView.InvalidateSurface();
        }
        else
        {
            darkenCanvasView.InvalidateSurface();
        }
    }

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

        canvas.Clear();

        // Find largest size rectangle in canvas
        float scale = Math.Min((float)info.Width / bitmap.Width,
                               (float)info.Height / bitmap.Height);
        SKRect rect = SKRect.Create(scale * bitmap.Width, scale * bitmap.Height);
        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Display bitmap
        canvas.DrawBitmap(bitmap, rect);

        // Display gray rectangle with blend mode
        using (SKPaint paint = new SKPaint())
        {
            if ((SKCanvasView)sender == lightenCanvasView)
            {
                byte value = (byte)(255 * lightenSlider.Value);
                paint.Color = new SKColor(value, value, value);
                paint.BlendMode = SKBlendMode.Lighten;
            }
            else
            {
                byte value = (byte)(255 * (1 - darkenSlider.Value));
                paint.Color = new SKColor(value, value, value);
                paint.BlendMode = SKBlendMode.Darken;
            }

            canvas.DrawRect(rect, paint);
        }
    }
}

Le PaintSurface gestionnaire calcule un rectangle adapté à la bitmap. Le gestionnaire affiche cette bitmap, puis affiche un rectangle sur la bitmap à l’aide d’un SKPaint objet avec sa BlendMode propriété définie SKBlendMode.Lighten sur ou SKBlendMode.Darken. La Color propriété est une nuance grise basée sur le Slider. Pour le Lighten mode, la couleur est comprise entre le noir et le blanc, mais pour le Darken mode, il varie de blanc à noir.

Les captures d’écran de gauche à droite affichent des valeurs de plus en plus grandes lorsque l’image supérieure est plus claire Slider et que l’image inférieure est plus sombre :

Éclaircir et assombrir

Ce programme illustre la façon normale dont les modes de fusion séparables sont utilisés : la destination est une image d’une sorte, très souvent une bitmap. La source est un rectangle affiché à l’aide d’un SKPaint objet dont la BlendMode propriété est définie sur un mode de fusion séparable. Le rectangle peut être une couleur unie (tel qu’il est ici) ou un dégradé. La transparence n’est généralement pas utilisée avec les modes de fusion séparables.

Au fur et à mesure que vous expérimentez ce programme, vous découvrirez que ces deux modes de fusion ne s’éclaircient pas et assombrir l’image uniformément. Au lieu de cela, il Slider semble définir un seuil de quelque sorte. Par exemple, à mesure que vous augmentez le SliderLighten mode, les zones plus sombres de l’image obtiennent d’abord la lumière tandis que les zones plus claires restent les mêmes.

Pour le Lighten mode, si le pixel de destination est la valeur de couleur RVB (Dr, Dg, Db) et que le pixel source est la couleur (Sr, Sg, Sb), la sortie est calculée comme suit :

Or = max(Dr, Sr) Og = max(Dg, Sg) Ob = max(Db, Sb)

Pour le rouge, le vert et le bleu séparément, le résultat est supérieur à la destination et à la source. Cela produit l’effet d’éclaircir les zones sombres de la destination en premier.

Le Darken mode est similaire, sauf que le résultat est le plus faible de la destination et de la source :

Or = min(Dr, Sr) Og = min(Dg, Sg) Ob = min(Db, Sb)

Les composants bleus, rouges, verts et bleus sont gérés séparément, c’est pourquoi ces modes de fusion sont appelés modes de fusion séparables . Pour cette raison, les abréviations Dc et Sc peuvent être utilisées pour les couleurs de destination et de source, et il est compris que les calculs s’appliquent à chacun des composants rouges, verts et bleus séparément.

Le tableau suivant présente tous les modes de fusion séparables avec de brèves explications sur ce qu’ils font. La deuxième colonne affiche la couleur source qui ne produit aucune modification :

Blend Mode Aucun changement Opération
Plus Noir Éclaircit en ajoutant des couleurs : Sc + Dc
Modulate White Sombre en multipliant les couleurs : Sc· Dc
Screen Noir Compléments produits de compléments : Sc + Dc – Sc· Dc
Overlay Gris Inverse de HardLight
Darken White Minimum des couleurs : min(Sc, Dc)
Lighten Noir Nombre maximal de couleurs : max(Sc, Dc)
ColorDodge Noir Destination brightens basée sur la source
ColorBurn White Destination Darkens basée sur la source
HardLight Gris Similaire à l’effet d’une mise à l’honneur dure
SoftLight Gris Similaire à l’effet de la mise à jour réversible
Difference Noir Soustrait le plus sombre de l’clair : Abs(Dc – Sc)
Exclusion Noir Similaire à un Difference contraste inférieur
Multiply White Sombre en multipliant les couleurs : Sc· Dc

Vous trouverez des algorithmes plus détaillés dans la spécification W3C Compositing et Blending Level 1 et la référence Skia SkBlendMode, bien que la notation de ces deux sources ne soit pas la même. Gardez à l’esprit qu’il Plus est généralement considéré comme un mode de fusion Porter-Duff et Modulate ne fait pas partie de la spécification W3C.

Si la source est transparente, pour tous les modes de fusion séparables à l’exception Modulatedu mode de fusion, le mode blend n’a aucun effet. Comme vous l’avez vu précédemment, le Modulate mode blend incorpore le canal alpha dans la multiplication. Sinon, Modulate a le même effet que Multiply.

Notez les deux modes nommés ColorDodge et ColorBurn. Les mots dodge and burn proviennent de pratiques photographiques de salle noire. Un agrandisseur fait une impression photographique en éclatant la lumière à travers un négatif. Sans lumière, l’impression est blanche. L’impression devient plus sombre, car plus de lumière tombe sur l’impression pendant une plus longue période. Les imprimeurs utilisaient souvent une main ou un petit objet pour empêcher une partie de la lumière de tomber sur une certaine partie de l’impression, ce qui rend cette zone plus claire. C’est ce qu’on appelle le dodging. À l’inverse, un matériau opaque avec un trou dans celui-ci (ou les mains bloquant la plupart de la lumière) peut être utilisé pour diriger plus de lumière dans un endroit particulier pour l’assombrir, appelé brûlure.

Le programme Dodge et Burn est très similaire à Lighten et Darken. Le fichier XAML est structuré de la même façon, mais avec des noms d’éléments différents, et le fichier code-behind est de même assez similaire, mais l’effet de ces deux modes de fusion est assez différent :

Dodge et Burn

Pour les petites Slider valeurs, le Lighten mode éclaircit d’abord les zones sombres, tout en ColorDodge éclaircit plus uniformément.

Les programmes d’application de traitement d’images permettent souvent de restreindre et de brûler des zones spécifiques, comme dans une salle sombre. Cela peut être effectué par dégradés ou par une bitmap avec des nuances de gris variables.

Exploration des modes de fusion séparables

La page Modes de fusion séparables vous permet d’examiner tous les modes de fusion séparables. Il affiche une destination bitmap et une source de rectangle coloré à l’aide de l’un des modes de fusion.

Le fichier XAML définit un Picker (pour sélectionner le mode blend) et quatre curseurs. Les trois premiers curseurs vous permettent de définir les composants rouges, verts et bleus de la source. Le quatrième curseur est destiné à remplacer ces valeurs en définissant une nuance grise. Les curseurs individuels ne sont pas identifiés, mais les couleurs indiquent leur fonction :

<?xml version="1.0" encoding="utf-8" ?>
<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.SeparableBlendModesPage"
             Title="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.Plus" />
                    <x:Static Member="skia:SKBlendMode.Modulate" />
                    <x:Static Member="skia:SKBlendMode.Screen" />
                    <x:Static Member="skia:SKBlendMode.Overlay" />
                    <x:Static Member="skia:SKBlendMode.Darken" />
                    <x:Static Member="skia:SKBlendMode.Lighten" />
                    <x:Static Member="skia:SKBlendMode.ColorDodge" />
                    <x:Static Member="skia:SKBlendMode.ColorBurn" />
                    <x:Static Member="skia:SKBlendMode.HardLight" />
                    <x:Static Member="skia:SKBlendMode.SoftLight" />
                    <x:Static Member="skia:SKBlendMode.Difference" />
                    <x:Static Member="skia:SKBlendMode.Exclusion" />
                    <x:Static Member="skia:SKBlendMode.Multiply" />
                </x:Array>
            </Picker.ItemsSource>

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

        <Slider x:Name="redSlider"
                MinimumTrackColor="Red"
                MaximumTrackColor="Red"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="greenSlider"
                MinimumTrackColor="Green"
                MaximumTrackColor="Green"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="blueSlider"
                MinimumTrackColor="Blue"
                MaximumTrackColor="Blue"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="graySlider"
                MinimumTrackColor="Gray"
                MaximumTrackColor="Gray"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label x:Name="colorLabel"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

Le fichier code-behind charge l’une des ressources bitmap et le dessine deux fois, une fois dans la moitié supérieure du canevas et à nouveau dans la moitié inférieure du canevas :

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

    public SeparableBlendModesPage()
    {
        InitializeComponent();
    }

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

    void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
    {
        if (sender == graySlider)
        {
            redSlider.Value = greenSlider.Value = blueSlider.Value = graySlider.Value;
        }

        colorLabel.Text = String.Format("Color = {0:X2} {1:X2} {2:X2}",
                                        (byte)(255 * redSlider.Value),
                                        (byte)(255 * greenSlider.Value),
                                        (byte)(255 * blueSlider.Value));

        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        // Draw bitmap in top half
        SKRect rect = new SKRect(0, 0, info.Width, info.Height / 2);
        canvas.DrawBitmap(bitmap, rect, BitmapStretch.Uniform);

        // Draw bitmap in bottom halr
        rect = new SKRect(0, info.Height / 2, info.Width, info.Height);
        canvas.DrawBitmap(bitmap, rect, BitmapStretch.Uniform);

        // Get values from XAML controls
        SKBlendMode blendMode =
            (SKBlendMode)(blendModePicker.SelectedIndex == -1 ?
                                        0 : blendModePicker.SelectedItem);

        SKColor color = new SKColor((byte)(255 * redSlider.Value),
                                    (byte)(255 * greenSlider.Value),
                                    (byte)(255 * blueSlider.Value));

        // Draw rectangle with blend mode in bottom half
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = color;
            paint.BlendMode = blendMode;
            canvas.DrawRect(rect, paint);
        }
    }
}

Vers le bas du PaintSurface gestionnaire, un rectangle est dessiné sur la deuxième bitmap avec le mode de fusion sélectionné et la couleur sélectionnée. Vous pouvez comparer la bitmap modifiée en bas avec la bitmap d’origine en haut :

Modes de fusion séparables

Couleurs primaires additives et soustractives

La page Couleurs primaires dessine trois cercles qui se chevauchent de rouge, de vert et de bleu :

Couleurs primaires additives

Il s’agit des couleurs primaires additives. Les combinaisons de deux produits cyan, magenta et jaune, et une combinaison des trois est blanche.

Ces trois cercles sont dessinés avec le SKBlendMode.Plus mode, mais vous pouvez également utiliser Screen, Lightenou Difference pour le même effet. Voici le programme :

public class PrimaryColorsPage : ContentPage
{
    bool isSubtractive;

    public PrimaryColorsPage ()
    {
        Title = "Primary Colors";

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

        // Switch between additive and subtractive primaries at tap
        TapGestureRecognizer tap = new TapGestureRecognizer();
        tap.Tapped += (sender, args) =>
        {
            isSubtractive ^= true;
            canvasView.InvalidateSurface();
        };
        canvasView.GestureRecognizers.Add(tap);

        Content = canvasView;
    }

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

        canvas.Clear();

        SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
        float radius = Math.Min(info.Width, info.Height) / 4;
        float distance = 0.8f * radius;     // from canvas center to circle center
        SKPoint center1 = center + 
            new SKPoint(distance * (float)Math.Cos(9 * Math.PI / 6),
                        distance * (float)Math.Sin(9 * Math.PI / 6));
        SKPoint center2 = center +
            new SKPoint(distance * (float)Math.Cos(1 * Math.PI / 6),
                        distance * (float)Math.Sin(1 * Math.PI / 6));
        SKPoint center3 = center +
            new SKPoint(distance * (float)Math.Cos(5 * Math.PI / 6),
                        distance * (float)Math.Sin(5 * Math.PI / 6));

        using (SKPaint paint = new SKPaint())
        {
            if (!isSubtractive)
            {
                paint.BlendMode = SKBlendMode.Plus; 
                System.Diagnostics.Debug.WriteLine(paint.BlendMode);

                paint.Color = SKColors.Red;
                canvas.DrawCircle(center1, radius, paint);

                paint.Color = SKColors.Lime;    // == (00, FF, 00)
                canvas.DrawCircle(center2, radius, paint);

                paint.Color = SKColors.Blue;
                canvas.DrawCircle(center3, radius, paint);
            }
            else
            {
                paint.BlendMode = SKBlendMode.Multiply
                System.Diagnostics.Debug.WriteLine(paint.BlendMode);

                paint.Color = SKColors.Cyan;
                canvas.DrawCircle(center1, radius, paint);

                paint.Color = SKColors.Magenta;
                canvas.DrawCircle(center2, radius, paint);

                paint.Color = SKColors.Yellow;
                canvas.DrawCircle(center3, radius, paint);
            }
        }
    }
}

Le programme comprend un TabGestureRecognizer. Lorsque vous appuyez ou cliquez sur l’écran, le programme utilise SKBlendMode.Multiply pour afficher les trois primaires soustractives :

Couleurs primaires soustractives

Le Darken mode fonctionne également pour ce même effet.