Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory Management with ImageCropper Control #460

Open
1 of 24 tasks
Zakariathr22 opened this issue Aug 6, 2024 · 0 comments
Open
1 of 24 tasks

Memory Management with ImageCropper Control #460

Zakariathr22 opened this issue Aug 6, 2024 · 0 comments

Comments

@Zakariathr22
Copy link

Zakariathr22 commented Aug 6, 2024

Describe the bug

When using the ImageCropper control in the Windows Community Toolkit Gallery, I have observed the following memory-related issues:

  1. Memory Consumption: When selecting a high-resolution image (dimensions: 8000 x 8000), the memory usage increases significantly. This is expected, but a concern arises when selecting additional images or re-selecting the same image. Each new image selection causes a cumulative increase in memory usage, indicating that the previous images are not being properly disposed of.

  2. Memory Usage Table:

    • After 1st image selection:
      image

    • After 2nd image selection:
      image

    • After 3rd image selection:
      image

    • After 4th image selection:
      image

  3. Behavior: The issue escalates when attempting to find a way to dispose of objects in a WinUI 3 app. After selecting high-resolution images multiple times (with memory usage exceeding 1000 MB), the application stops running unexpectedly without any error or exception. I have not been able to identify a method to release the memory effectively after selecting a new image.

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="MyApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp"
    xmlns:controls="using:CommunityToolkit.WinUI.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid RowSpacing="24">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <controls:ImageCropper x:Name="imageCropper"
                               Width="520"
                               Height="520"
                               AspectRatio="0"
                               CropShape="Rectangular"
                               ThumbPlacement="All" />
            <StackPanel Grid.Row="1"
                    HorizontalAlignment="Center"
                    Orientation="Horizontal"
                    Spacing="8">
                <Button Click="PickButton_Click"
                    Content="Pick image"
                    Style="{StaticResource AccentButtonStyle}" />
                <Button Click="SaveButton_Click"
                    Content="Save"
                    Style="{StaticResource AccentButtonStyle}" />
                <Button Click="ResetButton_Click"
                    Content="Reset"
                    Style="{StaticResource AccentButtonStyle}" />
            </StackPanel>
        </Grid>
    </StackPanel>
</Window>
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Storage.Pickers;
using Windows.Storage;

namespace MyApp
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private async Task PickImage()
        {
            var openPicker = new FileOpenPicker();
            var window = this;
            var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);

            WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);

            openPicker.ViewMode = PickerViewMode.Thumbnail;

            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

            openPicker.FileTypeFilter.Add(".jpg");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".png");

            var file = await openPicker.PickSingleFileAsync();

            if (file != null && imageCropper != null)
            {
                await imageCropper.LoadImageFromFile(file);
            }
        }

        private async Task SaveCroppedImage()
        {
            var savePicker = new FileSavePicker
            {
                SuggestedStartLocation = PickerLocationId.PicturesLibrary,
                SuggestedFileName = "Cropped_Image",
                FileTypeChoices =
                {
                    { "PNG Picture", new List<string> { ".png" } },
                    { "JPEG Picture", new List<string> { ".jpg" } }
                }
            };
            var imageFile = await savePicker.PickSaveFileAsync();
            if (imageFile != null)
            {
                BitmapFileFormat bitmapFileFormat;
                switch (imageFile.FileType.ToLower())
                {
                    case ".png":
                        bitmapFileFormat = BitmapFileFormat.Png;
                        break;
                    case ".jpg":
                        bitmapFileFormat = BitmapFileFormat.Jpeg;
                        break;
                    default:
                        bitmapFileFormat = BitmapFileFormat.Png;
                        break;
                }

                using (var fileStream = await imageFile.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
                {
                    await imageCropper.SaveAsync(fileStream, bitmapFileFormat);
                }
            }
        }

        private async void PickButton_Click(object sender, RoutedEventArgs e)
        {
            await PickImage();
        }

        private async void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            await SaveCroppedImage();
        }

        private void ResetButton_Click(object sender, RoutedEventArgs e)
        {
            imageCropper.Reset();
        }

    }
}

Steps to reproduce

I. Steps to Reproduce the Memory Management Issue in WCT Gallery:

  1. Run the Application:

    • Launch your application to display the MainWindow with the ImageCropper control and the "Pick image" button.
  2. Select a High-Resolution Image:

    • Click the "Pick image" button to open the file picker.
    • Choose a high-resolution image (e.g., 8000 x 8000 pixels) from your file system (this is the image I used).
  3. Observe Memory Usage:

    • Use Task Manager or a memory profiling tool to monitor the application's memory usage.
  4. Re-select the Same High-Resolution Image:

    • Click the "Pick image" button again and select the same high-resolution image you picked earlier.
    • Observe the increase in memory usage.
  5. Repeat the Image Selection:

    • Continue clicking the "Pick image" button and re-selecting the same high-resolution image multiple times.
    • Track the cumulative increase in memory usage with each image selection.

II. Steps to Reproduce the Memory Management Issue in WinUI3:

  1. Setup the Application:

    • Use the provided XAML and C# code to set up your WinUI 3 project.
  2. Run the Application:

    • Launch the application to display MainWindow with the ImageCropper control and the "Pick image" button.
  3. Select a High-Resolution Image:

    • Click the "Pick image" button.
    • Choose a high-resolution image (e.g., 8000 x 8000 pixels) from your file system.
  4. Observe Memory Usage:

    • Monitor the application’s memory usage using Task Manager or a similar profiling tool.
  5. Re-select the Same Image:

    • Click the "Pick image" button again and select the same high-resolution image you picked earlier.
    • Observe the increase in memory usage.
  6. Repeat the Process:

    • Continue clicking the "Pick image" button and re-selecting the same high-resolution image multiple times.
    • Monitor the cumulative increase in memory usage with each selection.
  7. Check Application Behavior:

    • Continue the process until you notice a significant increase in memory usage or if the application stops running without errors (e.g., after memory usage exceeds 1000 MB).

Expected behavior

  • Memory usage should not increase cumulatively with each selection of the same image. The previously selected image should be properly disposed of to prevent memory leaks.

  • Memory usage increases cumulatively with each image selection, suggesting that the previous image is not being disposed of correctly and remains in memory.

Screenshots

No response

Code Platform

  • UWP
  • WinAppSDK / WinUI 3
  • Web Assembly (WASM)
  • Android
  • iOS
  • MacOS
  • Linux / GTK

Windows Build Number

  • Windows 10 1809 (Build 17763)
  • Windows 10 1903 (Build 18362)
  • Windows 10 1909 (Build 18363)
  • Windows 10 2004 (Build 19041)
  • Windows 10 20H2 (Build 19042)
  • Windows 10 21H1 (Build 19043)
  • Windows 10 21H2 (Build 19044)
  • Windows 10 22H2 (Build 19045)
  • Windows 11 21H2 (Build 22000)
  • Other (specify)

Other Windows Build number

No response

App minimum and target SDK version

  • Windows 10, version 1809 (Build 17763)
  • Windows 10, version 1903 (Build 18362)
  • Windows 10, version 1909 (Build 18363)
  • Windows 10, version 2004 (Build 19041)
  • Windows 10, version 2104 (Build 20348)
  • Windows 11, version 22H2 (Build 22000)
  • Other (specify)

Other SDK version

No response

Visual Studio Version

No response

Visual Studio Build Number

No response

Device form factor

No response

Additional context

No response

Help us help you

Yes, but only if others can assist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant