Los modos de combinación no separables
Como ha visto en el artículo Modos de mezcla separable SkiaSharp, los modos de mezcla separable realizan operaciones en los canales rojo, verde y azul por separado. Los modos de combinación no separables no lo hacen. Al operar sobre los niveles de tono, saturación y luminosidad de color, los modos de mezcla no separables pueden modificar los colores de maneras interesantes:
El modelo Hue-Saturation-luminosidad
Para comprender los modos de mezcla no separables, es necesario tratar el destino y los píxeles de origen como colores en el modelo Hue-Saturación-luminosidad. (La luminosidad también se conoce como Ligereza.)
El modelo de color HSL se explicó en el artículo Integración con Xamarin.Forms y un programa de ejemplo en ese artículo permite experimentación con colores HSL. Puede crear un valor SKColor
mediante los valores Hue, saturación y luminosidad con el método estático SKColor.FromHsl
.
El matiz representa la longitud de onda dominante del color. Los valores de Hue van de 0 a 360 y hacen un ciclo a través de los primarios aditivos y sustractivos: el rojo es el valor 0, el amarillo es 60, el verde es 120, el cian es 180, el azul es 240, el magenta es 300, y el ciclo vuelve al rojo en 360.
Si no hay ningún color dominante (por ejemplo, el color es blanco o negro o un tono gris), el Hue no está definido y normalmente se establece en 0.
Los valores de saturación pueden oscilar entre 0 y 100 e indicar la pureza del color. Un valor de saturación de 100 es el color más puro, mientras que los valores inferiores a 100 hacen que el color se vuelva más gris. Un valor de saturación de 0 da como resultado un tono gris.
El valor luminosidad (o ligereza) indica lo brillante que es el color. Un valor de luminosidad de 0 es negro independientemente de la otra configuración. Del mismo modo, un valor de luminosidad de 100 es blanco.
El valor HSL (0, 100, 50) es el valor RGB (FF, 00, 00), que es rojo puro. El valor HSL (180, 100, 50) es el valor RGB (00, FF, FF), cian puro. A medida que se reduce la saturación, se reduce el componente de color dominante y se incrementan los demás componentes. En un nivel de saturación de 0, todos los componentes son iguales y el color es un tono gris. Disminuir la luminosidad para ir a negro; aumenta la luminosidad para ir a blanco.
Los modos de combinación en detalle
Al igual que los otros modos de mezcla, los cuatro modos de combinación no separables implican un destino (que suele ser una imagen de mapa de bits) y un origen, que suele ser un único color o un degradado. Los modos de combinación combinan los valores Hue, saturación y luminosidad del destino y del origen:
Modo de fusión | Componentes del origen | Componentes del destino |
---|---|---|
Hue |
Hue | Saturación y luminosidad |
Saturation |
Saturación | Matiz y luminosidad |
Color |
Matiz y saturación | Luminosidad |
Luminosity |
Luminosidad | Matiz y saturación |
Consulte la especificación W3C Compositing and Blending Level 1 para los algoritmos.
La página Modos de mezcla no separable contiene un Picker
para seleccionar uno de estos modos de combinación y tres vistas Slider
para seleccionar un color 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>
Para ahorrar espacio, las tres vistas Slider
no se identifican en la interfaz de usuario del programa. Tendrá que recordar que el orden es Hue, saturación y luminosidad. Dos vistas Label
en la parte inferior de la página muestran los valores de color HSL y RGB.
El archivo de código subyacente carga uno de los recursos de mapa de bits, muestra que lo más grande posible en el lienzo y a continuación, cubre el lienzo con un rectángulo. El color del rectángulo se basa en las tres vistas Slider
y el modo de combinación es el seleccionado en Picker
:
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);
}
}
}
Observe que el programa no muestra el valor de color HSL seleccionado por los tres controles deslizantes. En su lugar, crea un valor de color a partir de esos controles deslizantes y a continuación, usa el métodoToHsl
para obtener los valores Hue, saturación y luminosidad. Esto se debe a que el método FromHsl
convierte un color HSL en un color RGB, que se almacena internamente en la estructura SKColor
. El método ToHsl
convierte de RGB a HSL, pero el resultado no siempre será el valor original.
Por ejemplo, FromHsl
convierte el valor HSL (180, 50, 0) en el color RGB (0, 0, 0, 0) porque Luminosity
es cero. El método ToHsl
convierte el color RGB (0, 0, 0, 0) al color HSL (0, 0, 0, 0) porque los valores de Hue y Saturación son irrelevantes. Al usar este programa, es mejor que vea la representación del color HSL que usa el programa en lugar del especificado con los controles deslizantes.
El modo de mezcla SKBlendModes.Hue
usa el nivel Hue del origen mientras conserva los niveles de saturación y luminosidad del destino. Al probar este modo de mezcla, los controles deslizantes de saturación y luminosidad deben establecerse en algo distinto de 0 o 100 porque, en esos casos, el Hue no se define de forma única.
Cuando se usa establecer el control deslizante en 0 (como con la captura de pantalla de iOS a la izquierda), todo se vuelve rojizo. Pero esto no significa que la imagen esté completamente ausente de verde y azul. Obviamente, todavía hay tonos grises presentes en el resultado. Por ejemplo, el color RGB (40, 40, C0) es equivalente al color HSL (240, 50, 50). El Hue es azul, pero el valor de saturación de 50 indica que también hay componentes rojos y verdes. Si el Hue se establece en 0 con SKBlendModes.Hue
, el color HSL es (0, 50, 50), que es el color RGB (C0, 40, 40). Todavía hay componentes azules y verdes, pero ahora el componente dominante es rojo.
El modo de mezcla SKBlendModes.Saturation
combina el nivel saturación de la fuente con el Hue y la luminosidad del destino. Al igual que el Hue, la saturación no está bien definida si la luminosidad es 0 o 100. En teoría, cualquier configuración de luminosidad entre esos dos extremos debe funcionar. Sin embargo, el valor de luminosidad parece afectar al resultado más de lo que debería. Establezca la luminosidad en 50 y puede ver cómo puede establecer el nivel saturación de la imagen:
Puede usar este modo de mezcla para aumentar la saturación de color de una imagen opaca, o puede reducir la saturación a cero (como en la captura de pantalla de iOS a la izquierda) para una imagen resultante compuesta solo de tonos grises.
El modo de mezcla SKBlendModes.Color
conserva la luminosidad del destino, pero usa el Hue y la saturación de la fuente. De nuevo, eso implica que cualquier ajuste del control deslizante luminosidad en algún lugar entre los extremos debe funcionar.
Verá una aplicación de este modo de mezcla en breve.
Por último, el modo de mezcla SKBlendModes.Luminosity
es el contrario de SKBlendModes.Color
. Conserva el Hue y la saturación del destino, pero usa la luminosidad de la fuente. El modo de mezclaLuminosity
es el más misterioso del lote: los controles deslizantes Hue y saturación afectan a la imagen, pero incluso en la luminosidad media, la imagen no es distinta:
En teoría, aumentar o disminuir la luminosidad de una imagen debe hacer que sea más claro o oscuro. Este Ejemplo de propiedad luminosidad o esta Enumeración SKBlendMode Enum puede ser de interés.
Por lo general, no es el caso de que quiera usar uno de los modos de combinación no separables con un origen que consta de un único color aplicado a toda la imagen de destino. El efecto es demasiado grande. Querrá restringir el efecto a una parte de la imagen. En ese caso, es probable que la fuente incorpore transparencia, o puede que se limite a un gráfico más pequeño.
Un mate para un modo separable
Este es uno de los mapas de bits incluidos como un recurso en el ejemplo. El nombre de archivo es Banana.jpg:
Es posible crear un mate que abarque solo el plátano. Esto también es un recurso del ejemplo. El nombre de archivo es BananaMatte.png:
Aparte de la forma de plátano negro, el resto del mapa de bits es transparente.
La página Blue Banana utiliza ese mate para modificar el Hue y la saturación del plátano que el mono está sosteniendo, pero para cambiar nada más en la imagen.
En la siguiente clase BlueBananaPage
, el mapa de bits Banana.jpg se carga como un campo. El constructor carga el mapa de bitsBananaMatte.png como objeto matteBitmap
, pero no conserva ese objeto más allá del constructor. En su lugar, se crea un tercer mapa de bits denominado blueBananaBitmap
. El matteBitmap
se dibuja seguido de blueBananaBitmap
un objeto SKPaint
con su conjunto en azul Color
y su conjunto BlendMode
en SKBlendMode.SrcIn
. El blueBananaBitmap
permanece principalmente transparente pero con una sólida imagen azul pura del plátano:
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);
}
}
}
El controlador PaintSurface
dibuja el mapa de bits con el mono que mantiene el plátano. Este código va seguido de la presentación de blueBananaBitmap
con SKBlendMode.Color
. Sobre la superficie del plátano, el tono y la saturación de cada píxel se reemplazan por el azul sólido, que corresponde a un valor de matiz de 240 y un valor de saturación de 100. Sin embargo, la luminosidad sigue siendo la misma, lo que significa que el plátano sigue teniendo una textura realista a pesar de su nuevo color:
Intente cambiar el modo de mezcla a SKBlendMode.Saturation
. El plátano permanece amarillo, pero es un amarillo más intenso. En una aplicación real, esto podría ser un efecto más deseable que convertir el azul de plátano.