aherrick
aherrick

Reputation: 20169

Xamarin Forms UWP Capture Screenshot Include Signature Pad

I have a Xamarin Forms page using Signature Pad (https://github.com/xamarin/SignaturePad). I'm attempting to capture a screenshot of the entire view. It should include the signature as well.

However, using the following code I'm noticing the signature does not show up.

What is the best way to capture the full Page including the signature? (not just the signature)

public class ScreenshotService : IScreenshotService
{
    public async Task<byte[]> CaptureAsync()
    {
    var rtb = new RenderTargetBitmap();
    await rtb.RenderAsync(Window.Current.Content);

    var pixelBuffer = await rtb.GetPixelsAsync();
    var pixels = pixelBuffer.ToArray();

    // Useful for rendering in the correct DPI
    var displayInformation = DisplayInformation.GetForCurrentView();

    var stream = new InMemoryRandomAccessStream();
    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
    encoder.SetPixelData(BitmapPixelFormat.Bgra8,
    BitmapAlphaMode.Premultiplied,
    (uint)rtb.PixelWidth,
    (uint)rtb.PixelHeight,
    displayInformation.RawDpiX,
    displayInformation.RawDpiY,
    pixels);

    await encoder.FlushAsync();
    stream.Seek(0);

    var readStram = stream.AsStreamForRead();
    var bytes = new byte[readStram.Length];
    readStram.Read(bytes, 0, bytes.Length);

    return bytes;
}
}

Upvotes: 0

Views: 664

Answers (2)

aherrick
aherrick

Reputation: 20169

Here is my final implementation including converting to byte array.

    public async Task<byte[]> CaptureAsync(Stream signatureStream)
    {
        var rtb = new RenderTargetBitmap();
        await rtb.RenderAsync(Window.Current.Content);

        var pixelBuffer = await rtb.GetPixelsAsync();
        var pixels = pixelBuffer.ToArray();

        var displayInformation = DisplayInformation.GetForCurrentView();
        var stream = new InMemoryRandomAccessStream();
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
        encoder.SetPixelData(BitmapPixelFormat.Bgra8,
        BitmapAlphaMode.Premultiplied,
        (uint)rtb.PixelWidth,
        (uint)rtb.PixelHeight,
        displayInformation.RawDpiX,
        displayInformation.RawDpiY,
        pixels);
        await encoder.FlushAsync();
        stream.Seek(0);
        var readStram = stream.AsStreamForRead();

        var pagebitmap = await GetSoftwareBitmap(readStram);
        var softwareBitmap = await GetSoftwareBitmap(signatureStream);

        CanvasDevice device = CanvasDevice.GetSharedDevice();
        CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, rtb.PixelWidth, rtb.PixelHeight, 96);

        using (var ds = renderTarget.CreateDrawingSession())
        {
            ds.Clear(Colors.White);
            var page = CanvasBitmap.CreateFromSoftwareBitmap(device, pagebitmap);
            var image = CanvasBitmap.CreateFromSoftwareBitmap(device, softwareBitmap);
            ds.DrawImage(page);
            ds.DrawImage(image, 50, 55);
        }

        InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
        await renderTarget.SaveAsync(randomAccessStream, CanvasBitmapFileFormat.Jpeg, 1f);

        var fileBytes = new byte[randomAccessStream.Size];
        using (var reader = new DataReader(randomAccessStream))
        {
            await reader.LoadAsync((uint)randomAccessStream.Size);
            reader.ReadBytes(fileBytes);
        }

        return fileBytes;
    }

Upvotes: 1

Nico Zhu
Nico Zhu

Reputation: 32775

According to the "XAML visuals and RenderTargetBitmap capture capabilities" of RenderTargetBitmap class:

Content that can't be captured will appear as blank in the captured image, but other content in the same visual tree can still be captured and will render (the presence of content that can't be captured won't invalidate the entire capture of that XAML composition).

So it could be that the content of InkCanvas is not captureable. However, you can use Win2D. For more you could refer the following code.

public async Task<Stream> CaptureAsync(Stream Tem)
{
    var rtb = new RenderTargetBitmap();
    await rtb.RenderAsync(Window.Current.Content);

    var pixelBuffer = await rtb.GetPixelsAsync();
    var pixels = pixelBuffer.ToArray();

    var displayInformation = DisplayInformation.GetForCurrentView();
    var stream = new InMemoryRandomAccessStream();
    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
    encoder.SetPixelData(BitmapPixelFormat.Bgra8,
    BitmapAlphaMode.Premultiplied,
    (uint)rtb.PixelWidth,
    (uint)rtb.PixelHeight,
    displayInformation.RawDpiX,
    displayInformation.RawDpiY,
    pixels);
    await encoder.FlushAsync();
    stream.Seek(0);
    var readStram = stream.AsStreamForRead();

    var pagebitmap = await GetSoftwareBitmap(readStram);
    var softwareBitmap = await GetSoftwareBitmap(Tem);

    CanvasDevice device = CanvasDevice.GetSharedDevice();
    CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, rtb.PixelWidth, rtb.PixelHeight, 96);

    using (var ds = renderTarget.CreateDrawingSession())
    {
        ds.Clear(Colors.White);
        var page = CanvasBitmap.CreateFromSoftwareBitmap(device, pagebitmap);
        var image = CanvasBitmap.CreateFromSoftwareBitmap(device, softwareBitmap);
        ds.DrawImage(page);
        ds.DrawImage(image);
    }

    InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
    await renderTarget.SaveAsync(randomAccessStream, CanvasBitmapFileFormat.Jpeg, 1f);
    return randomAccessStream.AsStream();
}

 private async Task<SoftwareBitmap> GetSoftwareBitmap(Stream data)
 {
   BitmapDecoder pagedecoder = await BitmapDecoder.CreateAsync(data.AsRandomAccessStream());
   return await pagedecoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
 }

IScreenshotServicecs interface

public interface IScreenshotServicecs
{
    Task<Stream> CaptureAsync(Stream stream);
}

Usage

var stream = await SignatureView.GetImageStreamAsync(SignaturePad.Forms.SignatureImageFormat.Png);
var data = await DependencyService.Get<IScreenshotServicecs>().CaptureAsync(stream);
MyImage.Source = ImageSource.FromStream(() => data);

Upvotes: 2

Related Questions