mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
244 lines
9.8 KiB
C#
244 lines
9.8 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using NAPS2.Images.Bitwise;
|
|
|
|
namespace NAPS2.Images;
|
|
|
|
public static class ImageExtensions
|
|
{
|
|
public static IMemoryImage Render(this IRenderableImage image)
|
|
{
|
|
return image.ImageContext.Render(image);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if we can copy the source JPEG directly rather than re-encoding and suffering JPEG degradation.
|
|
/// </summary>
|
|
/// <param name="image"></param>
|
|
/// <param name="jpegPath"></param>
|
|
/// <returns></returns>
|
|
internal static bool IsUntransformedJpegFile(this IRenderableImage image, out string jpegPath)
|
|
{
|
|
if (image is { Storage: ImageFileStorage fileStorage, TransformState.IsEmpty: true } &&
|
|
ImageContext.GetFileFormatFromExtension(fileStorage.FullPath) == ImageFileFormat.Jpeg)
|
|
{
|
|
jpegPath = fileStorage.FullPath;
|
|
return true;
|
|
}
|
|
jpegPath = null!;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the image to the given file path. If the file format is unspecified, it will be inferred from the
|
|
/// file extension if possible.
|
|
/// </summary>
|
|
/// <param name="image">The image to save.</param>
|
|
/// <param name="path">The path to save the image file to.</param>
|
|
/// <param name="imageFormat">The file format to use.</param>
|
|
/// <param name="options">Options for saving, e.g. JPEG quality.</param>
|
|
public static void Save(this IRenderableImage image, string path,
|
|
ImageFileFormat imageFormat = ImageFileFormat.Unknown, ImageSaveOptions? options = null)
|
|
{
|
|
if (imageFormat == ImageFileFormat.Unknown)
|
|
{
|
|
imageFormat = ImageContext.GetFileFormatFromExtension(path);
|
|
}
|
|
if (imageFormat == ImageFileFormat.Jpeg && image.IsUntransformedJpegFile(out var jpegPath))
|
|
{
|
|
File.Copy(jpegPath, path);
|
|
return;
|
|
}
|
|
using var renderedImage = image.Render();
|
|
renderedImage.Save(path, imageFormat, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the image to the given stream. The file format must be specified.
|
|
/// </summary>
|
|
/// <param name="image">The image to save.</param>
|
|
/// <param name="stream">The stream to save the image to.</param>
|
|
/// <param name="imageFormat">The file format to use.</param>
|
|
/// <param name="options">Options for saving, e.g. JPEG quality.</param>
|
|
public static void Save(this IRenderableImage image, Stream stream,
|
|
ImageFileFormat imageFormat = ImageFileFormat.Unknown, ImageSaveOptions? options = null)
|
|
{
|
|
if (imageFormat == ImageFileFormat.Unknown)
|
|
{
|
|
throw new ArgumentException("Format required to save to a stream", nameof(imageFormat));
|
|
}
|
|
if (imageFormat == ImageFileFormat.Jpeg && image.IsUntransformedJpegFile(out var jpegPath))
|
|
{
|
|
using var fileStream = File.OpenRead(jpegPath);
|
|
fileStream.CopyTo(stream);
|
|
return;
|
|
}
|
|
using var renderedImage = image.Render();
|
|
renderedImage.Save(stream, imageFormat, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the image to a new MemoryStream object. The file format must be specified.
|
|
/// </summary>
|
|
/// <param name="image">The image to save.</param>
|
|
/// <param name="imageFormat">The file format to use.</param>
|
|
/// <param name="options">Options for saving, e.g. JPEG quality.</param>
|
|
public static MemoryStream SaveToMemoryStream(this IMemoryImage image, ImageFileFormat imageFormat,
|
|
ImageSaveOptions? options = null)
|
|
{
|
|
var stream = new MemoryStream();
|
|
image.Save(stream, imageFormat, options);
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
return stream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the image to a new MemoryStream object. The file format must be specified.
|
|
/// </summary>
|
|
/// <param name="image">The image to save.</param>
|
|
/// <param name="imageFormat">The file format to use.</param>
|
|
/// <param name="options">Options for saving, e.g. JPEG quality.</param>
|
|
public static MemoryStream SaveToMemoryStream(this IRenderableImage image, ImageFileFormat imageFormat,
|
|
ImageSaveOptions? options = null)
|
|
{
|
|
var stream = new MemoryStream();
|
|
image.Save(stream, imageFormat, options);
|
|
return stream;
|
|
}
|
|
|
|
public static IMemoryImage PerformTransform(this IMemoryImage image, Transform transform)
|
|
{
|
|
return image.ImageContext.PerformTransform(image, transform);
|
|
}
|
|
|
|
public static IMemoryImage PerformAllTransforms(this IMemoryImage image, IEnumerable<Transform> transforms)
|
|
{
|
|
return image.ImageContext.PerformAllTransforms(image, transforms);
|
|
}
|
|
|
|
public static ImagePixelFormat UpdateLogicalPixelFormat(this IMemoryImage image)
|
|
{
|
|
if (image.LogicalPixelFormat != ImagePixelFormat.Unknown)
|
|
{
|
|
return image.LogicalPixelFormat;
|
|
}
|
|
var op = new LogicalPixelFormatOp();
|
|
op.Perform(image);
|
|
image.LogicalPixelFormat = op.LogicalPixelFormat;
|
|
return image.LogicalPixelFormat;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the content of this image to the destination image. It does not need to be the same pixel format, but if it's different,
|
|
/// there may be some loss of information (e.g. when converting color to gray or black/white).
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="destination"></param>
|
|
public static void CopyTo(this IMemoryImage source, IMemoryImage destination)
|
|
{
|
|
new CopyBitwiseImageOp().Perform(source, destination);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new image with the same content, dimensions, and resolution as this image.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage Copy(this IMemoryImage source)
|
|
{
|
|
return source.CopyWithPixelFormat(source.PixelFormat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new image with the same content, dimensions, and resolution as this image.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="imageContext"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage Copy(this IMemoryImage source, ImageContext imageContext)
|
|
{
|
|
return source.CopyWithPixelFormat(imageContext, source.PixelFormat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new image with the same content, dimensions, and resolution as this image, but possibly with a different pixel format.
|
|
/// This can result in some loss of information (e.g. when converting color to gray or black/white).
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="pixelFormat"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage CopyWithPixelFormat(this IMemoryImage source, ImagePixelFormat pixelFormat)
|
|
{
|
|
return source.CopyWithPixelFormat(source.ImageContext, pixelFormat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new image with the same content, dimensions, and resolution as this image, but possibly with a different pixel format.
|
|
/// This can result in some loss of information (e.g. when converting color to gray or black/white).
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="imageContext"></param>
|
|
/// <param name="pixelFormat"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage CopyWithPixelFormat(this IMemoryImage source, ImageContext imageContext,
|
|
ImagePixelFormat pixelFormat)
|
|
{
|
|
if (pixelFormat == ImagePixelFormat.Unknown) throw new ArgumentException();
|
|
var newImage = source.CopyBlankWithPixelFormat(imageContext, pixelFormat);
|
|
new CopyBitwiseImageOp().Perform(source, newImage);
|
|
newImage.OriginalFileFormat = source.OriginalFileFormat;
|
|
if (source.LogicalPixelFormat != ImagePixelFormat.Unknown)
|
|
{
|
|
newImage.LogicalPixelFormat =
|
|
source.LogicalPixelFormat < pixelFormat ? source.LogicalPixelFormat : pixelFormat;
|
|
}
|
|
return newImage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new (empty) image with the same dimensions, pixel format, and resolution as this image.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage CopyBlank(this IMemoryImage source)
|
|
{
|
|
return source.CopyBlankWithPixelFormat(source.PixelFormat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new (empty) image with the same dimensions and resolution as this image, and the specified pixel format.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="pixelFormat"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage CopyBlankWithPixelFormat(this IMemoryImage source, ImagePixelFormat pixelFormat)
|
|
{
|
|
return CopyBlankWithPixelFormat(source, source.ImageContext, pixelFormat);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new (empty) image with the same dimensions and resolution as this image, and the specified pixel format.
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="imageContext"></param>
|
|
/// <param name="pixelFormat"></param>
|
|
/// <returns></returns>
|
|
public static IMemoryImage CopyBlankWithPixelFormat(this IMemoryImage source, ImageContext imageContext,
|
|
ImagePixelFormat pixelFormat)
|
|
{
|
|
if (pixelFormat == ImagePixelFormat.Unknown) throw new ArgumentException();
|
|
var newImage = imageContext.Create(source.Width, source.Height, pixelFormat);
|
|
newImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);
|
|
return newImage;
|
|
}
|
|
|
|
public static string AsTypeHint(this ImageFileFormat imageFormat)
|
|
{
|
|
return imageFormat switch
|
|
{
|
|
ImageFileFormat.Bmp => ".bmp",
|
|
ImageFileFormat.Jpeg => ".jpg",
|
|
ImageFileFormat.Png => ".png",
|
|
_ => ""
|
|
};
|
|
}
|
|
} |