I'm just playing around with Xamarin Forms and SkiaSharp. I've create a simple Xamarin forms ContentView with a SkiaCanvas. My PaintSurface is as follows to draw a simple green circle:
private void canvas_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var view = (SKCanvasView)sender;
var canvas = e.Surface.Canvas;
using(var paint = new SKPaint())
{
paint.Color = Colours.XamarinGreen;
paint.Style = SKPaintStyle.Fill;
paint.StrokeWidth = 1;
paint.IsAntialias = true;
canvas.DrawCircle((float)view.Width / 2.0f, (float)view.Height / 2.0f, (float)view.Height / 2.0f, paint);
}
}
In my test app, when I resize the window by dragging the corner of the window the circle flickers. I was wondering is there an easy way to enable double buffering when drawing graphics with SkiaSharp in a Xamarin.Forms app?
Related
I'm talking photo in my application. After I take it, on next screen in layout I want it be automatically cropped like in image.
But I'm constantly lose the boundaries of the photo and I have strange borders (indicated by RED in the image).
Here is my code:
Android code
private void NewElement_OnDrawBitmap(object sender, EventArgs e)
{
if (this.ViewGroup != null)
{
//get the subview
Android.Views.View subView = ViewGroup.GetChildAt(0);
int width = subView.Width;
int height = subView.Height;
//create and draw the bitmap
Bitmap b = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas c = new Canvas(b);
ViewGroup.Draw(c);
//save the bitmap to file
bytes = SaveBitmapToFile(b);
}
}
iOS code
UIGraphics.BeginImageContextWithOptions(this.Bounds.Size, true, 0);
this.Layer.RenderInContext(UIGraphics.GetCurrentContext());
var img = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
using (NSData imageData = img.AsPNG())
{
bytes = new Byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
}
private byte[] SaveBitmapToFile(Bitmap bm)
{
MemoryStream ms = new MemoryStream();
bm.Compress(Bitmap.CompressFormat.Png, 100, ms);
return ms.ToArray();
}
}
From your shared code, you're not doing any cropping, so your app just put your origin picture into the view, and the border you mentioned, is probably the background of your view.
And your project code, I saw you added
Bitmap bitmap = Bitmap.CreateBitmap(b, 0, 0, (98 * width) / 100, (82 * height) / 100);
Yes this is cropping the picture, but this just crop the image from top-left corner to 98% width and 82% height. much like
If you want to crop the picture into a square focused in center, you can simply:
int offset = (height - width) / 2;
Bitmap bitmap = Bitmap.CreateBitmap(b, 0, offset, width, height- offset);
But if you want to do more operations on Image like move/zoom... and then crop, I'd like to suggest you turn to existing solutions like FFImageLoading or Syncfusion. Or you'll have to calculate all the move/zoom data to do the crop.
I am using the example here, but I am struggeling to fill in the right size parameter here so that it represents the same size as the forms view.
https://michaelridland.com/xamarin/creating-native-view-xamarin-forms-viewpage/
public static UIView ConvertFormsToNative(Xamarin.Forms.View view, CGRect size)
{
var renderer = RendererFactory.GetRenderer(view);
renderer.NativeView.Frame = size;
renderer.NativeView.AutoresizingMask = UIViewAutoresizing.All;
renderer.NativeView.ContentMode = UIViewContentMode.ScaleToFill;
renderer.Element.Layout(size.ToRectangle());
var nativeView = renderer.NativeView;
nativeView.SetNeedsLayout();
return nativeView;
}
And for Android:
public static ViewGroup ConvertFormsToNative(Xamarin.Forms.View view, Rectangle size)
{
var vRenderer = RendererFactory.GetRenderer (view);
var viewGroup = vRenderer.ViewGroup;
vRenderer.Tracker.UpdateLayout ();
var layoutParams = new ViewGroup.LayoutParams ((int)size.Width, (int)size.Height);
viewGroup.LayoutParameters = layoutParams;
view.Layout (size);
viewGroup.Layout (0, 0, (int)view.WidthRequest, (int)view.HeightRequest);
return viewGroup;
}
}
What values from my Xamarin Forms should be put in, so that it will represent the CGRect required to have the same on the Native View.
var rect = new CGRect();
rect.X = (nfloat)view.X;
rect.Y = (nfloat)view.Y;
rect.Width = (nfloat)view.Width;
rect.Height = (nfloat)view.Height;
Solved it, where "view" is the Xamarin Forms view.
How can we create a dotted circle in xamarin forms using Skia Sharp, i had tried many but i could not make it happen can some one help me with this.
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = Color.Red.ToSKColor(),
StrokeWidth = 10
};
canvas.DrawCircle(info.Width / 3, info.Height / 2, 100, paint);
You are pretty close. You just need to understand what you are doing. Are you setting the Constructor of your page correctly? You have to
Create an instance of SKCanvasView and add it to the Content of your page.
So assuming that the name of your class or page is SimpleCirclePage.cs, you need to add this inside it, along with other items.
Add an event handler to the PaintSurface event of your SKCanvasView instance.
The constructor
public SimpleCirclePage()
{
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
The Event Handler
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Creating the Outline of the circle with Black
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = Color.Black.ToSKColor(),
StrokeWidth = 22
};
canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);
// Filling the circle with red
paint.Style = SKPaintStyle.Fill;
paint.Color = SKColors.Red;
canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);
}
I need to be able to do some direct drawing in Xamarin Forms. Is there a control equivelent to a canvas? I saw somewhere that maybe the AbsoluteLayout was, but I need something that I can, for example, execute a DrawString or DrawArc on. Does such a thing exist, or do you have to create a custom control?
Try with SkiaSharp, it basically allows you to do more or less the same than you can do in the native canvas of Android.
To use it:
Add the NuGet package SkiaSharp.Views.Forms to your projects.
Add the Skia namespace to your page or layout:
xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
Add the Skia canvas view, defining the name of the callback method that will be used to draw on it from the code behind:
<skia:SKCanvasView
x:Name="CanvasView"
PaintSurface="OnCanvasViewPaintSurface"/>
Draw whatever you want:
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
var info = args.Info;
var surface = args.Surface;
var canvas = surface.Canvas;
canvas.Clear();
// In this example, we will draw a circle in the middle of the canvas
var paint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = Color.Red.ToSKColor(), // Alternatively: SKColors.Red
};
canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);
}
I am trying to learn NME with haxe to create a small game. I have setup NME 3.5.5 with Haxe 2.10 in FlashDevelop. To draw the game background, I'm using
// Class level variable
var background : nme.display.Bitmap;
public function initResources() : Void
{
background = new Bitmap(Assets.getBitmapData("img/back.png"));
}
And in the render loop, I'm rendering like this.
g.clear();
g.beginBitmapFill(background.bitmapData, true, true);
g.drawRect(0, 0, 640, 480);
g.endFill();
This is drawing the image across the view and I need to resize it to fit the screen.
EDIT:
Here is the function i'm using to scale the bitmap. It doesn't work and nothing is rendered on the screen.
public static function resize( source:Bitmap, width:Int, height:Int ) : Bitmap
{
var scaleX:Int = Std.int(width / source.bitmapData.width);
var scaleY:Int = Std.int(height / source.bitmapData.height);
var data:BitmapData = new BitmapData(width, height, true);
var matrix:Matrix = new Matrix();
matrix.scale(scaleX, scaleY);
data.draw(source.bitmapData, matrix);
return new Bitmap(data);
}
Thanks.
EDIT 2:
Finally made it. I was unnecessarily casting it to int. Here's the solution.
public static function resize( source:Bitmap, width:Int, height:Int ) : Bitmap
{
var scaleX:Float = width / source.bitmapData.width;
var scaleY:Float = height / source.bitmapData.height;
var data:BitmapData = new BitmapData(width, height, true);
var matrix:Matrix = new Matrix();
matrix.scale(scaleX, scaleY);
data.draw(source.bitmapData, matrix);
return new Bitmap(data);
}
Look this Resizing BitmapData in ActionScript 3
You should use BitmapData.draw and scaled matrix.