How to call PaintSurface twice in skiasharp xamarin forms

I am doing a project wherein I can change the filter of the image. I am using skiasharp to change the filter of the image. It is like that of CamScanner Application. But when I change the filter to grayscale first and then Lighten and then Sepia and then again back to grayscale I hit save I get Sepia's Image. I understand that the last data being generated is that of sepia's hence it is saving that data. But if I want to change more than 3 times it is not working. Please help me out. Here is my coding.

  <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                 BackgroundColor="Black"
                 x:Class="JC.EditPage">
        <ContentPage.Content>

            <StackLayout Padding="10,10,10,10" Orientation="Vertical">


                <Image x:Name="imageView" HeightRequest="450"
                       HorizontalOptions="FillAndExpand"
                       VerticalOptions="CenterAndExpand" IsVisible="True"/>

                <StackLayout x:Name="canvasStackView" IsVisible="False">
                    <skia:SKCanvasView HeightRequest="450" PaintSurface="OnCanvasViewPaintSurface" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand"/>
                </StackLayout>

                <StackLayout x:Name="canvasLightenStackView" IsVisible="False">
                    <skia:SKCanvasView HeightRequest="450" PaintSurface="OnCanvasViewPaintSurfaceLighten" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand"/>
                </StackLayout>

                <StackLayout x:Name="canvasSepiaStackView" IsVisible="False">
                    <skia:SKCanvasView HeightRequest="450" PaintSurface="OnCanvasViewPaintSurfaceSepia" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand"/>
                </StackLayout>

      <ScrollView Orientation="Horizontal" HorizontalScrollBarVisibility="Never">
                    <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand" x:Name="filterStack" IsVisible="True">
                        <StackLayout WidthRequest="100" HeightRequest="70" BackgroundColor="White">
                            <Label Text="Original" FontFamily="Bold" Font="14" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer Tapped="Original_Tapped">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                        </StackLayout>

                        <StackLayout WidthRequest="100" HeightRequest="70" BackgroundColor="White" >
                            <Label Text="Grayscale" FontFamily="Bold" Font="14" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer Tapped="Grayscale_Tapped">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                        </StackLayout>

                        <StackLayout WidthRequest="100" HeightRequest="70" BackgroundColor="White">
                            <Label Text="Lighten" FontFamily="Bold" Font="14" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer Tapped="Lighten_Tapped">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                        </StackLayout>

                        <StackLayout WidthRequest="100" HeightRequest="70" BackgroundColor="White" >
                            <Label Text="Sepia" FontFamily="Bold" Font="14" TextColor="Black" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer Tapped="Speia_Tapped">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                        </StackLayout>
                    </StackLayout>
                </ScrollView>

                <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand" BackgroundColor="White">
                    <Image Source="goback.png" HorizontalOptions="StartAndExpand">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Tapped="goback_Tapped"/>
                        </Image.GestureRecognizers>
                    </Image>

                    <Image Source="tick.png" HorizontalOptions="EndAndExpand">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Tapped="Save_Tapped"/>
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>

and here is my XAML.CS file for that -

  private async void Grayscale_Tapped(object sender, EventArgs e)
    {
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");



        adjust = false;
        canvasEditStackView.IsVisible = false;
        canvasSepiaStackView.IsVisible = false;
        canvasLightenStackView.IsVisible = false;
        imageView.IsVisible = false;
        canvasStackView.IsVisible = true;
        filterStack.IsVisible = true;
        original = false;

        byte[] tempArray = await StorageHelper.LoadImage(image, path);

       canvasView = new SKCanvasView();
       canvasView.PaintSurface += OnCanvasViewPaintSurface;

        using (Stream stream = new MemoryStream(tempArray))
        {
            if (stream != null)
            {
                libraryBitmap = SKBitmap.Decode(stream);
                canvasView.InvalidateSurface();
            }
        }
        DependencyService.Get<IProgressInterface>().DismissLoader();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        Console.WriteLine("Hits");
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");

        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {

            paint.ColorFilter =
                SKColorFilter.CreateColorMatrix(new float[]
                {
                0.21f, 0.72f, 0.07f, 0, 0,
                0.21f, 0.72f, 0.07f, 0, 0,
                0.21f, 0.72f, 0.07f, 0, 0,
                0,     0,     0,     1, 0
                });

            canvas.DrawBitmap(libraryBitmap, info.Rect, BitmapStretch.Uniform, paint: paint);
            DependencyService.Get<IProgressInterface>().DismissLoader();
        }
        var snap = surface.Snapshot();
        SKData data = snap.Encode();
        saveData = data;

    }

    private async void Lighten_Tapped(object sender, EventArgs e)
    {
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");


        adjust = false;
        imageView.IsVisible = false;
        canvasEditStackView.IsVisible = false;
        canvasStackView.IsVisible = false;
        canvasSepiaStackView.IsVisible = false;
        canvasLightenStackView.IsVisible = true;
        filterStack.IsVisible = true;
        original = false;

        byte[] tempArray = await StorageHelper.LoadImage(image, path);

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurfaceLighten;

        using (Stream stream = new MemoryStream(tempArray))
        {
            if (stream != null)
            {
                libraryBitmap = SKBitmap.Decode(stream);
                canvasView.InvalidateSurface();
            }
        }
        DependencyService.Get<IProgressInterface>().DismissLoader();

    }

    void OnCanvasViewPaintSurfaceLighten(object sender, SKPaintSurfaceEventArgs args)
    {
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {
            paint.ColorFilter =
                SKColorFilter.CreateColorMatrix(new float[]
                {
                0.75f, 0.25f, 0.25f, 0, 0,
                0.25f, 0.75f, 0.25f, 0, 0,
                0.25f, 0.25f, 0.75f, 0, 0,
                0, 0, 0, 1, 0
                });
            canvas.DrawBitmap(libraryBitmap, info.Rect, BitmapStretch.Uniform, paint: paint);
            DependencyService.Get<IProgressInterface>().DismissLoader();
        }
        var snap = surface.Snapshot();
        SKData data = snap.Encode();
        saveData = data;

    }

    public async void Speia_Tapped(object sender, EventArgs e)
    {

        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");



        adjust = false;
        imageView.IsVisible = false;
        canvasEditStackView.IsVisible = false;
        canvasStackView.IsVisible = false;
        canvasLightenStackView.IsVisible = false;
        canvasSepiaStackView.IsVisible = true;
        filterStack.IsVisible = true;
        original = false;

        byte[] tempArray = await StorageHelper.LoadImage(image, path);

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurfaceSepia;

        using (Stream stream = new MemoryStream(tempArray))
        {
            if (stream != null)
            {
                libraryBitmap = SKBitmap.Decode(stream);
                canvasView.InvalidateSurface();

            }
        }
        DependencyService.Get<IProgressInterface>().DismissLoader();
    }

    void OnCanvasViewPaintSurfaceSepia(object sender, SKPaintSurfaceEventArgs args)
    {
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        using (SKPaint paint = new SKPaint())
        {
            paint.ColorFilter =
                SKColorFilter.CreateColorMatrix(new float[]
                {
                  1, 0,   0, 0, 0,
                  0, 1,   0, 0, 0,
                  0, 0, 0.8f, 0, 0,
                  0, 0,   0, 1, 0
                });
            canvas.DrawBitmap(libraryBitmap, info.Rect, BitmapStretch.Uniform, paint: paint);
            DependencyService.Get<IProgressInterface>().DismissLoader();
        }
        var snap = surface.Snapshot();
        SKData data = snap.Encode();
        saveData = data;
    }

and this is my save command.

 if (original == true)
            {
                var editPref = DependencyService.Get<IUserPreferences>();
                editPref.SaveString("edit", "false");

                await Navigation.PushModalAsync(new desiredLocationStoragePage(path));
            }
            else
            {
                var editPref = DependencyService.Get<IUserPreferences>();
                editPref.SaveString("edit", "true");
                string saveName = fileName;
                using (var stream = File.OpenWrite(saveName))
                {
                    saveData.SaveTo(stream);
                }
                await Navigation.PushModalAsync(new desiredLocationStoragePage(fileName));

            }



   please help me out guys I am quite stuck after this phase

1 answer

  • answered 2018-11-09 08:02 Markus Michel

    I hope I got it right, you are using several SKCanvasViews and depending on what "mode" the user chooses in your app, you activate the corresponding surface?

    I wouldn't recommend that. Even though it ~kinda~ works, it might become confusing very fast.

    My approach to your problem would be to rewrite the view in the following way:

    Create an enum containing the names of your filters and another entry for "none", e.g.

    public enum Filters
    {
        None = 0,
        Grayscale = 1,
        Lighten = 2,
        Sepia = 4
    } 
    

    Then create a property of the type of this enum in your page.

    Filters currentFilter = Filters.None;
    

    Now instead of copying your render code 4 times, you could change the main PaintSurface method to something like:

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        Console.WriteLine("Hits");
        DependencyService.Get<IProgressInterface>().ShowLoader("Please wait...");
    
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;
    
        canvas.Clear();
    
    
    
        using (SKPaint paint = new SKPaint())
        {
            // check if currentFilter is set to Filters.None
            if( (currentFilter & Filters.None) == Filters.None )
            {
                paint.ColorFilter =
                    SKColorFilter.CreateColorMatrix(new float[]
                    {
                        0.21f, 0.72f, 0.07f, 0, 0,
                        0.21f, 0.72f, 0.07f, 0, 0,
                        0.21f, 0.72f, 0.07f, 0, 0,
                        0,     0,     0,     1, 0
                    });
            }
            // check if currentFilter is set to Filters.Lighten
            else if ( (currentFilter & Filters.Lighten) == Filters.Lighten)
            {
                paint.ColorFilter =
                    SKColorFilter.CreateColorMatrix(new float[]
                    {
                        0.75f, 0.25f, 0.25f, 0, 0,
                        0.25f, 0.75f, 0.25f, 0, 0,
                        0.25f, 0.25f, 0.75f, 0, 0,
                        0, 0, 0, 1, 0
                });
            }
    
            /*
                ... proceed with other filters accordingly ....
            */
    
            canvas.DrawBitmap(libraryBitmap, info.Rect, BitmapStretch.Uniform, paint: paint);
            DependencyService.Get<IProgressInterface>().DismissLoader();
        }
        var snap = surface.Snapshot();
        SKData data = snap.Encode();
        saveData = data;
    
    }
    

    So when tapping your SetFilterButton all you need to do is setting the customFilter Property of your page to the corresponding filter and calling

    canvasView.InvalidateSurface();
    

    so the surface will be redrawn and then saving the image.