// Convert bitmap: // 1. Trim to square // 2. Resize to 28x28 pixels // 3. Grayscale with high contrast SKBitmap ConvertBitmap(SKBitmap bitmap, float contr = contrast) { // 0. bitmap initialized? if (bitmap == null) { return(null); } // 1. Square bitmap (if not done by media plugin) if (bitmap.Width != bitmap.Height) { // Calculate size and start coordinates of square int size = Math.Min(bitmap.Width, bitmap.Height); int left = (bitmap.Width - size) / 2; int top = (bitmap.Height - size) / 2; // Cut centered square bitmap.ExtractSubset(bitmap, new SKRectI(left, top, left + size, top + size)); } // 2. Resize to 28x28 pixels (if not done by media plugin) if (bitmap.Width != 28) { SKBitmap bitmap_copy = bitmap.Copy(); bitmap = new SKBitmap(28, 28, bitmap.ColorType, bitmap.AlphaType); bitmap_copy.Resize(bitmap, SKBitmapResizeMethod.Box); } // 3. Convert bitmap to grayscale and apply contrast to emphasize lines // Second grayscale conversion to highlight pen color again which may be suppressed by resizing bitmap = ConvertBitmapToGray(bitmap, contr); return(bitmap); }
private SKBitmap Crop(SKBitmap original, ResizeParams resizeParams) { var cropSides = 0; var cropTopBottom = 0; // calculate amount of pixels to remove from sides and top/bottom if ((float)resizeParams.w / original.Width < resizeParams.h / original.Height) // crop sides { cropSides = original.Width - (int)Math.Round((float)original.Height / resizeParams.h * resizeParams.w); } else { cropTopBottom = original.Height - (int)Math.Round((float)original.Width / resizeParams.w * resizeParams.h); } // setup crop rect var cropRect = new SKRectI { Left = cropSides / 2, Top = cropTopBottom / 2, Right = original.Width - cropSides + cropSides / 2, Bottom = original.Height - cropTopBottom + cropTopBottom / 2 }; // crop SKBitmap bitmap = new SKBitmap(cropRect.Width, cropRect.Height); original.ExtractSubset(bitmap, cropRect); original.Dispose(); return(bitmap); }
/// <summary> /// Generates a cubic thumbnail bitmap from a given <see cref="SKBitmap"/>. /// </summary> /// <param name="self">Object to be extended.</param> /// <param name="sideLength">Side length of the bitmap. (X and Y axis)</param> /// <returns>A new bitmap instance.</returns> public static SKBitmap ToThumbnail(this SKBitmap self, int sideLength) { var srcHeight = self.Height; var srcWidth = self.Width; var srcShortSide = srcWidth > srcHeight ? srcHeight : srcWidth; int top = 0, left = 0, right = 0, bottom = 0; using var thumb = new SKBitmap(srcShortSide, srcShortSide); if (srcHeight > srcWidth) { var offset = (srcHeight - srcWidth) / 2; top = offset; right = srcShortSide; bottom = srcShortSide + offset; } else { var offset = (srcWidth - srcHeight) / 2; left = offset; right = offset + srcShortSide; bottom = srcShortSide; } self.ExtractSubset(thumb, new SKRectI(left, top, right, bottom)); return(thumb.Resize(new SKSizeI(sideLength, sideLength), SKFilterQuality.Medium)); }
public static SKBitmap CropImageAtMidPoint(SKBitmap inImg) { int w = inImg.Width; int h = inImg.Height; int shortestLen = 0; int longestLen = 0; int x = 0; int y = 0; if (w < h) { shortestLen = w; longestLen = h; x = 0; y = (h - w) / 2; } else if (h < w) { shortestLen = h; longestLen = w; y = 0; x = (w - h) / 2; } else { // already square. return. return(inImg); } SKRectI square = SKRectI.Create(x, y, shortestLen, shortestLen); SKBitmap finalBmp = new SKBitmap(shortestLen, shortestLen); Debug.WriteLine("DHB:GlobalSingletonHelpers:rotateAndCrop pre extract"); inImg.ExtractSubset(finalBmp, square); return(finalBmp); }
/// <summary> /// Crop to a rectangle /// </summary> /// <param name="original"></param> /// <param name="cropRect"></param> /// <returns></returns> private SKBitmap Crop(SKBitmap original, SKRectI cropRect) { // crop SKBitmap bitmap = new SKBitmap(cropRect.Width, cropRect.Height); original.ExtractSubset(bitmap, cropRect); return(bitmap); }
public static SKImage CopyBitmapRegion(SKBitmap bmp, int width, int height, SKRectI srcRegion) { using (var output = new SKBitmap(width, height)) { bmp.ExtractSubset(output, srcRegion); var img = SKImage.FromBitmap(output); return(SKImage.FromBitmap(SKBitmap.FromImage(img))); } }
public static SKImage CopyImageRegion2(SKBitmap srcImage, int width, int height, SKRectI srcRegion) { using (var surface = SKSurface.Create(width: width, height: height, colorType: SKImageInfo.PlatformColorType, alphaType: SKAlphaType.Premul)) using (var output = new SKBitmap(width, height)) using (var paint = new SKPaint()) { paint.FilterQuality = SKFilterQuality.High; var canvas = surface.Canvas; srcImage.ExtractSubset(output, srcRegion); canvas.DrawBitmap(output, new SKRect(0, 0, output.Width, output.Height), new SKRect(0, 0, width, height), paint); return(surface.Snapshot()); } }
F9PImageData(SKBitmap skBitamp, string key) { RangeLists = skBitamp.PatchRanges(); if (RangeLists?.PatchesX != null && RangeLists.PatchesX.Count > 0 && RangeLists.PatchesY != null && RangeLists.PatchesY.Count > 0) { SKBitmap unmarkedBitmap = new SKBitmap(skBitamp.Width - 1, skBitamp.Height - 1, SKColorType.Rgba8888, SKAlphaType.Unpremul); skBitamp.ExtractSubset(unmarkedBitmap, SKRectI.Create(1, 1, skBitamp.Width - 2, skBitamp.Height - 2)); skBitamp.Dispose(); skBitamp = unmarkedBitmap.Copy(); } _width = skBitamp.Width; _height = skBitamp.Height; SKBitmap = skBitamp; Key = key; }
/// <summary> /// Pixelates a given image area defined by extractRect of the original image and draws it to the canvas. /// </summary> /// <param name="canvas"></param> /// <param name="extractRect"></param> /// <param name="original"></param> /// <param name="pixelSizeFunction"></param> private static void Pixelate(SKCanvas canvas, SKRectI extractRect, SKBitmap original, Func <int, int, int, int, int> pixelSizeFunction) { var pixelSize = pixelSizeFunction.Invoke(original.Width, original.Height, extractRect.Width, extractRect.Height); var downscaled = new SKBitmap(extractRect.Width / pixelSize, extractRect.Height / pixelSize); var upscaled = new SKBitmap(extractRect.Width, extractRect.Height); var sub = new SKBitmap(); original.ExtractSubset(sub, extractRect); sub.ScalePixels(downscaled, SKFilterQuality.None); downscaled.ScalePixels(upscaled, SKFilterQuality.None); canvas.DrawBitmap(upscaled, extractRect); }
/// <summary> /// On crop to rectangle complete /// --------------- /// NOTE: /// As of 23/04/2018, the cropping library utilized (TOCropViewController) has the following open issue on GitHub: /// https://github.com/TimOliver/TOCropViewController/issues/239 /// /// Steps to repro: /// 1. Select a non-square image /// 2. Zoom and/or pan on the image /// 3. Click the reset button at the bottom (2nd button from the right) /// 4. Click the done button /// /// Expected: /// cropViewController.FinalImage will be square /// /// Result: /// cropViewController.FinalImage will NOT be square: /// FinalImage Width and FinalImage Height will differ by a pixel (from the repro attempts conducted) /// /// Work around used: /// /// [Let diff = absolute(FinalImage.Width - FinalImage.Height)] /// /// 1. If {diff} > 1 px: throw error pop-up. This case is where the user /// purposely dragged the rectangular image out and then hitting done. /// Return from function. /// /// 2. If 1px >= {diff} > 0px: set the outImageBitmap to a square cropping, /// thus removing the {diff} offset on either the width or height /// --------------- /// </summary> /// <param name="cropVC">TOCropViewController object</param> /// <param name="cropRect">Rectangled cropped</param> /// <param name="angle">Angle at which cropped</param> public override void DidCropImageToRect(TOCropViewController cropViewController, CGRect cropRect, nint angle) { UIImage outImage = cropViewController.FinalImage; SKBitmap outImageBitmap = outImage.ToSKBitmap(); nfloat width = outImage.Size.Width, height = outImage.Size.Height; // Work-around epsilon const float epsilon = 1.0f; // Case 1 (See comment on work-around above) if (Math.Abs(width - height) > epsilon) { // if it's a non-square image var alert = UIAlertController.Create("Cropping Error", "The AI model can only except a 1:1 aspect ratio. Please crop to a square.", UIAlertControllerStyle.Alert); // Add Actions alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null)); // Present Alert cropViewController.PresentViewController(alert, true, null); return; } else if (width != height) { // Case 2 (See comment on work-around above) SKBitmap image = outImage.ToSKBitmap(); int minDimension = (int)Math.Min(width, height); // Crop to the minimum dimension image.ExtractSubset(outImageBitmap, new SKRectI(0, 0, minDimension, minDimension)); } // Get analysis storyboard UIStoryboard analysisStoryboard = UIStoryboard.FromName("Analysis", null); // Grab analysis view Controller reference AnalysisViewController analysisViewController = analysisStoryboard.InstantiateInitialViewController() as AnalysisViewController; analysisViewController.InputImage = outImage; analysisViewController.ImageUrl = imageUrl; analysisViewController.InputImageBitmap = outImageBitmap; // Push analysis view controller onto stack cropViewController.NavigationController.PushViewController(analysisViewController, true); }
private SKBitmap CropBitmap(SKBitmap original, int width, int height) { var cropRect = new SKRectI { Size = new SKSizeI(width, height), Location = new SKPointI(width < original.Width ? (original.Width - width) / 2 : 0, height < original.Height ? (original.Height - height) / 2 : 0) }; var bitmap = new SKBitmap(cropRect.Width, cropRect.Height, original.ColorType, original.AlphaType); original.ExtractSubset(bitmap, cropRect); original.Dispose(); return(bitmap); }
public static SKBitmap SquareFromTop(SKBitmap inImg) { int w = inImg.Width; int h = inImg.Height; int shortestLen = ((w < h) ? w : h); /* * SKImage image = SKImage.FromBitmap(inImg); * SKImage subset = image.Subset(SKRectI.Create(0, yOffset, shortestLen, shortestLen)); * return subset;*/ SKRectI square = SKRectI.Create(0, h - shortestLen, shortestLen, shortestLen); SKBitmap finalBmp = new SKBitmap(shortestLen, shortestLen); Debug.WriteLine("DHB:GlobalSingletonHelpers:rotateAndCrop pre extract"); inImg.ExtractSubset(finalBmp, square); return(finalBmp); }
public static SKBitmap CropImage(SKBitmap original, PointF cropTopLeft, PointF cropBottomRight) { var cropRect = new SKRectI { Left = (int)cropTopLeft.X, Top = (int)cropTopLeft.Y, Right = (int)cropBottomRight.X, Bottom = (int)cropBottomRight.Y }; SKBitmap bitmap = new SKBitmap(cropRect.Width, cropRect.Height); original.ExtractSubset(bitmap, cropRect); original.Dispose(); return(bitmap); }
public BitmapTileFlipModesPage() { InitializeComponent(); SKBitmap origBitmap = BitmapExtensions.LoadBitmapResource( GetType(), "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg"); // Define cropping rect SKRectI cropRect = new SKRectI(5, 27, 296, 260); // Get the cropped bitmap SKBitmap croppedBitmap = new SKBitmap(cropRect.Width, cropRect.Height); origBitmap.ExtractSubset(croppedBitmap, cropRect); // Resize to half the width and height SKImageInfo info = new SKImageInfo(cropRect.Width / 2, cropRect.Height / 2); bitmap = croppedBitmap.Resize(info, SKBitmapResizeMethod.Box); }
private static SKBitmap[] LoadTiles(SKBitmap texture, int frameCount) { var src = new SKRectI(0, 0, texture.Width / frameCount, texture.Height); var tileInfo = texture.Info.WithSize(src.Width, src.Height); Debug.Assert(tileInfo.RowBytes == src.Width * texture.Info.BytesPerPixel); var ret = new SKBitmap[frameCount]; for (int i = 0; i < frameCount; i++, src.Offset(src.Width, 0)) { var tile = new SKBitmap(tileInfo); if (!texture.ExtractSubset(tile, src)) { throw new InvalidOperationException($"Failed to extract tile {i} from texture {texture}"); } tile.SetImmutable(); ret[i] = tile; } return(ret); }
/// <summary> /// Crop the image to fit within the dimensions specified. /// </summary> /// <param name="original"></param> /// <param name="maxSize"></param> /// <returns></returns> private SKBitmap Crop(SKBitmap original, SKSize maxSize) { var cropSides = 0; var cropTopBottom = 0; // calculate amount of pixels to remove from sides and top/bottom if ((float)maxSize.Width / original.Width < maxSize.Height / original.Height) // crop sides { cropSides = original.Width - (int)Math.Round((float)original.Height / maxSize.Height * maxSize.Width); } else { cropTopBottom = original.Height - (int)Math.Round((float)original.Width / maxSize.Width * maxSize.Height); } if (cropSides > 0 || cropTopBottom > 0) { // setup crop rect var cropRect = new SKRectI { Left = cropSides / 2, Top = cropTopBottom / 2, Right = original.Width - cropSides + cropSides / 2, Bottom = original.Height - cropTopBottom + cropTopBottom / 2 }; // crop SKBitmap bitmap = new SKBitmap(cropRect.Width, cropRect.Height); original.ExtractSubset(bitmap, cropRect); return(bitmap); } else { return(original.Copy()); } }
public SearchFaces(byte[] photoArray, int[] additionalRotateAngles = null, int photoJpegQuality = 95, double faceClippingRatio = 1.2) { if (photoArray != null) { try { using (Stream input = new MemoryStream(photoArray)) { photoArray = null; using (var inputStream = new SKManagedStream(input)) using (var codec = SKCodec.Create(inputStream)) using (var original = SKBitmap.Decode(codec)) { // ЧИТАЕМ EXIF-ИНФОРМАЦИЮ input.Position = 0; try { using (ExifReader reader = new ExifReader(input)) { reader.GetTagValue(ExifTags.DateTime, out photoDateTime); reader.GetTagValue(ExifTags.BodySerialNumber, out cameraSerialNumber); } } catch (Exception exc) { } // НОРМАЛИЗУЕМ ИЗОБРАЖЕНИE ПО ВРАЩЕНИЮ SKBitmap normalized = AdjustOrientation(original, codec.EncodedOrigin); double scaleFactor = 2; // ПОЛУЧАЕМ ДЕТЕКТИРУЕМОЕ НА ЛИЦА ИЗОБРАЖЕНИЕ using (var scanned = normalized.Resize(new SKImageInfo((int)Math.Round((double)normalized.Width / scaleFactor), (int)Math.Round((double)normalized.Height / scaleFactor)), SKFilterQuality.High)) { if (scanned == null) { return; } int additionalFacesCounter = 0; List <FaceLocation> faceLocationList = new List <FaceLocation>(); using (var fd = Dlib.GetFrontalFaceDetector()) { DlibDotNet.Rectangle[] faces = null; using (var array2D = Dlib.LoadImageData <RgbPixel>(ImagePixelFormat.Rgba, scanned.Bytes, (uint)scanned.Height, (uint)scanned.Width, (uint)(scanned.Bytes.Length / scanned.Height))) faces = fd.Operator(array2D); if (faces != null && faces.Length > 0) { for (int f = 0; f < faces.Length; f++) { #region обрезаем лицо до квадрата Point center = faces[f].Center; int radius = 0; if (faces[f].Width < faces[f].Height) { radius = (int)faces[f].Width / 2; } else { radius = (int)faces[f].Height / 2; } faces[f].Left = center.X - radius; faces[f].Right = center.X + radius; faces[f].Top = center.Y - radius; faces[f].Bottom = center.Y + radius; #endregion обрезаем лицо до квадрата FaceLocation faceLocation = CalculateNormalFaceLocation(faces[f], normalized.Width, normalized.Height, scaleFactor, faceClippingRatio); faceLocationList.Add(faceLocation); } } if (additionalRotateAngles != null && additionalRotateAngles.Length > 0) { for (int r = 0; r < additionalRotateAngles.Length; r++) { if (additionalRotateAngles[r] != 0) { DlibDotNet.Rectangle[] addFaces = null; SKBitmap rotatedScanned = Rotate(scanned, additionalRotateAngles[r]); using (var array2D = Dlib.LoadImageData <RgbPixel>(ImagePixelFormat.Rgba, rotatedScanned.Bytes, (uint)rotatedScanned.Height, (uint)rotatedScanned.Width, (uint)(rotatedScanned.Bytes.Length / rotatedScanned.Height))) addFaces = fd.Operator(array2D); if (addFaces != null && addFaces.Length > 0) { for (int i = 0; i < addFaces.Length; i++) { #region обрезаем лицо до квадрата Point center = addFaces[i].Center; int radius = 0; if (addFaces[i].Width < addFaces[i].Height) { radius = (int)addFaces[i].Width / 2; } else { radius = (int)addFaces[i].Height / 2; } addFaces[i].Left = center.X - radius; addFaces[i].Right = center.X + radius; addFaces[i].Top = center.Y - radius; addFaces[i].Bottom = center.Y + radius; #endregion обрезаем лицо до квадрата FaceLocation faceLocation = CalculateRotatedFaceLocation((double)rotatedScanned.Width / 2, (double)rotatedScanned.Height / 2, addFaces[i], -additionalRotateAngles[r], normalized.Width, normalized.Height, scaleFactor, faceClippingRatio); additionalFacesCounter++; faceLocationList.Add(faceLocation); } } } } } } if (faceLocationList.Count > 0) { List <FaceLocation> correlatedFaceList = GetCorrelatedFaceList(faceLocationList, additionalFacesCounter); // пропускаем через коррелятор лиц для избавления от дублей и уменьшения бокового наклона if (correlatedFaceList != null && correlatedFaceList.Count > 0) { for (int i = 0; i < correlatedFaceList.Count; i++) { //var cropRect = new SKRectI { Left = correlatedFaceList[i].Left, Top = correlatedFaceList[i].Top, Right = correlatedFaceList[i].Right, Bottom = correlatedFaceList[i].Bottom }; var cropRect = new SKRectI(); int w = correlatedFaceList[i].Right - correlatedFaceList[i].Left; int h = correlatedFaceList[i].Bottom - correlatedFaceList[i].Top; int centerX = correlatedFaceList[i].Left + w / 2; int centerY = correlatedFaceList[i].Top + h / 2; if (w > h) { cropRect.Left = centerX - h / 2; cropRect.Right = centerX + h / 2; cropRect.Top = centerY - h / 2; cropRect.Bottom = centerY + h / 2; } else if (w < h) { cropRect.Left = centerX - w / 2; cropRect.Right = centerX + w / 2; cropRect.Top = centerY - w / 2; cropRect.Bottom = centerY + w / 2; } else { cropRect.Left = correlatedFaceList[i].Left; cropRect.Top = correlatedFaceList[i].Top; cropRect.Right = correlatedFaceList[i].Right; cropRect.Bottom = correlatedFaceList[i].Bottom; } var faceBitmap = new SKBitmap(cropRect.Width, cropRect.Height); normalized.ExtractSubset(faceBitmap, cropRect); //// ТЕПЕРЬ БУДЕМ ПОВОРАЧИВАТЬ SKBitmap rotated = Rotate(faceBitmap, -correlatedFaceList[i].Angle); // ТЕПЕРЬ НАКЛАДЫВАЕМ МАСКУ НА ЛИЦО В ВИДЕ КРУГА double radius = 0; if (cropRect.Width < cropRect.Height) { radius = (double)cropRect.Width / 2 * (1 + 0.5 / 2); } else { radius = (double)cropRect.Height / 2 * (1 + 0.5 / 2); } using (SKCanvas canvas = new SKCanvas(rotated)) { canvas.DrawBitmap(rotated, 0, 0); SKPaint paint = new SKPaint(); paint.Color = maskColor; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = (float)(radius * 0.4); canvas.DrawCircle((float)rotated.Width / 2, (float)rotated.Height / 2, (float)radius, paint); canvas.Flush(); } // ВЫРЕЗАЕМ ИТОГ double x = (double)rotated.Width / 2; double y = (double)rotated.Height / 2; var finalCropRect = new SKRectI { Left = (int)(x - (double)faceBitmap.Width / 2), Top = (int)(y - (double)faceBitmap.Height / 2), Right = (int)(x + (double)faceBitmap.Width / 2), Bottom = (int)(y + (double)faceBitmap.Height / 2) }; faceBitmap.Dispose(); using (SKBitmap face = new SKBitmap(finalCropRect.Width, finalCropRect.Height)) { rotated.ExtractSubset(face, finalCropRect); try { if (face.Width > 600 * scaleFactor) { using (var scaled = face.Resize(new SKImageInfo((int)Math.Round(400 * scaleFactor), (int)Math.Round(400 * scaleFactor)), SKFilterQuality.High)) { if (scaled != null) { using (var image = SKImage.FromBitmap(scaled)) using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 90)) { if (facesByteArrays == null) { facesByteArrays = new List <byte[]>(); } facesByteArrays.Add(data.ToArray()); } } } } else { using (var image = SKImage.FromBitmap(face)) using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 90)) { if (facesByteArrays == null) { facesByteArrays = new List <byte[]>(); } facesByteArrays.Add(data.ToArray()); } } } catch (Exception exc) { }; } } normalized.Dispose(); correlatedFaceList = null; } faceLocationList = null; } } } isSuccess = true; } } catch (Exception exc) { try { isSuccess = false; if (exc.StackTrace != null && exc.StackTrace != "") { log.Debug(String.Format("SearchFaces. Exception: {0}", exc.StackTrace)); } else if (exc.Message != null && exc.Message != "") { log.Debug(String.Format("SearchFaces. Exception: {0}", exc.Message)); } } catch (Exception ex) { } } } else { log.Debug("SearchFaces. Null request."); } }
public static Image buildBackgroundFromBytes(byte[] patternSource, Assembly assembly, int Width, int Height, double bottomAdjustment = GlobalStatusSingleton.PATTERN_PCT, double sideAdjustment = GlobalStatusSingleton.PATTERN_FULL_COVERAGE) { Image result = null; if ((Width == -1) || (Height == -1)) { return(result); } if (patternSource == null) { return(result); } SKBitmap bitmap = new SKBitmap(new SKImageInfo(Width, Height)); var bitmapFullSize = buildFixedRotationSKBitmapFromBytes(patternSource); if ((bitmapFullSize.Width > Width) || (bitmapFullSize.Height > Height)) { // passed in image is larger than the screen. shrink to fit. bitmapFullSize.Resize(bitmap, SKBitmapResizeMethod.Box); } else { bitmap = bitmapFullSize; } int tilesWide = (int)((Width * sideAdjustment) / bitmap.Width); int tilesHigh = (int)((Height * bottomAdjustment) / bitmap.Height); try { using (var tempSurface = SKSurface.Create(new SKImageInfo((int)Width, (int)Height))) { var canvas = tempSurface.Canvas; canvas.Clear(SKColors.White); SKBitmap bottomEdge = new SKBitmap(); SKBitmap rightEdge = new SKBitmap(); SKBitmap corner = new SKBitmap(); int excessH = (int)(Height * bottomAdjustment) - (tilesHigh * bitmap.Height); int excessW = (int)(Width * sideAdjustment) - (tilesWide * bitmap.Width); if (excessH > 0) { bitmap.ExtractSubset(bottomEdge, new SKRectI(0, 0, bitmap.Width, excessH)); } if (excessW > 0) { bitmap.ExtractSubset(rightEdge, new SKRectI(0, 0, excessW, bitmap.Height)); } if ((excessH > 0) && (excessW > 0)) { bitmap.ExtractSubset(corner, new SKRectI(0, 0, excessW, excessH)); } for (int i = 0; i < tilesWide; i++) { for (int j = 0; j < tilesHigh; j++) { canvas.DrawBitmap(bitmap, SKRect.Create(i * bitmap.Width, j * bitmap.Height, bitmap.Width, bitmap.Height)); } // this covers the bottom except lower right corner. if (Height > tilesHigh * bitmap.Height) { canvas.DrawBitmap(bottomEdge, SKRect.Create(i * bitmap.Width, tilesHigh * bitmap.Height, bitmap.Width, excessH)); } } // this is the far side, but not lower right corner. if (Width > tilesWide * bitmap.Width) { for (int k = 0; k < tilesHigh; k++) { canvas.DrawBitmap(rightEdge, SKRect.Create(tilesWide * bitmap.Width, k * bitmap.Height, excessW, bitmap.Height)); } } // and finally the bottom right corner. if ((Height > tilesHigh * bitmap.Height) && (Width > tilesWide * bitmap.Width)) { canvas.DrawBitmap(corner, SKRect.Create(tilesWide * bitmap.Width, tilesHigh * bitmap.Height, excessW, excessH)); } SKImage skImage = tempSurface.Snapshot(); result = SKImageToXamarinImage(skImage); } } catch (Exception e) { string msg = e.ToString(); } return(result); }