Udostępnij za pośrednictwem


Piksele i jednostki niezależne od urządzenia

Poznaj różnice między współrzędnymi SkiaSharp i Xamarin.Forms współrzędnymi

W tym artykule przedstawiono różnice w układzie współrzędnych używanym w systemach SkiaSharp i Xamarin.Forms. Możesz uzyskać informacje umożliwiające konwersję między dwoma układami współrzędnych, a także narysować grafiki, które wypełniają określony obszar:

Owalny, który wypełnia ekran

Jeśli programujesz od Xamarin.Forms jakiegoś czasu, możesz mieć poczucie Xamarin.Forms współrzędnych i rozmiarów. Okręgi rysowane w dwóch poprzednich artykułach mogą wydawać się nieco małe dla Ciebie.

Te okręgi małe w porównaniu z rozmiarami Xamarin.Forms . Domyślnie skiaSharp rysuje jednostki pikseli, podczas gdy Xamarin.Forms podstawy koordynują i rozmiary w jednostce niezależnej od urządzenia ustanowionej przez podstawową platformę. (Więcej informacji na Xamarin.Forms temat układu współrzędnych można znaleźć w rozdziale 5. Praca z rozmiarami książki Creating Mobile Apps with Xamarin.Forms.)

Strona w programie przykładowym zatytułowanym Surface Size używa danych wyjściowych tekstu SkiaSharp w celu wyświetlenia rozmiaru powierzchni ekranu z trzech różnych źródeł:

  • Normalne Xamarin.FormsWidth i Height właściwości SKCanvasView obiektu.
  • Właściwość CanvasSize SKCanvasView obiektu.
  • Właściwość Size SKImageInfo wartości, która jest zgodna z właściwościami Width i Height używanymi na dwóch poprzednich stronach.

Klasa SurfaceSizePage pokazuje, jak wyświetlić te wartości. Konstruktor zapisuje SKCanvasView obiekt jako pole, aby można było uzyskać do niego dostęp w programie obsługi zdarzeń PaintSurface :

SKCanvasView canvasView;

public SurfaceSizePage()
{
    Title = "Surface Size";

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

SKCanvas zawiera sześć różnych DrawText metod, ale ta DrawText metoda jest najprostsza:

public void DrawText (String text, Single x, Single y, SKPaint paint)

Należy określić ciąg tekstowy, współrzędne X i Y, w których tekst ma się rozpocząć, oraz SKPaint obiekt. Współrzędna X określa, gdzie jest umieszczona lewa strona tekstu, ale zwróć uwagę: Współrzędna Y określa położenie punktu odniesienia tekstu. Jeśli kiedykolwiek napisałeś ręcznie na papierze liniowym, linia bazowa to linia, w której znajdują się znaki, i poniżej, które maleją (takie jak litery g, p, q i y) maleją.

Obiekt SKPaint umożliwia określenie koloru tekstu, rodziny czcionek i rozmiaru tekstu. Domyślnie TextSize właściwość ma wartość 12, co powoduje niewielki tekst na urządzeniach o wysokiej rozdzielczości, takich jak telefony. W dowolnych aplikacjach, ale najprostszych, będziesz również potrzebować pewnych informacji na temat rozmiaru wyświetlanego tekstu. Klasa SKPaint definiuje FontMetrics właściwość i kilka MeasureText metod, ale w przypadku mniej fantazyjnego zapotrzebowania FontSpacing właściwość zapewnia zalecaną wartość dla odstępów między kolejnymi wierszami tekstu.

Poniższa PaintSurface procedura obsługi tworzy SKPaint obiekt dla TextSize 40 pikseli, który jest żądaną pionową wysokością tekstu od góry rosnąco do dołu malejących. Wartość FontSpacing zwracana przez SKPaint obiekt jest nieco większa niż, około 47 pikseli.

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

    canvas.Clear();

    SKPaint paint = new SKPaint
    {
        Color = SKColors.Black,
        TextSize = 40
    };

    float fontSpacing = paint.FontSpacing;
    float x = 20;               // left margin
    float y = fontSpacing;      // first baseline
    float indent = 100;

    canvas.DrawText("SKCanvasView Height and Width:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(String.Format("{0:F2} x {1:F2}",
                                  canvasView.Width, canvasView.Height),
                    x + indent, y, paint);
    y += fontSpacing * 2;
    canvas.DrawText("SKCanvasView CanvasSize:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(canvasView.CanvasSize.ToString(), x + indent, y, paint);
    y += fontSpacing * 2;
    canvas.DrawText("SKImageInfo Size:", x, y, paint);
    y += fontSpacing;
    canvas.DrawText(info.Size.ToString(), x + indent, y, paint);
}

Metoda rozpoczyna pierwszy wiersz tekstu ze współrzędną X 20 (dla małego marginesu po lewej stronie) i współrzędną fontSpacingY , która jest nieco większa niż to, co jest konieczne do wyświetlenia pełnej wysokości pierwszego wiersza tekstu w górnej części powierzchni wyświetlania. Po każdym wywołaniu metody DrawTextwspółrzędna Y jest zwiększana o jeden lub dwa przyrosty fontSpacing.

Oto uruchomiony program:

Zrzuty ekranu przedstawiają aplikację Surface Size uruchomioną na dwóch urządzeniach przenośnych.

Jak widać, CanvasSize właściwość SKCanvasView i Size właściwość SKImageInfo wartości są spójne w raportowaniu wymiarów pikseli. Właściwości Height SKCanvasView i Width to właściwości Xamarin.Forms oraz raportują rozmiar widoku w jednostkach niezależnych od urządzenia zdefiniowanych przez platformę.

Symulator systemu iOS po lewej stronie ma dwa piksele na jednostkę niezależną od urządzenia, a android Nexus 5 w środku ma trzy piksele na jednostkę. Dlatego pokazany wcześniej prosty okrąg ma różne rozmiary na różnych platformach.

Jeśli wolisz pracować całkowicie w jednostkach niezależnych od urządzenia, możesz to zrobić, ustawiając IgnorePixelScaling właściwość na SKCanvasView truewartość . Jednak wyniki mogą się nie podobać. SkiaSharp renderuje grafikę na mniejszej powierzchni urządzenia z rozmiarem pikseli równym rozmiarowi widoku w jednostkach niezależnych od urządzenia. (Na przykład SkiaSharp będzie używać powierzchni ekranu 360 x 512 pikseli na Nexus 5. Następnie skaluje w górę ten obraz o rozmiarze, co powoduje zauważalne jaggies mapy bitowej.

Aby zachować tę samą rozdzielczość obrazu, lepszym rozwiązaniem jest napisanie własnych prostych funkcji do konwersji między dwoma układami współrzędnych.

Oprócz DrawCircle metody definiuje również dwie DrawOval metody, SKCanvas które rysują wielokropek. Wielokropek jest definiowany przez dwa promienie, a nie jeden promień. Są one znane jako główny promień i mniejszy promień. Metoda DrawOval rysuje wielokropek z dwoma promieniami równoległymi do osi X i Y. (Jeśli musisz narysować wielokropek z osiami, które nie są równoległe do osi X i Y, możesz użyć przekształcenia obrotu zgodnie z opisem w artykule Obracanie transformacji lub ścieżki grafiki zgodnie z opisem w artykule Trzy sposoby rysowania łuku). To przeciążenie DrawOval metody nazywa dwa parametry rx promieniowe i ry wskazuje, że są one równoległe do osi X i Y:

public void DrawOval (Single cx, Single cy, Single rx, Single ry, SKPaint paint)

Czy można narysować wielokropek, który wypełnia powierzchnię wyświetlacza? Na stronie Wypełnienie wielokropka pokazano, jak to zrobić. Procedura PaintSurface obsługi zdarzeń w klasie EllipseFillPage.xaml.cs odejmuje połowę szerokości pociągnięcia od xRadius wartości i yRadius w celu dopasowania całej wielokropka i jej konturu na powierzchni ekranu:

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

    canvas.Clear();

    float strokeWidth = 50;
    float xRadius = (info.Width - strokeWidth) / 2;
    float yRadius = (info.Height - strokeWidth) / 2;

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Blue,
        StrokeWidth = strokeWidth
    };
    canvas.DrawOval(info.Width / 2, info.Height / 2, xRadius, yRadius, paint);
}

W tym miejscu działa:

Zrzuty ekranu pokazują aplikację Ellipse Fill działającą na dwóch urządzeniach przenośnych.

Druga DrawOval metoda ma SKRect argument, który jest prostokątem zdefiniowanym pod względem współrzędnych X i Y w lewym górnym rogu i prawym dolnym rogu. Owalu wypełnia ten prostokąt, co sugeruje, że może być możliwe użycie go na stronie Wypełnienie wielokropka w następujący sposób:

SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawOval(rect, paint);

Jednak to obcina wszystkie krawędzie konturu wielokropka po czterech stronach. Aby wykonać tę pracę, musisz dostosować wszystkie SKRect argumenty konstruktora na strokeWidth podstawie elementu :

SKRect rect = new SKRect(strokeWidth / 2,
                         strokeWidth / 2,
                         info.Width - strokeWidth / 2,
                         info.Height - strokeWidth / 2);
canvas.DrawOval(rect, paint);