xamarin / SignaturePad

MIT License
246 stars 150 forks source link

Background Image is not returning back #88

Open xyashapyx opened 7 years ago

xyashapyx commented 7 years ago

If you are setting BackgroundImage and draw on it, only drawn part will be returned, not BackgroundImage with drawings.

tro-solo commented 7 years ago

I have this problem too. It would be really helpful if you could add a boolean property 'IncludeBackgroundImage' to the ImageConstructionSettings, so it can be backwards compatible.

Sean-LINKSOFT commented 7 years ago

A feature to get the drawn component and the background as a single image is something that I would like to use as well. Would such a feature be considered to be added to SignaturePad?

deckertron9000 commented 7 years ago

I'd like to see this implemented as well.

Vandersteen commented 6 years ago

It would be awesome indeed,

I currently 'extended' the signaturepad view as follows:

Forms View

public class MarkingView : SignaturePadView
{
    public Func<string> SaveImageWithBackground;
}

IOS Renderer

[assembly: ExportRenderer (typeof(MarkingView), typeof(MarkingViewRenderer))]
namespace B2B.iOS
{
    public class MarkingViewRenderer : ViewRenderer<MarkingView, SignaturePadView>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<MarkingView> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                e.OldElement.SaveImageWithBackground = null;
            }
            if (e.NewElement != null)
            {
                e.NewElement.SaveImageWithBackground += NewElement_SaveImageWithBackground;
            }
        }

        string NewElement_SaveImageWithBackground()
        {
            UIGraphics.BeginImageContextWithOptions(Bounds.Size, false, 0f);

            using(var context = UIGraphics.GetCurrentContext())
            {
                Layer.RenderInContext(context);
                using (UIImage img = UIGraphics.GetImageFromCurrentImageContext())
                {
                    UIGraphics.EndImageContext();

                    string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                    string filename;
                    do
                    {
                        filename = Path.Combine(folder, "Marking-" + DateTime.Now.Ticks + ".png");
                    }
                    while (File.Exists(filename));

                    img.AsPNG().Save(filename, true);

                    return filename;
                }
            }
        }
    }
}

Android Renderer

[assembly: ExportRenderer (typeof(MarkingView), typeof(MarkingViewRenderer))]
namespace B2B.Droid
{
    public class MarkingViewRenderer : ViewRenderer<MarkingView, SignaturePadView>
    {
        protected override void OnElementChanged (ElementChangedEventArgs<MarkingView> e)
        {
            base.OnElementChanged (e);

            if (e.OldElement != null) {
                e.OldElement.SaveImageWithBackground = null;
            }
            if (e.NewElement != null) {
                e.NewElement.SaveImageWithBackground += NewElement_SaveImageWithBackground;
            }
        }

        string NewElement_SaveImageWithBackground()
        {
            using(Bitmap bm = Bitmap.CreateBitmap(Width, Height, Bitmap.Config.Argb8888))
            {
                using(Canvas canvas = new Canvas(bm))
                {
                    Drawable bgDrawable = Background;

                    if (bgDrawable != null)
                    {
                        bgDrawable.Draw(canvas);
                    }

                    Draw(canvas);

                    string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                    string filename;
                    do
                    {
                        filename = System.IO.Path.Combine(folder, "Marking-" + DateTime.Now.Ticks + ".png");
                    }
                    while (File.Exists(filename));

                    using (var fs = new System.IO.FileStream(filename, FileMode.CreateNew))
                    {
                        bm.Compress(Bitmap.CompressFormat.Png, 90, fs);
                        bm.Recycle();

                        return filename;
                    }
                }
            }
        }
    }
}

Feel free to use it. It's not perfect, but it works for my needs

mattleibow commented 6 years ago

This seems to be a popular request, we will put this on the list of things to do for a future release.

Sean-LINKSOFT commented 6 years ago

Vandersteen what are the using statements from your renderer classes? I get the following errors for the class definition line -

The type 'SignaturePad.Forms.SignaturePadView' cannot be used as type parameter 'TNativeView' in the generic type or method 'ViewRenderer<TView, TNativeView>'. There is no implicit reference conversion from 'SignaturePad.Forms.SignaturePadView' to 'UIKit.UIView'. & The type 'SignaturePad.Forms.SignaturePadView' cannot be used as type parameter 'TNativeView' in the generic type or method 'ViewRenderer<TView, TNativeView>'. There is no implicit reference conversion from 'SignaturePad.Forms.SignaturePadView' to 'Android.Views.View'.

Vandersteen commented 6 years ago

IOS:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using System.IO;
using Xamarin.Controls;
//our shared app namespace
//our native app namespace

Android:

using System;
using Xamarin.Forms;
using System.IO;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using Xamarin.Controls;
using Android.Graphics.Drawables;
//our shared app namespace
//our native app namespace
jemmett92 commented 6 years ago

Vandersteen thats a great renderer, well done. You can make a basic image editor from that. Do you have any idea if you can get rid extra part the signature pad adds to the background image for the signature line? I can make it transparent but its still on the image when i save it? any ideas? I was also wondering if you could add another image on top of the background image like a sticker using that renderer?

Vandersteen commented 6 years ago

I did not test it but I think you could do the following:

  1. Find the specific views that you want to render (Background, Lines)
  2. Render those in the context instead of the whole view
    //Replace
    Layer.RenderInContext(context);

    //With
    Control.BackgroundImageView.Layer.RenderInContext(context);
    Control.SignatureLine.Layer.RenderInContext(context);

Same thing for android:

//Replace
Draw(canvas);

//With
Control.BackgroundImageView.Draw(canvas);
Control.SignatureLine.Draw(canvas);
  1. You could do the same if you want to add an extra image on top:

var watermark = new UIImageView(); ... watermark.Layer.RenderInContext(context);

Again, not tested, but that's where I would start

PizzaBoatJim commented 6 years ago

Vandersteen NICE GOING! What you have developed is exactly what I need now. I have tried to implement it, but getting errors. Would it be possible for you to post a sample code of your render implementation?

Vandersteen commented 6 years ago

I posted the renderers above.

For the view usage you will just need to do the following:

var markingView = new MarkingView(); ... var pathToImage = markingView. SaveImageWithBackground();

PizzaBoatJim commented 6 years ago

Vandersteen - thank you for the post. I implemented your code, but many problems arise and I cannot compile. I must be doing something wrong. Here is my code:

`using System; using Xamarin.Forms; using System.IO; using Xamarin.Forms.Platform.Android; using Android.Graphics; using Xamarin.Controls; using Android.Graphics.Drawables; using ES; using ES.Droid; //our native app namespace [assembly: ExportRenderer(typeof(MarkingView), typeof(MarkingViewRenderer))] namespace ES.Droid { public class MarkingView : SignaturePadView { public Func SaveImageWithBackground; }

public class MarkingViewRenderer : ViewRenderer<MarkingView, SignaturePadView>
{
    protected override void OnElementChanged(ElementChangedEventArgs<MarkingView> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null)
        {
            e.OldElement.SaveImageWithBackground = null;
        }
        if (e.NewElement != null)
        {
            e.NewElement.SaveImageWithBackground += NewElement_SaveImageWithBackground;
        }
    }

    private string NewElement_SaveImageWithBackground()
    {
        using (Bitmap bm = Bitmap.CreateBitmap(Width, Height, Bitmap.Config.Argb8888))
        {
            using (Canvas canvas = new Canvas(bm))
            {
                Drawable bgDrawable = Background;

                if (bgDrawable != null)
                {
                    bgDrawable.Draw(canvas);
                }

                Draw(canvas);

                string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                string filename;
                do
                {
                    filename = System.IO.Path.Combine(folder, "Marking-" + DateTime.Now.Ticks + ".png");
                }
                while (File.Exists(filename));

                using (var fs = new FileStream(filename, FileMode.CreateNew))
                {
                    bm.Compress(Bitmap.CompressFormat.Png, 90, fs);
                    bm.Recycle();

                    return filename;
                }
            }
        }
    }
}

}`

Here are the errors: Severity Code Description Project File Line Suppression State Error CS0311 The type 'ES.Droid.MarkingView' cannot be used as type parameter 'TView' in the generic type or method 'ViewRenderer<TView, TNativeView>'. There is no implicit reference conversion from 'ES.Droid.MarkingView' to 'Xamarin.Forms.View'. EnSpect.Android C:\J\Software\ES\ES\ES.Android\MarkingView.cs 20 Active

Severity Code Description Project File Line Suppression State Error CS0311 The type 'ES.Droid.MarkingView' cannot be used as type parameter 'TElement' in the generic type or method 'ElementChangedEventArgs<TElement>'. There is no implicit reference conversion from 'ES.Droid.MarkingView' to 'Xamarin.Forms.Element'. ES.Android C:\J\Software\ES\ES\ES.Android\MarkingView.cs 22 Active

Severity Code Description Project File Line Suppression State Error CS1729 'SignaturePadView' does not contain a constructor that takes 0 arguments ES.Android C:\J\Software\ES\ES\ES.Android\MarkingView.cs 14 Active

Any suggestions how to fix?

mattleibow commented 6 years ago

This needs a bit more thought - same with other "chrome" features, similar to https://github.com/xamarin/SignaturePad/issues/127

PaulVrugt commented 6 years ago

I actually used the example of @Vandersteen to implement the signaturepad to allow us to annotate images. I know this component is meant for recording signatures, but we use it to let people annotate images they just selected or pictures they just took to clarify a photo of a situation. This works perfectly. Support built into the package would be great, because it would mean less code to maintain on our end.

chrislowcher commented 6 years ago

I would love to see this implemented as well. It would be really helpful. Thanks

tsafadi commented 4 years ago

@Vandersteen very nice solution! However, I would want to pass an argument to the function such that I can save it to a specific directory. Could you help me extend the function for an additional parameter that I can pass into the SaveImageWithBackground function. Something like: signPad.SaveImageWithBackground(“something”);

tsafadi commented 4 years ago

I extended @Vandersteen's example by taking one more argument, such that I can pass in the directory where to save the files to. In my case, I want to save them in a specific directory for each signature:

public class CustomSignaturePadView : SignaturePadView
{
    public Func<string, string> SaveImageWithBackground;
}

And then you only need to extend the NewElement_SaveImageWithBackground functions by one parameter, like so string NewElement_SaveImageWithBackground(string path).