Compartir vía


Transparencia de SkiaSharp

Como ha visto, la clase SKPaint incluye una propiedad Color de tipo SKColor. SKColor incluye un canal alfa, por lo que todo lo que se colorea con un valor SKColor puede ser parcialmente transparente.

Se mostró la transparencia en cierta medida en el artículo Animación básica en SkiaSharp. En este artículo, se profundiza algo más en la transparencia para combinar varios objetos en una sola escena, una técnica a veces conocida como combinación. En los artículos de la sección Sombreadores de SkiaSharp, se tratan técnicas de combinación más avanzadas.

Puede establecer el nivel de transparencia al crear por primera vez un color mediante el constructor SKColor de cuatro parámetros:

SKColor (byte red, byte green, byte blue, byte alpha);

Un valor alfa de 0 es totalmente transparente y un valor alfa de 0xFF es totalmente opaco. Los valores entre esos dos extremos crean colores que son parcialmente transparentes.

Además, SKColor define un práctico método WithAlpha que crea un nuevo color a partir de un color existente, pero con el nivel alfa especificado:

SKColor halfTransparentBlue = SKColors.Blue.WithAlpha(0x80);

El uso de texto parcialmente transparente se muestra en la página Code More Code del ejemplo. Esta página atenúa dos cadenas de texto de entrada y salida mediante la incorporación de transparencia en los valores SKColor:

public class CodeMoreCodePage : ContentPage
{
    SKCanvasView canvasView;
    bool isAnimating;
    Stopwatch stopwatch = new Stopwatch();
    double transparency;

    public CodeMoreCodePage ()
    {
        Title = "Code More Code";

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

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 5;     // seconds
        double progress = stopwatch.Elapsed.TotalSeconds % duration / duration;
        transparency = 0.5 * (1 + Math.Sin(progress * 2 * Math.PI));
        canvasView.InvalidateSurface();

        return isAnimating;
    }

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

        canvas.Clear();

        const string TEXT1 = "CODE";
        const string TEXT2 = "MORE";

        using (SKPaint paint = new SKPaint())
        {
            // Set text width to fit in width of canvas
            paint.TextSize = 100;
            float textWidth = paint.MeasureText(TEXT1);
            paint.TextSize *= 0.9f * info.Width / textWidth;

            // Center first text string
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT1, ref textBounds);

            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * (1 - transparency)));
            canvas.DrawText(TEXT1, xText, yText, paint);

            // Center second text string
            textBounds = new SKRect();
            paint.MeasureText(TEXT2, ref textBounds);

            xText = info.Width / 2 - textBounds.MidX;
            yText = info.Height / 2 - textBounds.MidY;

            paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * transparency));
            canvas.DrawText(TEXT2, xText, yText, paint);
        }
    }
}

El campo transparency se anima para variar de 0 a 1 y se devuelve a un ritmo sinusoidal. La primera cadena de texto se muestra con un valor alfa calculado al restar el valor transparency de 1:

paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * (1 - transparency)));

El método WithAlpha establece el componente alfa en un color existente, que aquí es SKColors.Blue. La segunda cadena de texto usa un valor alfa calculado a partir del propio valor de transparency:

paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * transparency));

En la animación, se alterna entre las dos palabras y se insta al usuario a "codificar más" (o quizás se solicite "más código"):

Código más código

En el artículo anterior sobre conceptos básicos de mapa de bits en SkiaSharp, ha visto cómo mostrar mapas de bits mediante uno de los métodos DrawBitmap de SKCanvas. Todos los métodos DrawBitmap incluyen un objeto SKPaint como último parámetro. De forma predeterminada, este parámetro se establece en null y puede omitirlo.

Como alternativa, puede establecer la propiedad Color de este objeto SKPaint para que muestre un mapa de bits con algún nivel de transparencia. Establecer un nivel de transparencia en la propiedad Color de SKPaint permite atenuar los mapas de bits dentro y fuera o disolver un mapa de bits en otro.

La transparencia del mapa de bits se muestra en la página Disolución de mapa de bits. El archivo XAML crea una instancia de SKCanvasView y de Slider:

<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.BitmapDissolvePage"
             Title="Bitmap Dissolve">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

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

El archivo de código subyacente carga dos recursos de mapa de bits. Estos mapas de bits no tienen el mismo tamaño, pero tienen la misma relación de aspecto:

public partial class BitmapDissolvePage : ContentPage
{
    SKBitmap bitmap1;
    SKBitmap bitmap2;

    public BitmapDissolvePage()
    {
        InitializeComponent();

        // Load two bitmaps
        Assembly assembly = GetType().GetTypeInfo().Assembly;

        using (Stream stream = assembly.GetManifestResourceStream(
                                "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg"))
        {
            bitmap1 = SKBitmap.Decode(stream);
        }
        using (Stream stream = assembly.GetManifestResourceStream(
                                "SkiaSharpFormsDemos.Media.FacePalm.jpg"))
        {
            bitmap2 = SKBitmap.Decode(stream);
        }
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        // Find rectangle to fit bitmap
        float scale = Math.Min((float)info.Width / bitmap1.Width,
                                (float)info.Height / bitmap1.Height);
        SKRect rect = SKRect.Create(scale * bitmap1.Width,
                                    scale * bitmap1.Height);
        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Get progress value from Slider
        float progress = (float)progressSlider.Value;

        // Display two bitmaps with transparency
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = paint.Color.WithAlpha((byte)(0xFF * (1 - progress)));
            canvas.DrawBitmap(bitmap1, rect, paint);

            paint.Color = paint.Color.WithAlpha((byte)(0xFF * progress));
            canvas.DrawBitmap(bitmap2, rect, paint);
        }
    }
}

La propiedad Color del objeto SKPaint se establece en dos niveles alfa complementarios para los dos mapas de bits. Cuando se usa SKPaint con mapas de bits, no importa cuál es el resto del valor Color. Lo único que importa es el canal alfa. El código aquí simplemente llama al método WithAlpha en el valor predeterminado de la propiedad Color.

Al mover el elemento Slider, se disuelve un mapa de bits en el otro:

Disolución de mapa de bits

En los últimos artículos, ha visto cómo usar SkiaSharp para dibujar texto, círculos, elipses, rectángulos redondeados y mapas de bits. El siguiente paso es Líneas y trazados de SkiaSharp, en el que aprenderá a dibujar líneas conectadas en una ruta de gráficos.