naps2/NAPS2.Images.Mac/MacBitmapHelper.cs
2024-02-13 23:27:41 -08:00

97 lines
3.9 KiB
C#

namespace NAPS2.Images.Mac;
internal static class MacBitmapHelper
{
public static NSBitmapImageRep CopyRep(NSBitmapImageRep original)
{
var w = original.PixelsWide;
var h = original.PixelsHigh;
// TODO: Consider creating something other than ARGB32 images based on the original rep
// Though it doesn't matter that much as we're not hitting this case in practice.
var copy = CreateRepForDrawing(w, h);
using var c = CreateContext(copy, false, true);
CGRect rect = new CGRect(0, 0, w, h);
using var cgImage = original.AsCGImage(ref rect, null, null);
c.DrawImage(rect, cgImage);
return copy;
}
public static NSBitmapImageRep CreateRep(long width, long height, ImagePixelFormat pixelFormat)
{
var samplesPerPixel = pixelFormat switch
{
ImagePixelFormat.ARGB32 => 4,
ImagePixelFormat.RGB24 => 4,
ImagePixelFormat.Gray8 => 1,
ImagePixelFormat.BW1 => 1,
_ => throw new ArgumentException("Unsupported pixel format")
};
var bitsPerSample = pixelFormat == ImagePixelFormat.BW1 ? 1 : 8;
var colorSpace = pixelFormat is ImagePixelFormat.Gray8 or ImagePixelFormat.BW1
? NSColorSpace.DeviceWhite
: NSColorSpace.DeviceRGB;
bool hasAlpha = pixelFormat == ImagePixelFormat.ARGB32;
return CreateRep(width, height, samplesPerPixel, bitsPerSample, colorSpace, hasAlpha);
}
public static NSBitmapImageRep CreateRepForDrawing(long width, long height)
{
return CreateRep(width, height, 4, 8, NSColorSpace.DeviceRGB, true);
}
private static NSBitmapImageRep CreateRep(long width, long height, int samplesPerPixel, int bitsPerSample,
NSString colorSpace, bool hasAlpha)
{
lock (MacImageContext.ConstructorLock)
{
var realSamples = samplesPerPixel == 4 && !hasAlpha ? 3 : samplesPerPixel;
return new NSBitmapImageRep(
IntPtr.Zero,
width.ToNInt(),
height.ToNInt(),
bitsPerSample,
realSamples,
hasAlpha,
false,
colorSpace,
(samplesPerPixel * width).ToNInt(),
samplesPerPixel * bitsPerSample);
}
}
public static CGBitmapContext CreateContext(MacImage image)
{
if (image.PixelFormat is not (ImagePixelFormat.Gray8 or ImagePixelFormat.RGB24 or ImagePixelFormat.ARGB32))
{
// Only some formats supported for drawing, see "Pixel formats supported for bitmap graphics contexts"
// https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
throw new ArgumentException($"Unsupported pixel format for CGBitmapContext: {image.PixelFormat}");
}
bool isGray = image.PixelFormat == ImagePixelFormat.Gray8;
return CreateContext(image.Rep, isGray, !isGray);
}
public static CGBitmapContext CreateContext(NSBitmapImageRep rep, bool isGray, bool hasAlpha)
{
var colorSpace = isGray
? CGColorSpace.CreateDeviceGray()
: CGColorSpace.CreateDeviceRGB();
var bytesPerPixel = rep.BytesPerRow / rep.PixelsWide;
var alphaInfo = hasAlpha
? CGImageAlphaInfo.PremultipliedLast
: bytesPerPixel is 4 or 8
? CGImageAlphaInfo.NoneSkipLast
: CGImageAlphaInfo.None;
lock (MacImageContext.ConstructorLock)
{
return new CGBitmapContext(
rep.BitmapData,
(int) rep.PixelsWide,
(int) rep.PixelsHigh,
(int) rep.BitsPerSample,
(int) rep.BytesPerRow,
colorSpace,
alphaInfo);
}
}
}