Udostępnij za pomocą


Rozdzielne tryby mieszania

Jak pokazano w artykule SkiaSharp Porter-Duff blend tryby mieszania Porter-Duff, tryby mieszania Porter-Duff zwykle wykonują operacje wycinków. Tryby mieszania z możliwością połączenia są różne. Tryby separowalne zmieniają poszczególne składniki koloru czerwonego, zielonego i niebieskiego obrazu. Separable tryby mieszania mogą mieszać kolor, aby wykazać, że połączenie czerwonego, zielonego i niebieskiego jest rzeczywiście białe:

Kolory podstawowe

Rozjaśnić i zaciemnić dwa sposoby

Często zdarza się, że mapa bitowa jest nieco zbyt ciemna lub zbyt jasna. Można użyć trybów mieszania separable, aby rozjaśnić lub zaciemnić obraz. Rzeczywiście, dwa z separowalnych trybów mieszania w wyliczenie SKBlendMode są nazwane Lighten i Darken.

Te dwa tryby są pokazane na stronie Lighten i Darken . Plik XAML tworzy wystąpienie dwóch SKCanvasView obiektów i dwóch Slider widoków:

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

SKCanvasView Pierwsza i Slider pokazanaSKBlendMode.Lighten, a druga para demonstruje SKBlendMode.Darken. Slider Oba widoki współdzielą tę samą procedurę obsługi, a dwa SKCanvasView współużytkuje tę samą ValueChanged PaintSurface procedurę obsługi. Oba programy obsługi zdarzeń sprawdzają, który obiekt jest wyzwalany przez zdarzenie:

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

Procedura PaintSurface obsługi oblicza prostokąt odpowiedni dla mapy bitowej. Program obsługi wyświetla mapę bitową, a następnie wyświetla prostokąt nad mapą bitową przy użyciu obiektu z właściwością BlendMode ustawioną SKPaint na SKBlendMode.Lighten lub SKBlendMode.Darken. Właściwość Color jest szarym odcieniem na podstawie elementu Slider. Lighten W przypadku trybu kolory wahają się od czarnej do białejDarken, ale dla trybu waha się od białego do czarnego.

Zrzuty ekranu od lewej do prawej pokazują coraz większe Slider wartości, ponieważ górny obraz staje się jaśniejszy, a dolny obraz staje się ciemniejszy:

Rozjaśnienie i ciemnienie

Ten program pokazuje normalny sposób, w jaki są używane tryby łączenia separowalne: miejsce docelowe jest obrazem pewnego rodzaju, bardzo często mapą bitową. Źródło jest prostokątem wyświetlanym SKPaint przy użyciu obiektu z jego BlendMode właściwością ustawioną na tryb mieszania, który można rozdzielić. Prostokąt może być kolorem stałym (tak jak tutaj) lub gradientem. Przezroczystość nie jest zwykle używana z trybami mieszania, które można rozdzielić.

Podczas eksperymentów z tym programem dowiesz się, że te dwa tryby mieszania nie rozjaśniają i zaciemniają obraz jednolicie. Zamiast tego wydaje się ustawiać Slider próg pewnego rodzaju. Na przykład w miarę Slider zwiększania wartości dla Lighten trybu ciemniejsze obszary obrazu są najpierw jasne, podczas gdy lżejsze obszary pozostają takie same.

Lighten W przypadku trybu, jeśli piksel docelowy jest wartością koloru RGB (Dr, Dg, Db), a piksel źródłowy jest kolorem (Sr, Sg, Sb), dane wyjściowe są obliczane (Lub, Og, Ob) w następujący sposób:

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

W przypadku czerwonego, zielonego i niebieskiego oddzielnie wynik jest większy od miejsca docelowego i źródła. Powoduje to najpierw rozjaśnienie ciemnych obszarów miejsca docelowego.

Tryb Darken jest podobny, z tą różnicą, że wynik jest mniejszy od miejsca docelowego i źródła:

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

Poszczególne składniki czerwonego, zielonego i niebieskiego są obsługiwane oddzielnie, dlatego te tryby mieszania są określane jako separowalne tryby mieszania. Z tego powodu skróty Dc i Sc mogą być używane dla kolorów docelowych i źródłowych i zrozumiałe jest, że obliczenia mają zastosowanie do każdego z czerwonych, zielonych i niebieskich składników oddzielnie.

W poniższej tabeli przedstawiono wszystkie separowalne tryby mieszania z krótkimi wyjaśnieniami ich działania. Druga kolumna zawiera kolor źródła, który nie powoduje zmiany:

Tryb mieszania Bez zmian Operacja
Plus Black Lightens przez dodanie kolorów: Sc + Dc
Modulate Biała Ciemniej przez pomnożenie kolorów: Sc· Dc
Screen Black Uzupełnia produkt uzupełnień: Sc + Dc – Sc· Dc
Overlay Szary Odwrotność HardLight
Darken Biała Minimalna liczba kolorów: min(Sc, Dc)
Lighten Black Maksymalna liczba kolorów: max(Sc, Dc)
ColorDodge Black Lokalizacja docelowa Brightens oparta na źródle
ColorBurn Biała Zaciemnianie miejsce docelowe na podstawie źródła
HardLight Szary Podobnie jak w przypadku ostrego reflektora
SoftLight Szary Podobny do efektu miękkiego w centrum uwagi
Difference Black Odejmuje ciemniejszy od jaśniejszego: Abs(Dc – Sc)
Exclusion Black Podobnie jak w przypadku mniejszego Difference kontrastu
Multiply Biała Ciemniej przez pomnożenie kolorów: Sc· Dc

Bardziej szczegółowe algorytmy można znaleźć w specyfikacji Kompositing i Blending Level 1 oraz Skia SkBlendMode Reference, chociaż notacja w tych dwóch źródłach nie jest taka sama. Należy pamiętać, że Plus jest powszechnie uważany za tryb mieszany Porter-Duff i Modulate nie jest częścią specyfikacji W3C.

Jeśli źródło jest przezroczyste, wówczas dla wszystkich trybów mieszania, z wyjątkiem Modulate, tryb mieszania nie ma żadnego efektu. Jak pokazano wcześniej, Modulate tryb mieszania zawiera kanał alfa w mnożenie. Modulate W przeciwnym razie ma taki sam efekt jak Multiply.

Zwróć uwagę na dwa tryby o nazwach ColorDodge i ColorBurn. Słowa dodge i burn pochodzi z fotograficznych praktyk ciemnych. Powiększający sprawia, że wydruk fotograficzny świeci światło przez negatyw. Bez światła wydruk jest biały. Wydruk staje się ciemniejszy, ponieważ więcej światła spada na wydruk przez dłuższy czas. Producenci wydruku często używali ręki lub małego obiektu, aby zablokować niektóre światło przed upadkiem na określoną część wydruku, dzięki czemu obszar jest lżejszy. Jest to nazywane dodging. Z drugiej strony nieprzezroczystym materiałem z dziurą w nim (lub rękami blokującymi większość światła) można użyć do kierowania większej ilości światła w konkretnym miejscu, aby go zaciemnić, zwane spalaniem.

Program Dodge i Burn jest bardzo podobny do Lighten i Darken. Plik XAML ma taką samą strukturę, ale ma różne nazwy elementów, a plik związany z kodem jest podobnie podobny, ale efekt tych dwóch trybów mieszania jest zupełnie inny:

Dodge i Burn

W przypadku małych Slider wartości Lighten tryb najpierw rozjaśnia ciemne obszary, podczas gdy ColorDodge rozjaśnia się bardziej równomiernie.

Programy aplikacji do przetwarzania obrazów często umożliwiają dodgowanie i spalanie do określonych obszarów, podobnie jak w ciemni. Można to osiągnąć gradientami lub mapą bitową o różnych odcieniach szarości.

Eksplorowanie trybów mieszania, które można połączyć

Strona Separable Blend Modes (Separable Blend Modes ) umożliwia sprawdzenie wszystkich trybów mieszania, które można rozdzielić. Wyświetla miejsce docelowe mapy bitowej i kolorowe źródło prostokąta przy użyciu jednego z trybów mieszania.

Plik XAML definiuje Picker element (aby wybrać tryb mieszania) i cztery suwaki. Pierwsze trzy suwaki umożliwiają ustawienie czerwonych, zielonych i niebieskich składników źródła. Czwarty suwak ma zastąpić te wartości, ustawiając szary odcień. Poszczególne suwaki nie są identyfikowane, ale kolory wskazują ich funkcję:

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

Plik związany z kodem ładuje jeden z zasobów mapy bitowej i rysuje go dwa razy, raz w górnej połowie kanwy i ponownie w dolnej połowie kanwy:

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

W dolnej części PaintSurface procedury obsługi prostokąt jest rysowany nad drugą mapą bitową z wybranym trybem mieszania i wybranym kolorem. Zmodyfikowaną mapę bitową można porównać u dołu z oryginalną mapą bitową u góry:

Rozdzielne tryby mieszania

Kolory podstawowe dodawania i odejmowanie

Strona Kolory podstawowe rysuje trzy nakładające się okręgi czerwonego, zielonego i niebieskiego:

Kolory podstawowe dodawania

Są to addytywne kolory podstawowe. Kombinacje wszystkich dwóch produktów cyjan, magenta i żółty, a kombinacja wszystkich trzech jest biała.

Te trzy okręgi są rysowane w trybieSKBlendMode.Plus, ale można również użyć ScreenLighten, lub Difference dla tego samego efektu. Oto program:

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

Program zawiera element TabGestureRecognizer. Po naciśnięciu lub kliknięciu ekranu program użyje SKBlendMode.Multiply polecenia , aby wyświetlić trzy odejmowane prawybory:

Odejmowane kolory podstawowe

Tryb Darken działa również dla tego samego efektu.