diff --git a/Wabbajack.App.Wpf/Util/UIUtils.cs b/Wabbajack.App.Wpf/Util/UIUtils.cs
index 152de857..df8aba19 100644
--- a/Wabbajack.App.Wpf/Util/UIUtils.cs
+++ b/Wabbajack.App.Wpf/Util/UIUtils.cs
@@ -19,6 +19,9 @@ using Wabbajack.Extensions;
using Wabbajack.Models;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
+using System.Drawing;
+using Catel.IO;
+using System.Drawing.Imaging;
namespace Wabbajack
{
@@ -37,6 +40,32 @@ namespace Wabbajack
return img;
}
+ public static BitmapImage BitmapImageFromWebp(byte[] bytes, bool getThumbnail = false)
+ {
+ using(WebP webp = new())
+ {
+ Bitmap bitmap;
+ if (getThumbnail)
+ bitmap = webp.GetThumbnailFast(bytes, 640, 360);
+ else
+ bitmap = webp.Decode(bytes);
+
+ using(var ms = new MemoryStream())
+ {
+ bitmap.Save(ms, ImageFormat.Png);
+ ms.Position = 0;
+
+ var img = new BitmapImage();
+ img.BeginInit();
+ img.CacheOption = BitmapCacheOption.OnLoad;
+ img.StreamSource = ms;
+ img.EndInit();
+ img.Freeze();
+ return img;
+ }
+ }
+ }
+
public static bool TryGetBitmapImageFromFile(AbsolutePath path, out BitmapImage bitmapImage)
{
try
@@ -95,7 +124,7 @@ namespace Wabbajack
try
{
var (found, mstream) = await FindCachedImage(url);
- if (found) return (ll, mstream);
+ if (found) return (ll, mstream, url);
var ret = new MemoryStream();
using (var client = new HttpClient())
@@ -107,21 +136,21 @@ namespace Wabbajack
ret.Seek(0, SeekOrigin.Begin);
await WriteCachedImage(url, ret.ToArray());
- return (ll, ret);
+ return (ll, ret, url);
}
catch (Exception ex)
{
exceptionHandler(ex);
- return (ll, default);
+ return (ll, default, url);
}
})
.Select(x =>
{
- var (ll, memStream) = x;
+ var (ll, memStream, url) = x;
if (memStream == null) return default;
try
{
- return BitmapImageFromStream(memStream);
+ return url.EndsWith("webp", StringComparison.InvariantCultureIgnoreCase) ? BitmapImageFromWebp(memStream.ToArray(), true) : BitmapImageFromStream(memStream);
}
catch (Exception ex)
{
diff --git a/Wabbajack.App.Wpf/Util/WebPWrapper.cs b/Wabbajack.App.Wpf/Util/WebPWrapper.cs
new file mode 100644
index 00000000..e0acad2b
--- /dev/null
+++ b/Wabbajack.App.Wpf/Util/WebPWrapper.cs
@@ -0,0 +1,1976 @@
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Wrapper for WebP format in C#. (MIT) Jose M. Piñeiro
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Decode Functions:
+/// Bitmap Load(string pathFileName) - Load a WebP file in bitmap.
+/// Bitmap Decode(byte[] rawWebP) - Decode WebP data (rawWebP) to bitmap.
+/// Bitmap Decode(byte[] rawWebP, WebPDecoderOptions options) - Decode WebP data (rawWebP) to bitmap using 'options'.
+/// Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height) - Get a thumbnail from WebP data (rawWebP) with dimensions 'width x height'. Fast mode.
+/// Bitmap GetThumbnailQuality(byte[] rawWebP, int width, int height) - Fast get a thumbnail from WebP data (rawWebP) with dimensions 'width x height'. Quality mode.
+///
+/// Encode Functions:
+/// Save(Bitmap bmp, string pathFileName, int quality) - Save bitmap with quality lost to WebP file. Opcionally select 'quality'.
+/// byte[] EncodeLossy(Bitmap bmp, int quality) - Encode bitmap with quality lost to WebP byte array. Opcionally select 'quality'.
+/// byte[] EncodeLossy(Bitmap bmp, int quality, int speed, bool info) - Encode bitmap with quality lost to WebP byte array. Select 'quality', 'speed' and optionally select 'info'.
+/// byte[] EncodeLossless(Bitmap bmp) - Encode bitmap without quality lost to WebP byte array.
+/// byte[] EncodeLossless(Bitmap bmp, int speed, bool info = false) - Encode bitmap without quality lost to WebP byte array. Select 'speed'.
+/// byte[] EncodeNearLossless(Bitmap bmp, int quality, int speed = 9, bool info = false) - Encode bitmap with a near lossless method to WebP byte array. Select 'quality', 'speed' and optionally select 'info'.
+///
+/// Another functions:
+/// string GetVersion() - Get the library version
+/// GetInfo(byte[] rawWebP, out int width, out int height, out bool has_alpha, out bool has_animation, out string format) - Get information of WEBP data
+/// float[] PictureDistortion(Bitmap source, Bitmap reference, int metric_type) - Get PSNR, SSIM or LSIM distortion metric between two pictures
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows.Forms;
+
+namespace Wabbajack
+{
+ public sealed class WebP : IDisposable
+ {
+ private const int WEBP_MAX_DIMENSION = 16383;
+ #region | Public Decode Functions |
+ /// Read a WebP file
+ /// WebP file to load
+ /// Bitmap with the WebP image
+ public Bitmap Load(string pathFileName)
+ {
+ try
+ {
+ byte[] rawWebP = File.ReadAllBytes(pathFileName);
+
+ return Decode(rawWebP);
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.Load"); }
+ }
+
+ /// Decode a WebP image
+ /// The data to uncompress
+ /// Bitmap with the WebP image
+ public Bitmap Decode(byte[] rawWebP)
+ {
+ Bitmap bmp = null;
+ BitmapData bmpData = null;
+ GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned);
+
+ try
+ {
+ //Get image width and height
+ GetInfo(rawWebP, out int imgWidth, out int imgHeight, out bool hasAlpha, out bool hasAnimation, out string format);
+
+ //Create a BitmapData and Lock all pixels to be written
+ if (hasAlpha)
+ bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format32bppArgb);
+ else
+ bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format24bppRgb);
+ bmpData = bmp.LockBits(new Rectangle(0, 0, imgWidth, imgHeight), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ //Uncompress the image
+ int outputSize = bmpData.Stride * imgHeight;
+ IntPtr ptrData = pinnedWebP.AddrOfPinnedObject();
+ if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
+ UnsafeNativeMethods.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
+ else
+ UnsafeNativeMethods.WebPDecodeBGRAInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
+
+ return bmp;
+ }
+ catch (Exception) { throw; }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (pinnedWebP.IsAllocated)
+ pinnedWebP.Free();
+ }
+ }
+
+ /// Decode a WebP image
+ /// the data to uncompress
+ /// Options for advanced decode
+ /// Bitmap with the WebP image
+ public Bitmap Decode(byte[] rawWebP, WebPDecoderOptions options, PixelFormat pixelFormat = PixelFormat.DontCare)
+ {
+ GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned);
+ Bitmap bmp = null;
+ BitmapData bmpData = null;
+ VP8StatusCode result;
+ try
+ {
+ WebPDecoderConfig config = new WebPDecoderConfig();
+ if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0)
+ {
+ throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
+ }
+ // Read the .webp input file information
+ IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject();
+ int height;
+ int width;
+ if (options.use_scaling == 0)
+ {
+ result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref config.input);
+ if (result != VP8StatusCode.VP8_STATUS_OK)
+ throw new Exception("Failed WebPGetFeatures with error " + result);
+
+ //Test cropping values
+ if (options.use_cropping == 1)
+ {
+ if (options.crop_left + options.crop_width > config.input.Width || options.crop_top + options.crop_height > config.input.Height)
+ throw new Exception("Crop options exceeded WebP image dimensions");
+ width = options.crop_width;
+ height = options.crop_height;
+ }
+ }
+ else
+ {
+ width = options.scaled_width;
+ height = options.scaled_height;
+ }
+
+ config.options.bypass_filtering = options.bypass_filtering;
+ config.options.no_fancy_upsampling = options.no_fancy_upsampling;
+ config.options.use_cropping = options.use_cropping;
+ config.options.crop_left = options.crop_left;
+ config.options.crop_top = options.crop_top;
+ config.options.crop_width = options.crop_width;
+ config.options.crop_height = options.crop_height;
+ config.options.use_scaling = options.use_scaling;
+ config.options.scaled_width = options.scaled_width;
+ config.options.scaled_height = options.scaled_height;
+ config.options.use_threads = options.use_threads;
+ config.options.dithering_strength = options.dithering_strength;
+ config.options.flip = options.flip;
+ config.options.alpha_dithering_strength = options.alpha_dithering_strength;
+
+ //Create a BitmapData and Lock all pixels to be written
+ if (config.input.Has_alpha == 1)
+ {
+ config.output.colorspace = WEBP_CSP_MODE.MODE_bgrA;
+ bmp = new Bitmap(config.input.Width, config.input.Height, PixelFormat.Format32bppArgb);
+ }
+ else
+ {
+ config.output.colorspace = WEBP_CSP_MODE.MODE_BGR;
+ bmp = new Bitmap(config.input.Width, config.input.Height, PixelFormat.Format24bppRgb);
+ }
+ bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ // Specify the output format
+ config.output.u.RGBA.rgba = bmpData.Scan0;
+ config.output.u.RGBA.stride = bmpData.Stride;
+ config.output.u.RGBA.size = (UIntPtr)(bmp.Height * bmpData.Stride);
+ config.output.height = bmp.Height;
+ config.output.width = bmp.Width;
+ config.output.is_external_memory = 1;
+
+ // Decode
+ result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config);
+ if (result != VP8StatusCode.VP8_STATUS_OK)
+ {
+ throw new Exception("Failed WebPDecode with error " + result);
+ }
+ UnsafeNativeMethods.WebPFreeDecBuffer(ref config.output);
+
+ return bmp;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.Decode"); }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (pinnedWebP.IsAllocated)
+ pinnedWebP.Free();
+ }
+ }
+
+ /// Get Thumbnail from webP in mode faster/low quality
+ /// The data to uncompress
+ /// Wanted width of thumbnail
+ /// Wanted height of thumbnail
+ /// Bitmap with the WebP thumbnail in 24bpp
+ public Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height)
+ {
+ GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned);
+ Bitmap bmp = null;
+ BitmapData bmpData = null;
+
+ try
+ {
+ WebPDecoderConfig config = new WebPDecoderConfig();
+ if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0)
+ throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
+
+ // Set up decode options
+ config.options.bypass_filtering = 1;
+ config.options.no_fancy_upsampling = 1;
+ config.options.use_threads = 1;
+ config.options.use_scaling = 1;
+ config.options.scaled_width = width;
+ config.options.scaled_height = height;
+
+ // Create a BitmapData and Lock all pixels to be written
+ bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
+ bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ // Specify the output format
+ config.output.colorspace = WEBP_CSP_MODE.MODE_BGR;
+ config.output.u.RGBA.rgba = bmpData.Scan0;
+ config.output.u.RGBA.stride = bmpData.Stride;
+ config.output.u.RGBA.size = (UIntPtr)(height * bmpData.Stride);
+ config.output.height = height;
+ config.output.width = width;
+ config.output.is_external_memory = 1;
+
+ // Decode
+ IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject();
+ VP8StatusCode result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config);
+ if (result != VP8StatusCode.VP8_STATUS_OK)
+ throw new Exception("Failed WebPDecode with error " + result);
+
+ UnsafeNativeMethods.WebPFreeDecBuffer(ref config.output);
+
+ return bmp;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.Thumbnail"); }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (pinnedWebP.IsAllocated)
+ pinnedWebP.Free();
+ }
+ }
+
+ /// Thumbnail from webP in mode slow/high quality
+ /// The data to uncompress
+ /// Wanted width of thumbnail
+ /// Wanted height of thumbnail
+ /// Bitmap with the WebP thumbnail
+ public Bitmap GetThumbnailQuality(byte[] rawWebP, int width, int height)
+ {
+ GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned);
+ Bitmap bmp = null;
+ BitmapData bmpData = null;
+
+ try
+ {
+ WebPDecoderConfig config = new WebPDecoderConfig();
+ if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0)
+ throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
+
+ IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject();
+ VP8StatusCode result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref config.input);
+ if (result != VP8StatusCode.VP8_STATUS_OK)
+ throw new Exception("Failed WebPGetFeatures with error " + result);
+
+ // Set up decode options
+ config.options.bypass_filtering = 0;
+ config.options.no_fancy_upsampling = 0;
+ config.options.use_threads = 1;
+ config.options.use_scaling = 1;
+ config.options.scaled_width = width;
+ config.options.scaled_height = height;
+
+ //Create a BitmapData and Lock all pixels to be written
+ if (config.input.Has_alpha == 1)
+ {
+ config.output.colorspace = WEBP_CSP_MODE.MODE_bgrA;
+ bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
+ }
+ else
+ {
+ config.output.colorspace = WEBP_CSP_MODE.MODE_BGR;
+ bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
+ }
+ bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ // Specify the output format
+ config.output.u.RGBA.rgba = bmpData.Scan0;
+ config.output.u.RGBA.stride = bmpData.Stride;
+ config.output.u.RGBA.size = (UIntPtr)(height * bmpData.Stride);
+ config.output.height = height;
+ config.output.width = width;
+ config.output.is_external_memory = 1;
+
+ // Decode
+ result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config);
+ if (result != VP8StatusCode.VP8_STATUS_OK)
+ throw new Exception("Failed WebPDecode with error " + result);
+
+ UnsafeNativeMethods.WebPFreeDecBuffer(ref config.output);
+
+ return bmp;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.Thumbnail"); }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (pinnedWebP.IsAllocated)
+ pinnedWebP.Free();
+ }
+ }
+ #endregion
+
+ #region | Public Encode Functions |
+ /// Save bitmap to file in WebP format
+ /// Bitmap with the WebP image
+ /// The file to write
+ /// Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)
+ public void Save(Bitmap bmp, string pathFileName, int quality = 75)
+ {
+ byte[] rawWebP;
+
+ try
+ {
+ //Encode in webP format
+ rawWebP = EncodeLossy(bmp, quality);
+
+ //Write webP file
+ File.WriteAllBytes(pathFileName, rawWebP);
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.Save"); }
+ }
+
+ /// Lossy encoding bitmap to WebP (Simple encoding API)
+ /// Bitmap with the image
+ /// Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)
+ /// Compressed data
+ public byte[] EncodeLossy(Bitmap bmp, int quality = 75)
+ {
+ //test bmp
+ if (bmp.Width == 0 || bmp.Height == 0)
+ throw new ArgumentException("Bitmap contains no data.", "bmp");
+ if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION)
+ throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels.");
+ if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb)
+ throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat.");
+
+ BitmapData bmpData = null;
+ IntPtr unmanagedData = IntPtr.Zero;
+
+ try
+ {
+ int size;
+
+ //Get bmp data
+ bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
+
+ //Compress the bmp data
+ if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
+ size = UnsafeNativeMethods.WebPEncodeBGR(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, quality, out unmanagedData);
+ else
+ size = UnsafeNativeMethods.WebPEncodeBGRA(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, quality, out unmanagedData);
+ if (size == 0)
+ throw new Exception("Can´t encode WebP");
+
+ //Copy image compress data to output array
+ byte[] rawWebP = new byte[size];
+ Marshal.Copy(unmanagedData, rawWebP, 0, size);
+
+ return rawWebP;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.EncodeLossly"); }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (unmanagedData != IntPtr.Zero)
+ UnsafeNativeMethods.WebPFree(unmanagedData);
+ }
+ }
+
+ /// Lossy encoding bitmap to WebP (Advanced encoding API)
+ /// Bitmap with the image
+ /// Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)
+ /// Between 0 (fastest, lowest compression) and 9 (slower, best compression)
+ /// Compressed data
+ public byte[] EncodeLossy(Bitmap bmp, int quality, int speed, bool info = false)
+ {
+ //Initialize configuration structure
+ WebPConfig config = new WebPConfig();
+
+ //Set compression parameters
+ if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, 75) == 0)
+ throw new Exception("Can´t configure preset");
+
+ // Add additional tuning:
+ config.method = speed;
+ if (config.method > 6)
+ config.method = 6;
+ config.quality = quality;
+ config.autofilter = 1;
+ config.pass = speed + 1;
+ config.segments = 4;
+ config.partitions = 3;
+ config.thread_level = 1;
+ config.alpha_quality = quality;
+ config.alpha_filtering = 2;
+ config.use_sharp_yuv = 1;
+
+ if (UnsafeNativeMethods.WebPGetDecoderVersion() > 1082) //Old version does not support preprocessing 4
+ {
+ config.preprocessing = 4;
+ config.use_sharp_yuv = 1;
+ }
+ else
+ config.preprocessing = 3;
+
+ return AdvancedEncode(bmp, config, info);
+ }
+
+ /// Lossless encoding bitmap to WebP (Simple encoding API)
+ /// Bitmap with the image
+ /// Compressed data
+ public byte[] EncodeLossless(Bitmap bmp)
+ {
+ //test bmp
+ if (bmp.Width == 0 || bmp.Height == 0)
+ throw new ArgumentException("Bitmap contains no data.", "bmp");
+ if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION)
+ throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels.");
+ if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb)
+ throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat.");
+
+ BitmapData bmpData = null;
+ IntPtr unmanagedData = IntPtr.Zero;
+ try
+ {
+ //Get bmp data
+ bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
+
+ //Compress the bmp data
+ int size;
+ if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
+ size = UnsafeNativeMethods.WebPEncodeLosslessBGR(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, out unmanagedData);
+ else
+ size = UnsafeNativeMethods.WebPEncodeLosslessBGRA(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, out unmanagedData);
+
+ //Copy image compress data to output array
+ byte[] rawWebP = new byte[size];
+ Marshal.Copy(unmanagedData, rawWebP, 0, size);
+
+ return rawWebP;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.EncodeLossless (Simple)"); }
+ finally
+ {
+ //Unlock the pixels
+ if (bmpData != null)
+ bmp.UnlockBits(bmpData);
+
+ //Free memory
+ if (unmanagedData != IntPtr.Zero)
+ UnsafeNativeMethods.WebPFree(unmanagedData);
+ }
+ }
+
+ /// Lossless encoding image in bitmap (Advanced encoding API)
+ /// Bitmap with the image
+ /// Between 0 (fastest, lowest compression) and 9 (slower, best compression)
+ /// Compressed data
+ public byte[] EncodeLossless(Bitmap bmp, int speed)
+ {
+ //Initialize configuration structure
+ WebPConfig config = new WebPConfig();
+
+ //Set compression parameters
+ if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, (speed + 1) * 10) == 0)
+ throw new Exception("Can´t config preset");
+
+ //Old version of DLL does not support info and WebPConfigLosslessPreset
+ if (UnsafeNativeMethods.WebPGetDecoderVersion() > 1082)
+ {
+ if (UnsafeNativeMethods.WebPConfigLosslessPreset(ref config, speed) == 0)
+ throw new Exception("Can´t configure lossless preset");
+ }
+ else
+ {
+ config.lossless = 1;
+ config.method = speed;
+ if (config.method > 6)
+ config.method = 6;
+ config.quality = (speed + 1) * 10;
+ }
+ config.pass = speed + 1;
+ config.thread_level = 1;
+ config.alpha_filtering = 2;
+ config.use_sharp_yuv = 1;
+ config.exact = 0;
+
+ return AdvancedEncode(bmp, config, false);
+ }
+
+ /// Near lossless encoding image in bitmap
+ /// Bitmap with the image
+ /// Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)
+ /// Between 0 (fastest, lowest compression) and 9 (slower, best compression)
+ /// Compress data
+ public byte[] EncodeNearLossless(Bitmap bmp, int quality, int speed = 9)
+ {
+ //test DLL version
+ if (UnsafeNativeMethods.WebPGetDecoderVersion() <= 1082)
+ throw new Exception("This DLL version not support EncodeNearLossless");
+
+ //Inicialize config struct
+ WebPConfig config = new WebPConfig();
+
+ //Set compression parameters
+ if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, (speed + 1) * 10) == 0)
+ throw new Exception("Can´t configure preset");
+ if (UnsafeNativeMethods.WebPConfigLosslessPreset(ref config, speed) == 0)
+ throw new Exception("Can´t configure lossless preset");
+ config.pass = speed + 1;
+ config.near_lossless = quality;
+ config.thread_level = 1;
+ config.alpha_filtering = 2;
+ config.use_sharp_yuv = 1;
+ config.exact = 0;
+
+ return AdvancedEncode(bmp, config, false);
+ }
+ #endregion
+
+ #region | Another Public Functions |
+ /// Get the libwebp version
+ /// Version of library
+ public string GetVersion()
+ {
+ try
+ {
+ uint v = (uint)UnsafeNativeMethods.WebPGetDecoderVersion();
+ var revision = v % 256;
+ var minor = (v >> 8) % 256;
+ var major = (v >> 16) % 256;
+ return major + "." + minor + "." + revision;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.GetVersion"); }
+ }
+
+ /// Get info of WEBP data
+ /// The data of WebP
+ /// width of image
+ /// height of image
+ /// Image has alpha channel
+ /// Image is a animation
+ /// Format of image: 0 = undefined (/mixed), 1 = lossy, 2 = lossless
+ public void GetInfo(byte[] rawWebP, out int width, out int height, out bool has_alpha, out bool has_animation, out string format)
+ {
+ VP8StatusCode result;
+ GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned);
+
+ try
+ {
+ IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject();
+
+ WebPBitstreamFeatures features = new WebPBitstreamFeatures();
+ result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref features);
+
+ if (result != 0)
+ throw new Exception(result.ToString());
+
+ width = features.Width;
+ height = features.Height;
+ if (features.Has_alpha == 1) has_alpha = true; else has_alpha = false;
+ if (features.Has_animation == 1) has_animation = true; else has_animation = false;
+ switch (features.Format)
+ {
+ case 1:
+ format = "lossy";
+ break;
+ case 2:
+ format = "lossless";
+ break;
+ default:
+ format = "undefined";
+ break;
+ }
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.GetInfo"); }
+ finally
+ {
+ //Free memory
+ if (pinnedWebP.IsAllocated)
+ pinnedWebP.Free();
+ }
+ }
+
+ /// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Warning: this function is rather CPU-intensive
+ /// Picture to measure
+ /// Reference picture
+ /// 0 = PSNR, 1 = SSIM, 2 = LSIM
+ /// dB in the Y/U/V/Alpha/All order
+ public float[] GetPictureDistortion(Bitmap source, Bitmap reference, int metric_type)
+ {
+ WebPPicture wpicSource = new WebPPicture();
+ WebPPicture wpicReference = new WebPPicture();
+ BitmapData sourceBmpData = null;
+ BitmapData referenceBmpData = null;
+ float[] result = new float[5];
+ GCHandle pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned);
+
+ try
+ {
+ if (source == null)
+ throw new Exception("Source picture is void");
+ if (reference == null)
+ throw new Exception("Reference picture is void");
+ if (metric_type > 2)
+ throw new Exception("Bad metric_type. Use 0 = PSNR, 1 = SSIM, 2 = LSIM");
+ if (source.Width != reference.Width || source.Height != reference.Height)
+ throw new Exception("Source and Reference pictures have different dimensions");
+
+ // Setup the source picture data, allocating the bitmap, width and height
+ sourceBmpData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat);
+ wpicSource = new WebPPicture();
+ if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpicSource) != 1)
+ throw new Exception("Can´t initialize WebPPictureInit");
+ wpicSource.width = (int)source.Width;
+ wpicSource.height = (int)source.Height;
+
+ //Put the source bitmap componets in wpic
+ if (sourceBmpData.PixelFormat == PixelFormat.Format32bppArgb)
+ {
+ wpicSource.use_argb = 1;
+ if (UnsafeNativeMethods.WebPPictureImportBGRA(ref wpicSource, sourceBmpData.Scan0, sourceBmpData.Stride) != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGR");
+ }
+ else
+ {
+ wpicSource.use_argb = 0;
+ if (UnsafeNativeMethods.WebPPictureImportBGR(ref wpicSource, sourceBmpData.Scan0, sourceBmpData.Stride) != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGR");
+ }
+
+ // Setup the reference picture data, allocating the bitmap, width and height
+ referenceBmpData = reference.LockBits(new Rectangle(0, 0, reference.Width, reference.Height), ImageLockMode.ReadOnly, reference.PixelFormat);
+ wpicReference = new WebPPicture();
+ if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpicReference) != 1)
+ throw new Exception("Can´t initialize WebPPictureInit");
+ wpicReference.width = (int)reference.Width;
+ wpicReference.height = (int)reference.Height;
+ wpicReference.use_argb = 1;
+
+ //Put the source bitmap contents in WebPPicture instance
+ if (sourceBmpData.PixelFormat == PixelFormat.Format32bppArgb)
+ {
+ wpicSource.use_argb = 1;
+ if (UnsafeNativeMethods.WebPPictureImportBGRA(ref wpicReference, referenceBmpData.Scan0, referenceBmpData.Stride) != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGR");
+ }
+ else
+ {
+ wpicSource.use_argb = 0;
+ if (UnsafeNativeMethods.WebPPictureImportBGR(ref wpicReference, referenceBmpData.Scan0, referenceBmpData.Stride) != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGR");
+ }
+
+ //Measure
+ IntPtr ptrResult = pinnedResult.AddrOfPinnedObject();
+ if (UnsafeNativeMethods.WebPPictureDistortion(ref wpicSource, ref wpicReference, metric_type, ptrResult) != 1)
+ throw new Exception("Can´t measure.");
+ return result;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.GetPictureDistortion"); }
+ finally
+ {
+ //Unlock the pixels
+ if (sourceBmpData != null)
+ source.UnlockBits(sourceBmpData);
+ if (referenceBmpData != null)
+ reference.UnlockBits(referenceBmpData);
+
+ //Free memory
+ if (wpicSource.argb != IntPtr.Zero)
+ UnsafeNativeMethods.WebPPictureFree(ref wpicSource);
+ if (wpicReference.argb != IntPtr.Zero)
+ UnsafeNativeMethods.WebPPictureFree(ref wpicReference);
+ //Free memory
+ if (pinnedResult.IsAllocated)
+ pinnedResult.Free();
+ }
+ }
+ #endregion
+
+ #region | Private Methods |
+ /// Encoding image using Advanced encoding API
+ /// Bitmap with the image
+ /// Configuration for encode
+ /// True if need encode info.
+ /// Compressed data
+ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info)
+ {
+ byte[] rawWebP = null;
+ byte[] dataWebp = null;
+ WebPPicture wpic = new WebPPicture();
+ BitmapData bmpData = null;
+ WebPAuxStats stats = new WebPAuxStats();
+ IntPtr ptrStats = IntPtr.Zero;
+ GCHandle pinnedArrayHandle = new GCHandle();
+ int dataWebpSize;
+ try
+ {
+ //Validate the configuration
+ if (UnsafeNativeMethods.WebPValidateConfig(ref config) != 1)
+ throw new Exception("Bad configuration parameters");
+
+ //test bmp
+ if (bmp.Width == 0 || bmp.Height == 0)
+ throw new ArgumentException("Bitmap contains no data.", "bmp");
+ if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION)
+ throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels.");
+ if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb)
+ throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat.");
+
+ // Setup the input data, allocating a the bitmap, width and height
+ bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
+ if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpic) != 1)
+ throw new Exception("Can´t initialize WebPPictureInit");
+ wpic.width = (int)bmp.Width;
+ wpic.height = (int)bmp.Height;
+ wpic.use_argb = 1;
+
+ if (bmp.PixelFormat == PixelFormat.Format32bppArgb)
+ {
+ //Put the bitmap componets in wpic
+ int result = UnsafeNativeMethods.WebPPictureImportBGRA(ref wpic, bmpData.Scan0, bmpData.Stride);
+ if (result != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGRA");
+ wpic.colorspace = (uint)WEBP_CSP_MODE.MODE_bgrA;
+ dataWebpSize = bmp.Width * bmp.Height * 32;
+ dataWebp = new byte[bmp.Width * bmp.Height * 32]; //Memory for WebP output
+ }
+ else
+ {
+ //Put the bitmap contents in WebPPicture instance
+ int result = UnsafeNativeMethods.WebPPictureImportBGR(ref wpic, bmpData.Scan0, bmpData.Stride);
+ if (result != 1)
+ throw new Exception("Can´t allocate memory in WebPPictureImportBGR");
+ dataWebpSize = bmp.Width * bmp.Height * 24;
+
+ }
+
+ //Set up statistics of compression
+ if (info)
+ {
+ stats = new WebPAuxStats();
+ ptrStats = Marshal.AllocHGlobal(Marshal.SizeOf(stats));
+ Marshal.StructureToPtr(stats, ptrStats, false);
+ wpic.stats = ptrStats;
+ }
+
+ //Memory for WebP output
+ if (dataWebpSize > 2147483591)
+ dataWebpSize = 2147483591;
+ dataWebp = new byte[bmp.Width * bmp.Height * 32];
+ pinnedArrayHandle = GCHandle.Alloc(dataWebp, GCHandleType.Pinned);
+ IntPtr initPtr = pinnedArrayHandle.AddrOfPinnedObject();
+ wpic.custom_ptr = initPtr;
+
+ //Set up a byte-writing method (write-to-memory, in this case)
+ UnsafeNativeMethods.OnCallback = new UnsafeNativeMethods.WebPMemoryWrite(MyWriter);
+ wpic.writer = Marshal.GetFunctionPointerForDelegate(UnsafeNativeMethods.OnCallback);
+
+ //compress the input samples
+ if (UnsafeNativeMethods.WebPEncode(ref config, ref wpic) != 1)
+ throw new Exception("Encoding error: " + ((WebPEncodingError)wpic.error_code).ToString());
+
+ //Remove OnCallback
+ UnsafeNativeMethods.OnCallback = null;
+
+ //Unlock the pixels
+ bmp.UnlockBits(bmpData);
+ bmpData = null;
+
+ //Copy webpData to rawWebP
+ int size = (int)((long)wpic.custom_ptr - (long)initPtr);
+ rawWebP = new byte[size];
+ Array.Copy(dataWebp, rawWebP, size);
+
+ //Remove compression data
+ pinnedArrayHandle.Free();
+ dataWebp = null;
+
+ //Show statistics
+ if (info)
+ {
+ stats = (WebPAuxStats)Marshal.PtrToStructure(ptrStats, typeof(WebPAuxStats));
+ MessageBox.Show("Dimension: " + wpic.width + " x " + wpic.height + " pixels\n" +
+ "Output: " + stats.coded_size + " bytes\n" +
+ "PSNR Y: " + stats.PSNRY + " db\n" +
+ "PSNR u: " + stats.PSNRU + " db\n" +
+ "PSNR v: " + stats.PSNRV + " db\n" +
+ "PSNR ALL: " + stats.PSNRALL + " db\n" +
+ "Block intra4: " + stats.block_count_intra4 + "\n" +
+ "Block intra16: " + stats.block_count_intra16 + "\n" +
+ "Block skipped: " + stats.block_count_skipped + "\n" +
+ "Header size: " + stats.header_bytes + " bytes\n" +
+ "Mode-partition: " + stats.mode_partition_0 + " bytes\n" +
+ "Macro-blocks 0: " + stats.segment_size_segments0 + " residuals bytes\n" +
+ "Macro-blocks 1: " + stats.segment_size_segments1 + " residuals bytes\n" +
+ "Macro-blocks 2: " + stats.segment_size_segments2 + " residuals bytes\n" +
+ "Macro-blocks 3: " + stats.segment_size_segments3 + " residuals bytes\n" +
+ "Quantizer 0: " + stats.segment_quant_segments0 + " residuals bytes\n" +
+ "Quantizer 1: " + stats.segment_quant_segments1 + " residuals bytes\n" +
+ "Quantizer 2: " + stats.segment_quant_segments2 + " residuals bytes\n" +
+ "Quantizer 3: " + stats.segment_quant_segments3 + " residuals bytes\n" +
+ "Filter level 0: " + stats.segment_level_segments0 + " residuals bytes\n" +
+ "Filter level 1: " + stats.segment_level_segments1 + " residuals bytes\n" +
+ "Filter level 2: " + stats.segment_level_segments2 + " residuals bytes\n" +
+ "Filter level 3: " + stats.segment_level_segments3 + " residuals bytes\n", "Compression statistics");
+ }
+
+ return rawWebP;
+ }
+ catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.AdvancedEncode"); }
+ finally
+ {
+ //Free temporal compress memory
+ if (pinnedArrayHandle.IsAllocated)
+ {
+ pinnedArrayHandle.Free();
+ }
+
+ //Free statistics memory
+ if (ptrStats != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(ptrStats);
+ }
+
+ //Unlock the pixels
+ if (bmpData != null)
+ {
+ bmp.UnlockBits(bmpData);
+ }
+
+ //Free memory
+ if (wpic.argb != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.WebPPictureFree(ref wpic);
+ }
+ }
+ }
+
+ private int MyWriter([InAttribute()] IntPtr data, UIntPtr data_size, ref WebPPicture picture)
+ {
+ UnsafeNativeMethods.CopyMemory(picture.custom_ptr, data, (uint)data_size);
+ //picture.custom_ptr = IntPtr.Add(picture.custom_ptr, (int)data_size); //Only in .NET > 4.0
+ picture.custom_ptr = new IntPtr(picture.custom_ptr.ToInt64() + (int)data_size);
+ return 1;
+ }
+
+ private delegate int MyWriterDelegate([InAttribute()] IntPtr data, UIntPtr data_size, ref WebPPicture picture);
+ #endregion
+
+ #region | Destruction |
+ /// Free memory
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+
+ #region | Import libwebp functions |
+ [SuppressUnmanagedCodeSecurityAttribute]
+ internal sealed partial class UnsafeNativeMethods
+ {
+
+ [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
+ internal static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
+
+ private static readonly int WEBP_DECODER_ABI_VERSION = 0x0208;
+
+ /// This function will initialize the configuration according to a predefined set of parameters (referred to by 'preset') and a given quality factor
+ /// The WebPConfig structure
+ /// Type of image
+ /// Quality of compression
+ /// 0 if error
+ internal static int WebPConfigInit(ref WebPConfig config, WebPPreset preset, float quality)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPConfigInitInternal_x86(ref config, preset, quality, WEBP_DECODER_ABI_VERSION);
+ case 8:
+ return WebPConfigInitInternal_x64(ref config, preset, quality, WEBP_DECODER_ABI_VERSION);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigInitInternal")]
+ private static extern int WebPConfigInitInternal_x86(ref WebPConfig config, WebPPreset preset, float quality, int WEBP_DECODER_ABI_VERSION);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigInitInternal")]
+ private static extern int WebPConfigInitInternal_x64(ref WebPConfig config, WebPPreset preset, float quality, int WEBP_DECODER_ABI_VERSION);
+
+ /// Get info of WepP image
+ /// Bytes[] of WebP image
+ /// Size of rawWebP
+ /// Features of WebP image
+ /// VP8StatusCode
+ internal static VP8StatusCode WebPGetFeatures(IntPtr rawWebP, int data_size, ref WebPBitstreamFeatures features)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPGetFeaturesInternal_x86(rawWebP, (UIntPtr)data_size, ref features, WEBP_DECODER_ABI_VERSION);
+ case 8:
+ return WebPGetFeaturesInternal_x64(rawWebP, (UIntPtr)data_size, ref features, WEBP_DECODER_ABI_VERSION);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetFeaturesInternal")]
+ private static extern VP8StatusCode WebPGetFeaturesInternal_x86([InAttribute()] IntPtr rawWebP, UIntPtr data_size, ref WebPBitstreamFeatures features, int WEBP_DECODER_ABI_VERSION);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetFeaturesInternal")]
+ private static extern VP8StatusCode WebPGetFeaturesInternal_x64([InAttribute()] IntPtr rawWebP, UIntPtr data_size, ref WebPBitstreamFeatures features, int WEBP_DECODER_ABI_VERSION);
+
+ /// Activate the lossless compression mode with the desired efficiency
+ /// The WebPConfig struct
+ /// between 0 (fastest, lowest compression) and 9 (slower, best compression)
+ /// 0 in case of parameter error
+ internal static int WebPConfigLosslessPreset(ref WebPConfig config, int level)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPConfigLosslessPreset_x86(ref config, level);
+ case 8:
+ return WebPConfigLosslessPreset_x64(ref config, level);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigLosslessPreset")]
+ private static extern int WebPConfigLosslessPreset_x86(ref WebPConfig config, int level);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigLosslessPreset")]
+ private static extern int WebPConfigLosslessPreset_x64(ref WebPConfig config, int level);
+
+ /// Check that configuration is non-NULL and all configuration parameters are within their valid ranges
+ /// The WebPConfig structure
+ /// 1 if configuration is OK
+ internal static int WebPValidateConfig(ref WebPConfig config)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPValidateConfig_x86(ref config);
+ case 8:
+ return WebPValidateConfig_x64(ref config);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPValidateConfig")]
+ private static extern int WebPValidateConfig_x86(ref WebPConfig config);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPValidateConfig")]
+ private static extern int WebPValidateConfig_x64(ref WebPConfig config);
+
+ /// Initialize the WebPPicture structure checking the DLL version
+ /// The WebPPicture structure
+ /// 1 if not error
+ internal static int WebPPictureInitInternal(ref WebPPicture wpic)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPPictureInitInternal_x86(ref wpic, WEBP_DECODER_ABI_VERSION);
+ case 8:
+ return WebPPictureInitInternal_x64(ref wpic, WEBP_DECODER_ABI_VERSION);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureInitInternal")]
+ private static extern int WebPPictureInitInternal_x86(ref WebPPicture wpic, int WEBP_DECODER_ABI_VERSION);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureInitInternal")]
+ private static extern int WebPPictureInitInternal_x64(ref WebPPicture wpic, int WEBP_DECODER_ABI_VERSION);
+
+ /// Colorspace conversion function to import RGB samples
+ /// The WebPPicture structure
+ /// Point to BGR data
+ /// stride of BGR data
+ /// Returns 0 in case of memory error.
+ internal static int WebPPictureImportBGR(ref WebPPicture wpic, IntPtr bgr, int stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPPictureImportBGR_x86(ref wpic, bgr, stride);
+ case 8:
+ return WebPPictureImportBGR_x64(ref wpic, bgr, stride);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGR")]
+ private static extern int WebPPictureImportBGR_x86(ref WebPPicture wpic, IntPtr bgr, int stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGR")]
+ private static extern int WebPPictureImportBGR_x64(ref WebPPicture wpic, IntPtr bgr, int stride);
+
+ /// Color-space conversion function to import RGB samples
+ /// The WebPPicture structure
+ /// Point to BGRA data
+ /// stride of BGRA data
+ /// Returns 0 in case of memory error.
+ internal static int WebPPictureImportBGRA(ref WebPPicture wpic, IntPtr bgra, int stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPPictureImportBGRA_x86(ref wpic, bgra, stride);
+ case 8:
+ return WebPPictureImportBGRA_x64(ref wpic, bgra, stride);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRA")]
+ private static extern int WebPPictureImportBGRA_x86(ref WebPPicture wpic, IntPtr bgra, int stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRA")]
+ private static extern int WebPPictureImportBGRA_x64(ref WebPPicture wpic, IntPtr bgra, int stride);
+
+ /// Color-space conversion function to import RGB samples
+ /// The WebPPicture structure
+ /// Point to BGR data
+ /// stride of BGR data
+ /// Returns 0 in case of memory error.
+ internal static int WebPPictureImportBGRX(ref WebPPicture wpic, IntPtr bgr, int stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPPictureImportBGRX_x86(ref wpic, bgr, stride);
+ case 8:
+ return WebPPictureImportBGRX_x64(ref wpic, bgr, stride);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRX")]
+ private static extern int WebPPictureImportBGRX_x86(ref WebPPicture wpic, IntPtr bgr, int stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRX")]
+ private static extern int WebPPictureImportBGRX_x64(ref WebPPicture wpic, IntPtr bgr, int stride);
+
+ /// The writer type for output compress data
+ /// Data returned
+ /// Size of data returned
+ /// Picture structure
+ ///
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int WebPMemoryWrite([In()] IntPtr data, UIntPtr data_size, ref WebPPicture wpic);
+ internal static WebPMemoryWrite OnCallback;
+
+ /// Compress to WebP format
+ /// The configuration structure for compression parameters
+ /// 'picture' hold the source samples in both YUV(A) or ARGB input
+ /// Returns 0 in case of error, 1 otherwise. In case of error, picture->error_code is updated accordingly.
+ internal static int WebPEncode(ref WebPConfig config, ref WebPPicture picture)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPEncode_x86(ref config, ref picture);
+ case 8:
+ return WebPEncode_x64(ref config, ref picture);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncode")]
+ private static extern int WebPEncode_x86(ref WebPConfig config, ref WebPPicture picture);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncode")]
+ private static extern int WebPEncode_x64(ref WebPConfig config, ref WebPPicture picture);
+
+ /// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*()
+ /// Note that this function does _not_ free the memory used by the 'picture' object itself.
+ /// Besides memory (which is reclaimed) all other fields of 'picture' are preserved
+ /// Picture structure
+ internal static void WebPPictureFree(ref WebPPicture picture)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ WebPPictureFree_x86(ref picture);
+ break;
+ case 8:
+ WebPPictureFree_x64(ref picture);
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureFree")]
+ private static extern void WebPPictureFree_x86(ref WebPPicture wpic);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureFree")]
+ private static extern void WebPPictureFree_x64(ref WebPPicture wpic);
+
+ /// Validate the WebP image header and retrieve the image height and width. Pointers *width and *height can be passed NULL if deemed irrelevant
+ /// Pointer to WebP image data
+ /// This is the size of the memory block pointed to by data containing the image data
+ /// The range is limited currently from 1 to 16383
+ /// The range is limited currently from 1 to 16383
+ /// 1 if success, otherwise error code returned in the case of (a) formatting error(s).
+ internal static int WebPGetInfo(IntPtr data, int data_size, out int width, out int height)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPGetInfo_x86(data, (UIntPtr)data_size, out width, out height);
+ case 8:
+ return WebPGetInfo_x64(data, (UIntPtr)data_size, out width, out height);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetInfo")]
+ private static extern int WebPGetInfo_x86([InAttribute()] IntPtr data, UIntPtr data_size, out int width, out int height);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetInfo")]
+ private static extern int WebPGetInfo_x64([InAttribute()] IntPtr data, UIntPtr data_size, out int width, out int height);
+
+ /// Decode WEBP image pointed to by *data and returns BGR samples into a preallocated buffer
+ /// Pointer to WebP image data
+ /// This is the size of the memory block pointed to by data containing the image data
+ /// Pointer to decoded WebP image
+ /// Size of allocated buffer
+ /// Specifies the distance between scan lines
+ internal static void WebPDecodeBGRInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ if (WebPDecodeBGRInto_x86(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ case 8:
+ if (WebPDecodeBGRInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRInto")]
+ private static extern IntPtr WebPDecodeBGRInto_x86([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRInto")]
+ private static extern IntPtr WebPDecodeBGRInto_x64([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+
+ /// Decode WEBP image pointed to by *data and returns BGRA samples into a preallocated buffer
+ /// Pointer to WebP image data
+ /// This is the size of the memory block pointed to by data containing the image data
+ /// Pointer to decoded WebP image
+ /// Size of allocated buffer
+ /// Specifies the distance between scan lines
+ internal static void WebPDecodeBGRAInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ if (WebPDecodeBGRAInto_x86(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ case 8:
+ if (WebPDecodeBGRAInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRAInto")]
+ private static extern IntPtr WebPDecodeBGRAInto_x86([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRAInto")]
+ private static extern IntPtr WebPDecodeBGRAInto_x64([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+
+ /// Decode WEBP image pointed to by *data and returns ARGB samples into a preallocated buffer
+ /// Pointer to WebP image data
+ /// This is the size of the memory block pointed to by data containing the image data
+ /// Pointer to decoded WebP image
+ /// Size of allocated buffer
+ /// Specifies the distance between scan lines
+ internal static void WebPDecodeARGBInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ if (WebPDecodeARGBInto_x86(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ case 8:
+ if (WebPDecodeARGBInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
+ throw new InvalidOperationException("Can not decode WebP");
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeARGBInto")]
+ private static extern IntPtr WebPDecodeARGBInto_x86([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeARGBInto")]
+ private static extern IntPtr WebPDecodeARGBInto_x64([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);
+
+ /// Initialize the configuration as empty. This function must always be called first, unless WebPGetFeatures() is to be called
+ /// Configuration structure
+ /// False in case of mismatched version.
+ internal static int WebPInitDecoderConfig(ref WebPDecoderConfig webPDecoderConfig)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPInitDecoderConfigInternal_x86(ref webPDecoderConfig, WEBP_DECODER_ABI_VERSION);
+ case 8:
+ return WebPInitDecoderConfigInternal_x64(ref webPDecoderConfig, WEBP_DECODER_ABI_VERSION);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPInitDecoderConfigInternal")]
+ private static extern int WebPInitDecoderConfigInternal_x86(ref WebPDecoderConfig webPDecoderConfig, int WEBP_DECODER_ABI_VERSION);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPInitDecoderConfigInternal")]
+ private static extern int WebPInitDecoderConfigInternal_x64(ref WebPDecoderConfig webPDecoderConfig, int WEBP_DECODER_ABI_VERSION);
+
+ /// Decodes the full data at once, taking configuration into account
+ /// WebP raw data to decode
+ /// Size of WebP data
+ /// Configuration structure
+ /// VP8_STATUS_OK if the decoding was successful
+ internal static VP8StatusCode WebPDecode(IntPtr data, int data_size, ref WebPDecoderConfig webPDecoderConfig)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPDecode_x86(data, (UIntPtr)data_size, ref webPDecoderConfig);
+ case 8:
+ return WebPDecode_x64(data, (UIntPtr)data_size, ref webPDecoderConfig);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecode")]
+ private static extern VP8StatusCode WebPDecode_x86(IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecode")]
+ private static extern VP8StatusCode WebPDecode_x64(IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);
+
+ /// Free any memory associated with the buffer. Must always be called last. Doesn't free the 'buffer' structure itself
+ /// WebPDecBuffer
+ internal static void WebPFreeDecBuffer(ref WebPDecBuffer buffer)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ WebPFreeDecBuffer_x86(ref buffer);
+ break;
+ case 8:
+ WebPFreeDecBuffer_x64(ref buffer);
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFreeDecBuffer")]
+ private static extern void WebPFreeDecBuffer_x86(ref WebPDecBuffer buffer);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFreeDecBuffer")]
+ private static extern void WebPFreeDecBuffer_x64(ref WebPDecBuffer buffer);
+
+ /// Lossy encoding images
+ /// Pointer to BGR image data
+ /// The range is limited currently from 1 to 16383
+ /// The range is limited currently from 1 to 16383
+ /// Specifies the distance between scanlines
+ /// Ranges from 0 (lower quality) to 100 (highest quality). Controls the loss and quality during compression
+ /// output_buffer with WebP image
+ /// Size of WebP Image or 0 if an error occurred
+ internal static int WebPEncodeBGR(IntPtr bgr, int width, int height, int stride, float quality_factor, out IntPtr output)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPEncodeBGR_x86(bgr, width, height, stride, quality_factor, out output);
+ case 8:
+ return WebPEncodeBGR_x64(bgr, width, height, stride, quality_factor, out output);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeBGR")]
+ private static extern int WebPEncodeBGR_x86([InAttribute()] IntPtr bgr, int width, int height, int stride, float quality_factor, out IntPtr output);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeBGR")]
+ private static extern int WebPEncodeBGR_x64([InAttribute()] IntPtr bgr, int width, int height, int stride, float quality_factor, out IntPtr output);
+
+ /// Lossy encoding images
+ /// Pointer to BGRA image data
+ /// The range is limited currently from 1 to 16383
+ /// The range is limited currently from 1 to 16383
+ /// Specifies the distance between scan lines
+ /// Ranges from 0 (lower quality) to 100 (highest quality). Controls the loss and quality during compression
+ /// output_buffer with WebP image
+ /// Size of WebP Image or 0 if an error occurred
+ internal static int WebPEncodeBGRA(IntPtr bgra, int width, int height, int stride, float quality_factor, out IntPtr output)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPEncodeBGRA_x86(bgra, width, height, stride, quality_factor, out output);
+ case 8:
+ return WebPEncodeBGRA_x64(bgra, width, height, stride, quality_factor, out output);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeBGRA")]
+ private static extern int WebPEncodeBGRA_x86([InAttribute()] IntPtr bgra, int width, int height, int stride, float quality_factor, out IntPtr output);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeBGRA")]
+ private static extern int WebPEncodeBGRA_x64([InAttribute()] IntPtr bgra, int width, int height, int stride, float quality_factor, out IntPtr output);
+
+ /// Lossless encoding images pointed to by *data in WebP format
+ /// Pointer to BGR image data
+ /// The range is limited currently from 1 to 16383
+ /// The range is limited currently from 1 to 16383
+ /// Specifies the distance between scan lines
+ /// output_buffer with WebP image
+ /// Size of WebP Image or 0 if an error occurred
+ internal static int WebPEncodeLosslessBGR(IntPtr bgr, int width, int height, int stride, out IntPtr output)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPEncodeLosslessBGR_x86(bgr, width, height, stride, out output);
+ case 8:
+ return WebPEncodeLosslessBGR_x64(bgr, width, height, stride, out output);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeLosslessBGR")]
+ private static extern int WebPEncodeLosslessBGR_x86([InAttribute()] IntPtr bgr, int width, int height, int stride, out IntPtr output);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeLosslessBGR")]
+ private static extern int WebPEncodeLosslessBGR_x64([InAttribute()] IntPtr bgr, int width, int height, int stride, out IntPtr output);
+
+ /// Lossless encoding images pointed to by *data in WebP format
+ /// Pointer to BGRA image data
+ /// The range is limited currently from 1 to 16383
+ /// The range is limited currently from 1 to 16383
+ /// Specifies the distance between scan lines
+ /// output_buffer with WebP image
+ /// Size of WebP Image or 0 if an error occurred
+ internal static int WebPEncodeLosslessBGRA(IntPtr bgra, int width, int height, int stride, out IntPtr output)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPEncodeLosslessBGRA_x86(bgra, width, height, stride, out output);
+ case 8:
+ return WebPEncodeLosslessBGRA_x64(bgra, width, height, stride, out output);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeLosslessBGRA")]
+ private static extern int WebPEncodeLosslessBGRA_x86([InAttribute()] IntPtr bgra, int width, int height, int stride, out IntPtr output);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncodeLosslessBGRA")]
+ private static extern int WebPEncodeLosslessBGRA_x64([InAttribute()] IntPtr bgra, int width, int height, int stride, out IntPtr output);
+
+ /// Releases memory returned by the WebPEncode
+ /// Pointer to memory
+ internal static void WebPFree(IntPtr p)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ WebPFree_x86(p);
+ break;
+ case 8:
+ WebPFree_x64(p);
+ break;
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFree")]
+ private static extern void WebPFree_x86(IntPtr p);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFree")]
+ private static extern void WebPFree_x64(IntPtr p);
+
+ /// Get the WebP version library
+ /// 8bits for each of major/minor/revision packet in integer. E.g: v2.5.7 is 0x020507
+ internal static int WebPGetDecoderVersion()
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPGetDecoderVersion_x86();
+ case 8:
+ return WebPGetDecoderVersion_x64();
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetDecoderVersion")]
+ private static extern int WebPGetDecoderVersion_x86();
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetDecoderVersion")]
+ private static extern int WebPGetDecoderVersion_x64();
+
+ /// Compute PSNR, SSIM or LSIM distortion metric between two pictures
+ /// Picture to measure
+ /// Reference picture
+ /// 0 = PSNR, 1 = SSIM, 2 = LSIM
+ /// dB in the Y/U/V/Alpha/All order
+ /// False in case of error (the two pictures don't have same dimension, ...)
+ internal static int WebPPictureDistortion(ref WebPPicture srcPicture, ref WebPPicture refPicture, int metric_type, IntPtr pResult)
+ {
+ switch (IntPtr.Size)
+ {
+ case 4:
+ return WebPPictureDistortion_x86(ref srcPicture, ref refPicture, metric_type, pResult);
+ case 8:
+ return WebPPictureDistortion_x64(ref srcPicture, ref refPicture, metric_type, pResult);
+ default:
+ throw new InvalidOperationException("Invalid platform. Can not find proper function");
+ }
+ }
+ [DllImport("libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureDistortion")]
+ private static extern int WebPPictureDistortion_x86(ref WebPPicture srcPicture, ref WebPPicture refPicture, int metric_type, IntPtr pResult);
+ [DllImport("libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureDistortion")]
+ private static extern int WebPPictureDistortion_x64(ref WebPPicture srcPicture, ref WebPPicture refPicture, int metric_type, IntPtr pResult);
+ }
+ #endregion
+
+ #region | Predefined |
+ /// Enumerate some predefined settings for WebPConfig, depending on the type of source picture. These presets are used when calling WebPConfigPreset()
+ internal enum WebPPreset
+ {
+ /// Default preset
+ 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
+ };
+
+ /// Encoding error conditions
+ internal enum WebPEncodingError
+ {
+ /// No error
+ VP8_ENC_OK = 0,
+ /// Memory error allocating objects
+ VP8_ENC_ERROR_OUT_OF_MEMORY,
+ /// Memory error while flushing bits
+ VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY,
+ /// A pointer parameter is NULL
+ VP8_ENC_ERROR_NULL_PARAMETER,
+ /// Configuration is invalid
+ VP8_ENC_ERROR_INVALID_CONFIGURATION,
+ /// Picture has invalid width/height
+ VP8_ENC_ERROR_BAD_DIMENSION,
+ /// Partition is bigger than 512k
+ VP8_ENC_ERROR_PARTITION0_OVERFLOW,
+ /// Partition is bigger than 16M
+ VP8_ENC_ERROR_PARTITION_OVERFLOW,
+ /// Error while flushing bytes
+ VP8_ENC_ERROR_BAD_WRITE,
+ /// File is bigger than 4G
+ VP8_ENC_ERROR_FILE_TOO_BIG,
+ /// Abort request by user
+ VP8_ENC_ERROR_USER_ABORT,
+ /// List terminator. Always last
+ VP8_ENC_ERROR_LAST,
+ }
+
+ /// Enumeration of the status codes
+ internal enum VP8StatusCode
+ {
+ /// No error
+ VP8_STATUS_OK = 0,
+ /// Memory error allocating objects
+ VP8_STATUS_OUT_OF_MEMORY,
+ /// Configuration is invalid
+ VP8_STATUS_INVALID_PARAM,
+ VP8_STATUS_BITSTREAM_ERROR,
+ /// Configuration is invalid
+ VP8_STATUS_UNSUPPORTED_FEATURE,
+ VP8_STATUS_SUSPENDED,
+ /// Abort request by user
+ VP8_STATUS_USER_ABORT,
+ VP8_STATUS_NOT_ENOUGH_DATA,
+ }
+
+ /// Image characteristics hint for the underlying encoder
+ internal enum WebPImageHint
+ {
+ /// Default preset
+ WEBP_HINT_DEFAULT = 0,
+ /// Digital picture, like portrait, inner shot
+ WEBP_HINT_PICTURE,
+ /// Outdoor photograph, with natural lighting
+ WEBP_HINT_PHOTO,
+ /// Discrete tone image (graph, map-tile etc)
+ WEBP_HINT_GRAPH,
+ /// List terminator. Always last
+ WEBP_HINT_LAST
+ };
+
+ /// Describes the byte-ordering of packed samples in memory
+ internal enum WEBP_CSP_MODE
+ {
+ /// Byte-order: R,G,B,R,G,B,..
+ MODE_RGB = 0,
+ /// Byte-order: R,G,B,A,R,G,B,A,..
+ MODE_RGBA = 1,
+ /// Byte-order: B,G,R,B,G,R,..
+ MODE_BGR = 2,
+ /// Byte-order: B,G,R,A,B,G,R,A,..
+ MODE_BGRA = 3,
+ /// Byte-order: A,R,G,B,A,R,G,B,..
+ MODE_ARGB = 4,
+ /// Byte-order: RGB-565: [a4 a3 a2 a1 a0 r5 r4 r3], [r2 r1 r0 g4 g3 g2 g1 g0], ...
+ /// WEBP_SWAP_16BITS_CSP is defined,
+ /// Byte-order: RGB-565: [a4 a3 a2 a1 a0 b5 b4 b3], [b2 b1 b0 g4 g3 g2 g1 g0], ..
+ MODE_RGBA_4444 = 5,
+ /// Byte-order: RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
+ /// WEBP_SWAP_16BITS_CSP is defined,
+ /// Byte-order: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ..
+ MODE_RGB_565 = 6,
+ /// RGB-premultiplied transparent modes (alpha value is preserved)
+ MODE_rgbA = 7,
+ /// RGB-premultiplied transparent modes (alpha value is preserved)
+ MODE_bgrA = 8,
+ /// RGB-premultiplied transparent modes (alpha value is preserved)
+ MODE_Argb = 9,
+ /// RGB-premultiplied transparent modes (alpha value is preserved)
+ MODE_rgbA_4444 = 10,
+ /// YUV 4:2:0
+ MODE_YUV = 11,
+ /// YUV 4:2:0
+ MODE_YUVA = 12,
+ /// MODE_LAST -> 13
+ MODE_LAST = 13,
+ }
+
+ ///
+ /// Decoding states. State normally flows as:
+ /// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
+ /// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
+ /// If there is any error the decoder goes into state ERROR.
+ ///
+ internal enum DecState
+ {
+ STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
+ STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
+ STATE_VP8_PARTS0,
+ STATE_VP8_DATA,
+ STATE_VP8L_HEADER,
+ STATE_VP8L_DATA,
+ STATE_DONE,
+ STATE_ERROR
+ };
+ #endregion
+
+ #region | libwebp structs |
+ /// Features gathered from the bit stream
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPBitstreamFeatures
+ {
+ /// Width in pixels, as read from the bit stream
+ public int Width;
+ /// Height in pixels, as read from the bit stream
+ public int Height;
+ /// True if the bit stream contains an alpha channel
+ public int Has_alpha;
+ /// True if the bit stream is an animation
+ public int Has_animation;
+ /// 0 = undefined (/mixed), 1 = lossy, 2 = lossless
+ public int Format;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad;
+ };
+
+ /// Compression parameters
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal 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;
+ /// Hint for image type (lossless only for now)
+ public WebPImageHint image_hint;
+ /// 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 auto-filter > 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;
+ /// Reserved for future lossless feature
+ public int delta_palettization;
+ /// If needed, use sharp (and slow) RGB->YUV conversion
+ public int use_sharp_yuv;
+ /// Padding for later use
+ private readonly int pad1;
+ private readonly int pad2;
+ };
+
+ /// Main exchange structure (input samples, output bytes, statistics)
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPPicture
+ {
+ /// Main flag for encoder selecting between ARGB or YUV input. Recommended to use ARGB input (*argb, argb_stride) for lossless, and YUV input (*y, *u, *v, etc.) for lossy
+ public int use_argb;
+ /// Color-space: should be YUV420 for now (=Y'CbCr). Value = 0
+ public UInt32 colorspace;
+ /// Width of picture (less or equal to WEBP_MAX_DIMENSION)
+ public int width;
+ /// Height of picture (less or equal to WEBP_MAX_DIMENSION)
+ public int height;
+ /// Pointer to luma plane
+ public IntPtr y;
+ /// Pointer to chroma U plane
+ public IntPtr u;
+ /// Pointer to chroma V plane
+ public IntPtr v;
+ /// Luma stride
+ public int y_stride;
+ /// Chroma stride
+ public int uv_stride;
+ /// Pointer to the alpha plane
+ public IntPtr a;
+ /// stride of the alpha plane
+ public int a_stride;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad1;
+ /// Pointer to ARGB (32 bit) plane
+ public IntPtr argb;
+ /// This is stride in pixels units, not bytes
+ public int argb_stride;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad2;
+ /// Byte-emission hook, to store compressed bytes as they are ready
+ public IntPtr writer;
+ /// Can be used by the writer
+ public IntPtr custom_ptr;
+ // map for extra information (only for lossy compression mode)
+ /// 1: intra type, 2: segment, 3: quant, 4: intra-16 prediction mode, 5: chroma prediction mode, 6: bit cost, 7: distortion
+ public int extra_info_type;
+ /// If not NULL, points to an array of size ((width + 15) / 16) * ((height + 15) / 16) that will be filled with a macroblock map, depending on extra_info_type
+ public IntPtr extra_info;
+ /// Pointer to side statistics (updated only if not NULL)
+ public IntPtr stats;
+ /// Error code for the latest error encountered during encoding
+ public UInt32 error_code;
+ /// If not NULL, report progress during encoding
+ public IntPtr progress_hook;
+ /// This field is free to be set to any value and used during callbacks (like progress-report e.g.)
+ public IntPtr user_data;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 13, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad3;
+ /// Row chunk of memory for YUVA planes
+ private readonly IntPtr memory_;
+ /// Row chunk of memory for ARGB planes
+ private readonly IntPtr memory_argb_;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad4;
+ };
+
+ /// Structure for storing auxiliary statistics (mostly for lossy encoding)
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPAuxStats
+ {
+ /// Final size
+ public int coded_size;
+ /// Peak-signal-to-noise ratio for Y
+ public float PSNRY;
+ /// Peak-signal-to-noise ratio for U
+ public float PSNRU;
+ /// Peak-signal-to-noise ratio for V
+ public float PSNRV;
+ /// Peak-signal-to-noise ratio for All
+ public float PSNRALL;
+ /// Peak-signal-to-noise ratio for Alpha
+ public float PSNRAlpha;
+ /// Number of intra4
+ public int block_count_intra4;
+ /// Number of intra16
+ public int block_count_intra16;
+ /// Number of skipped macro-blocks
+ public int block_count_skipped;
+ /// Approximate number of bytes spent for header
+ public int header_bytes;
+ /// Approximate number of bytes spent for mode-partition #0
+ public int mode_partition_0;
+ /// Approximate number of bytes spent for DC coefficients for segment 0
+ public int residual_bytes_DC_segments0;
+ /// Approximate number of bytes spent for AC coefficients for segment 0
+ public int residual_bytes_AC_segments0;
+ /// Approximate number of bytes spent for UV coefficients for segment 0
+ public int residual_bytes_uv_segments0;
+ /// Approximate number of bytes spent for DC coefficients for segment 1
+ public int residual_bytes_DC_segments1;
+ /// Approximate number of bytes spent for AC coefficients for segment 1
+ public int residual_bytes_AC_segments1;
+ /// Approximate number of bytes spent for UV coefficients for segment 1
+ public int residual_bytes_uv_segments1;
+ /// Approximate number of bytes spent for DC coefficients for segment 2
+ public int residual_bytes_DC_segments2;
+ /// Approximate number of bytes spent for AC coefficients for segment 2
+ public int residual_bytes_AC_segments2;
+ /// Approximate number of bytes spent for UV coefficients for segment 2
+ public int residual_bytes_uv_segments2;
+ /// Approximate number of bytes spent for DC coefficients for segment 3
+ public int residual_bytes_DC_segments3;
+ /// Approximate number of bytes spent for AC coefficients for segment 3
+ public int residual_bytes_AC_segments3;
+ /// Approximate number of bytes spent for UV coefficients for segment 3
+ public int residual_bytes_uv_segments3;
+ /// Number of macro-blocks in segments 0
+ public int segment_size_segments0;
+ /// Number of macro-blocks in segments 1
+ public int segment_size_segments1;
+ /// Number of macro-blocks in segments 2
+ public int segment_size_segments2;
+ /// Number of macro-blocks in segments 3
+ public int segment_size_segments3;
+ /// Quantizer values for segment 0
+ public int segment_quant_segments0;
+ /// Quantizer values for segment 1
+ public int segment_quant_segments1;
+ /// Quantizer values for segment 2
+ public int segment_quant_segments2;
+ /// Quantizer values for segment 3
+ public int segment_quant_segments3;
+ /// Filtering strength for segment 0 [0..63]
+ public int segment_level_segments0;
+ /// Filtering strength for segment 1 [0..63]
+ public int segment_level_segments1;
+ /// Filtering strength for segment 2 [0..63]
+ public int segment_level_segments2;
+ /// Filtering strength for segment 3 [0..63]
+ public int segment_level_segments3;
+ /// Size of the transparency data
+ public int alpha_data_size;
+ /// Size of the enhancement layer data
+ public int layer_data_size;
+
+ // lossless encoder statistics
+ /// bit0:predictor bit1:cross-color transform bit2:subtract-green bit3:color indexing
+ public Int32 lossless_features;
+ /// Number of precision bits of histogram
+ public int histogram_bits;
+ /// Precision bits for transform
+ public int transform_bits;
+ /// Number of bits for color cache lookup
+ public int cache_bits;
+ /// Number of color in palette, if used
+ public int palette_size;
+ /// Final lossless size
+ public int lossless_size;
+ /// Lossless header (transform, Huffman, etc) size
+ public int lossless_hdr_size;
+ /// Lossless image data size
+ public int lossless_data_size;
+ /// Padding for later use
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
+ private readonly uint[] pad;
+ };
+
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPDecoderConfig
+ {
+ /// Immutable bit stream features (optional)
+ public WebPBitstreamFeatures input;
+ /// Output buffer (can point to external memory)
+ public WebPDecBuffer output;
+ /// Decoding options
+ public WebPDecoderOptions options;
+ }
+
+ /// Output buffer
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPDecBuffer
+ {
+ /// Color space
+ public WEBP_CSP_MODE colorspace;
+ /// Width of image
+ public int width;
+ /// Height of image
+ public int height;
+ /// If non-zero, 'internal_memory' pointer is not used. If value is '2' or more, the external memory is considered 'slow' and multiple read/write will be avoided
+ public int is_external_memory;
+ /// Output buffer parameters
+ public RGBA_YUVA_Buffer u;
+ /// Padding for later use
+ private readonly UInt32 pad1;
+ /// Padding for later use
+ private readonly UInt32 pad2;
+ /// Padding for later use
+ private readonly UInt32 pad3;
+ /// Padding for later use
+ private readonly UInt32 pad4;
+ /// Internally allocated memory (only when is_external_memory is 0). Should not be used externally, but accessed via WebPRGBABuffer
+ public IntPtr private_memory;
+ }
+
+ /// Union of buffer parameters
+ [StructLayoutAttribute(LayoutKind.Explicit)]
+ internal struct RGBA_YUVA_Buffer
+ {
+ [FieldOffsetAttribute(0)]
+ public WebPRGBABuffer RGBA;
+
+ [FieldOffsetAttribute(0)]
+ public WebPYUVABuffer YUVA;
+ }
+
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPYUVABuffer
+ {
+ /// Pointer to luma samples
+ public IntPtr y;
+ /// Pointer to chroma U samples
+ public IntPtr u;
+ /// Pointer to chroma V samples
+ public IntPtr v;
+ /// Pointer to alpha samples
+ public IntPtr a;
+ /// Luma stride
+ public int y_stride;
+ /// Chroma U stride
+ public int u_stride;
+ /// Chroma V stride
+ public int v_stride;
+ /// Alpha stride
+ public int a_stride;
+ /// Luma plane size
+ public UIntPtr y_size;
+ /// Chroma plane U size
+ public UIntPtr u_size;
+ /// Chroma plane V size
+ public UIntPtr v_size;
+ /// Alpha plane size
+ public UIntPtr a_size;
+ }
+
+ /// Generic structure for describing the output sample buffer
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct WebPRGBABuffer
+ {
+ /// Pointer to RGBA samples
+ public IntPtr rgba;
+ /// Stride in bytes from one scanline to the next
+ public int stride;
+ /// Total size of the RGBA buffer
+ public UIntPtr size;
+ }
+
+ /// Decoding options
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WebPDecoderOptions
+ {
+ /// If true, skip the in-loop filtering
+ public int bypass_filtering;
+ /// If true, use faster point-wise up-sampler
+ public int no_fancy_upsampling;
+ /// If true, cropping is applied _first_
+ public int use_cropping;
+ /// Left position for cropping. Will be snapped to even values
+ public int crop_left;
+ /// Top position for cropping. Will be snapped to even values
+ public int crop_top;
+ /// Width of the cropping area
+ public int crop_width;
+ /// Height of the cropping area
+ public int crop_height;
+ /// If true, scaling is applied _afterward_
+ public int use_scaling;
+ /// Final width
+ public int scaled_width;
+ /// Final height
+ public int scaled_height;
+ /// If true, use multi-threaded decoding
+ public int use_threads;
+ /// Dithering strength (0=Off, 100=full)
+ public int dithering_strength;
+ /// Flip output vertically
+ public int flip;
+ /// Alpha dithering strength in [0..100]
+ public int alpha_dithering_strength;
+ /// Padding for later use
+ private readonly UInt32 pad1;
+ /// Padding for later use
+ private readonly UInt32 pad2;
+ /// Padding for later use
+ private readonly UInt32 pad3;
+ /// Padding for later use
+ private readonly UInt32 pad4;
+ /// Padding for later use
+ private readonly UInt32 pad5;
+ };
+ #endregion
+}
\ No newline at end of file
diff --git a/Wabbajack.App.Wpf/ViewModels/Gallery/ModListGalleryVM.cs b/Wabbajack.App.Wpf/ViewModels/Gallery/ModListGalleryVM.cs
index bdba2dff..a9336974 100644
--- a/Wabbajack.App.Wpf/ViewModels/Gallery/ModListGalleryVM.cs
+++ b/Wabbajack.App.Wpf/ViewModels/Gallery/ModListGalleryVM.cs
@@ -133,7 +133,8 @@ namespace Wabbajack
{
if (string.IsNullOrWhiteSpace(txt)) return _ => true;
return item => item.Metadata.Title.ContainsCaseInsensitive(txt) ||
- item.Metadata.Description.ContainsCaseInsensitive(txt);
+ item.Metadata.Description.ContainsCaseInsensitive(txt) ||
+ item.Metadata.Tags.Contains(txt);
});
var onlyInstalledGamesFilter = this.ObservableForProperty(vm => vm.OnlyInstalled)
@@ -169,12 +170,12 @@ namespace Wabbajack
})
.StartWith(_ => true);
- var searchSorter = this.WhenValueChanged(x => x.Search)
- .Where(x => !string.IsNullOrWhiteSpace(x))
+ var searchSorter = this.WhenValueChanged(vm => vm.Search)
+ .Where(s => !string.IsNullOrWhiteSpace(s))
.Throttle(searchThrottle, RxApp.MainThreadScheduler)
.Select(s => SortExpressionComparer
- .Descending(modlist => modlist.Metadata.Title.StartsWith(s, StringComparison.InvariantCultureIgnoreCase))
- .ThenByDescending(modlist => modlist.Metadata.Title.Contains(s, StringComparison.InvariantCultureIgnoreCase)));
+ .Descending(m => m.Metadata.Title.StartsWith(s, StringComparison.InvariantCultureIgnoreCase))
+ .ThenByDescending(m => m.Metadata.Title.Contains(s, StringComparison.InvariantCultureIgnoreCase)));
_modLists.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Filter(searchTextPredicates)
@@ -183,6 +184,7 @@ namespace Wabbajack
.Filter(showNSFWFilter)
.Filter(gameFilter)
.Sort(searchSorter)
+ .Sort(SortExpressionComparer.Descending(modlist => !modlist.IsBroken))
.TreatMovesAsRemoveAdd()
.Bind(out _filteredModLists)
.Subscribe((_) =>
diff --git a/Wabbajack.App.Wpf/ViewModels/Gallery/ModListMetadataVM.cs b/Wabbajack.App.Wpf/ViewModels/Gallery/ModListMetadataVM.cs
index 01e96839..de0b91fe 100644
--- a/Wabbajack.App.Wpf/ViewModels/Gallery/ModListMetadataVM.cs
+++ b/Wabbajack.App.Wpf/ViewModels/Gallery/ModListMetadataVM.cs
@@ -176,7 +176,7 @@ namespace Wabbajack
.ToGuiProperty(this, nameof(Exists));
var imageObs = Observable.Return(Metadata.Links.ImageUri)
- .DownloadBitmapImage((ex) => _logger.LogError("Error downloading modlist image {Title}", Metadata.Title), LoadingImageLock);
+ .DownloadBitmapImage((ex) => _logger.LogError("Error downloading modlist image {Title} from {ImageUri}: {Exception}", Metadata.Title, Metadata.Links.ImageUri, ex.Message), LoadingImageLock);
_Image = imageObs
.ToGuiProperty(this, nameof(Image));
diff --git a/Wabbajack.App.Wpf/Views/ModListGalleryView.xaml b/Wabbajack.App.Wpf/Views/ModListGalleryView.xaml
index 9bc699f7..8a90c7a6 100644
--- a/Wabbajack.App.Wpf/Views/ModListGalleryView.xaml
+++ b/Wabbajack.App.Wpf/Views/ModListGalleryView.xaml
@@ -17,7 +17,7 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -172,6 +155,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack.App.Wpf/Views/ModListTileView.xaml b/Wabbajack.App.Wpf/Views/ModListTileView.xaml
index 7d9dc59a..292c1458 100644
--- a/Wabbajack.App.Wpf/Views/ModListTileView.xaml
+++ b/Wabbajack.App.Wpf/Views/ModListTileView.xaml
@@ -48,14 +48,15 @@
+ CornerRadius="8"
+ BorderThickness="0">
+ Opacity="0.25"
+ ShadowDepth="3" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs b/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs
index 1b7adb87..215bcd1b 100644
--- a/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs
+++ b/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs
@@ -2,6 +2,8 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using MahApps.Metro.IconPacks;
using ReactiveUI;
@@ -19,14 +21,39 @@ namespace Wabbajack
this.WhenActivated(disposables =>
{
ViewModel.WhenAnyValue(vm => vm.Image)
- .BindToStrict(this, view => view.ModListImage.Source)
+ .BindToStrict(this, v => v.ModlistImage.ImageSource)
+ .DisposeWith(disposables);
+ /*
+ this.WhenAny(x => x.ViewModel.Metadata.Links.ImageUri)
+ .Select(x => new BitmapImage() { UriSource = new Uri(x) })
+ .BindToStrict(this, v => v.ModlistImage.ImageSource)
.DisposeWith(disposables);
+ */
+ /*
+ ViewModel.WhenAnyValue(x => x.Metadata.Links.ImageUri)
+ .Select(x => {
+ var img = new BitmapImage();
+ img.BeginInit();
+ img.CacheOption = BitmapCacheOption.OnDemand;
+ img.DecodePixelWidth = 327;
+ var uri = new Uri(x, UriKind.Absolute);
+ img.UriSource = uri;
+ img.EndInit();
+
+ return img;
+ })
+ .BindToStrict(this, v => v.ModlistImage.ImageSource)
+ .DisposeWith(disposables);
+ */
+
var textXformed = ViewModel.WhenAnyValue(vm => vm.Metadata.Title)
.CombineLatest(ViewModel.WhenAnyValue(vm => vm.Metadata.ImageContainsTitle),
ViewModel.WhenAnyValue(vm => vm.IsBroken))
.Select(x => x.Second && !x.Third ? "" : x.First);
+
+ /*
textXformed
.BindToStrict(this, view => view.ModListTitle.Text)
.DisposeWith(disposables);
@@ -42,6 +69,7 @@ namespace Wabbajack
ViewModel.WhenAnyValue(x => x.ModListTagList)
.BindToStrict(this, x => x.TagsList.ItemsSource)
.DisposeWith(disposables);
+ */
ViewModel.WhenAnyValue(x => x.LoadingImageLock.IsLoading)
.Select(x => x ? Visibility.Visible : Visibility.Collapsed)
@@ -53,6 +81,7 @@ namespace Wabbajack
.BindToStrict(this, view => view.Overlay.Visibility)
.DisposeWith(disposables);
+ /*
ViewModel.WhenAnyValue(x => x.OpenWebsiteCommand)
.BindToStrict(this, x => x.OpenWebsiteButton.Command)
.DisposeWith(disposables);
@@ -64,14 +93,18 @@ namespace Wabbajack
ViewModel.WhenAnyValue(x => x.ExecuteCommand)
.BindToStrict(this, x => x.ExecuteButton.Command)
.DisposeWith(disposables);
+ */
+ /*
ViewModel.WhenAnyValue(x => x.ProgressPercent)
- .ObserveOnDispatcher()
+ .ObserveOn(RxApp.MainThreadScheduler)
.Select(p => p.Value)
.BindTo(this, x => x.DownloadProgressBar.Value)
.DisposeWith(disposables);
+ */
+ /*
ViewModel.WhenAnyValue(x => x.Status)
.ObserveOnGuiThread()
.Subscribe(x =>
@@ -91,6 +124,7 @@ namespace Wabbajack
});
})
.DisposeWith(disposables);
+ */
/*
this.MarkAsNeeded(this.ViewModel, x => x.IsBroken);
diff --git a/Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj b/Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj
index cbac7eda..49ea479e 100644
--- a/Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj
+++ b/Wabbajack.App.Wpf/Wabbajack.App.Wpf.csproj
@@ -96,6 +96,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+