Image Crop along with a square box using SkiaSharp?

jitenpjitenp INMember

Guys,

Is there any way to load a image and place a rect or square box on it and then to save only particular part of the whole image using SkiaSharp?

Please also help me out various other options to do the same on crossplatform.

Thanks
JP

Answers

  • mattleibowmattleibow ZAXamarin Team Xamurai

    SkiaSharp is cross-platform, so whatever you do on one platform will automatically be 100% reusable (even portable).

    You can do what you are asking very easily with SkiaSharp:

    // load/decode the image
    var bitmap = SKBitmap.Decode("path/to/image.png");
    
    // create a canvas for drawing
    var canvas = new SKCanvas(bitmap);
    
    // draw a rectangle with a red border
    var paint = new SKPaint {
        Style = SKPaintStyle.Stroke
        Color = SKColors.Red
        StrokeWidth = 5
    };
    canvas.DrawRect(SKRect.Create(10, 10, 100, 100), paint);
    
    // get an image subset to save
    var image = SKImage.FromBitmap(bitmap);
    var subset = image.Subset(SKRect.Create(20, 20, 90, 90));
    
    // encode the image
    var encodedData = image.Encode(SKImageEncodeFormat.Png, 75);
    
    // get a stream that can be saved to disk/memory/etc
    var stream = encodedData.AsStream();
    

    There is also stacks of information about the APIs on the docs: https://developer.xamarin.com/api/root/SkiaSharp

  • trishelwoodtrishelwood USMember ✭✭
    edited May 2017

    Hi, @mattleibow when I run this line
    var bitmap = SKBitmap.Decode("icon.png"); I get null.

    What is this path ? My Image is in Resource/drawable folder in android. also I tried to put that image in PCL project and run again but no success.

    I also changed image's build action from properties. Should it be Embedded Resource or something else ?

    could you please help ?

    Thanks !

  • DavidBlankleyDavidBlankley USMember ✭✭

    Here is a function that loads a set of resources that are passed in based on their names:

    using System.Reflection;
    using SkiaSharp;
    public void LoadImageList(string[] filenames, List outList) {
      try {
       var assembly = this.GetType().GetTypeInfo().Assembly;
       foreach (string image in filenames) {
        // don't do using here! we need these objects!
        using (var resource = assembly.GetManifestResourceStream(image))
        using (var stream = new SKManagedStream(resource)) {
         var bitmap = SKBitmap.Decode(stream);
         if (bitmap != null) {
          outList.Add(bitmap);
         }
        }
       }
      } catch (Exception e) {
       string msg = e.ToString();
      }
      //int test = outList[0].Width;
    }

    The tricky part with the filename is to be sure you properly qualify it...
    If the resource is a png in a directory in your Solution Explorer called Images... the file name would be:
    "Images.imageName.png"
    The other thing to be sure of is that the image is specified with the Build Action: "Embedded Resource" in the images properties dialog.

  • KevinLi.9546KevinLi.9546 USMember

    Same problem, var bitmap = SKBitmap.Decode("icon.png"); return null

  • mattleibowmattleibow ZAXamarin Team Xamurai

    @KevinLi.9546 You can't easily decode an drawable Android Resource. You may want to use a raw resource or an asset. Or an embedded resource.

    With embedded resources, you need to get the stream from the assembly using the Assembly.GetManifestResourceStream(...)

  • KevinLi.9546KevinLi.9546 USMember
    edited August 2017

    @mattleibow

    This my code, I keep getting null form "assembly.GetManifestResourceStream("background.png")". I try a raw resource and an asset, none of them working for me. I am working on a PCL project, both ios and android have this problem.

    var assembly = this.GetType().GetTypeInfo().Assembly;
    // don't do using here! we need these objects!
    var resource = assembly.GetManifestResourceStream("background.png");
    using (var stream = new SKManagedStream(resource))
    {
        var bitmap = SKBitmap.Decode(stream);
    }
    
  • mattleibowmattleibow ZAXamarin Team Xamurai

    Ah, the age old error... The compiler changes your file name. If it is an embedded resource, the chances are you have to add the namespace to the filename:

    var resource = assembly.GetManifestResourceStream("my.namespace.here.background.png");
    

    The actual namespace is the "Default Namespace" - this is usually the same as the "main" namespace, but you can check the project options. This namespace is usually the project name.

  • KevinLi.9546KevinLi.9546 USMember

    @mattleibow Ah. Thanks a lot :D :D

  • hi, guys, I was digging around for a long time I want to draw a resizable rectangle to crop an image
    any help

  • AndyDentAndyDent AUMember ✭✭

    I have been wrestling with a couple of issues and noticed some gotchas.

    Firstly, when you add a resource to an Android project it will get a default resource ID set by the IDE (visible in the Properties tab) which you can edit to be anything you like for GetManifestResourceStream.

    This may seem obvious but I see a lot of people commenting here and in other pages as if the resource ID dotted path is something magical - it's not, just an initial convention from the IDE. So the chances are you have to add the namespace to the filename is only because that's the default Resource ID.

    However, I'm still not happy - I really want to take advantage of the AndroidResource type because I have already got all those images being used in Image controls with the resource system loading the appropriate resolution from the drawable* folders.

    As far as I can tell, this is not possible with SKBitmap's decoders on Android.

  • mattleibowmattleibow ZAXamarin Team Xamurai

    @AndyDent, some good news. Since at least the v1.60.0 release, SkiaSharp can decode Android Resources and Assets. This requires that you open a stream to the resource or asset.

    For resources:

    var res = Resources.OpenRawResource(Resource.Drawable.icon);
    var bmp = SKBitmap.Decode(res);
    

    For assets:

    var asset = Assets.Open("image.png");
    var bmp = SKBitmap.Decode(asset);
    

    I hope this helps.

  • AndyDentAndyDent AUMember ✭✭

    @mattleibow Thanks for pointing me in the direction of those classes - I hadn't been looking at the platform-specific Android stuff. I now have a working solution.

  • AndyDentAndyDent AUMember ✭✭

    If anyone finds this thread and wants a more comprehensive example, I've just uploaded our internal example for free sharing to github.

Sign In or Register to comment.