Freigeben über


Die separierbaren Mischmodi

Wie Sie im Artikel SkiaSharp Porter-Duff Blend Modi gesehen haben, führen die Porter-Duff Blend Modi in der Regel Clipping-Vorgänge aus. Die separierbaren Mischmodi unterscheiden sich. Die separierbaren Modi ändern die einzelnen Rot-, Grün- und Blaufarbenkomponenten eines Bilds. Separierbare Mischungsmodi können Farben mischen, um zu zeigen, dass die Kombination aus Rot, Grün und Blau tatsächlich weiß ist:

Primäre Farben

Aufhellen und Abdunkelt auf zwei Arten

Es ist üblich, eine Bitmap zu verwenden, die etwas zu dunkel oder zu hell ist. Sie können separierbare Blendmodi verwenden, um das Bild zu erhellen oder abzudunkelt. Tatsächlich werden zwei der separierbaren Mischmodi in der SKBlendMode Enumeration benannt Lighten und Darken.

Diese beiden Modi werden auf der Seite "Aufhellen" und "Dunkler" veranschaulicht. Die XAML-Datei instanziiert zwei SKCanvasView Objekte und zwei Slider Ansichten:

<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>

Das erste SKCanvasView und vorführen SKBlendMode.Lighten und Slider das zweite Paar veranschaulicht SKBlendMode.Darken. Die beiden Slider Ansichten verwenden denselben ValueChanged Handler, und die beiden SKCanvasView teilen denselben PaintSurface Handler. Beide Ereignishandler überprüfen, welches Objekt das Ereignis auslöst:

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);
        }
    }
}

Der PaintSurface Handler berechnet ein Rechteck, das für die Bitmap geeignet ist. Der Handler zeigt diese Bitmap an und zeigt dann ein Rechteck über die Bitmap mithilfe eines SKPaint Objekts an, dessen BlendMode Eigenschaft auf SKBlendMode.Lighten oder SKBlendMode.Darken. Die Color Eigenschaft ist eine graue Schattierung basierend auf der Slider. Für den Lighten Modus reicht die Farbe von Schwarz bis Weiß, aber für den Darken Modus reicht sie von Weiß bis Schwarz.

Die Screenshots von links nach rechts zeigen immer größere Slider Werte an, da das obere Bild heller wird und das untere Bild dunkler wird:

Aufhellen und Abdunken

Dieses Programm veranschaulicht die normale Art und Weise, in der die separierbaren Mischmodi verwendet werden: Das Ziel ist ein Bild einer Art, sehr oft eine Bitmap. Die Quelle ist ein Rechteck, das mit einem SKPaint Objekt angezeigt wird, dessen BlendMode Eigenschaft auf einen separierbaren Mischmodus festgelegt ist. Das Rechteck kann eine Volltonfarbe (wie hier) oder ein Farbverlauf sein. Transparenz wird in der Regel nicht mit den trennbaren Mischmodi verwendet.

Während Sie mit diesem Programm experimentieren, werden Sie feststellen, dass diese beiden Blendmodi das Bild nicht gleichmäßig aufhellen und abdunkelt. Stattdessen scheint dies Slider einen Schwellenwert für eine Art von Sortierung festzulegen. Wenn Sie beispielsweise den Slider Lighten Modus erhöhen, werden die dunkleren Bereiche des Bilds zuerst hell, während die helleren Bereiche wieder Standard identisch sind.

Wenn für den Lighten Modus das Zielpixel der RGB-Farbwert (Dr, Dg, Db) ist und das Quellpixel die Farbe (Sr, Sg, Sb) ist, wird die Ausgabe (Or, Og, Ob) wie folgt berechnet:

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

Bei Rot, Grün und Blau ist das Ergebnis der Ziel- und Quelle größer. Dies bewirkt, dass die dunklen Bereiche des Ziels zuerst aufhellt werden.

Der Darken Modus ist ähnlich, mit der Ausnahme, dass das Ergebnis der geringere des Ziels und der Quelle ist:

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

Die roten, grünen und blauen Komponenten werden jeweils separat behandelt, weshalb diese Mischmodi als trennbare Mischungsmodi bezeichnet werden. Aus diesem Grund können die Abkürzungen Dc und Sc für die Ziel- und Quellfarben verwendet werden, und es wird verstanden, dass Berechnungen für jede der roten, grünen und blauen Komponenten separat gelten.

In der folgenden Tabelle sind alle separierbaren Mischmodi mit kurzen Erläuterungen zu ihrer Funktionsweise aufgeführt. In der zweiten Spalte wird die Quellfarbe angezeigt, die keine Änderung erzeugt:

Blendmodus Keine Änderung Vorgang
Plus Schwarz Hellt durch Hinzufügen von Farben: Sc + Dc
Modulate White Verdunkelt durch Multiplizieren von Farben: Sc· Dc
Screen Schwarz Ergänzt Produkt von Ergänzungen: Sc + Dc – Sc· Dc
Overlay Grau Umkehrung von HardLight
Darken White Mindestfarben: min(Sc, Dc)
Lighten Schwarz Maximale Anzahl von Farben: max(Sc, Dc)
ColorDodge Schwarz Aufhellen des Ziels basierend auf der Quelle
ColorBurn White Dunkles Ziel basierend auf der Quelle
HardLight Grau Ähnlich wie die Wirkung von rauen Blickpunkt
SoftLight Grau Ähnlich wie bei weichem Blickpunkt
Difference Schwarz Subtrahiert den dunkleren vom Helleren: Abs(Dc – Sc)
Exclusion Schwarz Ähnlich wie Difference, aber der Kontrast ist niedriger.
Multiply White Verdunkelt durch Multiplizieren von Farben: Sc· Dc

Detailliertere Algorithmen finden Sie in der W3C Compositing- und Blending Level 1-Spezifikation und der Skia SkBlendMode Reference, obwohl die Notation in diesen beiden Quellen nicht identisch ist. Denken Sie daran, dass Plus es sich häufig um einen Porter-Duff-Blend-Modus handelt und Modulate nicht Teil der W3C-Spezifikation ist.

Wenn die Quelle transparent ist, hat der Blendmodus für alle trennbaren Mischmodi Modulatekeine Auswirkung. Wie Sie bereits gesehen haben, enthält der Modulate Blendmodus den Alphakanal in die Multiplikation. Modulate Andernfalls hat die gleiche Wirkung wie Multiply.

Beachten Sie die beiden benannten ColorDodge und ColorBurn. Die Worte verdüsteten und verbrennen aus fotografischen Dunklen Raumpraktiken. Ein Vergrößerungselement macht einen fotografischen Druck durch ein negatives Licht. Ohne Licht ist der Druck weiß. Der Druck wird dunkler, da mehr Licht für einen längeren Zeitraum auf den Druck fällt. Print-Maker verwendeten häufig ein Hand- oder kleines Objekt, um zu verhindern, dass ein teil des Drucks auf ein bestimmtes Teil des Drucks fällt, wodurch dieser Bereich heller wird. Dies wird als Verdrückung bezeichnet. Umgekehrt könnte undurchsichtiges Material mit einem Loch darin (oder händen, die den größten Teil des Lichts blockieren) verwendet werden, um das Licht an einem bestimmten Punkt zu verdunkelt, der als Brennen bezeichnet wird.

Das Dodge and Burn-Programm ist sehr ähnlich wie "Aufhellen" und "Dunkler". Die XAML-Datei ist identisch, aber mit unterschiedlichen Elementnamen, und die CodeBehind-Datei ist ebenfalls ziemlich ähnlich, aber der Effekt dieser beiden Blendmodi unterscheidet sich ziemlich:

Dodge und Burn

Bei kleinen Slider Werten hellt der Lighten Modus zuerst dunkle Bereiche auf, während ColorDodge er gleichmäßiger aufhellt.

Anwendungsprogramme zur Bildverarbeitung ermöglichen häufig das Ausweichen und Brennen auf bestimmte Bereiche, genau wie in einem Dunklen Raum. Dies kann durch Farbverläufe oder durch eine Bitmap mit unterschiedlichen Graustufen erreicht werden.

Erkunden der separierbaren Mischmodi

Auf der Seite "Separable Blend Modes " können Sie alle separierbaren Mischmodi untersuchen. Es zeigt ein Bitmapziel und eine farbige Rechteckquelle mit einem der Blendmodi an.

Die XAML-Datei definiert einen Picker (zum Auswählen des Mischmodus) und vier Schieberegler. Mit den ersten drei Schiebereglern können Sie die roten, grünen und blauen Komponenten der Quelle festlegen. Der vierte Schieberegler soll diese Werte überschreiben, indem ein grauer Schatten festgelegt wird. Die einzelnen Schieberegler werden nicht identifiziert, aber Farben geben ihre Funktion an:

<?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>

Die CodeBehind-Datei lädt eine der Bitmapressourcen und zeichnet sie zweimal, einmal in der oberen Hälfte des Zeichenbereichs und wieder in der unteren Hälfte des Zeichenbereichs:

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);
        }
    }
}

Am unteren Rand des PaintSurface Handlers wird ein Rechteck über die zweite Bitmap mit dem ausgewählten Blendmodus und der ausgewählten Farbe gezeichnet. Sie können die geänderte Bitmap unten mit der ursprünglichen Bitmap oben vergleichen:

Trennbare Füllmethoden

Additive und subtrahierende Primärfarben

Auf der Seite " Primärfarben " werden drei überlappende Kreise von Rot, Grün und Blau gezeichnet:

Additive Primärfarben

Dies sind die additiven Primärfarben. Kombinationen von zwei beliebigen erzeugen Zyan, Magenta und Gelb, und eine Kombination aller drei ist weiß.

Diese drei Kreise werden mit dem SKBlendMode.Plus Modus gezeichnet, sie können aber auch für denselben Effekt verwendet ScreenLightenwerdenDifference. Hier ist das Programm:

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);
            }
        }
    }
}

Das Programm enthält ein TabGestureRecognizer. Wenn Sie auf den Bildschirm tippen oder klicken, zeigt SKBlendMode.Multiply das Programm die drei subtrahativen Primarvorlagen an:

Subtraktive Primärfarben

Der Darken Modus funktioniert auch für diesen Effekt.