Reputation: 83
I understood that the origin for SkiaSharp Rectangles lies in the upper left corner. But after this my understanding fails. I tried it with this tutorial but this did not quite help: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/bitmaps/drawing#copying-and-modifying-bitmaps
Imagine the code at the End i have a destination Bitmap of Size 1080 by 1080. From my understanding when i make my dstination SKRect to have coordinates (0,0,1080,1080) the Canves should draw on the whole destination Bitmap, however, it does not work i get blank white space. When i instead create a Maui.Rect with(0,0,1080,1080) which, from my understanding, should have the same properties with the origin in the upper left corner and then transform it to and SKRect it works.
Next try was to set the origin a bit further into the image like they did on the tutporial. So i created a new SKRect with Coordinates (100,100,980,980) since when i go further into the image i have to reduce the lower right coordinates so that they are not outside the Bitmap. This, however, does not work and produces a blank white space again. The coordinates of the Rectangle declaring the space of the source Bitmap i want to copy stayed the same in both cases. These were (0,0,150,150). I wanted to move enlarge the source Rectangle a bit so i used (100,100,150,150) which i made sure still lies in the source Bitmap, however, this also produces a blank white space. How does this work i do not understand this.
Edit: In the discussion in the Comments in my other Post i got told that the coordinates are based o nthe image control. Which would make sense for me but i do not know how to get the Image Control here as the image just got uploaded and was not displayed anywhere yet.
Edit 2: The Goal of this simple project is to understand how i wou.d set up the source Rectangle and change its coordinates to geth arbitrary specific parts of the image.
Edit 3: An Example of what i do not understand: When i set the Rectangles up like this:
SKBitmap destination = new SKBitmap(1080, 1080);
Rect destRec = new Rect(0,0,1080,1080);
SKRect dest = SkiaSharp.Views.Maui.Extensions.ToSKRect(destRec);
var height = sourceMap.Height;
var width = sourceMap.Width;
SKRect source = new SKRect(0,0,width,height);
But when i substract 100, where the height of the uploaded Image is 1332 and the Width 2000, the result looks liek that, which is totally not understandable for me:
SKBitmap destination = new SKBitmap(1080, 1080);
Rect destRec = new Rect(0,0,1080,1080);
SKRect dest = SkiaSharp.Views.Maui.Extensions.ToSKRect(destRec);
var height = sourceMap.Height;
var width = sourceMap.Width;
SKRect source = new SKRect(0,0,width+100,height+100);
The View:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:ImageCroppingTry2="clr-namespace:ImageCroppingTry2"
x:DataType="ImageCroppingTry2:MainPageViewModel"
x:Class="ImageCroppingTry2.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:ByteArrayToImageSourceConverter x:Key="ByteArrayToImageSourceConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout>
<Image x:Name="displayImage" Source="{Binding Image, Converter={StaticResource ByteArrayToImageSourceConverter}}"/>
<Button Command="{Binding UploadImageCommand}"></Button>
</VerticalStackLayout>
Its ViewModel:
[ObservableProperty]
public byte[] image;
[RelayCommand]
public async void UploadImage()
{
//Well i pick a photo for convenience
FileResult im = await MediaPicker.Default.PickPhotoAsync();
SKBitmap sourceMap;
using (Stream s = await im.OpenReadAsync())
{
using (MemoryStream ms = new MemoryStream())
{
await s.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
sourceMap = SKBitmap.Decode(ms);
}
}
SKBitmap destination = new SKBitmap(1080, 1080);
Rect destRec = new Rect(0,0,1080,1080);
//SKRect dest = new SKRect(0,0 , 1080,1080);
SKRect dest = SkiaSharp.Views.Maui.Extensions.ToSKRect(destRec);
//Rect sourceRect = new Rect(0,0,destination.Width,destination.Height);
SKRect source = new SKRect(0,0,150,150);
//SKRect source = SkiaSharp.Views.Maui.Extensions.ToSKRect(sourceRect);
using (SKCanvas canvas = new SKCanvas(destination))
{
canvas.Clear();
canvas.DrawBitmap(sourceMap, source, dest);
}
SKImage skImage = SKImage.FromBitmap(destination);
SKData encoded = skImage.Encode();
using (MemoryStream memory = new MemoryStream())
{
encoded.AsStream().CopyTo(memory);
Image = memory.ToArray();
}
}
Upvotes: 0
Views: 1682
Reputation: 21321
Maui Image is in DIPs (~160 per inch). If Density is 2, then 1800 dips becomes 1800x2 = 3600 pixels.
Doc is unclear, but ToSkRect
might be multiplying by Density. Don't use it, unless you intend to take Maui coords and convert them to Skia coords. For example, if you will be drawing to a control with (WidthRequest=400, HeightRequest=320), the size for Skia will be (400 * Density, 320 * Density). I think ToSkRect might do that multiplication for you. But I have not tested.
For your usage, do SkRect.Create(x, y, width, height)
(https://learn.microsoft.com/en-us/dotnet/api/skiasharp.skrect.create?view=skiasharp-2.88#skiasharp-skrect-create(system-single-system-single-system-single-system-single), or new SkRect(left, top, right, bottom)
. Left and x have same meaning. Right = left + width.
Re "coords based on image control". Right now, your goal is to manipulate a bitmap. This first step is not affected by what you will do with the bitmap to draw it. Then you render that to a control. Don't need to consider where the control will be on the screen.
Skia can render bitmaps. An alternative approach is to use Skia's command to draw the bitmap, inside an OnPaint method. Inside that, coordinates are all in pixels.
Upvotes: 3