diff --git a/Source/Imouto.Viewer/ImoutoViewer/Converters/StringToInlineCollectionConverter.cs b/Source/Imouto.Viewer/ImoutoViewer/Converters/StringToInlineCollectionConverter.cs index 7fca92f4..d8bbfb91 100644 --- a/Source/Imouto.Viewer/ImoutoViewer/Converters/StringToInlineCollectionConverter.cs +++ b/Source/Imouto.Viewer/ImoutoViewer/Converters/StringToInlineCollectionConverter.cs @@ -9,7 +9,7 @@ internal class StringToInlineCollectionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - var str = value as string + "<"; + var str = (value as string) + "<"; str = str.Replace("<", "<"); str = str.Replace(">", ">"); str = str.Replace("\n", ""); @@ -131,4 +131,4 @@ private enum Modifier Big = 16, ExtraBig = 32 } -} \ No newline at end of file +} diff --git a/Source/Imouto.Viewer/ImoutoViewer/ImoutoViewer.csproj b/Source/Imouto.Viewer/ImoutoViewer/ImoutoViewer.csproj index aee1bea2..e0bb6511 100644 --- a/Source/Imouto.Viewer/ImoutoViewer/ImoutoViewer.csproj +++ b/Source/Imouto.Viewer/ImoutoViewer/ImoutoViewer.csproj @@ -42,7 +42,6 @@ - @@ -73,6 +72,7 @@ + diff --git a/Source/Imouto.Viewer/ImoutoViewer/Model/LocalImage.cs b/Source/Imouto.Viewer/ImoutoViewer/Model/LocalImage.cs index 30f98305..4f1ce91d 100644 --- a/Source/Imouto.Viewer/ImoutoViewer/Model/LocalImage.cs +++ b/Source/Imouto.Viewer/ImoutoViewer/Model/LocalImage.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; +using ImoutoRebirth.Common.WebP; using ImoutoViewer.ViewModel.SettingsModels; using Size = System.Windows.Size; @@ -382,7 +383,7 @@ private static BitmapSource GetBitmap(BitmapCreateOptions options, string filePa } using var stream = new MemoryStream(); - var decoder = new Imazen.WebP.SimpleDecoder(); + var decoder = new SimpleDecoder(); var bytes = File.ReadAllBytes(filePath); var bitmap = decoder.DecodeFromBytes(bytes, bytes.Length); bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Constants.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Constants.cs new file mode 100644 index 00000000..2c08771c --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Constants.cs @@ -0,0 +1,16 @@ +// ReSharper disable InconsistentNaming +namespace ImoutoRebirth.Common.WebP.Extern; + +public partial class NativeMethods +{ + /// WEBP_DECODER_ABI_VERSION 0x0208 // MAJOR(8b) + MINOR(8b) + public const int WEBP_DECODER_ABI_VERSION = 520; + + /// WEBP_ENCODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b) + public const int WEBP_ENCODER_ABI_VERSION = 521; + + /// + /// The maximum length of any dimension of a WebP image is 16383 + /// + public const int WEBP_MAX_DIMENSION = 16383; +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Decode.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Decode.cs new file mode 100644 index 00000000..d2cd8f76 --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Decode.cs @@ -0,0 +1,678 @@ +using System.Runtime.InteropServices; + +#pragma warning disable 1591 + +namespace ImoutoRebirth.Common.WebP.Extern; + +[StructLayout(LayoutKind.Sequential)] +public struct WebPIDecoder +{ +} + +public enum WEBP_CSP_MODE +{ + /// MODE_RGB -> 0 + MODE_RGB = 0, + + /// MODE_RGBA -> 1 + MODE_RGBA = 1, + + /// MODE_BGR -> 2 + MODE_BGR = 2, + + /// MODE_BGRA -> 3 + MODE_BGRA = 3, + + /// MODE_ARGB -> 4 + MODE_ARGB = 4, + + /// MODE_RGBA_4444 -> 5 + MODE_RGBA_4444 = 5, + + /// MODE_RGB_565 -> 6 + MODE_RGB_565 = 6, + + /// MODE_rgbA -> 7 + MODE_rgbA = 7, + + /// MODE_bgrA -> 8 + MODE_bgrA = 8, + + /// MODE_Argb -> 9 + MODE_Argb = 9, + + /// MODE_rgbA_4444 -> 10 + MODE_rgbA_4444 = 10, + + /// MODE_YUV -> 11 + MODE_YUV = 11, + + /// MODE_YUVA -> 12 + MODE_YUVA = 12, + + /// MODE_LAST -> 13 + MODE_LAST = 13 +} + + +//------------------------------------------------------------------------------ +// WebPDecBuffer: Generic structure for describing the output sample buffer. + +[StructLayout(LayoutKind.Sequential)] +public struct WebPRGBABuffer +{ + /// uint8_t* + public IntPtr rgba; + + /// int + public int stride; + + /// size_t->unsigned int + public UIntPtr size; +} + +[StructLayout(LayoutKind.Sequential)] +public struct WebPYUVABuffer +{ + /// uint8_t* + public IntPtr y; + + /// uint8_t* + public IntPtr u; + + /// uint8_t* + public IntPtr v; + + /// uint8_t* + public IntPtr a; + + /// int + public int y_stride; + + /// int + public int u_stride; + + /// int + public int v_stride; + + /// int + public int a_stride; + + /// size_t->unsigned int + public UIntPtr y_size; + + /// size_t->unsigned int + public UIntPtr u_size; + + /// size_t->unsigned int + public UIntPtr v_size; + + /// size_t->unsigned int + public UIntPtr a_size; +} + +[StructLayout(LayoutKind.Explicit)] +public struct Anonymous_690ed5ec_4c3d_40c6_9bd0_0747b5a28b54 +{ + /// WebPRGBABuffer->Anonymous_47cdec86_3c1a_4b39_ab93_76bc7499076a + [FieldOffset(0)] public WebPRGBABuffer RGBA; + + /// WebPYUVABuffer->Anonymous_70de6e8e_c3ae_4506_bef0_c17f17a7e678 + [FieldOffset(0)] public WebPYUVABuffer YUVA; +} + +[StructLayout(LayoutKind.Sequential)] +public struct WebPDecBuffer +{ + /// WEBP_CSP_MODE->Anonymous_cb136f5b_1d5d_49a0_aca4_656a79e9d159 + public WEBP_CSP_MODE colorspace; + + /// int + public int width; + + /// int + public int height; + + /// int + public int is_external_memory; + + /// Anonymous_690ed5ec_4c3d_40c6_9bd0_0747b5a28b54 + public Anonymous_690ed5ec_4c3d_40c6_9bd0_0747b5a28b54 u; + + /// uint32_t[4] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)] + public uint[] pad; + + /// uint8_t* + public IntPtr private_memory; +} + + +//------------------------------------------------------------------------------ +// Enumeration of the status codes + +public enum VP8StatusCode +{ + /// VP8_STATUS_OK -> 0 + VP8_STATUS_OK = 0, + + VP8_STATUS_OUT_OF_MEMORY, + + VP8_STATUS_INVALID_PARAM, + + VP8_STATUS_BITSTREAM_ERROR, + + VP8_STATUS_UNSUPPORTED_FEATURE, + + VP8_STATUS_SUSPENDED, + + VP8_STATUS_USER_ABORT, + + VP8_STATUS_NOT_ENOUGH_DATA +} + + +[StructLayout(LayoutKind.Sequential)] +public struct WebPBitstreamFeatures +{ + /// + /// Width in pixels, as read from the bitstream + /// + public int width; + + /// + /// Height in pixels, as read from the bitstream. + /// + public int height; + + /// + /// // True if the bitstream contains an alpha channel. + /// + public int has_alpha; + + /// + /// True if the bitstream contains an animation + /// + public int has_animation; + + /// + /// 0 = undefined (/mixed), 1 = lossy, 2 = lossless + /// + public int format; + + + /// + /// Padding for later use + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)] + public uint[] pad; +} + + +// Decoding options +[StructLayout(LayoutKind.Sequential)] +public struct WebPDecoderOptions +{ + public int bypass_filtering; // if true, skip the in-loop filtering + public int no_fancy_upsampling; // if true, use faster pointwise upsampler + public int use_cropping; // if true, cropping is applied _first_ + + public int crop_left, crop_top; // top-left position for cropping. + + // Will be snapped to even values. + public int crop_width, crop_height; // dimension of the cropping area + public int use_scaling; // if true, scaling is applied _afterward_ + public int scaled_width, scaled_height; // final resolution + public int use_threads; // if true, use multi-threaded decoding + public int dithering_strength; // dithering strength (0=Off, 100=full) + + public int flip; // flip output vertically + public int alpha_dithering_strength; // alpha dithering strength in [0..100] + + /// uint32_t[5] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)] + public uint[] pad; +} + +[StructLayout(LayoutKind.Sequential)] +public struct WebPDecoderConfig +{ + /// WebPBitstreamFeatures->Anonymous_c6b01f0b_3e38_4731_b2d6_9c0e3bdb71aa + public WebPBitstreamFeatures input; + + /// WebPDecBuffer->Anonymous_5c438b36_7de6_498e_934a_d3613b37f5fc + public WebPDecBuffer output; + + /// WebPDecoderOptions->Anonymous_78066979_3e1e_4d74_aee5_f316b20e3385 + public WebPDecoderOptions options; +} + + +public partial class NativeMethods +{ + /// Return Type: int + [DllImport("libwebp", EntryPoint = "WebPGetDecoderVersion", + CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPGetDecoderVersion(); + + + /// + /// Retrieve basic header information: width, height. + /// This function will also validate the header and return 0 in + /// case of formatting error. + /// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant. + /// + /// + /// + /// + /// + /// + [DllImport("libwebp", EntryPoint = "WebPGetInfo", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPGetInfo( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeRGBA", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeRGBA( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeARGB", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeARGB( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeBGRA", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeBGRA( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeRGB( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeBGR", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeBGR( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// width: int* + /// height: int* + /// u: uint8_t** + /// v: uint8_t** + /// stride: int* + /// uv_stride: int* + [DllImport("libwebp", EntryPoint = "WebPDecodeYUV", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeYUV( + [In] IntPtr data, + UIntPtr data_size, + ref int width, + ref int height, + ref IntPtr u, + ref IntPtr v, + ref int stride, + ref int uv_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeRGBAInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeRGBAInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeARGBInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeARGBInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeBGRAInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeBGRAInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeRGBInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeRGBInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeBGRInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeBGRInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: uint8_t* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// luma: uint8_t* + /// luma_size: size_t->unsigned int + /// luma_stride: int + /// u: uint8_t* + /// u_size: size_t->unsigned int + /// u_stride: int + /// v: uint8_t* + /// v_size: size_t->unsigned int + /// v_stride: int + [DllImport("libwebp", EntryPoint = "WebPDecodeYUVInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeYUVInto( + [In] IntPtr data, + UIntPtr data_size, + IntPtr luma, + UIntPtr luma_size, + int luma_stride, + IntPtr u, + UIntPtr u_size, + int u_stride, + IntPtr v, + UIntPtr v_size, + int v_stride); + + + /// Return Type: int + /// param0: WebPDecBuffer* + /// param1: int + [DllImport("libwebp", EntryPoint = "WebPInitDecBufferInternal", + CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPInitDecBufferInternal(ref WebPDecBuffer param0, int param1); + + + /// Return Type: void + /// buffer: WebPDecBuffer* + [DllImport("libwebp", EntryPoint = "WebPFreeDecBuffer", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPFreeDecBuffer(ref WebPDecBuffer buffer); + + + /// Return Type: WebPIDecoder* + /// output_buffer: WebPDecBuffer* + [DllImport("libwebp", EntryPoint = "WebPINewDecoder", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPINewDecoder(ref WebPDecBuffer output_buffer); + + + /// Return Type: WebPIDecoder* + /// csp: WEBP_CSP_MODE->Anonymous_cb136f5b_1d5d_49a0_aca4_656a79e9d159 + /// output_buffer: uint8_t* + /// output_buffer_size: size_t->unsigned int + /// output_stride: int + [DllImport("libwebp", EntryPoint = "WebPINewRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPINewRGB( + WEBP_CSP_MODE csp, + IntPtr output_buffer, + UIntPtr output_buffer_size, + int output_stride); + + + /// Return Type: WebPIDecoder* + /// luma: uint8_t* + /// luma_size: size_t->unsigned int + /// luma_stride: int + /// u: uint8_t* + /// u_size: size_t->unsigned int + /// u_stride: int + /// v: uint8_t* + /// v_size: size_t->unsigned int + /// v_stride: int + /// a: uint8_t* + /// a_size: size_t->unsigned int + /// a_stride: int + [DllImport("libwebp", EntryPoint = "WebPINewYUVA", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPINewYUVA( + IntPtr luma, + UIntPtr luma_size, + int luma_stride, + IntPtr u, + UIntPtr u_size, + int u_stride, + IntPtr v, + UIntPtr v_size, + int v_stride, + IntPtr a, + UIntPtr a_size, + int a_stride); + + + /// Return Type: WebPIDecoder* + /// luma: uint8_t* + /// luma_size: size_t->unsigned int + /// luma_stride: int + /// u: uint8_t* + /// u_size: size_t->unsigned int + /// u_stride: int + /// v: uint8_t* + /// v_size: size_t->unsigned int + /// v_stride: int + [DllImport("libwebp", EntryPoint = "WebPINewYUV", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPINewYUV( + IntPtr luma, + UIntPtr luma_size, + int luma_stride, + IntPtr u, + UIntPtr u_size, + int u_stride, + IntPtr v, + UIntPtr v_size, + int v_stride); + + + /// Return Type: void + /// idec: WebPIDecoder* + [DllImport("libwebp", EntryPoint = "WebPIDelete", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPIDelete(ref WebPIDecoder idec); + + + /// Return Type: VP8StatusCode->Anonymous_b244cc15_fbc7_4c41_8884_71fe4f515cd6 + /// idec: WebPIDecoder* + /// data: uint8_t* + /// data_size: size_t->unsigned int + [DllImport("libwebp", EntryPoint = "WebPIAppend", CallingConvention = CallingConvention.Cdecl)] + public static extern VP8StatusCode WebPIAppend( + ref WebPIDecoder idec, + [In] IntPtr data, + UIntPtr data_size); + + + /// Return Type: VP8StatusCode->Anonymous_b244cc15_fbc7_4c41_8884_71fe4f515cd6 + /// idec: WebPIDecoder* + /// data: uint8_t* + /// data_size: size_t->unsigned int + [DllImport("libwebp", EntryPoint = "WebPIUpdate", CallingConvention = CallingConvention.Cdecl)] + public static extern VP8StatusCode WebPIUpdate( + ref WebPIDecoder idec, + [In] IntPtr data, + UIntPtr data_size); + + + /// Return Type: uint8_t* + /// idec: WebPIDecoder* + /// last_y: int* + /// width: int* + /// height: int* + /// stride: int* + [DllImport("libwebp", EntryPoint = "WebPIDecGetRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPIDecGetRGB( + ref WebPIDecoder idec, + ref int last_y, + ref int width, + ref int height, + ref int stride); + + + /// Return Type: uint8_t* + /// idec: WebPIDecoder* + /// last_y: int* + /// u: uint8_t** + /// v: uint8_t** + /// a: uint8_t** + /// width: int* + /// height: int* + /// stride: int* + /// uv_stride: int* + /// a_stride: int* + [DllImport("libwebp", EntryPoint = "WebPIDecGetYUVA", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPIDecGetYUVA( + ref WebPIDecoder idec, + ref int last_y, + ref IntPtr u, + ref IntPtr v, + ref IntPtr a, + ref int width, + ref int height, + ref int stride, + ref int uv_stride, + ref int a_stride); + + + /// Return Type: WebPDecBuffer* + /// idec: WebPIDecoder* + /// left: int* + /// top: int* + /// width: int* + /// height: int* + [DllImport("libwebp", EntryPoint = "WebPIDecodedArea", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPIDecodedArea( + ref WebPIDecoder idec, + ref int left, + ref int top, + ref int width, + ref int height); + + + /// Return Type: VP8StatusCode->Anonymous_b244cc15_fbc7_4c41_8884_71fe4f515cd6 + /// param0: uint8_t* + /// param1: size_t->unsigned int + /// param2: WebPBitstreamFeatures* + /// param3: int + [DllImport("libwebp", EntryPoint = "WebPGetFeaturesInternal", + CallingConvention = CallingConvention.Cdecl)] + public static extern VP8StatusCode WebPGetFeaturesInternal( + [In] IntPtr param0, + UIntPtr param1, + ref WebPBitstreamFeatures param2, + int param3); + + + /// Return Type: int + /// param0: WebPDecoderConfig* + /// param1: int + [DllImport("libwebp", EntryPoint = "WebPInitDecoderConfigInternal", + CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPInitDecoderConfigInternal(ref WebPDecoderConfig param0, int param1); + + + /// Return Type: WebPIDecoder* + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// config: WebPDecoderConfig* + [DllImport("libwebp", EntryPoint = "WebPIDecode", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPIDecode( + [In] IntPtr data, + UIntPtr data_size, + ref WebPDecoderConfig config); + + + /// Return Type: VP8StatusCode->Anonymous_b244cc15_fbc7_4c41_8884_71fe4f515cd6 + /// data: uint8_t* + /// data_size: size_t->unsigned int + /// config: WebPDecoderConfig* + [DllImport("libwebp", EntryPoint = "WebPDecode", CallingConvention = CallingConvention.Cdecl)] + public static extern VP8StatusCode WebPDecode( + [In] IntPtr data, + UIntPtr data_size, + ref WebPDecoderConfig config); +} + +#pragma warning restore 1591 diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/DecodeInlines.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/DecodeInlines.cs new file mode 100644 index 00000000..8885783b --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/DecodeInlines.cs @@ -0,0 +1,97 @@ +namespace ImoutoRebirth.Common.WebP.Extern; + +public partial class NativeMethods +{ + // Some useful macros: + + /// + /// Returns true if the specified mode uses a premultiplied alpha + /// + /// + /// + public static bool WebPIsPremultipliedMode(WEBP_CSP_MODE mode) + { + return mode == WEBP_CSP_MODE.MODE_rgbA || mode == WEBP_CSP_MODE.MODE_bgrA || + mode == WEBP_CSP_MODE.MODE_Argb || + mode == WEBP_CSP_MODE.MODE_rgbA_4444; + } + + /// + /// Returns true if the given mode is RGB(A) + /// + /// + /// + public static bool WebPIsRGBMode(WEBP_CSP_MODE mode) + { + return mode < WEBP_CSP_MODE.MODE_YUV; + } + + + /// + /// Returns true if the given mode has an alpha channel + /// + /// + /// + public static bool WebPIsAlphaMode(WEBP_CSP_MODE mode) + { + return mode == WEBP_CSP_MODE.MODE_RGBA || mode == WEBP_CSP_MODE.MODE_BGRA || + mode == WEBP_CSP_MODE.MODE_ARGB || + mode == WEBP_CSP_MODE.MODE_RGBA_4444 || mode == WEBP_CSP_MODE.MODE_YUVA || + WebPIsPremultipliedMode(mode); + } + + + // + + /// + /// Retrieve features from the bitstream. The *features structure is filled + /// with information gathered from the bitstream. + /// Returns false in case of error or version mismatch. + /// In case of error, features->bitstream_status will reflect the error code. + /// + /// + /// + /// + /// + public static VP8StatusCode WebPGetFeatures(IntPtr data, UIntPtr data_size, ref WebPBitstreamFeatures features) + { + return NativeMethods.WebPGetFeaturesInternal(data, data_size, + ref features, WEBP_DECODER_ABI_VERSION); + } + + /// + /// Initialize the configuration as empty. This function must always be + /// called first, unless WebPGetFeatures() is to be called. + /// Returns false in case of mismatched version. + /// + /// + /// + public static int WebPInitDecoderConfig(ref WebPDecoderConfig config) + { + return WebPInitDecoderConfigInternal(ref config, WEBP_DECODER_ABI_VERSION); + } + + + /// + /// Initialize the structure as empty. Must be called before any other use. Returns false in case of version mismatch + /// + /// + /// + public static int WebPInitDecBuffer(ref WebPDecBuffer buffer) + { + return WebPInitDecBufferInternal(ref buffer, WEBP_DECODER_ABI_VERSION); + } + + + // // Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the + + //// alpha information (if present). Kept for backward compatibility. + + //public IntPtr WebPIDecGetYUV(IntPtr decoder, int* last_y, uint8_t** u, uint8_t** v, + + // int* width, int* height, int* stride, int* uv_stride) { + + // return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height, + + // stride, uv_stride, NULL); +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Encode.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Encode.cs new file mode 100644 index 00000000..6736c689 --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Encode.cs @@ -0,0 +1,724 @@ +using System.Runtime.InteropServices; + +#pragma warning disable 1591 + +namespace ImoutoRebirth.Common.WebP.Extern; + +public enum WebPImageHint +{ + + /// WEBP_HINT_DEFAULT -> 0 + WEBP_HINT_DEFAULT = 0, + + WEBP_HINT_PICTURE, + + WEBP_HINT_PHOTO, + + WEBP_HINT_GRAPH, + + WEBP_HINT_LAST, +} + +[StructLayout(LayoutKind.Sequential)] +public struct WebPConfig +{ + + /// Lossless encoding (0=lossy(default), 1=lossless). + public int lossless; + + /// between 0 (smallest file) and 100 (biggest) + public float quality; + + /// quality/speed trade-off (0=fast, 6=slower-better) + public int method; + + /// WebPImageHint->Anonymous_838f22f5_6f57_48a0_9ecb_8eec917009f9 + public WebPImageHint image_hint; + + // Parameters related to lossy compression only: + + /// if non-zero, set the desired target size in bytes. Takes precedence over the 'compression' parameter. + public int target_size; + + /// if non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target_size. + public float target_PSNR; + + /// maximum number of segments to use, in [1..4] + public int segments; + + /// Spatial Noise Shaping. 0=off, 100=maximum. + public int sns_strength; + + /// range: [0 = off .. 100 = strongest] + public int filter_strength; + + /// range: [0 = off .. 7 = least sharp] + public int filter_sharpness; + + /// filtering type: 0 = simple, 1 = strong (only used + /// if filter_strength > 0 or autofilter > 0) + public int filter_type; + + /// Auto adjust filter's strength [0 = off, 1 = on] + public int autofilter; + + /// Algorithm for encoding the alpha plane (0 = none, + /// 1 = compressed with WebP lossless). Default is 1. + public int alpha_compression; + + /// Predictive filtering method for alpha plane. + /// 0: none, 1: fast, 2: best. Default if 1. + public int alpha_filtering; + + /// Between 0 (smallest size) and 100 (lossless). + // Default is 100. + public int alpha_quality; + + /// number of entropy-analysis passes (in [1..10]). + public int pass; + + /// if true, export the compressed picture back. + /// In-loop filtering is not applied. + public int show_compressed; + + /// preprocessing filter: + /// 0=none, 1=segment-smooth, 2=pseudo-random dithering + public int preprocessing; + + /// log2(number of token partitions) in [0..3]. Default + /// is set to 0 for easier progressive decoding. + public int partitions; + + /// quality degradation allowed to fit the 512k limit + /// on prediction modes coding (0: no degradation, + /// 100: maximum possible degradation). + public int partition_limit; + + /// + /// If true, compression parameters will be remapped + /// to better match the expected output size from + /// JPEG compression. Generally, the output size will + /// be similar but the degradation will be lower. + /// + public int emulate_jpeg_size; + + /// If non-zero, try and use multi-threaded encoding. + public int thread_level; + + /// + /// If set, reduce memory usage (but increase CPU use). + /// + public int low_memory; + + /// + /// Near lossless encoding [0 = max loss .. 100 = off (default)]. + /// + public int near_lossless; + + /// if non-zero, preserve the exact RGB values under + /// transparent area. Otherwise, discard this invisible + /// RGB information for better compression. The default + /// value is 0. + public int exact; + + /// uint32_t[3] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)] + public uint[] pad; +} + +public enum WebPPreset +{ + + /// WEBP_PRESET_DEFAULT -> 0 + WEBP_PRESET_DEFAULT = 0, + + /// digital picture, like portrait, inner shot + WEBP_PRESET_PICTURE, + + /// outdoor photograph, with natural lighting + WEBP_PRESET_PHOTO, + + /// hand or line drawing, with high-contrast details + WEBP_PRESET_DRAWING, + + /// small-sized colorful images + WEBP_PRESET_ICON, + + /// text-like + WEBP_PRESET_TEXT, +} + + + + + + +[StructLayout(LayoutKind.Sequential)] +public struct WebPAuxStats +{ + + /// int + public int coded_size; + + /// float[5] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.R4)] + public float[] PSNR; + + /// int[3] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I4)] + public int[] block_count; + + /// int[2] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.I4)] + public int[] header_bytes; + + /// int[12] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12, ArraySubType = UnmanagedType.I4)] + public int[] residual_bytes; + + /// int[4] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)] + public int[] segment_size; + + /// int[4] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)] + public int[] segment_quant; + + /// int[4] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)] + public int[] segment_level; + + /// int + public int alpha_data_size; + + /// int + public int layer_data_size; + + /// uint32_t->unsigned int + public uint lossless_features; + + /// int + public int histogram_bits; + + /// int + public int transform_bits; + + /// int + public int cache_bits; + + /// int + public int palette_size; + + /// int + public int lossless_size; + + /// lossless header (transform, huffman etc) size + public int lossless_hdr_size; + + /// lossless image data size + public int lossless_data_size; + /// uint32_t[2] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)] + public uint[] pad; +} + + +/// Return Type: int +///data: uint8_t* +///data_size: size_t->unsigned int +///picture: WebPPicture* +public delegate int WebPWriterFunction([In()] IntPtr data, UIntPtr data_size, ref WebPPicture picture); + +[StructLayout(LayoutKind.Sequential)] +public struct WebPMemoryWriter +{ + + /// uint8_t* + public IntPtr mem; + + /// size_t->unsigned int + public UIntPtr size; + + /// size_t->unsigned int + public UIntPtr max_size; + + /// uint32_t[1] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.U4)] + public uint[] pad; +} + + + +/// Return Type: int +///percent: int +///picture: WebPPicture* +public delegate int WebPProgressHook(int percent, ref WebPPicture picture); + +public enum WebPEncCSP +{ + + /// 4:2:0 (half-res chroma x and y) + WEBP_YUV420 = 0, + + /// bit-mask to get the UV sampling factors + WEBP_CSP_UV_MASK = 3, + + /// 4:2:0 with alpha + WEBP_YUV420A = 4, + + /// Bit mask to set alpha + WEBP_CSP_ALPHA_BIT = 4, +} + + +public enum WebPEncodingError +{ + + /// VP8_ENC_OK -> 0 + VP8_ENC_OK = 0, + + VP8_ENC_ERROR_OUT_OF_MEMORY, + + VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, + + VP8_ENC_ERROR_NULL_PARAMETER, + + VP8_ENC_ERROR_INVALID_CONFIGURATION, + + VP8_ENC_ERROR_BAD_DIMENSION, + + VP8_ENC_ERROR_PARTITION0_OVERFLOW, + + VP8_ENC_ERROR_PARTITION_OVERFLOW, + + VP8_ENC_ERROR_BAD_WRITE, + + VP8_ENC_ERROR_FILE_TOO_BIG, + + VP8_ENC_ERROR_USER_ABORT, + + VP8_ENC_ERROR_LAST, +} + + +/// +/// Main exchange structure (input samples, output bytes, statistics) +/// +[StructLayout(LayoutKind.Sequential)] +public struct WebPPicture +{ + + // INPUT + ////////////// + // Main flag for encoder selecting between ARGB or YUV input. + // It is recommended to use ARGB input (*argb, argb_stride) for lossless + // compression, and YUV input (*y, *u, *v, etc.) for lossy compression + // since these are the respective native colorspace for these formats. + public int use_argb; + + // YUV input (mostly used for input to lossy compression) + + /// colorspace: should be YUV420 for now (=Y'CbCr). WebPEncCSP->Anonymous_84ce7065_fe91_48b4_93d8_1f0e84319dba + public WebPEncCSP colorspace; + + /// int + public int width; + + /// int + public int height; + + /// uint8_t* pointers to luma/chroma planes. + public IntPtr y; + + /// uint8_t* + public IntPtr u; + + /// uint8_t* + public IntPtr v; + + /// int + public int y_stride; + + /// int + public int uv_stride; + + /// uint8_t* + public IntPtr a; + + /// int + public int a_stride; + + /// uint32_t[2] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)] + public uint[] pad1; + + /// uint32_t* + public IntPtr argb; + + /// int + public int argb_stride; + + /// uint32_t[3] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)] + public uint[] pad2; + + // OUTPUT + + + /// WebPWriterFunction + public WebPWriterFunction writer; + + /// void* + public IntPtr custom_ptr; + + /// int + public int extra_info_type; + + /// uint8_t* + public IntPtr extra_info; + + /// WebPAuxStats* + public IntPtr stats; + + /// WebPEncodingError->Anonymous_8b714d63_f91b_46af_b0d0_667c703ed356 + public WebPEncodingError error_code; + + /// WebPProgressHook + public WebPProgressHook progress_hook; + + /// void* + public IntPtr user_data; + + /// uint32_t[3] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)] + public uint[] pad3; + + /// uint8_t* + public IntPtr pad4; + + /// uint8_t* + public IntPtr pad5; + + + /// uint32_t[8] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8, ArraySubType = UnmanagedType.U4)] + public uint[] pad6; + + /// void* + public IntPtr memory_; + + /// void* + public IntPtr memory_argb_; + + /// void*[2] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.SysUInt)] + public IntPtr[] pad7; +} + + +public partial class NativeMethods +{ + + /// Return Type: int + [DllImport("libwebp", EntryPoint = "WebPGetEncoderVersion", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPGetEncoderVersion(); + + + /// Return Type: size_t->unsigned int + ///rgb: uint8_t* + ///width: int + ///height: int + ///stride: int + ///quality_factor: float + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeRGB([In()] IntPtr rgb, int width, int height, int stride, float quality_factor, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///bgr: uint8_t* + ///width: int + ///height: int + ///stride: int + ///quality_factor: float + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeBGR", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeBGR([In()] IntPtr bgr, int width, int height, int stride, float quality_factor, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///rgba: uint8_t* + ///width: int + ///height: int + ///stride: int + ///quality_factor: float + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeRGBA", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeRGBA([In()] IntPtr rgba, int width, int height, int stride, float quality_factor, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///bgra: uint8_t* + ///width: int + ///height: int + ///stride: int + ///quality_factor: float + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeBGRA", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPEncodeBGRA([In()] IntPtr bgra, int width, int height, int stride, float quality_factor, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///rgb: uint8_t* + ///width: int + ///height: int + ///stride: int + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeLosslessRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeLosslessRGB([In()] IntPtr rgb, int width, int height, int stride, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///bgr: uint8_t* + ///width: int + ///height: int + ///stride: int + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeLosslessBGR", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeLosslessBGR([In()] IntPtr bgr, int width, int height, int stride, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///rgba: uint8_t* + ///width: int + ///height: int + ///stride: int + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeLosslessRGBA", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeLosslessRGBA([In()] IntPtr rgba, int width, int height, int stride, ref IntPtr output); + + + /// Return Type: size_t->unsigned int + ///bgra: uint8_t* + ///width: int + ///height: int + ///stride: int + ///output: uint8_t** + [DllImport("libwebp", EntryPoint = "WebPEncodeLosslessBGRA", CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr WebPEncodeLosslessBGRA([In()] IntPtr bgra, int width, int height, int stride, ref IntPtr output); + + + /// Return Type: int + ///param0: WebPConfig* + ///param1: WebPPreset->Anonymous_017d4167_f53e_4b3d_b029_592ff5c3f80b + ///param2: float + ///param3: int + [DllImport("libwebp", EntryPoint = "WebPConfigInitInternal", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPConfigInitInternal(ref WebPConfig param0, WebPPreset param1, float param2, int param3); + + + /// Return Type: int + ///config: WebPConfig* + [DllImport("libwebp", EntryPoint = "WebPConfigLosslessPreset", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPConfigLosslessPreset(ref WebPConfig config); + + /// Return Type: int + ///config: WebPConfig* + [DllImport("libwebp", EntryPoint = "WebPValidateConfig", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPValidateConfig(ref WebPConfig config); + + + /// Return Type: void + ///writer: WebPMemoryWriter* + [DllImport("libwebp", EntryPoint = "WebPMemoryWriterInit", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPMemoryWriterInit(ref WebPMemoryWriter writer); + + /// Return Type: void + ///writer: WebPMemoryWriter* + [DllImport("libwebp", EntryPoint = "WebPMemoryWriterClear", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPMemoryWriterClear(ref WebPMemoryWriter writer); + + + /// Return Type: int + ///data: uint8_t* + ///data_size: size_t->unsigned int + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPMemoryWrite", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPMemoryWrite([In()] IntPtr data, UIntPtr data_size, ref WebPPicture picture); + + + /// Return Type: int + ///param0: WebPPicture* + ///param1: int + [DllImport("libwebp", EntryPoint = "WebPPictureInitInternal", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureInitInternal(ref WebPPicture param0, int param1); + + + /// Return Type: int + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureAlloc", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureAlloc(ref WebPPicture picture); + + + /// Return Type: void + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureFree", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPPictureFree(ref WebPPicture picture); + + + /// Return Type: int + ///src: WebPPicture* + ///dst: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureCopy", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureCopy(ref WebPPicture src, ref WebPPicture dst); + + + /// Return Type: int + ///pic1: WebPPicture* + ///pic2: WebPPicture* + ///metric_type: int + ///result: float* result[5] + /// + + /// + /// Compute PSNR, SSIM or LSIM distortion metric between two pictures. + /// Result is in dB, stores in result[] in the Y/U/V/Alpha/All or B/G/R/A/All order. + /// Returns false in case of error (src and ref don't have same dimension, ...) + /// Warning: this function is rather CPU-intensive. + /// + /// + /// + /// 0 = PSNR, 1 = SSIM, 2 = LSIM + /// + /// + [DllImport("libwebp", EntryPoint = "WebPPictureDistortion", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureDistortion(ref WebPPicture src, ref WebPPicture reference, int metric_type, ref float result); + + + + + /// Return Type: int + ///picture: WebPPicture* + ///left: int + ///top: int + ///width: int + ///height: int + [DllImport("libwebp", EntryPoint = "WebPPictureCrop", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureCrop(ref WebPPicture picture, int left, int top, int width, int height); + + + /// Return Type: int + ///src: WebPPicture* + ///left: int + ///top: int + ///width: int + ///height: int + ///dst: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureView", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureView(ref WebPPicture src, int left, int top, int width, int height, ref WebPPicture dst); + + + /// Return Type: int + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureIsView", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureIsView(ref WebPPicture picture); + + + /// Rescale a picture to new dimension width x height. + /// ow gamma correction is applied. + /// + /// Return Type: int + ///pic: WebPPicture* + ///width: int + ///height: int + [DllImport("libwebp", EntryPoint = "WebPPictureRescale", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureRescale(ref WebPPicture pic, int width, int height); + + + /// Return Type: int + ///picture: WebPPicture* + ///rgb: uint8_t* + ///rgb_stride: int + [DllImport("libwebp", EntryPoint = "WebPPictureImportRGB", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportRGB(ref WebPPicture picture, [In()] IntPtr rgb, int rgb_stride); + + + /// Return Type: int + ///picture: WebPPicture* + ///rgba: uint8_t* + ///rgba_stride: int + [DllImport("libwebp", EntryPoint = "WebPPictureImportRGBA", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportRGBA(ref WebPPicture picture, [In()] IntPtr rgba, int rgba_stride); + + + /// Performs 'smart' RGBA->YUVA420 downsampling and colorspace conversion. + /// Downsampling is handled with extra care in case of color clipping. This + /// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better + /// YUV representation. + /// Returns false in case of error. + [DllImport("libwebp", EntryPoint = "WebPPictureSmartARGBToYUVA", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureSmartARGBToYUVA(ref WebPPicture picture); + + [DllImport("libwebp", EntryPoint = "WebPPictureImportRGBX", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportRGBX(ref WebPPicture picture, [In()] IntPtr rgbx, int rgbx_stride); + + + + /// Return Type: int + ///picture: WebPPicture* + ///bgr: uint8_t* + ///bgr_stride: int + [DllImport("libwebp", EntryPoint = "WebPPictureImportBGR", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportBGR(ref WebPPicture picture, [In()] IntPtr bgr, int bgr_stride); + + + /// Return Type: int + ///picture: WebPPicture* + ///bgra: uint8_t* + ///bgra_stride: int + [DllImport("libwebp", EntryPoint = "WebPPictureImportBGRA", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportBGRA(ref WebPPicture picture, [In()] IntPtr bgra, int bgra_stride); + + + /// Return Type: int + ///picture: WebPPicture* + ///bgrx: uint8_t* + ///bgrx_stride: int + [DllImport("libwebp", EntryPoint = "WebPPictureImportBGRX", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureImportBGRX(ref WebPPicture picture, [In()] IntPtr bgrx, int bgrx_stride); + + + /// Return Type: int + ///picture: WebPPicture* + ///colorspace: WebPEncCSP->Anonymous_84ce7065_fe91_48b4_93d8_1f0e84319dba + [DllImport("libwebp", EntryPoint = "WebPPictureARGBToYUVA", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureARGBToYUVA(ref WebPPicture picture, WebPEncCSP colorspace); + + + /// Return Type: int + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureYUVAToARGB", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureYUVAToARGB(ref WebPPicture picture); + + + /// Return Type: void + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPCleanupTransparentArea", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPCleanupTransparentArea(ref WebPPicture picture); + + + /// Return Type: int + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPPictureHasTransparency", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPPictureHasTransparency(ref WebPPicture picture); + + + /// Return Type: int + ///config: WebPConfig* + ///picture: WebPPicture* + [DllImport("libwebp", EntryPoint = "WebPEncode", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPEncode(ref WebPConfig config, ref WebPPicture picture); + +} + +#pragma warning restore 1591 diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/EncodeInlines.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/EncodeInlines.cs new file mode 100644 index 00000000..158cfe07 --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/EncodeInlines.cs @@ -0,0 +1,47 @@ +namespace ImoutoRebirth.Common.WebP.Extern; + +public partial class NativeMethods +{ + /// + /// Should always be called, to initialize a fresh WebPConfig structure before + /// modification. Returns false in case of version mismatch. WebPConfigInit() + /// must have succeeded before using the 'config' object. + /// Note that the default values are lossless=0 and quality=75. + /// + /// + /// + public static int WebPConfigInit(ref WebPConfig config) + { + return NativeMethods.WebPConfigInitInternal(ref config, + WebPPreset.WEBP_PRESET_DEFAULT, 75.0f, WEBP_ENCODER_ABI_VERSION); + } + + /// + /// This function will initialize the configuration according to a predefined + /// set of parameters (referred to by 'preset') and a given quality factor. + /// This function can be called as a replacement to WebPConfigInit(). Will return false in case of error. + /// + /// + /// + /// + /// + public static int WebPConfigPreset(ref WebPConfig config, WebPPreset preset, float quality) + { + return NativeMethods.WebPConfigInitInternal(ref config, preset, quality, + WEBP_ENCODER_ABI_VERSION); + } + + /// + /// Should always be called, to initialize the structure. Returns false in case + /// of version mismatch. WebPPictureInit() must have succeeded before using the + /// 'picture' object. + /// Note that, by default, use_argb is false and colorspace is WEBP_YUV420. + /// + /// + /// + public static int WebPPictureInit(ref WebPPicture picture) + { + return NativeMethods.WebPPictureInitInternal(ref picture, + WEBP_ENCODER_ABI_VERSION); + } +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Extra.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Extra.cs new file mode 100644 index 00000000..85f1d9bc --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/Extra.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace ImoutoRebirth.Common.WebP.Extern; + +public partial class NativeMethods +{ + public static void WebPSafeFree(IntPtr toDeallocate) + { + WebPFree(toDeallocate); + } + + + [DllImport("libwebp", EntryPoint = "WebPFree", CallingConvention = CallingConvention.Cdecl)] + public static extern void WebPFree(IntPtr toDeallocate); +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/LoadLibrary.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/LoadLibrary.cs new file mode 100644 index 00000000..3404d74c --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/Extern/LoadLibrary.cs @@ -0,0 +1,111 @@ +using System.ComponentModel; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace ImoutoRebirth.Common.WebP.Extern; + +public static class LoadLibrary +{ + [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, uint dwFlags); + + public static bool EnsureLoadedByPath(string fullPath, bool throwException) + { + //canonicalize as much as we can + fullPath = Path.GetFullPath(fullPath); + lock (lockObj) + { + IntPtr handle; + if (loaded.TryGetValue(fullPath, out handle)) + { + return true; + } + else + { + handle = LoadByPath(fullPath, throwException); + if (handle != IntPtr.Zero) + { + loaded.Add(fullPath, handle); + return true; + } + } + } + return false; + } + + /// + /// Calls LoadLibraryEx with (Search DLL Load Dir and System32) flags. May increment reference count. Use EnsureLoadedByPath instead if you don't need a pointer. + /// + /// + /// + /// + public static IntPtr LoadByPath(string fullPath, bool throwException) + { + const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100; + const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; + + var moduleHandle = LoadLibraryEx(fullPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); + if (moduleHandle == IntPtr.Zero && throwException) + throw new Win32Exception(Marshal.GetLastWin32Error()); + return moduleHandle; + } + + + public static void LoadWebPOrFail() + { + if (!AutoLoadNearby("libwebp.dll", true)) + { + throw new FileNotFoundException("Failed to locate libwebp.dll"); + } + } + /// + /// Looks for 'name' inside /x86/ or /x64/ (depending on arch) subfolders of known assembly locations + /// + /// + /// + /// + public static bool AutoLoadNearby(string name, bool throwFailure) + { + var a = Assembly.GetExecutingAssembly(); + return AutoLoad(name, new string[]{Path.GetDirectoryName(a.Location), Path.GetDirectoryName(new Uri(a.CodeBase).LocalPath)},throwFailure,throwFailure); + } + + static object lockObj = new object(); + static Dictionary loaded = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Looks for 'name' inside /x86/ and /x64/ subfolders of 'folder', depending on executing architecture. + /// + /// + /// + /// + /// + /// + public static bool AutoLoad(string name, string[] searchFolders, bool throwNotFound, bool throwExceptions) + { + string searched = ""; + foreach (string folder in searchFolders) + { + var basePath = Path.Combine(folder, (IntPtr.Size == 8) ? "x64" : "x86"); + var fullPath = Path.Combine(basePath, name); + if (string.IsNullOrEmpty(Path.GetExtension(fullPath))) + { + fullPath = fullPath + ".dll"; + } + searched = searched + "\"" + fullPath + "\", "; + if (File.Exists(fullPath)) + { + if (EnsureLoadedByPath(fullPath, throwExceptions)) + { + return true; + } + } + } + if (throwNotFound) + { + throw new FileNotFoundException("Failed to locate '" + name + "' as " + searched.TrimEnd(' ', ',')); + } + return false; + } + +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/ImoutoRebirth.Common.WebP.csproj b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/ImoutoRebirth.Common.WebP.csproj new file mode 100644 index 00000000..d08686ba --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/ImoutoRebirth.Common.WebP.csproj @@ -0,0 +1,15 @@ + + + + net7.0 + enable + enable + net7.0-windows + true + + + + + + + diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleDecoder.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleDecoder.cs new file mode 100644 index 00000000..f938cf78 --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleDecoder.cs @@ -0,0 +1,67 @@ +using System.Drawing; +using System.Drawing.Imaging; +using NativeMethods = ImoutoRebirth.Common.WebP.Extern.NativeMethods; + +namespace ImoutoRebirth.Common.WebP; + +public class SimpleDecoder +{ + /// + /// Creates a new instance of SimpleDecoder + /// + public SimpleDecoder() + { + } + + + public static string GetDecoderVersion() + { + var v = (uint)NativeMethods.WebPGetDecoderVersion(); + var revision = v % 256; + var minor = (v >> 8) % 256; + var major = (v >> 16) % 256; + return major + "." + minor + "." + revision; + } + + + public unsafe Bitmap DecodeFromBytes(byte[] data, long length) + { + fixed (byte* dataptr = data) + { + return DecodeFromPointer((IntPtr)dataptr, length); + } + } + + public Bitmap DecodeFromPointer(IntPtr data, long length) + { + int w = 0, h = 0; + //Validate header and determine size + if (NativeMethods.WebPGetInfo(data, (UIntPtr)length, ref w, ref h) == 0) + throw new Exception("Invalid WebP header detected"); + + var success = false; + Bitmap b = null; + BitmapData bd = null; + try + { + //Allocate canvas + b = new Bitmap(w, h, PixelFormat.Format32bppArgb); + //Lock surface for writing + bd = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + //Decode to surface + var result = NativeMethods.WebPDecodeBGRAInto(data, (UIntPtr)length, bd.Scan0, + (UIntPtr)(bd.Stride * bd.Height), bd.Stride); + if (bd.Scan0 != result) throw new Exception("Failed to decode WebP image with error " + (long)result); + success = true; + } + finally + { + //Unlock surface + if (bd != null && b != null) b.UnlockBits(bd); + //Dispose of bitmap if anything went wrong + if (!success && b != null) b.Dispose(); + } + + return b; + } +} diff --git a/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleEncoder.cs b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleEncoder.cs new file mode 100644 index 00000000..0da7889e --- /dev/null +++ b/Source/ImoutoRebirth.Common/ImoutoRebirth.Common.WebP/SimpleEncoder.cs @@ -0,0 +1,127 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using NativeMethods = ImoutoRebirth.Common.WebP.Extern.NativeMethods; + +namespace ImoutoRebirth.Common.WebP +{ + /// + /// Encodes Bitmap objects into WebP format + /// + public class SimpleEncoder + { + public static string GetEncoderVersion() + { + var v = (uint)NativeMethods.WebPGetEncoderVersion(); + var revision = v % 256; + var minor = (v >> 8) % 256; + var major = (v >> 16) % 256; + return major + "." + minor + "." + revision; + } + + /// + /// Encodes the given RGB(A) bitmap to the given stream. Specify quality = -1 for lossless, otherwise specify a value + /// between 0 and 100. + /// + /// + /// + /// + /// + [Obsolete] + public void Encode(Bitmap from, Stream to, float quality, bool noAlpha) + { + Encode(from, to, quality); + } + + /// + /// Encodes the given RGB(A) bitmap to the given stream. Specify quality = -1 for lossless, otherwise specify a value + /// between 0 and 100. + /// + /// + /// + /// + public void Encode(Bitmap from, Stream to, float quality) + { + IntPtr result; + long length; + + Encode(from, quality, out result, out length); + try + { + var buffer = new byte[4096]; + for (var i = 0; i < length; i += buffer.Length) + { + var used = (int)Math.Min(buffer.Length, length - i); + Marshal.Copy((IntPtr)((long)result + i), buffer, 0, used); + to.Write(buffer, 0, used); + } + } + finally + { + NativeMethods.WebPSafeFree(result); + } + } + + /// + /// Encodes the given RGB(A) bitmap to the given stream. Specify quality = -1 for lossless, otherwise specify a value + /// between 0 and 100. + /// + /// + /// + /// + /// + /// + [Obsolete] + public void Encode(Bitmap b, float quality, bool noAlpha, out IntPtr result, out long length) + { + Encode(b, quality, out result, out length); + } + + /// + /// Encodes the given RGB(A) bitmap to an unmanged memory buffer (returned via result/length). Specify quality = -1 for + /// lossless, otherwise specify a value between 0 and 100. + /// + /// + /// + /// + /// + public void Encode(Bitmap b, float quality, out IntPtr result, out long length) + { + if (quality < -1) quality = -1; + if (quality > 100) quality = 100; + var w = b.Width; + var h = b.Height; + var bd = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, b.PixelFormat); + try + { + result = IntPtr.Zero; + + if (b.PixelFormat == PixelFormat.Format32bppArgb) + { + if (quality == -1) + length = (long)NativeMethods.WebPEncodeLosslessBGRA(bd.Scan0, w, h, bd.Stride, ref result); + else length = NativeMethods.WebPEncodeBGRA(bd.Scan0, w, h, bd.Stride, quality, ref result); + } + else if (b.PixelFormat == PixelFormat.Format24bppRgb) + { + if (quality == -1) + length = (long)NativeMethods.WebPEncodeLosslessBGR(bd.Scan0, w, h, bd.Stride, ref result); + else length = (long)NativeMethods.WebPEncodeBGR(bd.Scan0, w, h, bd.Stride, quality, ref result); + } + else + { + using (var b2 = b.Clone(new Rectangle(0, 0, b.Width, b.Height), PixelFormat.Format32bppArgb)) + { + Encode(b2, quality, out result, out length); + } + } + + if (length == 0) throw new Exception("WebP encode failed!"); + } + finally + { + b.UnlockBits(bd); + } + } + } +} diff --git a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator.csproj b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator.csproj index 66254072..bd47f864 100644 --- a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator.csproj +++ b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator.csproj @@ -45,7 +45,6 @@ - @@ -167,6 +166,7 @@ + diff --git a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/Model/ImageEntry.cs b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/Model/ImageEntry.cs index f54cf7f3..401f8e43 100644 --- a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/Model/ImageEntry.cs +++ b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/Model/ImageEntry.cs @@ -2,6 +2,7 @@ using System.IO; using System.Windows; using System.Windows.Media.Imaging; +using ImoutoRebirth.Common.WebP; using ImoutoRebirth.Navigator.Utils; namespace ImoutoRebirth.Navigator.Model; @@ -137,7 +138,7 @@ private async Task Load() } else if (_frameSize == null) { - var decoder = new Imazen.WebP.SimpleDecoder(); + var decoder = new SimpleDecoder(); var bytes = await File.ReadAllBytesAsync(_path); var bitmap = decoder.DecodeFromBytes(bytes, bytes.Length); @@ -185,7 +186,7 @@ private async Task LoadBitmapImage( var useStream = false; if (IsWebp) { - var decoder = new Imazen.WebP.SimpleDecoder(); + var decoder = new SimpleDecoder(); var bytes = File.ReadAllBytes(_path); var bitmap = decoder.DecodeFromBytes(bytes, bytes.Length); bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); diff --git a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/MouseWheelGesture.cs b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/MouseWheelGesture.cs index a59541ea..49c708f2 100644 --- a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/MouseWheelGesture.cs +++ b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/MouseWheelGesture.cs @@ -6,76 +6,26 @@ public class MouseWheelGesture : MouseGesture { private WheelDirection Direction { get; set; } - public static MouseWheelGesture Up - { - get - { - return new MouseWheelGesture { Direction = WheelDirection.Up }; - } - } + public static MouseWheelGesture Up => new() { Direction = WheelDirection.Up }; - public static MouseWheelGesture Down - { - get - { - return new MouseWheelGesture { Direction = WheelDirection.Down }; - } - } + public static MouseWheelGesture Down => new() { Direction = WheelDirection.Down }; - public static MouseWheelGesture CtrlUp - { - get - { - return new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Up }; - } - } + public static MouseWheelGesture CtrlUp => new(ModifierKeys.Control) { Direction = WheelDirection.Up }; - public static MouseWheelGesture CtrlDown - { - get - { - return new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Down }; - } - } + public static MouseWheelGesture CtrlDown => new(ModifierKeys.Control) { Direction = WheelDirection.Down }; - public static MouseWheelGesture AltUp - { - get - { - return new MouseWheelGesture(ModifierKeys.Alt) { Direction = WheelDirection.Up }; - } - } + public static MouseWheelGesture AltUp => new(ModifierKeys.Alt) { Direction = WheelDirection.Up }; - public static MouseWheelGesture AltDown - { - get - { - return new MouseWheelGesture(ModifierKeys.Alt) { Direction = WheelDirection.Down }; - } - } + public static MouseWheelGesture AltDown => new(ModifierKeys.Alt) { Direction = WheelDirection.Down }; - public static MouseWheelGesture ShiftUp - { - get - { - return new MouseWheelGesture(ModifierKeys.Shift) { Direction = WheelDirection.Up }; - } - } + public static MouseWheelGesture ShiftUp => new(ModifierKeys.Shift) { Direction = WheelDirection.Up }; - public static MouseWheelGesture ShiftDown - { - get - { - return new MouseWheelGesture(ModifierKeys.Shift) { Direction = WheelDirection.Down }; - } - } + public static MouseWheelGesture ShiftDown => new(ModifierKeys.Shift) { Direction = WheelDirection.Down }; - public MouseWheelGesture() - : base(MouseAction.WheelClick) + public MouseWheelGesture() : base(MouseAction.WheelClick) { } - public MouseWheelGesture(ModifierKeys modifiers) - : base(MouseAction.WheelClick, modifiers) + public MouseWheelGesture(ModifierKeys modifiers) : base(MouseAction.WheelClick, modifiers) { } public override bool Matches(object targetElement, InputEventArgs inputEventArgs) @@ -105,4 +55,4 @@ private enum WheelDirection Up, Down, } -} \ No newline at end of file +} diff --git a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/RatingControl.xaml.cs b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/RatingControl.xaml.cs index 48aef732..93d06a2e 100644 --- a/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/RatingControl.xaml.cs +++ b/Source/ImoutoRebirth.Navigator/ImoutoRebirth.Navigator/UserControls/RatingControl.xaml.cs @@ -15,11 +15,16 @@ public RatingControl() { InitializeComponent(); } - + /// /// Value Dependency Property /// - public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof (int), typeof (RatingControl), new PropertyMetadata(0, OnValueChanged)); + public static readonly DependencyProperty ValueProperty + = DependencyProperty.Register( + nameof(Value), + typeof(int), + typeof(RatingControl), + new PropertyMetadata(0, OnValueChanged)); /// /// Gets or sets the Value property. @@ -37,10 +42,7 @@ public int Value return 0; } } - set - { - SetValue(ValueProperty, value); - } + set => SetValue(ValueProperty, value); } /// @@ -104,4 +106,4 @@ private void GetRating(Border img) string strImgName = img.Name; _ratingUnderMouse = Convert.ToInt32(strImgName.Substring(strImgName.Length - 1, 1)); } -} \ No newline at end of file +} diff --git a/Source/ImoutoRebirth.sln b/Source/ImoutoRebirth.sln index cd6ae76c..57468233 100644 --- a/Source/ImoutoRebirth.sln +++ b/Source/ImoutoRebirth.sln @@ -137,6 +137,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImoutoRebirth.Meido.Applica EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImoutoRebirth.Meido.UI", "ImoutoRebirth.Meido\ImoutoRebirth.Meido.UI\ImoutoRebirth.Meido.UI.csproj", "{ABA7639A-2342-4050-8ACA-F795C9EA59A1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImoutoRebirth.Common.WebP", "ImoutoRebirth.Common\ImoutoRebirth.Common.WebP\ImoutoRebirth.Common.WebP.csproj", "{36C7BF70-F817-44C8-ABBE-3015D73D2B96}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -352,6 +354,10 @@ Global {ABA7639A-2342-4050-8ACA-F795C9EA59A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {ABA7639A-2342-4050-8ACA-F795C9EA59A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {ABA7639A-2342-4050-8ACA-F795C9EA59A1}.Release|Any CPU.Build.0 = Release|Any CPU + {36C7BF70-F817-44C8-ABBE-3015D73D2B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36C7BF70-F817-44C8-ABBE-3015D73D2B96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36C7BF70-F817-44C8-ABBE-3015D73D2B96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36C7BF70-F817-44C8-ABBE-3015D73D2B96}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {9BC327CE-D36E-4926-9830-DCEB0CB73884} = {6BEB0A5C-9E6B-4440-93B2-F4C63BE43298} @@ -405,5 +411,6 @@ Global {904A4D4E-FA47-45EF-9FB4-6A3F5820E5D1} = {614D6032-416A-4B4D-A772-3C86BADFDBDD} {BC055FCD-527B-4628-8B3A-78D20AFA322B} = {614D6032-416A-4B4D-A772-3C86BADFDBDD} {ABA7639A-2342-4050-8ACA-F795C9EA59A1} = {614D6032-416A-4B4D-A772-3C86BADFDBDD} + {36C7BF70-F817-44C8-ABBE-3015D73D2B96} = {4383FD00-3B54-4C2B-9222-BDC045D3FD9B} EndGlobalSection EndGlobal