Compartilhar via


A transformação de distorção

Veja como a transformação de inclinação pode criar objetos gráficos inclinados no SkiaSharp

No SkiaSharp, a transformação de inclinação inclina objetos gráficos, como a sombra nesta imagem:

Um exemplo de inclinação do programa Skew Shadow Text

A inclinação transforma um retângulo em um paralelogramo, mas uma elipse enviesada ainda é uma elipse.

Embora Xamarin.Forms defina propriedades para translação, dimensionamento e rotações, não há nenhuma propriedade correspondente em Xamarin.Forms para inclinação.

O Skew método de SKCanvas aceita dois argumentos para inclinação horizontal e inclinação vertical:

public void Skew (Single xSkew, Single ySkew)

Um segundo Skew método combina esses argumentos em um único SKPoint valor:

public void Skew (SKPoint skew)

No entanto, é improvável que você use qualquer um desses dois métodos isoladamente.

A página Experimento de inclinação permite que você experimente valores de inclinação que variam entre –10 e 10. Uma cadeia de texto é posicionada no canto superior esquerdo da página, com valores de inclinação obtidos de dois Slider elementos. Aqui está o PaintSurface manipulador na SkewExperimentPage classe:

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

Os valores do xSkew argumento deslocam a parte inferior do texto para a direita para valores positivos ou para a esquerda para valores negativos. Valores de ySkew deslocar a direita do texto para baixo para valores positivos ou para cima para valores negativos:

Captura de tela tripla da página Skew Experiment

Se o xSkew valor for o negativo do valor, o resultado é rotação, mas também dimensionado ySkew um pouco.

As fórmulas de transformação são as seguintes:

x' = x + xSkew · y

y' = ySkew · x + y

Por exemplo, para um valor positivo xSkew , o valor transformado x' aumenta à medida y que aumenta. É isso que causa a inclinação.

Se um triângulo de 200 pixels de largura e 100 pixels de altura for posicionado com seu canto superior esquerdo no ponto (0, 0) e for renderizado com um xSkew valor de 1,5, o seguinte paralelogramo resultará:

O efeito da transformada de inclinação em um retângulo

As coordenadas da borda inferior têm y valores de 100, portanto, ela é deslocada 150 pixels para a direita.

Para valores diferentes de zero de xSkew ou ySkew, apenas o ponto (0, 0) permanece o mesmo. Esse ponto pode ser considerado o centro da inclinação. Se você precisa que o centro de inclinação seja outra coisa (o que geralmente é o caso), não há nenhum Skew método que forneça isso. Você precisará combinar Translate explicitamente as chamadas com a Skew chamada. Para centralizar a inclinação em px e py, faça as seguintes chamadas:

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

As fórmulas de transformação composta são:

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

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

Se ySkew for zero, o px valor não será usado. O valor é irrelevante, e da mesma forma para ySkew e py.

Você pode se sentir mais confortável em especificar a inclinação como um ângulo de inclinação, como o ângulo α neste diagrama:

O efeito da transformada de inclinação em um retângulo com um ângulo de inclinação indicado

A proporção do deslocamento de 150 pixels para a vertical de 100 pixels é a tangente desse ângulo, neste exemplo de 56,3 graus.

O arquivo XAML da página Skew Angle Experiment é semelhante à página Skew Angle , exceto que os Slider elementos variam de –90 graus a 90 graus. O SkewAngleExperiment arquivo code-behind centraliza o texto na página e usa Translate para definir um centro de inclinação para o centro da página. Um método curto SkewDegrees na parte inferior do código converte ângulos em valores de inclinação:

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

À medida que um ângulo se aproxima de 90 graus positivos ou negativos, a tangente se aproxima do infinito, mas ângulos de até cerca de 80 graus ou mais são utilizáveis:

Captura de tela tripla da página Skew Angle Experiment

Uma pequena inclinação horizontal negativa pode imitar texto oblíquo ou itálico, como demonstra a página Texto oblíquo. A ObliqueTextPage aula mostra como é feito:

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

A TextAlign propriedade de SKPaint é definida como Center. Sem transformações, a DrawText chamada com coordenadas de (0, 0) posicionaria o texto com o centro horizontal da linha de base no canto superior esquerdo. O SkewDegrees distorce o texto horizontalmente 20 graus em relação à linha de base. A Translate chamada move o centro horizontal da linha de base do texto para o centro da tela:

Captura de tela tripla da página Texto oblíquo

A página Texto de sombra de inclinação demonstra como usar uma combinação de inclinação de 45 graus e escala vertical para criar uma sombra de texto que se inclina para longe do texto. Aqui está a parte pertinente do PaintSurface manipulador:

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

A sombra é exibida primeiro e, em seguida, o texto:

Captura de tela tripla da página Texto de sombra distorcida

A coordenada vertical passada para o DrawText método indica a posição do texto em relação à linha de base. Essa é a mesma coordenada vertical usada para o centro de inclinação. Essa técnica não funcionará se a cadeia de caracteres de texto contiver descendentes. Por exemplo, substitua a palavra "peculiar" por "Sombra" e aqui está o resultado:

Captura de tela tripla da página Texto de sombra de inclinação com uma palavra alternativa com descendentes

A sombra e o texto ainda estão alinhados na linha de base, mas o efeito parece errado. Para corrigi-lo, você precisa obter os limites de texto:

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

As Translate chamadas precisam ser ajustadas pela altura dos descendentes:

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

Agora a sombra se estende do fundo daqueles descendentes:

Captura de tela tripla da página Texto de sombra de inclinação com ajustes para descendentes