Draw rotated text in SkiaSharp

2019-05-07 12:47发布

问题:

How to draw rotated text in SkiaSharp.

Currently I'm rotating the SKCanvas, drawing the text and then rotating it back. But I thought may be there is a more efficient way to do this.

   canvas.RotateDegrees(45, 20, 20);
   canvas.DrawText("Text", 20, 20, paint);
   canvas.RotateDegrees(-45, 20, 20);

回答1:

That is the correct way (the only way, I think).

It is not that inefficient as you are not really rotating the canvas, but rather adjusting the drawing matrix (an int[9] array). Under the hood it is doing something like this:

var m = SKMatrix.CreateRotation(45); // simple struct over int[9]
canvas.CurrentMatrix.Concat(m)       // simple multiplication

and then when you draw, it it just uses the matrix:

canvas.DrawText(text, matrix);

and when you rotate back, it just does the math again.

Another way is to save the canvas and then restore:

canvas.Save();
canvas.Rotate(45);
canvas.DrawText(text);
canvas.Restore();

This just makes a copy of the current matrix during the save. And when you restore, it just reverts it. This is a "better" way in that you can possible do a series of transformations, without having to reverse.

Or, you can make use of a convenience type:

// auto save
using (new SKAutoCanvasRestore(canvas)) {
    // do any transformations
    canvas.Rotate(45);
    // do serious work
    canvas.DrawText(text);
    // auto restore, even on exceptions or errors
}

Another, totally different way to draw text, is to draw along a path:

var path = new SKPath();
// create path ...

// draw using path
canvas.DrawText("text", path, hOffset: 0, vOffset: 0, paint);


回答2:

You can draw text along a path in SkiaSharp, so (for example) to draw text diagonally across an image:

using (SKPaint skPaint = new SKPaint())
{
  SKPath path = new SKPath();
  path.MoveTo(original.Width / 10, original.Height - original.Height / 10);
  path.LineTo(original.Width - original.Width / 10, original.Height / 10);

  canvas.DrawTextOnPath(textToWrite, path, 0, 0, skPaint);
  path.Dispose();
}

use MeasureText to get the length of the text and use that to fit it in the centre either by working out the length of the diagonal path and setting the startpoint value in DrawTextOnPath parameters or setting the path origin other than 0,0 (as I do in the example)

I'm not sure if it is more efficient than rotating the canvas though, but it seems a more 'obvious' way of doing the drawing.