diff --git a/NAPS2.Images.Gdi/BitmapExtensions.cs b/NAPS2.Images.Gdi/BitmapExtensions.cs
deleted file mode 100644
index b49e0d972..000000000
--- a/NAPS2.Images.Gdi/BitmapExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Drawing;
-
-namespace NAPS2.Images.Gdi;
-
-public static class BitmapExtensions
-{
- public static void SafeSetResolution(this Bitmap image, float xDpi, float yDpi)
- {
- if (xDpi > 0 && yDpi > 0)
- {
- image.SetResolution(xDpi, yDpi);
- }
- }
-}
\ No newline at end of file
diff --git a/NAPS2.Images.Gdi/GdiExtensions.cs b/NAPS2.Images.Gdi/GdiExtensions.cs
index 9a465c75e..d97421228 100644
--- a/NAPS2.Images.Gdi/GdiExtensions.cs
+++ b/NAPS2.Images.Gdi/GdiExtensions.cs
@@ -5,6 +5,14 @@ namespace NAPS2.Images.Gdi;
public static class GdiExtensions
{
+ public static void SafeSetResolution(this Bitmap image, float xDpi, float yDpi)
+ {
+ if (xDpi > 0 && yDpi > 0)
+ {
+ image.SetResolution(xDpi, yDpi);
+ }
+ }
+
public static Bitmap AsBitmap(this IMemoryImage image)
{
var gdiImage = image as GdiImage ?? throw new ArgumentException("Expected a GdiImage", nameof(image));
@@ -56,4 +64,32 @@ public static class GdiExtensions
}
throw new ArgumentException("Unsupported pixel format: " + pixelFormat);
}
+
+ public static ImagePixelFormat AsImagePixelFormat(this PixelFormat pixelFormat)
+ {
+ switch (pixelFormat)
+ {
+ case PixelFormat.Format24bppRgb:
+ return ImagePixelFormat.RGB24;
+ case PixelFormat.Format32bppArgb:
+ return ImagePixelFormat.ARGB32;
+ case PixelFormat.Format1bppIndexed:
+ return ImagePixelFormat.BW1;
+ default:
+ return ImagePixelFormat.Unsupported;
+ }
+ }
+
+ public static ImageLockMode AsImageLockMode(this LockMode lockMode)
+ {
+ switch (lockMode)
+ {
+ case LockMode.ReadOnly:
+ return ImageLockMode.ReadOnly;
+ case LockMode.WriteOnly:
+ return ImageLockMode.WriteOnly;
+ default:
+ return ImageLockMode.ReadWrite;
+ }
+ }
}
diff --git a/NAPS2.Images.Gdi/GdiImage.cs b/NAPS2.Images.Gdi/GdiImage.cs
index 169ec4004..fe21bde8c 100644
--- a/NAPS2.Images.Gdi/GdiImage.cs
+++ b/NAPS2.Images.Gdi/GdiImage.cs
@@ -13,6 +13,9 @@ public class GdiImage : IMemoryImage
Bitmap = bitmap ?? throw new ArgumentNullException(nameof(bitmap));
}
+ ///
+ /// Gets the underlying System.Drawing.Bitmap object for this image.
+ ///
public Bitmap Bitmap { get; }
public int Width => Bitmap.Width;
@@ -25,48 +28,19 @@ public class GdiImage : IMemoryImage
public void SetResolution(float xDpi, float yDpi) => Bitmap.SafeSetResolution(xDpi, yDpi);
- public ImagePixelFormat PixelFormat
- {
- get
- {
- switch (Bitmap.PixelFormat)
- {
- case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
- return ImagePixelFormat.RGB24;
- case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
- return ImagePixelFormat.ARGB32;
- case System.Drawing.Imaging.PixelFormat.Format1bppIndexed:
- return ImagePixelFormat.BW1;
- default:
- return ImagePixelFormat.Unsupported;
- }
- }
- }
+ public ImagePixelFormat PixelFormat => Bitmap.PixelFormat.AsImagePixelFormat();
public ImageFileFormat OriginalFileFormat => Bitmap.RawFormat.AsImageFileFormat();
public ImageLockState Lock(LockMode lockMode, out IntPtr scan0, out int stride)
{
- var bitmapData = Bitmap.LockBits(new Rectangle(0, 0, Bitmap.Width, Bitmap.Height), GetGdiLockMode(lockMode),
+ var bitmapData = Bitmap.LockBits(new Rectangle(0, 0, Bitmap.Width, Bitmap.Height), lockMode.AsImageLockMode(),
Bitmap.PixelFormat);
scan0 = bitmapData.Scan0;
stride = Math.Abs(bitmapData.Stride);
return new GdiImageLockState(Bitmap, bitmapData);
}
- private ImageLockMode GetGdiLockMode(LockMode lockMode)
- {
- switch (lockMode)
- {
- case LockMode.ReadOnly:
- return ImageLockMode.ReadOnly;
- case LockMode.WriteOnly:
- return ImageLockMode.WriteOnly;
- default:
- return ImageLockMode.ReadWrite;
- }
- }
-
public void Save(string path, ImageFileFormat imageFileFormat = ImageFileFormat.Unspecified, int quality = -1)
{
if (imageFileFormat == ImageFileFormat.Unspecified)
@@ -75,10 +49,7 @@ public class GdiImage : IMemoryImage
}
if (imageFileFormat == ImageFileFormat.Jpeg && quality != -1)
{
- quality = Math.Max(Math.Min(quality, 100), 0);
- var encoder = ImageCodecInfo.GetImageEncoders().First(x => x.FormatID == ImageFormat.Jpeg.Guid);
- var encoderParams = new EncoderParameters(1);
- encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
+ var (encoder, encoderParams) = GetJpegSaveArgs(quality);
Bitmap.Save(path, encoder, encoderParams);
}
else
@@ -95,10 +66,7 @@ public class GdiImage : IMemoryImage
}
if (imageFileFormat == ImageFileFormat.Jpeg && quality != -1)
{
- quality = Math.Max(Math.Min(quality, 100), 0);
- var encoder = ImageCodecInfo.GetImageEncoders().First(x => x.FormatID == ImageFormat.Jpeg.Guid);
- var encoderParams = new EncoderParameters(1);
- encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
+ var (encoder, encoderParams) = GetJpegSaveArgs(quality);
Bitmap.Save(stream, encoder, encoderParams);
}
else
@@ -107,6 +75,15 @@ public class GdiImage : IMemoryImage
}
}
+ private static (ImageCodecInfo, EncoderParameters) GetJpegSaveArgs(int quality)
+ {
+ quality = Math.Max(Math.Min(quality, 100), 0);
+ var encoder = ImageCodecInfo.GetImageEncoders().First(x => x.FormatID == ImageFormat.Jpeg.Guid);
+ var encoderParams = new EncoderParameters(1);
+ encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
+ return (encoder, encoderParams);
+ }
+
private ImageFileFormat GetFileFormatFromExtension(string path)
{
return Path.GetExtension(path).ToLowerInvariant() switch
@@ -118,13 +95,13 @@ public class GdiImage : IMemoryImage
};
}
- public void Dispose()
- {
- Bitmap.Dispose();
- }
-
public IMemoryImage Clone()
{
return new GdiImage((Bitmap) Bitmap.Clone());
}
+
+ public void Dispose()
+ {
+ Bitmap.Dispose();
+ }
}
\ No newline at end of file
diff --git a/NAPS2.Images/IMemoryImage.cs b/NAPS2.Images/IMemoryImage.cs
index bfc4598e1..68ca90227 100644
--- a/NAPS2.Images/IMemoryImage.cs
+++ b/NAPS2.Images/IMemoryImage.cs
@@ -6,25 +6,73 @@
///
public interface IMemoryImage : IImageStorage
{
+ ///
+ /// Gets the image's width in pixels.
+ ///
int Width { get; }
+ ///
+ /// Gets the image's height in pixels.
+ ///
int Height { get; }
+ ///
+ /// Gets the image's horizontal resolution in pixels per inch.
+ ///
float HorizontalResolution { get; }
+ ///
+ /// Gets the image's vertical resolution in pixels per inch.
+ ///
float VerticalResolution { get; }
+ ///
+ /// Sets the image's horizontal and vertical resolution in pixels per inch. This has no effect for invalid (zero or
+ /// negative) values.
+ ///
+ /// The horizontal resolution.
+ /// The vertical resolution.
void SetResolution(float xDpi, float yDpi);
+ ///
+ /// Gets the bits per pixel for the image's underlying binary data.
+ ///
ImagePixelFormat PixelFormat { get; }
- ImageFileFormat OriginalFileFormat { get; }
-
+ ///
+ /// Obtains access to the underlying binary data for the image.
+ ///
+ /// The access level (read/write) needed.
+ /// A pointer to the start of the binary data.
+ /// The number of bytes per row in the binary data.
+ /// An object that, when disposed, releases the lock.
ImageLockState Lock(LockMode lockMode, out IntPtr scan0, out int stride);
+ ///
+ /// Gets the original image file's format (e.g. png/jpeg) if known.
+ ///
+ ImageFileFormat OriginalFileFormat { get; }
+
+ ///
+ /// Saves the image to the given file path. If the file format is unspecified, it will be inferred from the
+ /// file extension if possible.
+ ///
+ /// The path to save the image file to.
+ /// The file format to use.
+ /// The quality parameter for JPEG compression, if applicable. -1 for default.
void Save(string path, ImageFileFormat imageFormat = ImageFileFormat.Unspecified, int quality = -1);
+ ///
+ /// Saves the image to the given stream. The file format must be specified.
+ ///
+ /// The stream to save the image to.
+ /// The file format to use.
+ /// The quality parameter for JPEG compression, if applicable. -1 for default.
void Save(Stream stream, ImageFileFormat imageFormat, int quality = -1);
+ ///
+ /// Creates a copy of the image so that one can be edited or disposed without affecting the other.
+ ///
+ /// The copy.
IMemoryImage Clone();
}
\ No newline at end of file