mirror of
https://github.com/lifegpc/GARbro.git
synced 2026-06-06 05:28:49 +08:00
252 lines
9.1 KiB
C#
252 lines
9.1 KiB
C#
using System;
|
|
using System.ComponentModel.Composition;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
|
|
namespace GameRes.Formats
|
|
{
|
|
[Export(typeof(ImageFormat))]
|
|
public class AvifImageFormat : ImageFormat, IDisposable
|
|
{
|
|
public override string Tag { get { return "AVIF"; } }
|
|
public override string Description { get { return "AV1 Image File Format"; } }
|
|
public override uint Signature { get { return 0; } }
|
|
public override bool CanWrite { get { return false; } }
|
|
|
|
public AvifImageFormat()
|
|
{
|
|
Extensions = new[] { "avif" };
|
|
}
|
|
|
|
public enum avifResult
|
|
{
|
|
OK = 0,
|
|
UnknownError = 1,
|
|
InvalidFtyp = 2,
|
|
NoContent = 3,
|
|
NoYuvFormatSelected = 4,
|
|
ReformatFailed = 5,
|
|
UnsupportedDepth = 6,
|
|
EncodeColorFailed = 7,
|
|
EncodeAlphaFailed = 8,
|
|
BmffParseFailed = 9,
|
|
MissingImageItem = 10,
|
|
DecodeColorFailed = 11,
|
|
DecodeAlphaFailed = 12,
|
|
ColorAlphaSizeMismatch = 13,
|
|
IspeSizeMismatch = 14,
|
|
NoCodecAvailable = 15,
|
|
NoImagesRemaining = 16,
|
|
InvalidExifPayload = 17,
|
|
InvalidImageGrid = 18,
|
|
InvalidCodecSpecificOption = 19,
|
|
TruncatedData = 20,
|
|
IoNotSet = 21,
|
|
IoError = 22,
|
|
WaitingOnIo = 23,
|
|
InvalidArgument = 24,
|
|
NotImplemented = 25,
|
|
OutOfMemory = 26,
|
|
CannotChangeSetting = 27,
|
|
IncompatibleImage = 28,
|
|
InternalError = 29,
|
|
EncodeGainMapFailed = 30,
|
|
DecodeGainMapFailed = 31,
|
|
InvalidToneMappedImage = 32,
|
|
}
|
|
|
|
public enum avifRGBFormat
|
|
{
|
|
RGB = 0,
|
|
RGBA,
|
|
ARGB,
|
|
BGR,
|
|
BGRA,
|
|
ABGR,
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct avifRWData
|
|
{
|
|
public IntPtr data;
|
|
public UIntPtr size;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct avifImage
|
|
{
|
|
public uint width;
|
|
public uint height;
|
|
public uint depth;
|
|
public int yuvFormat;
|
|
public int yuvRange;
|
|
public int yuvChromaSamplePosition;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
|
public IntPtr[] yuvPlanes;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
|
public uint[] yuvRowBytes;
|
|
public int imageOwnsYUVPlanes;
|
|
public IntPtr alphaPlane;
|
|
public uint alphaRowBytes;
|
|
public int imageOwnsAlphaPlane;
|
|
public int alphaPremultiplied;
|
|
public avifRWData icc;
|
|
public ushort colorPrimaries;
|
|
public ushort transferCharacteristics;
|
|
public ushort matrixCoefficients;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct avifRGBImage
|
|
{
|
|
public uint width;
|
|
public uint height;
|
|
public uint depth;
|
|
public avifRGBFormat format;
|
|
public int chromaUpsampling;
|
|
public int chromaDownsampling;
|
|
public int avoidLibYUV;
|
|
public int ignoreAlpha;
|
|
public int alphaPremultiplied;
|
|
public int isFloat;
|
|
public int maxThreads;
|
|
public IntPtr pixels;
|
|
public uint rowBytes;
|
|
}
|
|
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern IntPtr avifDecoderCreate();
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern void avifDecoderDestroy(IntPtr decoder);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern avifResult avifDecoderReadMemory(IntPtr decoder, IntPtr image, byte[] data, UIntPtr size);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern IntPtr avifImageCreateEmpty();
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern void avifImageDestroy(IntPtr image);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern void avifRGBImageSetDefaults(ref avifRGBImage rgb, IntPtr image);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern avifResult avifImageYUVToRGB(IntPtr image, ref avifRGBImage rgb);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern avifResult avifRGBImageAllocatePixels(ref avifRGBImage rgb);
|
|
[DllImport("avif.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
public static extern void avifRGBImageFreePixels(ref avifRGBImage rgb);
|
|
|
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
|
{
|
|
var header = file.ReadHeader(12);
|
|
if (header.Length < 12 || !header.AsciiEqual(4, "ftyp"))
|
|
return null;
|
|
if (!header.AsciiEqual(8, "avif") && !header.AsciiEqual(8, "avis"))
|
|
return null;
|
|
|
|
IntPtr decoder = avifDecoderCreate();
|
|
if (decoder == IntPtr.Zero)
|
|
return null;
|
|
IntPtr image = avifImageCreateEmpty();
|
|
if (image == IntPtr.Zero)
|
|
{
|
|
avifDecoderDestroy(decoder);
|
|
return null;
|
|
}
|
|
try
|
|
{
|
|
file.Position = 0;
|
|
var input = file.ReadBytes((int)file.Length);
|
|
var result = avifDecoderReadMemory(decoder, image, input, (UIntPtr)input.Length);
|
|
if (result != avifResult.OK)
|
|
return null;
|
|
|
|
var img_info = (avifImage)Marshal.PtrToStructure(image, typeof(avifImage));
|
|
return new ImageMetaData
|
|
{
|
|
Width = img_info.width,
|
|
Height = img_info.height,
|
|
BPP = img_info.alphaPlane != IntPtr.Zero ? 32 : 24,
|
|
};
|
|
}
|
|
finally
|
|
{
|
|
avifImageDestroy(image);
|
|
avifDecoderDestroy(decoder);
|
|
}
|
|
}
|
|
|
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
|
{
|
|
IntPtr decoder = avifDecoderCreate();
|
|
if (decoder == IntPtr.Zero)
|
|
throw new ApplicationException("avifDecoderCreate failed.");
|
|
IntPtr image = avifImageCreateEmpty();
|
|
if (image == IntPtr.Zero)
|
|
{
|
|
avifDecoderDestroy(decoder);
|
|
throw new ApplicationException("avifImageCreateEmpty failed.");
|
|
}
|
|
try
|
|
{
|
|
file.Position = 0;
|
|
var input = file.ReadBytes((int)file.Length);
|
|
var result = avifDecoderReadMemory(decoder, image, input, (UIntPtr)input.Length);
|
|
if (result != avifResult.OK)
|
|
throw new InvalidFormatException();
|
|
|
|
var img_info = (avifImage)Marshal.PtrToStructure(image, typeof(avifImage));
|
|
|
|
var rgb = new avifRGBImage();
|
|
avifRGBImageSetDefaults(ref rgb, image);
|
|
|
|
PixelFormat wpf_format;
|
|
if (img_info.alphaPlane != IntPtr.Zero)
|
|
{
|
|
rgb.format = avifRGBFormat.BGRA;
|
|
wpf_format = PixelFormats.Bgra32;
|
|
}
|
|
else
|
|
{
|
|
rgb.format = avifRGBFormat.BGR;
|
|
wpf_format = PixelFormats.Bgr24;
|
|
}
|
|
rgb.depth = 8;
|
|
|
|
if (avifResult.OK != avifRGBImageAllocatePixels(ref rgb))
|
|
throw new ApplicationException("avifRGBImageAllocatePixels failed.");
|
|
try
|
|
{
|
|
if (avifResult.OK != avifImageYUVToRGB(image, ref rgb))
|
|
throw new ApplicationException("avifImageYUVToRGB failed.");
|
|
|
|
int stride = (int)rgb.rowBytes;
|
|
var pixels = new byte[stride * rgb.height];
|
|
Marshal.Copy(rgb.pixels, pixels, 0, pixels.Length);
|
|
|
|
var bitmap = BitmapSource.Create((int)info.Width, (int)info.Height, 96, 96, wpf_format, null, pixels, stride);
|
|
bitmap.Freeze();
|
|
return new ImageData(bitmap, info);
|
|
}
|
|
finally
|
|
{
|
|
avifRGBImageFreePixels(ref rgb);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
avifImageDestroy(image);
|
|
avifDecoderDestroy(decoder);
|
|
}
|
|
}
|
|
|
|
public override void Write (Stream file, ImageData image)
|
|
{
|
|
throw new System.NotImplementedException ("AvifImageFormat.Write not implemented");
|
|
}
|
|
|
|
public void Dispose ()
|
|
{
|
|
}
|
|
}
|
|
}
|