The quality of the image rendered using RenderTargetBitmap.RenderAsync is poor on consecutive conversion

Imman Kumar 6 Reputation points
2023-01-17T13:55:23.8833333+00:00

Hi,

I am trying to convert the convert the Xamarin.Forms.Image to image in UWP using RenderTargetBitmap.RenderAsync API. The quality of the converted image is reduced. On consecutive conversion i.e., adding the converted image back to the page and converting that UIElement to image, the image quality is degraded. The code used to add the image to the page and converting the UIElement to image is as follows,

XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="StampUWP.MainPage">

    <StackLayout>
        <Button Text="Add" Clicked="Add"/>
        <Button Text="Convert" Clicked="Convert"/>
        <Button Text="Remove" Clicked="Remove"/>
        <Button Text="Open Folder" Clicked="Open_Folder"/>

        <Label Text="Click 1.Add -> 2.Convert -> 3.Remove repeatedly"/>
        <Label Text="Then click open folder"/>
    </StackLayout>
</ContentPage>

Code behind:

public partial class MainPage : ContentPage
    {
        static int count = 0;
        string directory = string.Empty;
        string filePath = string.Empty;

        public MainPage()
        {
            InitializeComponent();
            directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            filePath = Path.Combine(directory, "sample.png");

            if (File.Exists(filePath)) File.Delete(filePath);
            using (Stream file = File.Create(filePath))
            {
                Stream imageStream = typeof(App).GetTypeInfo().Assembly.GetManifestResourceStream("StampUWP.Assets.sample.png");
                imageStream.CopyTo(file);
            }
        }

        private async void Convert(object sender, EventArgs e)
        {
            await DependencyService.Get<IConverter>().ConvertToImage(count++);
        }

        private async void Add(object sender, EventArgs e)
        {
            Image image = new Image();            
            if (count == 0)
                filePath = Path.Combine(directory, "sample.png");
            else
                filePath = Path.Combine(directory, $"savedimage_{count - 1}.png");
            image.Source = ImageSource.FromFile(filePath);
            image.WidthRequest = 400;
            image.HeightRequest = 400;
            await DependencyService.Get<IConverter>().Add(image);
        }

        private void Remove(object sender, EventArgs e)
        {
            DependencyService.Get<IConverter>().Remove();
        }

        private void Open_Folder(object sender, EventArgs e)
        {
            DependencyService.Get<IConverter>().OpenFolder(directory);
        }
    }
}

Interface:

public interface IConverter
    {
        Task Add(Xamarin.Forms.View view);
        Task ConvertToImage(int count);
        void Remove();
    }

Conversion logic:

[assembly: Dependency(typeof(StampUWP.UWP.Converter))]
namespace StampUWP.UWP
{
    internal class Converter : IConverter
    {
        Windows.Foundation.Rect bounds = Windows.Foundation.Rect.Empty;
        UIElement element = null;
        DisplayInfo displayInfo = DeviceDisplay.MainDisplayInfo;
        Type pageChild = null;
        Canvas customCanvas = null;
        Windows.UI.Xaml.Controls.Page page = null;

        static int saveCount = 0;

        public async Task Add(View view)
        {
            var size = view.Measure(double.PositiveInfinity, double.PositiveInfinity);
            view.Layout(new Xamarin.Forms.Rectangle(0, 0, size.Request.Width, size.Request.Height));
            var visualElement = Xamarin.Forms.Platform.UWP.Platform.CreateRenderer(view);

            var viewGroup = visualElement.ContainerElement;
            bounds = new Windows.Foundation.Rect(0, 0, displayInfo.Density * size.Request.Width, displayInfo.Density * size.Request.Height);
            Viewbox viewbox = (Viewbox)AddViewToCanvas(viewGroup, bounds);

            element = viewbox.Child as Canvas;
            viewbox.Child = null;
            await AddToPage(element, bounds);
        }

        public async Task ConvertToImage(int count)
        {
            if (bounds != Windows.Foundation.Rect.Empty && element != null)
                await ConvertAndSave(element, bounds, count);
        }

        UIElement AddViewToCanvas(UIElement view, Windows.Foundation.Rect rect)
        {
            Viewbox viewBox = new Viewbox();
            Canvas canvas = new Canvas(); 
            canvas.Height = rect.Height;
            canvas.Width = rect.Width;
            viewBox.Stretch = Windows.UI.Xaml.Media.Stretch.Fill;
            viewBox.Width = canvas.Width;
            viewBox.Height = canvas.Height;
            canvas.Children.Add(view);
            Canvas.SetLeft(viewBox, rect.X);
            Canvas.SetTop(viewBox, rect.Y);

            viewBox.Child = canvas;
            view.UpdateLayout();
            return viewBox;
        }

        async Task AddToPage(UIElement element, Windows.Foundation.Rect rect)
        {
            if (Window.Current.Content is Windows.UI.Xaml.Controls.Frame)
            {
                Windows.UI.Xaml.Controls.Frame rootFrame = Window.Current.Content as Windows.UI.Xaml.Controls.Frame;
                page = rootFrame.Content as Windows.UI.Xaml.Controls.Page;
            }
            else if (Window.Current.Content is Windows.UI.Xaml.Controls.Page)
            {
                page = Window.Current.Content as Windows.UI.Xaml.Controls.Page;
            }
            pageChild = page.Content.GetType();
            customCanvas = new Canvas();
            customCanvas.Height = (element as FrameworkElement).Height;
            customCanvas.Width = (element as FrameworkElement).Width;
            customCanvas.Children.Add(element);
            if (pageChild == typeof(Windows.UI.Xaml.Controls.Grid))
            {
                ((Windows.UI.Xaml.Controls.Grid)page.Content).Children.Add(customCanvas);
            }
            else if (pageChild == typeof(Canvas))
            {
                ((Canvas)page.Content).Children.Add(customCanvas);
            }
            else if (pageChild == typeof(Panel))
            {
                ((Panel)page.Content).Children.Add(customCanvas);
            }
            else if (pageChild == typeof(StackPanel))
            {
                ((StackPanel)page.Content).Children.Add(customCanvas);
            }
        }

        async static Task ConvertAndSave(UIElement element, Windows.Foundation.Rect rect, int count)
        {
            InMemoryRandomAccessStream randomAccessStream = null;
            IBuffer pixelBuffer = null;
            RenderTargetBitmap bitmap = new RenderTargetBitmap();
            await bitmap.RenderAsync(element, (int)rect.Width,(int)rect.Height);

            if (bitmap.PixelWidth == 0 || bitmap.PixelHeight == 0)
            {
                return;
            }

            pixelBuffer = await bitmap.GetPixelsAsync();
            DataReader dataReader = DataReader.FromBuffer(pixelBuffer);
            byte[] pixels = new byte[pixelBuffer.Length];
            dataReader.ReadBytes(pixels);

            var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
            using (randomAccessStream = new InMemoryRandomAccessStream())
            {
                var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, randomAccessStream);
                encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Straight,
                (uint)bitmap.PixelWidth,
                (uint)bitmap.PixelHeight,
                logicalDpi,
                logicalDpi,
                pixels);
                await encoder.FlushAsync();

                Stream stream = randomAccessStream.AsStream();
                stream.Position = 0;
                MemoryStream ms = new MemoryStream();
                stream.CopyTo(ms);

                string directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

                string filePath = System.IO.Path.Combine(directory, $"savedimage_{count}.png");
                if (File.Exists(filePath)) File.Delete(filePath);
                File.WriteAllBytes(filePath, ms.ToArray());


                //if (File.Exists(textFile)) File.Delete(textFile);
                //using (StreamWriter streamWriter = File.CreateText(textFile))
                //{
                //    for (int i = 0; i < pixels.Length; i++)
                //    {
                //        if (i % bitmap.PixelWidth == 0)
                //        {
                //            streamWriter.Write("\n");
                //        }
                //        streamWriter.Write($"{pixels[i]}\t");
                //    }
                //}
            }
        }

        public void Remove()
        {
            if (page != null && customCanvas != null)
            {
                if (pageChild == typeof(Windows.UI.Xaml.Controls.Grid))
                {
                    ((Windows.UI.Xaml.Controls.Grid)page.Content).Children.Remove(customCanvas);
                }
                else if (pageChild == typeof(Canvas))
                {
                    ((Canvas)page.Content).Children.Remove(customCanvas);
                }
                else if (pageChild == typeof(Panel))
                {
                    ((Panel)page.Content).Children.Remove(customCanvas);
                }
                else if (pageChild == typeof(StackPanel))
                {
                    ((StackPanel)page.Content).Children.Remove(customCanvas);
                }

                page = null;
                customCanvas = null;
                element = null;
            }
        }

Replication procedure:

  1. Click Add, Convert, Remove multiple times in the exact order.
  2. Then open LocalApplicationFolder.

The saved images decreases in quality.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
4,811 questions
Universal Windows Platform (UWP)
No comments
{count} votes