SkiaSharp bit eşlemlerini kırpma
SkiaSharp Bit Eşlemleri Oluşturma ve Çizme makalesinde, bir SKBitmap
nesnenin bir oluşturucuya SKCanvas
nasıl geçirilebileceği açıklanmıştır. Bu tuvalde çağrılan herhangi bir çizim yöntemi, bit eşlem üzerinde grafiklerin işlenmesine neden olur. Bu çizim yöntemleri içerir. DrawBitmap
Bu, bu tekniğin bir bit eşlem parçasının veya tamamının başka bir bit eşleme aktarılmasına izin verdiği anlamına gelir ve dönüşümler uygulanmış olabilir.
Yöntemini kaynak ve hedef dikdörtgenlerle çağırarak DrawBitmap
bit eşlem kırpmak için bu tekniği kullanabilirsiniz:
canvas.DrawBitmap(bitmap, sourceRect, destRect);
Ancak kırpma uygulayan uygulamalar genellikle kullanıcının kırpma dikdörtgenini etkileşimli olarak seçmesi için bir arabirim sağlar:
Bu makale bu arabirime odaklanır.
Kırpma dikdörtgenini kapsülleme
adlı CroppingRectangle
bir sınıftaki kırpma mantığının bazılarını yalıtmak yararlı olur. Oluşturucu parametreleri, genellikle kırpılmakta olan bit eşlem boyutunu ve isteğe bağlı en boy oranını içeren bir maksimum dikdörtgen içerir. Oluşturucu ilk olarak türünün SKRect
özelliğinde Rect
ortak hale getiren ilk kırpma dikdörtgenini tanımlar. Bu ilk kırpma dikdörtgeni bit eşlem dikdörtgeninin genişliğinin ve yüksekliğinin %80'idir, ancak en boy oranı belirtilirse ayarlanır:
class CroppingRectangle
{
···
SKRect maxRect; // generally the size of the bitmap
float? aspectRatio;
public CroppingRectangle(SKRect maxRect, float? aspectRatio = null)
{
this.maxRect = maxRect;
this.aspectRatio = aspectRatio;
// Set initial cropping rectangle
Rect = new SKRect(0.9f * maxRect.Left + 0.1f * maxRect.Right,
0.9f * maxRect.Top + 0.1f * maxRect.Bottom,
0.1f * maxRect.Left + 0.9f * maxRect.Right,
0.1f * maxRect.Top + 0.9f * maxRect.Bottom);
// Adjust for aspect ratio
if (aspectRatio.HasValue)
{
SKRect rect = Rect;
float aspect = aspectRatio.Value;
if (rect.Width > aspect * rect.Height)
{
float width = aspect * rect.Height;
rect.Left = (maxRect.Width - width) / 2;
rect.Right = rect.Left + width;
}
else
{
float height = rect.Width / aspect;
rect.Top = (maxRect.Height - height) / 2;
rect.Bottom = rect.Top + height;
}
Rect = rect;
}
}
public SKRect Rect { set; get; }
···
}
Kullanılabilir hale getiren CroppingRectangle
kullanışlı bilgilerden biri, kırpma dikdörtgeninin SKPoint
dört köşesine karşılık gelen bir değer dizisidir ve sırasıyla sol üst, sağ üst, sağ alt ve sol alt:
class CroppingRectangle
{
···
public SKPoint[] Corners
{
get
{
return new SKPoint[]
{
new SKPoint(Rect.Left, Rect.Top),
new SKPoint(Rect.Right, Rect.Top),
new SKPoint(Rect.Right, Rect.Bottom),
new SKPoint(Rect.Left, Rect.Bottom)
};
}
}
···
}
Bu dizi, adlı HitTest
aşağıdaki yöntemde kullanılır. SKPoint
parametresi, parmakla dokunmaya veya fare tıklamasına karşılık gelen bir noktadır. yöntemi, parametre tarafından radius
verilen uzaklık içinde, parmağın veya fare işaretçisinin dokunduğu köşeye karşılık gelen bir dizin (0, 1, 2 veya 3) döndürür:
class CroppingRectangle
{
···
public int HitTest(SKPoint point, float radius)
{
SKPoint[] corners = Corners;
for (int index = 0; index < corners.Length; index++)
{
SKPoint diff = point - corners[index];
if ((float)Math.Sqrt(diff.X * diff.X + diff.Y * diff.Y) < radius)
{
return index;
}
}
return -1;
}
···
}
Dokunma veya fare noktası herhangi bir köşenin birimleri içinde radius
değilse, yöntem –1 döndürür.
içindeki CroppingRectangle
MoveCorner
son yöntem, dokunma veya fare hareketine yanıt olarak çağrılan olarak adlandırılır. İki parametre taşınmakta olan köşenin dizinini ve bu köşenin yeni konumunu gösterir. yöntemin ilk yarısı kırpma dikdörtgenini köşenin yeni konumuna göre ayarlar, ancak her zaman bit eşlem boyutu olan sınırları maxRect
içindedir. Bu mantık, kırpma dikdörtgeninin MINIMUM
hiçbir şeye daraltılmasını önlemek için alanı da dikkate alır:
class CroppingRectangle
{
const float MINIMUM = 10; // pixels width or height
···
public void MoveCorner(int index, SKPoint point)
{
SKRect rect = Rect;
switch (index)
{
case 0: // upper-left
rect.Left = Math.Min(Math.Max(point.X, maxRect.Left), rect.Right - MINIMUM);
rect.Top = Math.Min(Math.Max(point.Y, maxRect.Top), rect.Bottom - MINIMUM);
break;
case 1: // upper-right
rect.Right = Math.Max(Math.Min(point.X, maxRect.Right), rect.Left + MINIMUM);
rect.Top = Math.Min(Math.Max(point.Y, maxRect.Top), rect.Bottom - MINIMUM);
break;
case 2: // lower-right
rect.Right = Math.Max(Math.Min(point.X, maxRect.Right), rect.Left + MINIMUM);
rect.Bottom = Math.Max(Math.Min(point.Y, maxRect.Bottom), rect.Top + MINIMUM);
break;
case 3: // lower-left
rect.Left = Math.Min(Math.Max(point.X, maxRect.Left), rect.Right - MINIMUM);
rect.Bottom = Math.Max(Math.Min(point.Y, maxRect.Bottom), rect.Top + MINIMUM);
break;
}
// Adjust for aspect ratio
if (aspectRatio.HasValue)
{
float aspect = aspectRatio.Value;
if (rect.Width > aspect * rect.Height)
{
float width = aspect * rect.Height;
switch (index)
{
case 0:
case 3: rect.Left = rect.Right - width; break;
case 1:
case 2: rect.Right = rect.Left + width; break;
}
}
else
{
float height = rect.Width / aspect;
switch (index)
{
case 0:
case 1: rect.Top = rect.Bottom - height; break;
case 2:
case 3: rect.Bottom = rect.Top + height; break;
}
}
}
Rect = rect;
}
}
Yöntemin ikinci yarısı isteğe bağlı en boy oranına göre ayarlanır.
Bu sınıftaki her şeyin piksel cinsinden olduğunu unutmayın.
Yalnızca kırpma için tuval görünümü
CroppingRectangle
Az önce gördüğünüz sınıf, öğesinden SKCanvasView
türetilen sınıfı tarafından PhotoCropperCanvasView
kullanılır. Bu sınıf bit eşlem ve kırpma dikdörtgenini görüntülemenin yanı sıra kırpma dikdörtgenini değiştirmek için dokunma veya fare olaylarını işlemekle sorumludur.
PhotoCropperCanvasView
Oluşturucu bir bit eşlem gerektirir. En boy oranı isteğe bağlıdır. Oluşturucu, bu bit eşlem ve en boy oranına göre türdeki CroppingRectangle
bir nesnenin örneğini oluşturur ve bunu alan olarak kaydeder:
class PhotoCropperCanvasView : SKCanvasView
{
···
SKBitmap bitmap;
CroppingRectangle croppingRect;
···
public PhotoCropperCanvasView(SKBitmap bitmap, float? aspectRatio = null)
{
this.bitmap = bitmap;
SKRect bitmapRect = new SKRect(0, 0, bitmap.Width, bitmap.Height);
croppingRect = new CroppingRectangle(bitmapRect, aspectRatio);
···
}
···
}
Bu sınıf öğesinden SKCanvasView
türetildiği için, olay için PaintSurface
bir işleyici yüklemesi gerekmez. Bunun yerine yöntemini geçersiz kılabilir OnPaintSurface
. yöntemi bit eşlemi görüntüler ve geçerli kırpma dikdörtgenini SKPaint
çizmek için alan olarak kaydedilen birkaç nesne kullanır:
class PhotoCropperCanvasView : SKCanvasView
{
const int CORNER = 50; // pixel length of cropper corner
···
SKBitmap bitmap;
CroppingRectangle croppingRect;
SKMatrix inverseBitmapMatrix;
···
// Drawing objects
SKPaint cornerStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 10
};
SKPaint edgeStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 2
};
···
protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
{
base.OnPaintSurface(args);
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Gray);
// Calculate rectangle for displaying bitmap
float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);
float x = (info.Width - scale * bitmap.Width) / 2;
float y = (info.Height - scale * bitmap.Height) / 2;
SKRect bitmapRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
canvas.DrawBitmap(bitmap, bitmapRect);
// Calculate a matrix transform for displaying the cropping rectangle
SKMatrix bitmapScaleMatrix = SKMatrix.MakeIdentity();
bitmapScaleMatrix.SetScaleTranslate(scale, scale, x, y);
// Display rectangle
SKRect scaledCropRect = bitmapScaleMatrix.MapRect(croppingRect.Rect);
canvas.DrawRect(scaledCropRect, edgeStroke);
// Display heavier corners
using (SKPath path = new SKPath())
{
path.MoveTo(scaledCropRect.Left, scaledCropRect.Top + CORNER);
path.LineTo(scaledCropRect.Left, scaledCropRect.Top);
path.LineTo(scaledCropRect.Left + CORNER, scaledCropRect.Top);
path.MoveTo(scaledCropRect.Right - CORNER, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top + CORNER);
path.MoveTo(scaledCropRect.Right, scaledCropRect.Bottom - CORNER);
path.LineTo(scaledCropRect.Right, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Right - CORNER, scaledCropRect.Bottom);
path.MoveTo(scaledCropRect.Left + CORNER, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom - CORNER);
canvas.DrawPath(path, cornerStroke);
}
// Invert the transform for touch tracking
bitmapScaleMatrix.TryInvert(out inverseBitmapMatrix);
}
···
}
sınıfındaki CroppingRectangle
kod, kırpma dikdörtgenini bit eşlem piksel boyutuna göre temel alır. Ancak, bit eşleminin sınıfı tarafından PhotoCropperCanvasView
görüntülenmesi, görüntüleme alanının boyutuna göre ölçeklendirilir. Geçersiz bitmapScaleMatrix
kılmada OnPaintSurface
hesaplanan, bit eşlem piksellerinden, görüntülendiği şekilde bit eşlem piksellerinin boyutuna ve konumuna eşler. Bu matris daha sonra kırpma dikdörtgenini bit eşleme göre görüntülenecek şekilde dönüştürmek için kullanılır.
Geçersiz kılmanın OnPaintSurface
son satırı, öğesinin tersini bitmapScaleMatrix
alır ve alan olarak inverseBitmapMatrix
kaydeder. Bu, dokunma işleme için kullanılır.
Bir TouchEffect
nesne bir alan olarak örneği oluşturulur ve oluşturucu olaya bir işleyici TouchAction
ekler, ancak TouchEffect
türevin üst SKCanvasView
öğesinin koleksiyonuna Effects
eklenmesi gerekir, böylece geçersiz kılmada OnParentSet
yapılır:
class PhotoCropperCanvasView : SKCanvasView
{
···
const int RADIUS = 100; // pixel radius of touch hit-test
···
CroppingRectangle croppingRect;
SKMatrix inverseBitmapMatrix;
// Touch tracking
TouchEffect touchEffect = new TouchEffect();
struct TouchPoint
{
public int CornerIndex { set; get; }
public SKPoint Offset { set; get; }
}
Dictionary<long, TouchPoint> touchPoints = new Dictionary<long, TouchPoint>();
···
public PhotoCropperCanvasView(SKBitmap bitmap, float? aspectRatio = null)
{
···
touchEffect.TouchAction += OnTouchEffectTouchAction;
}
···
protected override void OnParentSet()
{
base.OnParentSet();
// Attach TouchEffect to parent view
Parent.Effects.Add(touchEffect);
}
···
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
touchPoints.Remove(args.Id);
}
break;
}
}
SKPoint ConvertToPixel(Xamarin.Forms.Point pt)
{
return new SKPoint((float)(CanvasSize.Width * pt.X / Width),
(float)(CanvasSize.Height * pt.Y / Height));
}
}
İşleyici tarafından işlenen dokunma olayları cihazdan TouchAction
bağımsız birimlerdedir. Bunların önce sınıfının en altındaki yöntemi kullanılarak ConvertToPixel
piksellere dönüştürülmesi ve ardından kullanılarak inverseBitmapMatrix
birimlere CroppingRectangle
dönüştürülmesi gerekir.
İşleyici olaylar TouchAction
için Pressed
yöntemini CroppingRectangle
çağırırHitTest
. Bu işlem –1 dışında bir dizin döndürürse, kırpma dikdörtgeninin köşelerinden biri değiştirilir. Bu dizin ve gerçek dokunma noktasının köşeden uzaklığı bir TouchPoint
nesnede depolanır ve sözlüğe eklenir touchPoints
.
Olay için Moved
, MoveCorner
en boy oranı için olası ayarlamalarla köşeyi taşımak için yöntemi CroppingRectangle
çağrılır.
herhangi bir zamanda, kullanan PhotoCropperCanvasView
bir program özelliğine CroppedBitmap
erişebilir. Bu özellik kırpılan boyutta yeni bir bit eşlem oluşturmak için özelliğini CroppingRectangle
kullanırRect
. Hedef ve kaynak dikdörtgenlerle sürümü DrawBitmap
, özgün bit eşleminin bir alt kümesini ayıklar:
class PhotoCropperCanvasView : SKCanvasView
{
···
SKBitmap bitmap;
CroppingRectangle croppingRect;
···
public SKBitmap CroppedBitmap
{
get
{
SKRect cropRect = croppingRect.Rect;
SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
(int)cropRect.Height);
SKRect dest = new SKRect(0, 0, cropRect.Width, cropRect.Height);
SKRect source = new SKRect(cropRect.Left, cropRect.Top,
cropRect.Right, cropRect.Bottom);
using (SKCanvas canvas = new SKCanvas(croppedBitmap))
{
canvas.DrawBitmap(bitmap, source, dest);
}
return croppedBitmap;
}
}
···
}
Fotoğraf kırpıcı tuval görünümünü barındırma
Bu iki sınıf kırpma mantığını işlerken, örnek uygulamadaki Fotoğraf Kırpma sayfasının yapacak çok az işi vardır. XAML dosyası, ve Bitti düğmesini barındırmak PhotoCropperCanvasView
için bir örneği Grid
oluşturur:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SkiaSharpFormsDemos.Bitmaps.PhotoCroppingPage"
Title="Photo Cropping">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid x:Name="canvasViewHost"
Grid.Row="0"
BackgroundColor="Gray"
Padding="5" />
<Button Text="Done"
Grid.Row="1"
HorizontalOptions="Center"
Margin="5"
Clicked="OnDoneButtonClicked" />
</Grid>
</ContentPage>
PhotoCropperCanvasView
türünde SKBitmap
bir parametre gerektirdiğinden XAML dosyasında örneği oluşturulamıyor.
Bunun yerine, PhotoCropperCanvasView
kaynak bit eşlemlerinden biri kullanılarak arka planda kod dosyasının oluşturucusunda örneği oluşturulur:
public partial class PhotoCroppingPage : ContentPage
{
PhotoCropperCanvasView photoCropper;
SKBitmap croppedBitmap;
public PhotoCroppingPage ()
{
InitializeComponent ();
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(GetType(),
"SkiaSharpFormsDemos.Media.MountainClimbers.jpg");
photoCropper = new PhotoCropperCanvasView(bitmap);
canvasViewHost.Children.Add(photoCropper);
}
void OnDoneButtonClicked(object sender, EventArgs args)
{
croppedBitmap = photoCropper.CroppedBitmap;
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(croppedBitmap, info.Rect, BitmapStretch.Uniform);
}
}
Kullanıcı daha sonra kırpma dikdörtgenini işleyebilir:
İyi bir kırpma dikdörtgeni tanımlandığında Bitti düğmesine tıklayın. İşleyici Clicked
kırpılan bit eşlemi özelliğinden CroppedBitmap
PhotoCropperCanvasView
alır ve sayfanın tüm içeriğini bu kırpılmış bit eşlemi görüntüleyen yeni SKCanvasView
bir nesneyle değiştirir:
İkinci bağımsız değişkenini PhotoCropperCanvasView
1,78f olarak ayarlamayı deneyin (örneğin):
photoCropper = new PhotoCropperCanvasView(bitmap, 1.78f);
Kırpma dikdörtgeninin yüksek tanımlı televizyonun 16-9 en boy oranı özelliğiyle sınırlı olduğunu göreceksiniz.
Bit eşlemi kutucuklara bölme
Ünlü 14-15 bulmacanın bir Xamarin.Forms sürümü ile Xamarin.FormsMobil Uygulama Oluşturma kitabının 22. Bölümünde ortaya çıktı ve XamagonXuzzle olarak indirilebilir. Ancak, bulmaca kendi fotoğraf kitaplığınızdan bir görüntüye dayandığında daha eğlenceli (ve genellikle daha zorlayıcı) hale gelir.
14-15 bulmacanın bu sürümü örnek uygulamanın bir parçasıdır ve Photo Puzzle adlı bir dizi sayfadan oluşur.
PhotoPuzzlePage1.xaml dosyası bir Button
oluşur:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SkiaSharpFormsDemos.Bitmaps.PhotoPuzzlePage1"
Title="Photo Puzzle">
<Button Text="Pick a photo from your library"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
Clicked="OnPickButtonClicked"/>
</ContentPage>
Arka planda kod dosyası, kullanıcının fotoğraf kitaplığından bir Clicked
fotoğraf seçmesine izin vermek için bağımlılık hizmetini kullanan IPhotoLibrary
bir işleyici uygular:
public partial class PhotoPuzzlePage1 : ContentPage
{
public PhotoPuzzlePage1 ()
{
InitializeComponent ();
}
async void OnPickButtonClicked(object sender, EventArgs args)
{
IPhotoLibrary photoLibrary = DependencyService.Get<IPhotoLibrary>();
using (Stream stream = await photoLibrary.PickPhotoAsync())
{
if (stream != null)
{
SKBitmap bitmap = SKBitmap.Decode(stream);
await Navigation.PushAsync(new PhotoPuzzlePage2(bitmap));
}
}
}
}
Yöntemi daha sonra öğesine gider PhotoPuzzlePage2
ve seçili bit eşlemciyi constuctor'a geçirir.
Kitaplıktan seçilen fotoğraf, fotoğraf kitaplığında göründüğü gibi yönlendirilmemiş olabilir, ancak döndürülmüş veya baş aşağı döndürülmüş olabilir. (Bu özellikle iOS cihazlarıyla ilgili bir sorundur.) Bu nedenle, PhotoPuzzlePage2
görüntüyü istediğiniz yönde döndürmenize olanak tanır. XAML dosyası 90° Sağ (saat yönünde anlamına gelir), 90° Sol (saat yönünün tersine) ve Bitti etiketli üç düğme içerir.
Arka planda kod dosyası, SkiaSharp Bit Eşlemlerinde Oluşturma ve Çizme makalesinde gösterilen bit eşlem döndürme mantığını uygular. Kullanıcı görüntüyü herhangi bir sayıda saat yönünde veya saat yönünün tersine 90 derece döndürebilir:
public partial class PhotoPuzzlePage2 : ContentPage
{
SKBitmap bitmap;
public PhotoPuzzlePage2 (SKBitmap bitmap)
{
this.bitmap = bitmap;
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 OnRotateRightButtonClicked(object sender, EventArgs args)
{
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());
}
bitmap = rotatedBitmap;
canvasView.InvalidateSurface();
}
void OnRotateLeftButtonClicked(object sender, EventArgs args)
{
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());
}
bitmap = rotatedBitmap;
canvasView.InvalidateSurface();
}
async void OnDoneButtonClicked(object sender, EventArgs args)
{
await Navigation.PushAsync(new PhotoPuzzlePage3(bitmap));
}
}
Kullanıcı Bitti düğmesine tıkladığında, Clicked
işleyici sayfasına gider PhotoPuzzlePage3
ve sayfanın oluşturucusunda son döndürülmüş bit eşlemi geçirir.
PhotoPuzzlePage3
fotoğrafın kırpılmasına izin verir. Program, 4'e 4 kutucuk kılavuzuna bölmek için kare bit eşlem gerektirir.
PhotoPuzzlePage3.xaml dosyası, öğesini barındırmak PhotoCropperCanvasView
için bir Grid
Label
, ve başka bir Bitti düğmesi içerir:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SkiaSharpFormsDemos.Bitmaps.PhotoPuzzlePage3"
Title="Photo Puzzle">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="Crop the photo to a square"
Grid.Row="0"
FontSize="Large"
HorizontalTextAlignment="Center"
Margin="5" />
<Grid x:Name="canvasViewHost"
Grid.Row="1"
BackgroundColor="Gray"
Padding="5" />
<Button Text="Done"
Grid.Row="2"
HorizontalOptions="Center"
Margin="5"
Clicked="OnDoneButtonClicked" />
</Grid>
</ContentPage>
Arka planda kod dosyası, oluşturucusunun PhotoCropperCanvasView
bit eşlemi geçirilirken örneğini oluşturur. İkinci bağımsız değişken olarak 1'in geçirildiğini fark edin PhotoCropperCanvasView
. 1'in bu en boy oranı, kırpma dikdörtgenini kareye zorlar:
public partial class PhotoPuzzlePage3 : ContentPage
{
PhotoCropperCanvasView photoCropper;
public PhotoPuzzlePage3(SKBitmap bitmap)
{
InitializeComponent ();
photoCropper = new PhotoCropperCanvasView(bitmap, 1f);
canvasViewHost.Children.Add(photoCropper);
}
async void OnDoneButtonClicked(object sender, EventArgs args)
{
SKBitmap croppedBitmap = photoCropper.CroppedBitmap;
int width = croppedBitmap.Width / 4;
int height = croppedBitmap.Height / 4;
ImageSource[] imgSources = new ImageSource[15];
for (int row = 0; row < 4; row++)
{
for (int col = 0; col < 4; col++)
{
// Skip the last one!
if (row == 3 && col == 3)
break;
// Create a bitmap 1/4 the width and height of the original
SKBitmap bitmap = new SKBitmap(width, height);
SKRect dest = new SKRect(0, 0, width, height);
SKRect source = new SKRect(col * width, row * height, (col + 1) * width, (row + 1) * height);
// Copy 1/16 of the original into that bitmap
using (SKCanvas canvas = new SKCanvas(bitmap))
{
canvas.DrawBitmap(croppedBitmap, source, dest);
}
imgSources[4 * row + col] = (SKBitmapImageSource)bitmap;
}
}
await Navigation.PushAsync(new PhotoPuzzlePage4(imgSources));
}
}
Bitti düğmesi işleyicisi kırpılan bit eşlem genişliğini ve yüksekliğini alır (bu iki değer aynı olmalıdır) ve ardından her biri özgün bit eşlem genişliği ve yüksekliği 1/4 olan 15 ayrı bit eşlem içine böler. (Olası 16 bit eşlemlerin sonuncusu oluşturulmaz.) DrawBitmap
Kaynak ve hedef dikdörtgen içeren yöntem, daha büyük bir bit eşlem alt kümesine göre bir bit eşlem oluşturulmasını sağlar.
Bit eşlemlere Xamarin.Forms dönüştürme
yönteminde OnDoneButtonClicked
, 15 bit eşlem için oluşturulan dizi türündedir ImageSource
:
ImageSource[] imgSources = new ImageSource[15];
ImageSource
Xamarin.Forms bit eşlemi kapsülleyen temel türdür. Neyse ki SkiaSharp, SkiaSharp bit eşlemlerinden bit eşlemlere dönüştürmeye Xamarin.Forms izin verir. SkiaSharp.Views.Forms derlemesi, bir SkiaSharp SKBitmap
nesnesine göre türetilen ImageSource
ancak oluşturulabilen bir sınıf tanımlarSKBitmapImageSource
. SKBitmapImageSource
ve arasındaki SKBitmapImageSource
SKBitmap
dönüştürmeleri tanımlar ve nesneler dizide bit eşlem olarak bu şekilde SKBitmap
Xamarin.Forms depolanır:
imgSources[4 * row + col] = (SKBitmapImageSource)bitmap;
Bu bit eşlem dizisi öğesine bir oluşturucu PhotoPuzzlePage4
olarak geçirilir. Bu sayfa tamamen Xamarin.Forms ve skiaSharp kullanmaz. XamagonXuzzle'a çok benzer, bu nedenle burada açıklanamaz, ancak seçtiğiniz fotoğrafı 15 kare kutucuk olarak görüntüler:
Rastgele seç düğmesine basıldığında tüm kutucuklar karıştırılır:
Artık bunları doğru sıraya koyabilirsiniz. Boş kareyle aynı satır veya sütundaki kutucuklar, boş kareye taşımak için dokunabilir.