diff --git a/src/Filee.App/Services/HideService.cs b/src/Filee.App/Services/HideService.cs
index 022c07359..3fda0e3c3 100644
--- a/src/Filee.App/Services/HideService.cs
+++ b/src/Filee.App/Services/HideService.cs
@@ -78,7 +78,7 @@ public static Bitmap Cover(MagickImage preview, HideStyle style, double size)
/// The picture as a bitmap the window draws.
public static Bitmap ToBitmap(IMagickImage image)
{
- var pixels = image.ToByteArray(MagickFormat.Bgra);
+ var pixels = PixelsOf(image);
var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
try
{
@@ -90,4 +90,19 @@ public static Bitmap ToBitmap(IMagickImage image)
handle.Free();
}
}
+
+ ///
+ /// The picture's pixels as BGRA, four bytes each, for : read from its pixels, 8 bits a
+ /// channel whatever depth it was stored with. Written out as "BGRA" a 1-bit or palette PNG kept its depth, an
+ /// eighth of the bytes, and the bitmap read past their end.
+ ///
+ internal static byte[] PixelsOf(IMagickImage image)
+ {
+ using var pixels = image.GetPixels();
+ var bytes = pixels.ToByteArray(PixelMapping.BGRA)
+ ?? throw new InvalidOperationException("The picture's pixels could not be read.");
+ if (bytes.Length != (long)image.Width * image.Height * 4)
+ throw new InvalidOperationException($"Expected {image.Width * image.Height * 4} bytes of pixels, got {bytes.Length}.");
+ return bytes;
+ }
}
diff --git a/tests/Filee.App.Tests/HidePreviewPixelsTests.cs b/tests/Filee.App.Tests/HidePreviewPixelsTests.cs
new file mode 100644
index 000000000..6445d9c55
--- /dev/null
+++ b/tests/Filee.App.Tests/HidePreviewPixelsTests.cs
@@ -0,0 +1,39 @@
+// The Pixelate / blur window's preview of a picture stored with fewer than 8 bits a channel: a 1-bit grey or palette
+// PNG (a flat screenshot, a diagram). Its pixels come out at 8 bits a channel, four bytes each, in their real colours;
+// reading them as the picture's own depth gave an eighth of the bytes, and the window then read past their end.
+
+using Filee.App.Services;
+using ImageMagick;
+using ImageMagick.Drawing;
+
+namespace Filee.App.Tests;
+
+public class HidePreviewPixelsTests
+{
+ private static MagickImage Saved(MagickImage image)
+ {
+ var path = Path.Combine(Directory.CreateTempSubdirectory("filee-depth-").FullName, "picture.png");
+ image.Write(path, MagickFormat.Png);
+ return new MagickImage(path);
+ }
+
+ [Fact]
+ public void A_one_bit_and_a_palette_png_give_four_bytes_a_pixel_in_their_colours()
+ {
+ using var white = Saved(new MagickImage(MagickColors.White, 40, 30));
+ using var drawing = new MagickImage(MagickColors.White, 40, 30);
+ new Drawables().FillColor(new MagickColor("#3E63DD")).Rectangle(0, 0, 9, 9).Draw(drawing);
+ using var palette = Saved(drawing);
+ Assert.True(white.Depth < 8 || palette.Depth < 8, "the pictures should be stored with fewer than 8 bits");
+
+ var whitePixels = HideService.PixelsOf(white);
+ var palettePixels = HideService.PixelsOf(palette);
+
+ Assert.Equal(40 * 30 * 4, whitePixels.Length);
+ Assert.Equal(40 * 30 * 4, palettePixels.Length);
+ Assert.Equal(new byte[] { 255, 255, 255, 255 }, whitePixels[..4]);
+ // BGRA: the blue square in the top left corner, white beside it.
+ Assert.Equal(new byte[] { 0xDD, 0x63, 0x3E, 255 }, palettePixels[..4]);
+ Assert.Equal(new byte[] { 255, 255, 255, 255 }, palettePixels[(20 * 4)..(20 * 4 + 4)]);
+ }
+}