Compartir vía


Transformación de sesgo

Ver cómo la transformación de distorsión puede crear objetos gráficos inclinados en SkiaSharp

En SkiaSharp, la transformación de distorsión inclina los objetos gráficos, como la sombra de esta imagen:

Un ejemplo de distorsión del programa Skew Shadow Text

La distorsión convierte un rectángulo en un paralelogramo, pero una elipse distorsionada sigue siendo una elipse.

Aunque Xamarin.Forms define propiedades para la traslación, el escalado y las rotaciones, no hay ninguna propiedad correspondiente en Xamarin.Forms para la distorsión.

El método Skew de SKCanvas acepta dos argumentos para la distorsión horizontal y la distorsión vertical:

public void Skew (Single xSkew, Single ySkew)

Un segundo método Skew combina esos argumentos en un único valor SKPoint:

public void Skew (SKPoint skew)

Sin embargo, es poco probable que vaya a usar cualquiera de estos dos métodos de forma aislada.

La página de experimento de distorsión le permite experimentar con valores de distorsión que oscilan entre -10 y 10. Se coloca una cadena de texto en la esquina superior izquierda de la página, con valores de distorsión obtenidos de dos elementos Slider. Este es el controlador PaintSurface de la clase SkewExperimentPage:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);

        canvas.Skew((float)xSkewSlider.Value, (float)ySkewSlider.Value);
        canvas.DrawText(text, 0, -textBounds.Top, textPaint);
    }
}

Los valores del argumento xSkew desplazan la parte inferior del texto a la derecha para los valores positivos o a la izquierda para los valores negativos. Los valores de ySkew desplazan la derecha del texto hacia abajo para los valores positivos o hacia arriba para los valores negativos:

Captura de pantalla triple de la página Skew Experiment

Si el valor de xSkew es el negativo del valor de ySkew, el resultado es la rotación, pero también se escala un poco.

Las fórmulas de transformación son las siguientes:

x' = x + xSkew · y

y' = ySkew · x + y

Por ejemplo, para un valor positivo de xSkew, el valor transformado de x' aumenta a medida que aumenta y. Eso es lo que causa la inclinación.

Si se coloca un triángulo de 200 píxeles de ancho y 100 píxeles de alto con su esquina superior izquierda en el punto (0, 0) y se representa con un valor de 1,5 para xSkew, se obtiene el siguiente paralelograma:

Efecto de la transformación de distorsión en un rectángulo

Las coordenadas del borde inferior tienen valores de 100 para y, por lo que se desplaza 150 píxeles a la derecha.

Para valores distintos de cero de xSkew o ySkew, solo el punto (0, 0) sigue siendo el mismo. Ese punto se puede considerar el centro de distorsión. Si necesita que el centro de distorsión sea otro (que suele ser el caso), no hay ningún método Skew que proporcione eso. Deberá combinar explícitamente las llamadas a Translate con la llamada a Skew. Para centrar la distorsión en px y py, realice las siguientes llamadas:

canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);

Las fórmulas de transformación compuesta son:

x' = x + xSkew · (y – py)

y' = ySkew · (x – px) + y

Si ySkew es cero, no se usa el valor de px. El valor es irrelevante y, de forma similar, para ySkew y py.

Es posible que le parezca más cómodo especificar la distorsión como un ángulo de inclinación, como el ángulo α en este diagrama:

Efecto de la transformación de distorsión en un rectángulo con un ángulo de inclinación indicado

La relación entre el desplazamiento de 150 píxeles y la vertical de 100 píxeles es la tangente de ese ángulo, que en este ejemplo es de 56,3 grados.

El archivo XAML de la página del experimento de ángulo de distorsión es similar a la página del ángulo de distorsión, excepto que los elementos Slider oscilan entre -90 grados y 90 grados. El archivo de código subyacente SkewAngleExperiment centra el texto en la página y usa Translate para establecer un centro de distorsión en el centro de la página. Un método corto SkewDegrees en la parte inferior del código convierte los ángulos en valores de distorsión:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        float xCenter = info.Width / 2;
        float yCenter = info.Height / 2;

        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);
        float xText = xCenter - textBounds.MidX;
        float yText = yCenter - textBounds.MidY;

        canvas.Translate(xCenter, yCenter);
        SkewDegrees(canvas, xSkewSlider.Value, ySkewSlider.Value);
        canvas.Translate(-xCenter, -yCenter);
        canvas.DrawText(text, xText, yText, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

A medida que un ángulo se aproxima a 90 grados positivos o negativos, la tangente se aproxima a infinito, pero se pueden usar ángulos de hasta 80 grados aproximadamente:

Captura de pantalla triple de la página Skew Angle Experiment

Una pequeña distorsión horizontal negativa puede imitar texto oblicuo o cursiva, como se muestra en la página de texto oblicuo. En la clase ObliqueTextPage, se muestra cómo se hace:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint()
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Maroon,
        TextAlign = SKTextAlign.Center,
        TextSize = info.Width / 8       // empirically determined
    })
    {
        canvas.Translate(info.Width / 2, info.Height / 2);
        SkewDegrees(canvas, -20, 0);
        canvas.DrawText(Title, 0, 0, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

La propiedad TextAlign de SKPaint se ha establecido en Center. Sin ninguna transformación, la llamada a DrawText con las coordenadas (0, 0) colocaría el texto con el centro horizontal de la línea base en la esquina superior izquierda. El elemento SkewDegrees distorsiona el texto 20 grados horizontalmente en relación con la línea base. La llamada a Translate mueve el centro horizontal de la línea base del texto al centro del lienzo:

Captura de pantalla triple de la página Texto oblicuo

La página de texto con sombra distorsionada muestra cómo usar una combinación de una distorsión de 45 grados y una escala vertical para hacer una sombra de texto que se inclina hacia fuera del texto. Esta es la parte correspondiente del controlador PaintSurface:

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = info.Width / 6;   // empirically determined

    // Common to shadow and text
    string text = "Shadow";
    float xText = 20;
    float yText = info.Height / 2;

    // Shadow
    textPaint.Color = SKColors.LightGray;
    canvas.Save();
    canvas.Translate(xText, yText);
    canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
    canvas.Scale(1, 3);
    canvas.Translate(-xText, -yText);
    canvas.DrawText(text, xText, yText, textPaint);
    canvas.Restore();

    // Text
    textPaint.Color = SKColors.Blue;
    canvas.DrawText(text, xText, yText, textPaint);
}

Se muestra primero la sombra y, a continuación, el texto:

Captura de pantalla triple de la página Skew Shadow Text

La coordenada vertical que se pasa al método DrawText indica la posición del texto en relación con la línea base. Esa es la misma coordenada vertical que se usa para el centro de distorsión. Esta técnica no funcionará si la cadena de texto contiene partes descendientes. Por ejemplo, sustituya la palabra "Shadow" por "quirky" y este es el resultado:

Captura de pantalla triple de la página Skew Shadow Text con una palabra alternativa con descendientes

La sombra y el texto están alineados en la línea base, pero el efecto tiene un aspecto incorrecto. Para corregirlo, debe obtener los límites de texto:

SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);

Las llamadas a Translate se deben ajustar con la altura de las partes descendientes:

canvas.Translate(xText, yText + textBounds.Bottom);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText - textBounds.Bottom);

Ahora, la sombra se extiende desde la parte inferior de esas partes descendientes:

Captura de pantalla triple de la página Skew Shadow Text con ajustes para descendientes