Browse Source

Merge pull request #233 from Xiphereal/limit-memory-usage-for-recently-opened-documents-preview

Limit big file loading for recent opened files
Krzysztof Krysiński 3 years ago
parent
commit
38399fcb7a

+ 28 - 6
PixiEditor/Models/DataHolders/RecentlyOpenedDocument.cs

@@ -1,11 +1,9 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
-using System.Linq;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
-using PixiEditor.Models.Layers;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
 
 
 namespace PixiEditor.Models.DataHolders
 namespace PixiEditor.Models.DataHolders
@@ -75,7 +73,7 @@ namespace PixiEditor.Models.DataHolders
         {
         {
             if (FileExtension == ".pixi")
             if (FileExtension == ".pixi")
             {
             {
-                SerializableDocument serializableDocument = null;
+                SerializableDocument serializableDocument;
 
 
                 try
                 try
                 {
                 {
@@ -87,9 +85,23 @@ namespace PixiEditor.Models.DataHolders
                     return null;
                     return null;
                 }
                 }
 
 
-                return BitmapUtils.GeneratePreviewBitmap(serializableDocument.Layers, serializableDocument.Width, serializableDocument.Height, 80, 50);
+                WriteableBitmap writeableBitmap =
+                    BitmapUtils.GeneratePreviewBitmap(serializableDocument.Layers, serializableDocument.Width, serializableDocument.Height, 80, 50);
+
+                const int MaxWidthInPixels = 1080;
+                const int MaxHeightInPixels = 1080;
+                PixiFileMaxSizeChecker pixiFileMaxSizeChecker = new PixiFileMaxSizeChecker()
+                {
+                    MaxAllowedWidthInPixels = MaxWidthInPixels,
+                    MaxAllowedHeightInPixels = MaxHeightInPixels,
+                    MaxAllowedLayerCount = 5,
+                };
+
+                return pixiFileMaxSizeChecker.IsFileUnderMaxSize(serializableDocument) ?
+                    writeableBitmap
+                    : writeableBitmap.Resize(width: MaxWidthInPixels, height: MaxHeightInPixels, WriteableBitmapExtensions.Interpolation.Bilinear);
             }
             }
-            else if (FileExtension == ".png" || FileExtension == ".jpg" || FileExtension == ".jpeg")
+            else if (FileExtension is ".png" or ".jpg" or ".jpeg")
             {
             {
                 WriteableBitmap bitmap = null;
                 WriteableBitmap bitmap = null;
 
 
@@ -102,7 +114,17 @@ namespace PixiEditor.Models.DataHolders
                     corrupt = true;
                     corrupt = true;
                 }
                 }
 
 
-                return bitmap;
+                const int MaxWidthInPixels = 2048;
+                const int MaxHeightInPixels = 2048;
+                ImageFileMaxSizeChecker imageFileMaxSizeChecker = new ImageFileMaxSizeChecker()
+                {
+                    MaxAllowedWidthInPixels = MaxWidthInPixels,
+                    MaxAllowedHeightInPixels = MaxHeightInPixels,
+                };
+
+                return imageFileMaxSizeChecker.IsFileUnderMaxSize(bitmap) ?
+                    bitmap
+                    : bitmap.Resize(width: MaxWidthInPixels, height: MaxHeightInPixels, WriteableBitmapExtensions.Interpolation.Bilinear);
             }
             }
 
 
             return null;
             return null;

+ 20 - 0
PixiEditor/Models/IO/ImageFileMaxSizeChecker.cs

@@ -0,0 +1,20 @@
+using System.Windows.Media.Imaging;
+
+namespace PixiEditor.Models.IO
+{
+    internal class ImageFileMaxSizeChecker
+    {
+        public int MaxAllowedWidthInPixels { get; init; } = 2048;
+        public int MaxAllowedHeightInPixels { get; init; } = 2048;
+
+        public ImageFileMaxSizeChecker()
+        {
+        }
+
+        public bool IsFileUnderMaxSize(WriteableBitmap fileToCheck)
+        {
+            return fileToCheck.PixelWidth <= MaxAllowedWidthInPixels
+                && fileToCheck.PixelHeight <= MaxAllowedHeightInPixels;
+        }
+    }
+}

+ 22 - 0
PixiEditor/Models/IO/PixiFileMaxSizeChecker.cs

@@ -0,0 +1,22 @@
+using PixiEditor.Parser;
+
+namespace PixiEditor.Models.IO
+{
+    internal class PixiFileMaxSizeChecker
+    {
+        public int MaxAllowedWidthInPixels { get; init; } = 1080;
+        public int MaxAllowedHeightInPixels { get; init; } = 1080;
+        public int MaxAllowedLayerCount { get; init; } = 5;
+
+        public PixiFileMaxSizeChecker()
+        {
+        }
+
+        public bool IsFileUnderMaxSize(SerializableDocument fileToCheck)
+        {
+            return fileToCheck.Width <= MaxAllowedWidthInPixels
+                && fileToCheck.Height <= MaxAllowedHeightInPixels
+                && fileToCheck.Layers.Count <= MaxAllowedLayerCount;
+        }
+    }
+}

+ 72 - 0
PixiEditorTests/ModelsTests/DataHoldersTests/RecentlyOpenedDocumentTests.cs

@@ -0,0 +1,72 @@
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Parser;
+using System;
+using Xunit;
+
+namespace PixiEditorTests.ModelsTests.DataHoldersTests
+{
+    [Collection("Application collection")]
+    public class RecentlyOpenedDocumentTests
+    {
+        [Fact]
+        public void TestThatForBigPixiFilesPreviewImageIsResizedToMaxSize()
+        {
+            string bigFilePath = $@"{Environment.CurrentDirectory}\..\..\..\ModelsTests\IO\BigPixiFile.pixi";
+            RecentlyOpenedDocument recentlyOpenedDocument = new RecentlyOpenedDocument(bigFilePath);
+
+            var bigPixiFilePreviewImage = recentlyOpenedDocument.PreviewBitmap;
+
+            const int MaxWidthInPixels = 1080;
+            Assert.True(bigPixiFilePreviewImage.PixelWidth <= MaxWidthInPixels);
+
+            const int MaxHeightInPixels = 1080;
+            Assert.True(bigPixiFilePreviewImage.PixelHeight <= MaxHeightInPixels);
+
+            // This is a workaround for checking the Pixi file layers.
+            Assert.True(PixiParser.Deserialize(bigFilePath).Layers.Count <= 5);
+        }
+
+        [Fact]
+        public void TestThatForSmallEnoughPixiFilesPreviewImageIsLoaded()
+        {
+            string smallEnoughFilePath = $@"{Environment.CurrentDirectory}\..\..\..\ModelsTests\IO\SmallEnoughPixiFile.pixi";
+            RecentlyOpenedDocument recentlyOpenedDocument = new RecentlyOpenedDocument(smallEnoughFilePath);
+
+            var smallEnoughFilePreviewImage = recentlyOpenedDocument.PreviewBitmap;
+
+            Assert.NotNull(smallEnoughFilePreviewImage);
+        }
+
+        [Theory]
+        [InlineData("png")]
+        [InlineData("jpg")]
+        [InlineData("jpeg")]
+        public void TestThatForBigImageFilesPreviewImageIsResizedToMaxSize(string imageFormat)
+        {
+            string bigImageFilePath = $@"{Environment.CurrentDirectory}\..\..\..\ModelsTests\IO\BigImage.{imageFormat}";
+            RecentlyOpenedDocument recentlyOpenedDocument = new RecentlyOpenedDocument(bigImageFilePath);
+
+            var bigImagePreviewImage = recentlyOpenedDocument.PreviewBitmap;
+
+            const int MaxWidthInPixels = 2048;
+            Assert.True(bigImagePreviewImage.PixelWidth <= MaxWidthInPixels);
+
+            const int MaxHeightInPixels = 2048;
+            Assert.True(bigImagePreviewImage.PixelHeight <= MaxHeightInPixels);
+        }
+
+        [Theory]
+        [InlineData("png")]
+        [InlineData("jpg")]
+        [InlineData("jpeg")]
+        public void TestThatForSmallEnoughImageFilesPreviewImageIsLoaded(string imageFormat)
+        {
+            string smallEnoughImageFilePath = $@"{Environment.CurrentDirectory}\..\..\..\ModelsTests\IO\SmallEnoughImage.{imageFormat}";
+            RecentlyOpenedDocument recentlyOpenedDocument = new RecentlyOpenedDocument(smallEnoughImageFilePath);
+
+            var smallEnoughImagePreviewImage = recentlyOpenedDocument.PreviewBitmap;
+
+            Assert.NotNull(smallEnoughImagePreviewImage);
+        }
+    }
+}

BIN
PixiEditorTests/ModelsTests/IO/BigImage.jpeg


BIN
PixiEditorTests/ModelsTests/IO/BigImage.jpg


BIN
PixiEditorTests/ModelsTests/IO/BigImage.png


BIN
PixiEditorTests/ModelsTests/IO/BigPixiFile.pixi


BIN
PixiEditorTests/ModelsTests/IO/SmallEnoughImage.jpeg


BIN
PixiEditorTests/ModelsTests/IO/SmallEnoughImage.jpg


BIN
PixiEditorTests/ModelsTests/IO/SmallEnoughImage.png


BIN
PixiEditorTests/ModelsTests/IO/SmallEnoughPixiFile.pixi