Tworzenie i rysowanie na mapach bitowych SkiaSharp
Wiesz już, jak aplikacja może ładować mapy bitowe z internetu, z zasobów aplikacji i z biblioteki zdjęć użytkownika. Istnieje również możliwość utworzenia nowych map bitowych w aplikacji. Najprostsze podejście obejmuje jeden z konstruktorów klasy SKBitmap
:
SKBitmap bitmap = new SKBitmap(width, height);
Parametry width
i height
są liczbami całkowitymi i określają wymiary pikseli mapy bitowej. Ten konstruktor tworzy pełną mapę bitową z czterema bajtami na piksel: jeden bajt dla składników czerwonego, zielonego, niebieskiego i alfa (nieprzezroczystości).
Po utworzeniu nowej mapy bitowej musisz uzyskać coś na powierzchni mapy bitowej. Zazwyczaj można to zrobić na jeden z dwóch sposobów:
- Rysuj na mapie bitowej przy użyciu standardowych
Canvas
metod rysowania. - Uzyskaj bezpośredni dostęp do bitów pikseli.
W tym artykule przedstawiono pierwsze podejście:
Drugie podejście zostało omówione w artykule Accessing SkiaSharp Bitmap Pixel (Uzyskiwanie dostępu do pikseli mapy bitowej SkiaSharp).
Rysowanie na mapie bitowej
Rysunek na powierzchni mapy bitowej jest taki sam jak rysunek na ekranie wideo. Aby rysować na ekranie wideo, uzyskujesz SKCanvas
obiekt z PaintSurface
argumentów zdarzenia. Aby rysować na mapie bitowej, należy utworzyć SKCanvas
obiekt przy użyciu konstruktora SKCanvas
:
SKCanvas canvas = new SKCanvas(bitmap);
Po zakończeniu rysowania na mapie bitowej można usunąć SKCanvas
obiekt. Z tego powodu SKCanvas
konstruktor jest zwykle wywoływany w instrukcji using
:
using (SKCanvas canvas = new SKCanvas(bitmap))
{
··· // call drawing function
}
Następnie można wyświetlić mapę bitową. W późniejszym czasie program może utworzyć nowy SKCanvas
obiekt na podstawie tej samej mapy bitowej i rysować na nim trochę więcej.
Strona Hello Bitmap w przykładowej aplikacji zapisuje tekst "Hello, Bitmap!" na mapie bitowej, a następnie wyświetla ten mapa bitowa wiele razy.
Konstruktor obiektu HelloBitmapPage
rozpoczyna się od utworzenia SKPaint
obiektu do wyświetlania tekstu. Określa wymiary ciągu tekstowego i tworzy mapę bitową z tymi wymiarami. Następnie tworzy obiekt oparty na tej mapie bitowej, wywołuje metodę SKCanvas
Clear
, a następnie wywołuje metodę DrawText
. Zawsze dobrym pomysłem jest wywołanie Clear
nowej mapy bitowej, ponieważ nowo utworzona mapa bitowa może zawierać dane losowe.
Konstruktor kończy, tworząc SKCanvasView
obiekt do wyświetlania mapy bitowej:
public partial class HelloBitmapPage : ContentPage
{
const string TEXT = "Hello, Bitmap!";
SKBitmap helloBitmap;
public HelloBitmapPage()
{
Title = TEXT;
// Create bitmap and draw on it
using (SKPaint textPaint = new SKPaint { TextSize = 48 })
{
SKRect bounds = new SKRect();
textPaint.MeasureText(TEXT, ref bounds);
helloBitmap = new SKBitmap((int)bounds.Right,
(int)bounds.Height);
using (SKCanvas bitmapCanvas = new SKCanvas(helloBitmap))
{
bitmapCanvas.Clear();
bitmapCanvas.DrawText(TEXT, 0, -bounds.Top, textPaint);
}
}
// Create SKCanvasView to view result
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Aqua);
for (float y = 0; y < info.Height; y += helloBitmap.Height)
for (float x = 0; x < info.Width; x += helloBitmap.Width)
{
canvas.DrawBitmap(helloBitmap, x, y);
}
}
}
Program PaintSurface
obsługi renderuje mapę bitową wiele razy w wierszach i kolumnach wyświetlacza. Zwróć uwagę, że Clear
metoda w procedurze PaintSurface
obsługi ma argument SKColors.Aqua
, który koloruje tło powierzchni wyświetlania:
Wygląd tła wodnego ujawnia, że mapa bitowa jest przezroczysta z wyjątkiem tekstu.
Czyszczenie i przejrzystość
Na ekranie strony Witaj mapa bitowa pokazuje, że mapa bitowa utworzona przez program jest przezroczysta z wyjątkiem czarnego tekstu. Dlatego kolor wodny powierzchni wyświetlacza pokazuje przez.
Dokumentacja Clear
metod opisanych SKCanvas
w instrukcji: "Zastępuje wszystkie piksele w bieżącym klipie kanwy". Użycie słowa "replaces" ujawnia ważną cechę tych metod: Wszystkie metody SKCanvas
rysowania dodają coś do istniejącej powierzchni wyświetlania. Metody Clear
zastępują już to, co już istnieje.
Clear
istnieje w dwóch różnych wersjach:
Metoda
Clear
z parametremSKColor
zastępuje piksele powierzchni wyświetlania pikselami tego koloru.Metoda
Clear
bez parametrów zastępuje piksele koloremSKColors.Empty
, który jest kolorem, w którym wszystkie składniki (czerwony, zielony, niebieski i alfa) są ustawione na zero. Ten kolor jest czasami określany jako "przezroczysty czarny".
Wywołanie Clear
bez argumentów na nowej mapie bitowej inicjuje całą mapę bitową, aby było całkowicie przezroczyste. Wszystko następnie rysowane na mapie bitowej będzie zwykle nieprzezroczyste lub częściowo nieprzezroczyste.
Oto coś do wypróbowania: na stronie Witaj mapa bitowa zastąp Clear
metodę zastosowaną bitmapCanvas
do tej metody:
bitmapCanvas.Clear(new SKColor(255, 0, 0, 128));
Kolejność parametrów konstruktora SKColor
to czerwony, zielony, niebieski i alfa, gdzie każda wartość może wahać się od 0 do 255. Należy pamiętać, że wartość alfa 0 jest przezroczysta, a wartość alfa 255 jest nieprzezroczysta.
Wartość (255, 0, 0, 128) czyści piksele mapy bitowej na czerwone piksele z nieprzezroczystością 50%. Oznacza to, że tło mapy bitowej jest półprzezroczyste. Półprzezroczyste czerwone tło mapy bitowej łączy się z wodnym tłem powierzchni wyświetlacza, aby utworzyć szare tło.
Spróbuj ustawić kolor tekstu na przezroczysty czarny, umieszczając następujące przypisanie w inicjatorze SKPaint
:
Color = new SKColor(0, 0, 0, 0)
Można pomyśleć, że ten przezroczysty tekst stworzy w pełni przezroczyste obszary mapy bitowej, za pomocą której zobaczysz tło wodne powierzchni ekranu. Ale tak nie jest. Tekst jest rysowany na podstawie tego, co już znajduje się na mapie bitowej. Przezroczysty tekst nie będzie w ogóle widoczny.
Żadna metoda nigdy nie Draw
sprawia, że mapa bitowa jest bardziej przezroczysta. Tylko Clear
to może zrobić.
Typy kolorów mapy bitowej
Najprostszy SKBitmap
konstruktor umożliwia określenie szerokości i wysokości pikseli całkowitej dla mapy bitowej. Inne SKBitmap
konstruktory są bardziej złożone. Te konstruktory wymagają argumentów dwóch typów wyliczenia: SKColorType
i SKAlphaType
. Inne konstruktory używają SKImageInfo
struktury, która konsoliduje te informacje.
Wyliczenie SKColorType
ma 9 członków. Każdy z tych elementów członkowskich opisuje konkretny sposób przechowywania pikseli mapy bitowej:
Unknown
Alpha8
— każdy piksel ma 8 bitów, reprezentując wartość alfa z pełnej przezroczystości do pełnej nieprzezroczystościRgb565
— każdy piksel ma 16 bitów, 5 bitów dla czerwonego i niebieskiego i 6 dla zielonegoArgb4444
— każdy piksel ma 16 bitów, 4 każdy dla alfa, czerwony, zielony i niebieskiRgba8888
— każdy piksel ma 32 bity, 8 każdy dla czerwonego, zielonego, niebieskiego i alfaBgra8888
— każdy piksel ma 32 bity, 8 bitów dla niebieskiego, zielonego, czerwonego i alfaIndex8
— każdy piksel ma 8 bitów i reprezentuje indeks w obiekcieSKColorTable
Gray8
— każdy piksel to 8 bitów reprezentujący szary odcień od czarnego do białegoRgbaF16
— każdy piksel ma 64 bity, z czerwonym, zielonym, niebieskim i alfa w formacie zmiennoprzecinkowym 16-bitowym
Dwa formaty, w których każdy piksel to 32 piksele (4 bajty) są często nazywane formatami pełnokolorowymi . Wiele innych formatów datuje się od godziny, gdy same filmy wideo nie były w stanie uzyskać pełnego koloru. Mapy bitowe o ograniczonym kolorze były odpowiednie dla tych wyświetlaczy i pozwoliły mapom bitowym zajmować mniej miejsca w pamięci.
W dzisiejszych czasach programiści prawie zawsze używają pełnokolorowych map bitowych i nie przeszkadzają w innych formatach. Wyjątkiem jest RgbaF16
format, który umożliwia większą rozdzielczość kolorów niż nawet formaty pełnokolorowe. Jednak ten format jest używany do celów wyspecjalizowanych, takich jak obrazowanie medyczne, i nie ma większego sensu, gdy jest używany z standardowymi wyświetlaczami pełnokolorowymi.
Ta seria artykułów ogranicza się do SKBitmap
formatów kolorów używanych domyślnie, gdy nie określono żadnego SKColorType
elementu członkowskiego. Ten format domyślny jest oparty na podstawowej platformie. W przypadku platform obsługiwanych przez Xamarin.Formsprogram domyślnym typem koloru jest:
Rgba8888
dla systemów iOS i AndroidBgra8888
dla platformy UWP
Jedyną różnicą jest kolejność 4 bajtów w pamięci i staje się to problemem tylko wtedy, gdy uzyskujesz bezpośredni dostęp do bitów pikseli. Nie stanie się to ważne, dopóki nie zostanie wyświetlony artykuł Uzyskiwanie dostępu do pikseli mapy bitowej SkiaSharp.
Wyliczenie SKAlphaType
ma cztery elementy członkowskie:
Unknown
Opaque
— mapa bitowa nie ma przezroczystościPremul
— składniki kolorów są wstępnie mnożone przez składnik alfaUnpremul
— składniki kolorów nie są wstępnie mnożone przez składnik alfa
Oto 4-bajtowy czerwony piksel mapy bitowej z przezroczystością 50% z bajtami wyświetlanymi w kolejności czerwonej, zielonej, niebieskiej, alfa:
0xFF 0x00 0x00 0x80
Gdy mapa bitowa zawierająca półprzezroczyste piksele jest renderowana na powierzchni ekranu, składniki kolorów każdego piksela mapy bitowej muszą być mnożone przez wartość alfa tego piksela, a składniki kolorów odpowiadającego piksela powierzchni wyświetlania muszą być mnożone przez 255 minus wartość alfa. Następnie można połączyć dwa piksele. Mapa bitowa może być renderowana szybciej, jeśli składniki kolorów w pikselach mapy bitowej zostały już wstępnie wyciszone przez wartość alfa. Ten sam czerwony piksel będzie przechowywany w formacie wstępnie pomnożonym:
0x80 0x00 0x00 0x80
Ta poprawa wydajności polega na tym, że SkiaSharp
mapy bitowe są domyślnie tworzone w Premul
formacie. Ale znowu staje się konieczne, aby wiedzieć to tylko wtedy, gdy uzyskujesz dostęp do bitów pikseli i manipulujesz nimi.
Rysowanie na istniejących mapach bitowych
Nie trzeba tworzyć nowej mapy bitowej, aby ją rysować. Możesz również rysować na istniejącej mapie bitowej.
Strona Monkey Moustache używa swojego konstruktora do załadowania obrazu MonkeyFace.png . Następnie tworzy SKCanvas
obiekt oparty na tej mapie bitowej i używa SKPaint
obiektów i SKPath
do narysowania na nim wąsu:
public partial class MonkeyMoustachePage : ContentPage
{
SKBitmap monkeyBitmap;
public MonkeyMoustachePage()
{
Title = "Monkey Moustache";
monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
"SkiaSharpFormsDemos.Media.MonkeyFace.png");
// Create canvas based on bitmap
using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
{
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Black;
paint.StrokeWidth = 24;
paint.StrokeCap = SKStrokeCap.Round;
using (SKPath path = new SKPath())
{
path.MoveTo(380, 390);
path.CubicTo(560, 390, 560, 280, 500, 280);
path.MoveTo(320, 390);
path.CubicTo(140, 390, 140, 280, 200, 280);
canvas.DrawPath(path, paint);
}
}
}
// Create SKCanvasView to view result
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
}
}
Konstruktor kończy się utworzeniem SKCanvasView
programu obsługi, którego PaintSurface
program obsługi po prostu wyświetla wynik:
Kopiowanie i modyfikowanie map bitowych
Metody SKCanvas
, których można użyć do rysowania na mapie bitowej, obejmują DrawBitmap
. Oznacza to, że można narysować jedną mapę bitową na innej, zwykle modyfikując ją w jakiś sposób.
Najbardziej wszechstronnym sposobem modyfikowania mapy bitowej jest uzyskiwanie dostępu do rzeczywistych bitów pikseli, temat omówiony w artykule Uzyskiwanie dostępu do pikseli mapy bitowej SkiaSharp. Istnieje jednak wiele innych technik modyfikowania map bitowych, które nie wymagają dostępu do bitów pikseli.
Poniższa mapa bitowa dołączona do przykładowej aplikacji ma szerokość 360 pikseli i 480 pikseli wysokości:
Załóżmy, że nie otrzymałeś zgody małpy po lewej stronie, aby opublikować to zdjęcie. Jednym z rozwiązań jest ukrycie twarzy małpy przy użyciu techniki nazywanej pixelizacją. Piksele twarzy są zastępowane blokami koloru, dzięki czemu nie można wymyślić funkcji. Bloki koloru są zwykle uzyskiwane z oryginalnego obrazu przez uśrednianie kolorów pikseli odpowiadających tym blokom. Ale nie musisz wykonywać tej średniej samodzielnie. Dzieje się to automatycznie po skopiowaniu mapy bitowej do mniejszego wymiaru pikseli.
Lewa twarz małpy zajmuje około 72-pikselowy obszar kwadratowy z lewym górnym rogu w punkcie (112, 238). Zastąpmy ten 72-pikselowy obszar kwadratowy tablicą 9-do-9 kolorowych bloków, z których każdy ma kwadrat 8 do 8 pikseli.
Strona Pixelize Image ładuje się w tej mapie bitowej i najpierw tworzy małą 9-pikselową kwadratową mapę bitową o nazwie faceBitmap
. Jest to miejsce docelowe kopiowania tylko twarzy małpy. Prostokąt docelowy ma tylko 9 pikseli kwadratu, ale prostokąt źródłowy ma kwadrat o powierzchni 72 pikseli. Każdy blok 8-do-8 pikseli źródłowych jest konsolidowany do zaledwie jednego piksela przez uśrednianie kolorów.
Następnym krokiem jest skopiowanie oryginalnej mapy bitowej do nowej mapy bitowej o tym samym rozmiarze o pixelizedBitmap
nazwie . Malutka faceBitmap
jest następnie kopiowana na wierzchu z prostokątem docelowym o rozmiarze 72 pikseli, dzięki czemu każdy piksel faceBitmap
jest rozszerzany do 8 razy większy:
public class PixelizedImagePage : ContentPage
{
SKBitmap pixelizedBitmap;
public PixelizedImagePage ()
{
Title = "Pixelize Image";
SKBitmap originalBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
"SkiaSharpFormsDemos.Media.MountainClimbers.jpg");
// Create tiny bitmap for pixelized face
SKBitmap faceBitmap = new SKBitmap(9, 9);
// Copy subset of original bitmap to that
using (SKCanvas canvas = new SKCanvas(faceBitmap))
{
canvas.Clear();
canvas.DrawBitmap(originalBitmap,
new SKRect(112, 238, 184, 310), // source
new SKRect(0, 0, 9, 9)); // destination
}
// Create full-sized bitmap for copy
pixelizedBitmap = new SKBitmap(originalBitmap.Width, originalBitmap.Height);
using (SKCanvas canvas = new SKCanvas(pixelizedBitmap))
{
canvas.Clear();
// Draw original in full size
canvas.DrawBitmap(originalBitmap, new SKPoint());
// Draw tiny bitmap to cover face
canvas.DrawBitmap(faceBitmap,
new SKRect(112, 238, 184, 310)); // destination
}
// Create SKCanvasView to view result
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(pixelizedBitmap, info.Rect, BitmapStretch.Uniform);
}
}
Konstruktor kończy się utworzeniem obiektu SKCanvasView
, aby wyświetlić wynik:
Obracanie map bitowych
Innym typowym zadaniem jest obracanie map bitowych. Jest to szczególnie przydatne podczas pobierania map bitowych z biblioteki zdjęć i Telefon lub iPad. Jeśli urządzenie nie zostało zatrzymane w określonej orientacji, gdy zdjęcie zostało zrobione, obraz prawdopodobnie będzie do góry nogami lub w bok.
Obracanie mapy bitowej do góry nogami wymaga utworzenia innej mapy bitowej o taki sam rozmiar jak pierwszy, a następnie ustawienie przekształcenia w celu obracania o 180 stopni podczas kopiowania pierwszego do drugiego. We wszystkich przykładach w tej sekcji znajduje SKBitmap
się obiekt, bitmap
który należy obrócić:
SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear();
canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
canvas.DrawBitmap(bitmap, new SKPoint());
}
Podczas obracania o 90 stopni należy utworzyć mapę bitową o innym rozmiarze niż oryginalny, zamieniając wysokość i szerokość. Jeśli na przykład oryginalna mapa bitowa ma szerokość 1200 pikseli i 800 pikseli, obrócona mapa bitowa ma szerokość 800 pikseli i szerokość 1200 pikseli. Ustaw tłumaczenie i rotację, aby mapa bitowa została obrócona wokół lewego górnego rogu, a następnie przesunięta w widok. (Pamiętaj, że Translate
metody i RotateDegrees
są wywoływane w odwrotnej kolejności sposobu ich stosowania). Oto kod obracania 90 stopni zgodnie z ruchem wskazówek zegara:
SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear();
canvas.Translate(bitmap.Height, 0);
canvas.RotateDegrees(90);
canvas.DrawBitmap(bitmap, new SKPoint());
}
A oto podobna funkcja obracania 90 stopni w kierunku wskazówek zegara:
SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear();
canvas.Translate(0, bitmap.Width);
canvas.RotateDegrees(-90);
canvas.DrawBitmap(bitmap, new SKPoint());
}
Te dwie metody są używane na stronach Photo Puzzle opisanych w artykule Cropping SkiaSharp Bitmaps.
Program, który umożliwia użytkownikowi obracanie mapy bitowej w 90-stopniowych przyrostach, wymaga zaimplementowania tylko jednej funkcji do rotacji o 90 stopni. Użytkownik może następnie obracać się w dowolnej inkrementacji 90 stopni, powtarzając wykonanie tej funkcji.
Program może również obracać mapę bitową o dowolną ilość. Jedną z prostych metod jest zmodyfikowanie funkcji, która obraca się o 180 stopni, zastępując wartość 180 zmienną uogólnioną angle
:
SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear();
canvas.RotateDegrees(angle, bitmap.Width / 2, bitmap.Height / 2);
canvas.DrawBitmap(bitmap, new SKPoint());
}
Jednak w ogólnym przypadku ta logika przycina narożniki obróconej mapy bitowej. Lepszym rozwiązaniem jest obliczenie rozmiaru obróconej mapy bitowej przy użyciu trygonometrii w celu uwzględnienia tych narożników.
Ta trygonometria jest wyświetlana na stronie Rottor mapy bitowej. Plik XAML tworzy wystąpienie elementu SKCanvasView
i , Slider
który może wahać się od 0 do 360 stopni z wyświetloną bieżącą wartością Label
:
<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.Bitmaps.BitmapRotatorPage"
Title="Bitmap Rotator">
<StackLayout>
<skia:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Slider x:Name="slider"
Maximum="360"
Margin="10, 0"
ValueChanged="OnSliderValueChanged" />
<Label Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='Rotate by {0:F0}°'}"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentPage>
Plik z kodem ładuje zasób mapy bitowej i zapisuje go jako statyczne pole tylko do odczytu o nazwie originalBitmap
. Mapa bitowa wyświetlana w procedurze PaintSurface
obsługi to rotatedBitmap
, która jest początkowo ustawiona na wartość originalBitmap
:
public partial class BitmapRotatorPage : ContentPage
{
static readonly SKBitmap originalBitmap =
BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
"SkiaSharpFormsDemos.Media.Banana.jpg");
SKBitmap rotatedBitmap = originalBitmap;
public BitmapRotatorPage ()
{
InitializeComponent ();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(rotatedBitmap, info.Rect, BitmapStretch.Uniform);
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
double angle = args.NewValue;
double radians = Math.PI * angle / 180;
float sine = (float)Math.Abs(Math.Sin(radians));
float cosine = (float)Math.Abs(Math.Cos(radians));
int originalWidth = originalBitmap.Width;
int originalHeight = originalBitmap.Height;
int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);
rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);
using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
canvas.Clear(SKColors.LightPink);
canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
canvas.RotateDegrees((float)angle);
canvas.Translate(-originalWidth / 2, -originalHeight / 2);
canvas.DrawBitmap(originalBitmap, new SKPoint());
}
canvasView.InvalidateSurface();
}
}
Procedura ValueChanged
obsługi Slider
wykonuje operacje, które tworzą nowe rotatedBitmap
na podstawie kąta obrotu. Nowa szerokość i wysokość są oparte na wartościach bezwzględnych sinusów i cosinusów oryginalnych szerokości i wysokości. Przekształcenia używane do rysowania oryginalnej mapy bitowej na obróconej mapie bitowej przenoszą oryginalny środek mapy bitowej do źródła, a następnie obracają ją według określonej liczby stopni, a następnie przetłumaczą ten środek na środek obróconej mapy bitowej. (Metody Translate
i RotateDegrees
są wywoływane w odwrotnej kolejności niż sposób ich stosowania).
Zwróć uwagę na użycie Clear
metody , aby tło rotatedBitmap
jasnoróżowe. Ma to na celu wyłącznie zilustrowanie rozmiaru rotatedBitmap
na wyświetlaczu:
Obrócona mapa bitowa jest wystarczająco duża, aby uwzględnić całą oryginalną mapę bitową, ale nie większą.
Przerzucanie map bitowych
Inna operacja często wykonywana na mapach bitowych jest nazywana przerzucaniem. Koncepcyjnie mapa bitowa jest obracana w trzech wymiarach wokół osi pionowej lub osi poziomej przez środek mapy bitowej. Przerzucanie pionowe powoduje utworzenie obrazu dublowanego.
Strona Flipper mapy bitowej w przykładowej aplikacji demonstruje te procesy. Plik XAML zawiera dwa SKCanvasView
przyciski i umożliwiające przerzucanie w pionie i w poziomie:
<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.Bitmaps.BitmapFlipperPage"
Title="Bitmap Flipper">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<skia:SKCanvasView x:Name="canvasView"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
PaintSurface="OnCanvasViewPaintSurface" />
<Button Text="Flip Vertical"
Grid.Row="1" Grid.Column="0"
Margin="0, 10"
Clicked="OnFlipVerticalClicked" />
<Button Text="Flip Horizontal"
Grid.Row="1" Grid.Column="1"
Margin="0, 10"
Clicked="OnFlipHorizontalClicked" />
</Grid>
</ContentPage>
Plik związany z kodem implementuje te dwie operacje w Clicked
programach obsługi przycisków:
public partial class BitmapFlipperPage : ContentPage
{
SKBitmap bitmap =
BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
"SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
public BitmapFlipperPage()
{
InitializeComponent();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);
}
void OnFlipVerticalClicked(object sender, ValueChangedEventArgs args)
{
SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
using (SKCanvas canvas = new SKCanvas(flippedBitmap))
{
canvas.Clear();
canvas.Scale(-1, 1, bitmap.Width / 2, 0);
canvas.DrawBitmap(bitmap, new SKPoint());
}
bitmap = flippedBitmap;
canvasView.InvalidateSurface();
}
void OnFlipHorizontalClicked(object sender, ValueChangedEventArgs args)
{
SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);
using (SKCanvas canvas = new SKCanvas(flippedBitmap))
{
canvas.Clear();
canvas.Scale(1, -1, 0, bitmap.Height / 2);
canvas.DrawBitmap(bitmap, new SKPoint());
}
bitmap = flippedBitmap;
canvasView.InvalidateSurface();
}
}
Przerzucanie w pionie jest realizowane przez przekształcenie skalowania z współczynnikiem skalowania w poziomie –1. Środek skalowania to pionowy środek mapy bitowej. Przerzucanie w poziomie to przekształcenie skalowania z pionowym współczynnikiem skalowania –1.
Jak widać z odwróconej litery na koszuli małpy, przerzucanie nie jest takie samo jak rotacja. Ale jak pokazuje zrzut ekranu platformy UWP po prawej stronie, przerzucanie zarówno w poziomie, jak i w pionie jest takie samo jak obracanie 180 stopni:
Innym typowym zadaniem, które można obsłużyć przy użyciu podobnych technik, jest przycinanie mapy bitowej do prostokątnego podzestawu. Jest to opisane w następnym artykule Przycinanie map bitowych SkiaSharp.