wabbajack/Wabbajack.App.Wpf/Util/WebPWrapper.cs

1976 lines
103 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 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 |
/// <summary>Read a WebP file</summary>
/// <param name="pathFileName">WebP file to load</param>
/// <returns>Bitmap with the WebP image</returns>
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"); }
}
/// <summary>Decode a WebP image</summary>
/// <param name="rawWebP">The data to uncompress</param>
/// <returns>Bitmap with the WebP image</returns>
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();
}
}
/// <summary>Decode a WebP image</summary>
/// <param name="rawWebP">the data to uncompress</param>
/// <param name="options">Options for advanced decode</param>
/// <returns>Bitmap with the WebP image</returns>
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();
}
}
/// <summary>Get Thumbnail from webP in mode faster/low quality</summary>
/// <param name="rawWebP">The data to uncompress</param>
/// <param name="width">Wanted width of thumbnail</param>
/// <param name="height">Wanted height of thumbnail</param>
/// <returns>Bitmap with the WebP thumbnail in 24bpp</returns>
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();
}
}
/// <summary>Thumbnail from webP in mode slow/high quality</summary>
/// <param name="rawWebP">The data to uncompress</param>
/// <param name="width">Wanted width of thumbnail</param>
/// <param name="height">Wanted height of thumbnail</param>
/// <returns>Bitmap with the WebP thumbnail</returns>
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 |
/// <summary>Save bitmap to file in WebP format</summary>
/// <param name="bmp">Bitmap with the WebP image</param>
/// <param name="pathFileName">The file to write</param>
/// <param name="quality">Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)</param>
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"); }
}
/// <summary>Lossy encoding bitmap to WebP (Simple encoding API)</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <param name="quality">Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)</param>
/// <returns>Compressed data</returns>
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);
}
}
/// <summary>Lossy encoding bitmap to WebP (Advanced encoding API)</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <param name="quality">Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)</param>
/// <param name="speed">Between 0 (fastest, lowest compression) and 9 (slower, best compression)</param>
/// <returns>Compressed data</returns>
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);
}
/// <summary>Lossless encoding bitmap to WebP (Simple encoding API)</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <returns>Compressed data</returns>
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);
}
}
/// <summary>Lossless encoding image in bitmap (Advanced encoding API)</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <param name="speed">Between 0 (fastest, lowest compression) and 9 (slower, best compression)</param>
/// <returns>Compressed data</returns>
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);
}
/// <summary>Near lossless encoding image in bitmap</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <param name="quality">Between 0 (lower quality, lowest file size) and 100 (highest quality, higher file size)</param>
/// <param name="speed">Between 0 (fastest, lowest compression) and 9 (slower, best compression)</param>
/// <returns>Compress data</returns>
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 |
/// <summary>Get the libwebp version</summary>
/// <returns>Version of library</returns>
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"); }
}
/// <summary>Get info of WEBP data</summary>
/// <param name="rawWebP">The data of WebP</param>
/// <param name="width">width of image</param>
/// <param name="height">height of image</param>
/// <param name="has_alpha">Image has alpha channel</param>
/// <param name="has_animation">Image is a animation</param>
/// <param name="format">Format of image: 0 = undefined (/mixed), 1 = lossy, 2 = lossless</param>
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();
}
}
/// <summary>Compute PSNR, SSIM or LSIM distortion metric between two pictures. Warning: this function is rather CPU-intensive</summary>
/// <param name="source">Picture to measure</param>
/// <param name="reference">Reference picture</param>
/// <param name="metric_type">0 = PSNR, 1 = SSIM, 2 = LSIM</param>
/// <returns>dB in the Y/U/V/Alpha/All order</returns>
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 |
/// <summary>Encoding image using Advanced encoding API</summary>
/// <param name="bmp">Bitmap with the image</param>
/// <param name="config">Configuration for encode</param>
/// <param name="info">True if need encode info.</param>
/// <returns>Compressed data</returns>
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 |
/// <summary>Free memory</summary>
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;
/// <summary>This function will initialize the configuration according to a predefined set of parameters (referred to by 'preset') and a given quality factor</summary>
/// <param name="config">The WebPConfig structure</param>
/// <param name="preset">Type of image</param>
/// <param name="quality">Quality of compression</param>
/// <returns>0 if error</returns>
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("Resources/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("Resources/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);
/// <summary>Get info of WepP image</summary>
/// <param name="rawWebP">Bytes[] of WebP image</param>
/// <param name="data_size">Size of rawWebP</param>
/// <param name="features">Features of WebP image</param>
/// <returns>VP8StatusCode</returns>
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("Resources/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("Resources/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);
/// <summary>Activate the lossless compression mode with the desired efficiency</summary>
/// <param name="config">The WebPConfig struct</param>
/// <param name="level">between 0 (fastest, lowest compression) and 9 (slower, best compression)</param>
/// <returns>0 in case of parameter error</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigLosslessPreset")]
private static extern int WebPConfigLosslessPreset_x86(ref WebPConfig config, int level);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPConfigLosslessPreset")]
private static extern int WebPConfigLosslessPreset_x64(ref WebPConfig config, int level);
/// <summary>Check that configuration is non-NULL and all configuration parameters are within their valid ranges</summary>
/// <param name="config">The WebPConfig structure</param>
/// <returns>1 if configuration is OK</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPValidateConfig")]
private static extern int WebPValidateConfig_x86(ref WebPConfig config);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPValidateConfig")]
private static extern int WebPValidateConfig_x64(ref WebPConfig config);
/// <summary>Initialize the WebPPicture structure checking the DLL version</summary>
/// <param name="wpic">The WebPPicture structure</param>
/// <returns>1 if not error</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureInitInternal")]
private static extern int WebPPictureInitInternal_x86(ref WebPPicture wpic, int WEBP_DECODER_ABI_VERSION);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureInitInternal")]
private static extern int WebPPictureInitInternal_x64(ref WebPPicture wpic, int WEBP_DECODER_ABI_VERSION);
/// <summary>Colorspace conversion function to import RGB samples</summary>
/// <param name="wpic">The WebPPicture structure</param>
/// <param name="bgr">Point to BGR data</param>
/// <param name="stride">stride of BGR data</param>
/// <returns>Returns 0 in case of memory error.</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGR")]
private static extern int WebPPictureImportBGR_x86(ref WebPPicture wpic, IntPtr bgr, int stride);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGR")]
private static extern int WebPPictureImportBGR_x64(ref WebPPicture wpic, IntPtr bgr, int stride);
/// <summary>Color-space conversion function to import RGB samples</summary>
/// <param name="wpic">The WebPPicture structure</param>
/// <param name="bgra">Point to BGRA data</param>
/// <param name="stride">stride of BGRA data</param>
/// <returns>Returns 0 in case of memory error.</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRA")]
private static extern int WebPPictureImportBGRA_x86(ref WebPPicture wpic, IntPtr bgra, int stride);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRA")]
private static extern int WebPPictureImportBGRA_x64(ref WebPPicture wpic, IntPtr bgra, int stride);
/// <summary>Color-space conversion function to import RGB samples</summary>
/// <param name="wpic">The WebPPicture structure</param>
/// <param name="bgr">Point to BGR data</param>
/// <param name="stride">stride of BGR data</param>
/// <returns>Returns 0 in case of memory error.</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRX")]
private static extern int WebPPictureImportBGRX_x86(ref WebPPicture wpic, IntPtr bgr, int stride);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureImportBGRX")]
private static extern int WebPPictureImportBGRX_x64(ref WebPPicture wpic, IntPtr bgr, int stride);
/// <summary>The writer type for output compress data</summary>
/// <param name="data">Data returned</param>
/// <param name="data_size">Size of data returned</param>
/// <param name="wpic">Picture structure</param>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int WebPMemoryWrite([In()] IntPtr data, UIntPtr data_size, ref WebPPicture wpic);
internal static WebPMemoryWrite OnCallback;
/// <summary>Compress to WebP format</summary>
/// <param name="config">The configuration structure for compression parameters</param>
/// <param name="picture">'picture' hold the source samples in both YUV(A) or ARGB input</param>
/// <returns>Returns 0 in case of error, 1 otherwise. In case of error, picture->error_code is updated accordingly.</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncode")]
private static extern int WebPEncode_x86(ref WebPConfig config, ref WebPPicture picture);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPEncode")]
private static extern int WebPEncode_x64(ref WebPConfig config, ref WebPPicture picture);
/// <summary>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</summary>
/// <param name="picture">Picture structure</param>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureFree")]
private static extern void WebPPictureFree_x86(ref WebPPicture wpic);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPPictureFree")]
private static extern void WebPPictureFree_x64(ref WebPPicture wpic);
/// <summary>Validate the WebP image header and retrieve the image height and width. Pointers *width and *height can be passed NULL if deemed irrelevant</summary>
/// <param name="data">Pointer to WebP image data</param>
/// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
/// <param name="width">The range is limited currently from 1 to 16383</param>
/// <param name="height">The range is limited currently from 1 to 16383</param>
/// <returns>1 if success, otherwise error code returned in the case of (a) formatting error(s).</returns>
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("Resources/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("Resources/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);
/// <summary>Decode WEBP image pointed to by *data and returns BGR samples into a preallocated buffer</summary>
/// <param name="data">Pointer to WebP image data</param>
/// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
/// <param name="output_buffer">Pointer to decoded WebP image</param>
/// <param name="output_buffer_size">Size of allocated buffer</param>
/// <param name="output_stride">Specifies the distance between scan lines</param>
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("Resources/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("Resources/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);
/// <summary>Decode WEBP image pointed to by *data and returns BGRA samples into a preallocated buffer</summary>
/// <param name="data">Pointer to WebP image data</param>
/// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
/// <param name="output_buffer">Pointer to decoded WebP image</param>
/// <param name="output_buffer_size">Size of allocated buffer</param>
/// <param name="output_stride">Specifies the distance between scan lines</param>
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("Resources/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("Resources/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);
/// <summary>Decode WEBP image pointed to by *data and returns ARGB samples into a preallocated buffer</summary>
/// <param name="data">Pointer to WebP image data</param>
/// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
/// <param name="output_buffer">Pointer to decoded WebP image</param>
/// <param name="output_buffer_size">Size of allocated buffer</param>
/// <param name="output_stride">Specifies the distance between scan lines</param>
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("Resources/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("Resources/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);
/// <summary>Initialize the configuration as empty. This function must always be called first, unless WebPGetFeatures() is to be called</summary>
/// <param name="webPDecoderConfig">Configuration structure</param>
/// <returns>False in case of mismatched version.</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPInitDecoderConfigInternal")]
private static extern int WebPInitDecoderConfigInternal_x86(ref WebPDecoderConfig webPDecoderConfig, int WEBP_DECODER_ABI_VERSION);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPInitDecoderConfigInternal")]
private static extern int WebPInitDecoderConfigInternal_x64(ref WebPDecoderConfig webPDecoderConfig, int WEBP_DECODER_ABI_VERSION);
/// <summary>Decodes the full data at once, taking configuration into account</summary>
/// <param name="data">WebP raw data to decode</param>
/// <param name="data_size">Size of WebP data </param>
/// <param name="webPDecoderConfig">Configuration structure</param>
/// <returns>VP8_STATUS_OK if the decoding was successful</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecode")]
private static extern VP8StatusCode WebPDecode_x86(IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecode")]
private static extern VP8StatusCode WebPDecode_x64(IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);
/// <summary>Free any memory associated with the buffer. Must always be called last. Doesn't free the 'buffer' structure itself</summary>
/// <param name="buffer">WebPDecBuffer</param>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFreeDecBuffer")]
private static extern void WebPFreeDecBuffer_x86(ref WebPDecBuffer buffer);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFreeDecBuffer")]
private static extern void WebPFreeDecBuffer_x64(ref WebPDecBuffer buffer);
/// <summary>Lossy encoding images</summary>
/// <param name="bgr">Pointer to BGR image data</param>
/// <param name="width">The range is limited currently from 1 to 16383</param>
/// <param name="height">The range is limited currently from 1 to 16383</param>
/// <param name="stride">Specifies the distance between scanlines</param>
/// <param name="quality_factor">Ranges from 0 (lower quality) to 100 (highest quality). Controls the loss and quality during compression</param>
/// <param name="output">output_buffer with WebP image</param>
/// <returns>Size of WebP Image or 0 if an error occurred</returns>
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("Resources/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("Resources/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);
/// <summary>Lossy encoding images</summary>
/// <param name="bgr">Pointer to BGRA image data</param>
/// <param name="width">The range is limited currently from 1 to 16383</param>
/// <param name="height">The range is limited currently from 1 to 16383</param>
/// <param name="stride">Specifies the distance between scan lines</param>
/// <param name="quality_factor">Ranges from 0 (lower quality) to 100 (highest quality). Controls the loss and quality during compression</param>
/// <param name="output">output_buffer with WebP image</param>
/// <returns>Size of WebP Image or 0 if an error occurred</returns>
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("Resources/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("Resources/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);
/// <summary>Lossless encoding images pointed to by *data in WebP format</summary>
/// <param name="bgr">Pointer to BGR image data</param>
/// <param name="width">The range is limited currently from 1 to 16383</param>
/// <param name="height">The range is limited currently from 1 to 16383</param>
/// <param name="stride">Specifies the distance between scan lines</param>
/// <param name="output">output_buffer with WebP image</param>
/// <returns>Size of WebP Image or 0 if an error occurred</returns>
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("Resources/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("Resources/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);
/// <summary>Lossless encoding images pointed to by *data in WebP format</summary>
/// <param name="bgra">Pointer to BGRA image data</param>
/// <param name="width">The range is limited currently from 1 to 16383</param>
/// <param name="height">The range is limited currently from 1 to 16383</param>
/// <param name="stride">Specifies the distance between scan lines</param>
/// <param name="output">output_buffer with WebP image</param>
/// <returns>Size of WebP Image or 0 if an error occurred</returns>
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("Resources/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("Resources/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);
/// <summary>Releases memory returned by the WebPEncode</summary>
/// <param name="p">Pointer to memory</param>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFree")]
private static extern void WebPFree_x86(IntPtr p);
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPFree")]
private static extern void WebPFree_x64(IntPtr p);
/// <summary>Get the WebP version library</summary>
/// <returns>8bits for each of major/minor/revision packet in integer. E.g: v2.5.7 is 0x020507</returns>
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("Resources/libwebp_x86.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetDecoderVersion")]
private static extern int WebPGetDecoderVersion_x86();
[DllImport("Resources/libwebp_x64.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetDecoderVersion")]
private static extern int WebPGetDecoderVersion_x64();
/// <summary>Compute PSNR, SSIM or LSIM distortion metric between two pictures</summary>
/// <param name="srcPicture">Picture to measure</param>
/// <param name="refPicture">Reference picture</param>
/// <param name="metric_type">0 = PSNR, 1 = SSIM, 2 = LSIM</param>
/// <param name="pResult">dB in the Y/U/V/Alpha/All order</param>
/// <returns>False in case of error (the two pictures don't have same dimension, ...)</returns>
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("Resources/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("Resources/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 |
/// <summary>Enumerate some predefined settings for WebPConfig, depending on the type of source picture. These presets are used when calling WebPConfigPreset()</summary>
internal enum WebPPreset
{
/// <summary>Default preset</summary>
WEBP_PRESET_DEFAULT = 0,
/// <summary>Digital picture, like portrait, inner shot</summary>
WEBP_PRESET_PICTURE,
/// <summary>Outdoor photograph, with natural lighting</summary>
WEBP_PRESET_PHOTO,
/// <summary>Hand or line drawing, with high-contrast details</summary>
WEBP_PRESET_DRAWING,
/// <summary>Small-sized colorful images</summary>
WEBP_PRESET_ICON,
/// <summary>Text-like</summary>
WEBP_PRESET_TEXT
};
/// <summary>Encoding error conditions</summary>
internal enum WebPEncodingError
{
/// <summary>No error</summary>
VP8_ENC_OK = 0,
/// <summary>Memory error allocating objects</summary>
VP8_ENC_ERROR_OUT_OF_MEMORY,
/// <summary>Memory error while flushing bits</summary>
VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY,
/// <summary>A pointer parameter is NULL</summary>
VP8_ENC_ERROR_NULL_PARAMETER,
/// <summary>Configuration is invalid</summary>
VP8_ENC_ERROR_INVALID_CONFIGURATION,
/// <summary>Picture has invalid width/height</summary>
VP8_ENC_ERROR_BAD_DIMENSION,
/// <summary>Partition is bigger than 512k</summary>
VP8_ENC_ERROR_PARTITION0_OVERFLOW,
/// <summary>Partition is bigger than 16M</summary>
VP8_ENC_ERROR_PARTITION_OVERFLOW,
/// <summary>Error while flushing bytes</summary>
VP8_ENC_ERROR_BAD_WRITE,
/// <summary>File is bigger than 4G</summary>
VP8_ENC_ERROR_FILE_TOO_BIG,
/// <summary>Abort request by user</summary>
VP8_ENC_ERROR_USER_ABORT,
/// <summary>List terminator. Always last</summary>
VP8_ENC_ERROR_LAST,
}
/// <summary>Enumeration of the status codes</summary>
internal enum VP8StatusCode
{
/// <summary>No error</summary>
VP8_STATUS_OK = 0,
/// <summary>Memory error allocating objects</summary>
VP8_STATUS_OUT_OF_MEMORY,
/// <summary>Configuration is invalid</summary>
VP8_STATUS_INVALID_PARAM,
VP8_STATUS_BITSTREAM_ERROR,
/// <summary>Configuration is invalid</summary>
VP8_STATUS_UNSUPPORTED_FEATURE,
VP8_STATUS_SUSPENDED,
/// <summary>Abort request by user</summary>
VP8_STATUS_USER_ABORT,
VP8_STATUS_NOT_ENOUGH_DATA,
}
/// <summary>Image characteristics hint for the underlying encoder</summary>
internal enum WebPImageHint
{
/// <summary>Default preset</summary>
WEBP_HINT_DEFAULT = 0,
/// <summary>Digital picture, like portrait, inner shot</summary>
WEBP_HINT_PICTURE,
/// <summary>Outdoor photograph, with natural lighting</summary>
WEBP_HINT_PHOTO,
/// <summary>Discrete tone image (graph, map-tile etc)</summary>
WEBP_HINT_GRAPH,
/// <summary>List terminator. Always last</summary>
WEBP_HINT_LAST
};
/// <summary>Describes the byte-ordering of packed samples in memory</summary>
internal enum WEBP_CSP_MODE
{
/// <summary>Byte-order: R,G,B,R,G,B,..</summary>
MODE_RGB = 0,
/// <summary>Byte-order: R,G,B,A,R,G,B,A,..</summary>
MODE_RGBA = 1,
/// <summary>Byte-order: B,G,R,B,G,R,..</summary>
MODE_BGR = 2,
/// <summary>Byte-order: B,G,R,A,B,G,R,A,..</summary>
MODE_BGRA = 3,
/// <summary>Byte-order: A,R,G,B,A,R,G,B,..</summary>
MODE_ARGB = 4,
/// <summary>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], ..</summary>
MODE_RGBA_4444 = 5,
/// <summary>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], ..</summary>
MODE_RGB_565 = 6,
/// <summary>RGB-premultiplied transparent modes (alpha value is preserved)</summary>
MODE_rgbA = 7,
/// <summary>RGB-premultiplied transparent modes (alpha value is preserved)</summary>
MODE_bgrA = 8,
/// <summary>RGB-premultiplied transparent modes (alpha value is preserved)</summary>
MODE_Argb = 9,
/// <summary>RGB-premultiplied transparent modes (alpha value is preserved)</summary>
MODE_rgbA_4444 = 10,
/// <summary>YUV 4:2:0</summary>
MODE_YUV = 11,
/// <summary>YUV 4:2:0</summary>
MODE_YUVA = 12,
/// <summary>MODE_LAST -> 13</summary>
MODE_LAST = 13,
}
/// <summary>
/// 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.
/// </summary>
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 |
/// <summary>Features gathered from the bit stream</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPBitstreamFeatures
{
/// <summary>Width in pixels, as read from the bit stream</summary>
public int Width;
/// <summary>Height in pixels, as read from the bit stream</summary>
public int Height;
/// <summary>True if the bit stream contains an alpha channel</summary>
public int Has_alpha;
/// <summary>True if the bit stream is an animation</summary>
public int Has_animation;
/// <summary>0 = undefined (/mixed), 1 = lossy, 2 = lossless</summary>
public int Format;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad;
};
/// <summary>Compression parameters</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPConfig
{
/// <summary>Lossless encoding (0=lossy(default), 1=lossless)</summary>
public int lossless;
/// <summary>Between 0 (smallest file) and 100 (biggest)</summary>
public float quality;
/// <summary>Quality/speed trade-off (0=fast, 6=slower-better)</summary>
public int method;
/// <summary>Hint for image type (lossless only for now)</summary>
public WebPImageHint image_hint;
/// <summary>If non-zero, set the desired target size in bytes. Takes precedence over the 'compression' parameter</summary>
public int target_size;
/// <summary>If non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target_size</summary>
public float target_PSNR;
/// <summary>Maximum number of segments to use, in [1..4]</summary>
public int segments;
/// <summary>Spatial Noise Shaping. 0=off, 100=maximum</summary>
public int sns_strength;
/// <summary>Range: [0 = off .. 100 = strongest]</summary>
public int filter_strength;
/// <summary>Range: [0 = off .. 7 = least sharp]</summary>
public int filter_sharpness;
/// <summary>Filtering type: 0 = simple, 1 = strong (only used if filter_strength > 0 or auto-filter > 0)</summary>
public int filter_type;
/// <summary>Auto adjust filter's strength [0 = off, 1 = on]</summary>
public int autofilter;
/// <summary>Algorithm for encoding the alpha plane (0 = none, 1 = compressed with WebP lossless). Default is 1</summary>
public int alpha_compression;
/// <summary>Predictive filtering method for alpha plane. 0: none, 1: fast, 2: best. Default if 1</summary>
public int alpha_filtering;
/// <summary>Between 0 (smallest size) and 100 (lossless). Default is 100</summary>
public int alpha_quality;
/// <summary>Number of entropy-analysis passes (in [1..10])</summary>
public int pass;
/// <summary>If true, export the compressed picture back. In-loop filtering is not applied</summary>
public int show_compressed;
/// <summary>Preprocessing filter (0=none, 1=segment-smooth, 2=pseudo-random dithering)</summary>
public int preprocessing;
/// <summary>Log2(number of token partitions) in [0..3] Default is set to 0 for easier progressive decoding</summary>
public int partitions;
/// <summary>Quality degradation allowed to fit the 512k limit on prediction modes coding (0: no degradation, 100: maximum possible degradation)</summary>
public int partition_limit;
/// <summary>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</summary>
public int emulate_jpeg_size;
/// <summary>If non-zero, try and use multi-threaded encoding</summary>
public int thread_level;
/// <summary>If set, reduce memory usage (but increase CPU use)</summary>
public int low_memory;
/// <summary>Near lossless encoding [0 = max loss .. 100 = off (default)]</summary>
public int near_lossless;
/// <summary>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</summary>
public int exact;
/// <summary>Reserved for future lossless feature</summary>
public int delta_palettization;
/// <summary>If needed, use sharp (and slow) RGB->YUV conversion</summary>
public int use_sharp_yuv;
/// <summary>Padding for later use</summary>
private readonly int pad1;
private readonly int pad2;
};
/// <summary>Main exchange structure (input samples, output bytes, statistics)</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPPicture
{
/// <summary>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</summary>
public int use_argb;
/// <summary>Color-space: should be YUV420 for now (=Y'CbCr). Value = 0</summary>
public UInt32 colorspace;
/// <summary>Width of picture (less or equal to WEBP_MAX_DIMENSION)</summary>
public int width;
/// <summary>Height of picture (less or equal to WEBP_MAX_DIMENSION)</summary>
public int height;
/// <summary>Pointer to luma plane</summary>
public IntPtr y;
/// <summary>Pointer to chroma U plane</summary>
public IntPtr u;
/// <summary>Pointer to chroma V plane</summary>
public IntPtr v;
/// <summary>Luma stride</summary>
public int y_stride;
/// <summary>Chroma stride</summary>
public int uv_stride;
/// <summary>Pointer to the alpha plane</summary>
public IntPtr a;
/// <summary>stride of the alpha plane</summary>
public int a_stride;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad1;
/// <summary>Pointer to ARGB (32 bit) plane</summary>
public IntPtr argb;
/// <summary>This is stride in pixels units, not bytes</summary>
public int argb_stride;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad2;
/// <summary>Byte-emission hook, to store compressed bytes as they are ready</summary>
public IntPtr writer;
/// <summary>Can be used by the writer</summary>
public IntPtr custom_ptr;
// map for extra information (only for lossy compression mode)
/// <summary>1: intra type, 2: segment, 3: quant, 4: intra-16 prediction mode, 5: chroma prediction mode, 6: bit cost, 7: distortion</summary>
public int extra_info_type;
/// <summary>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</summary>
public IntPtr extra_info;
/// <summary>Pointer to side statistics (updated only if not NULL)</summary>
public IntPtr stats;
/// <summary>Error code for the latest error encountered during encoding</summary>
public UInt32 error_code;
/// <summary>If not NULL, report progress during encoding</summary>
public IntPtr progress_hook;
/// <summary>This field is free to be set to any value and used during callbacks (like progress-report e.g.)</summary>
public IntPtr user_data;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 13, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad3;
/// <summary>Row chunk of memory for YUVA planes</summary>
private readonly IntPtr memory_;
/// <summary>Row chunk of memory for ARGB planes</summary>
private readonly IntPtr memory_argb_;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad4;
};
/// <summary>Structure for storing auxiliary statistics (mostly for lossy encoding)</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPAuxStats
{
/// <summary>Final size</summary>
public int coded_size;
/// <summary>Peak-signal-to-noise ratio for Y</summary>
public float PSNRY;
/// <summary>Peak-signal-to-noise ratio for U</summary>
public float PSNRU;
/// <summary>Peak-signal-to-noise ratio for V</summary>
public float PSNRV;
/// <summary>Peak-signal-to-noise ratio for All</summary>
public float PSNRALL;
/// <summary>Peak-signal-to-noise ratio for Alpha</summary>
public float PSNRAlpha;
/// <summary>Number of intra4</summary>
public int block_count_intra4;
/// <summary>Number of intra16</summary>
public int block_count_intra16;
/// <summary>Number of skipped macro-blocks</summary>
public int block_count_skipped;
/// <summary>Approximate number of bytes spent for header</summary>
public int header_bytes;
/// <summary>Approximate number of bytes spent for mode-partition #0</summary>
public int mode_partition_0;
/// <summary>Approximate number of bytes spent for DC coefficients for segment 0</summary>
public int residual_bytes_DC_segments0;
/// <summary>Approximate number of bytes spent for AC coefficients for segment 0</summary>
public int residual_bytes_AC_segments0;
/// <summary>Approximate number of bytes spent for UV coefficients for segment 0</summary>
public int residual_bytes_uv_segments0;
/// <summary>Approximate number of bytes spent for DC coefficients for segment 1</summary>
public int residual_bytes_DC_segments1;
/// <summary>Approximate number of bytes spent for AC coefficients for segment 1</summary>
public int residual_bytes_AC_segments1;
/// <summary>Approximate number of bytes spent for UV coefficients for segment 1</summary>
public int residual_bytes_uv_segments1;
/// <summary>Approximate number of bytes spent for DC coefficients for segment 2</summary>
public int residual_bytes_DC_segments2;
/// <summary>Approximate number of bytes spent for AC coefficients for segment 2</summary>
public int residual_bytes_AC_segments2;
/// <summary>Approximate number of bytes spent for UV coefficients for segment 2</summary>
public int residual_bytes_uv_segments2;
/// <summary>Approximate number of bytes spent for DC coefficients for segment 3</summary>
public int residual_bytes_DC_segments3;
/// <summary>Approximate number of bytes spent for AC coefficients for segment 3</summary>
public int residual_bytes_AC_segments3;
/// <summary>Approximate number of bytes spent for UV coefficients for segment 3</summary>
public int residual_bytes_uv_segments3;
/// <summary>Number of macro-blocks in segments 0</summary>
public int segment_size_segments0;
/// <summary>Number of macro-blocks in segments 1</summary>
public int segment_size_segments1;
/// <summary>Number of macro-blocks in segments 2</summary>
public int segment_size_segments2;
/// <summary>Number of macro-blocks in segments 3</summary>
public int segment_size_segments3;
/// <summary>Quantizer values for segment 0</summary>
public int segment_quant_segments0;
/// <summary>Quantizer values for segment 1</summary>
public int segment_quant_segments1;
/// <summary>Quantizer values for segment 2</summary>
public int segment_quant_segments2;
/// <summary>Quantizer values for segment 3</summary>
public int segment_quant_segments3;
/// <summary>Filtering strength for segment 0 [0..63]</summary>
public int segment_level_segments0;
/// <summary>Filtering strength for segment 1 [0..63]</summary>
public int segment_level_segments1;
/// <summary>Filtering strength for segment 2 [0..63]</summary>
public int segment_level_segments2;
/// <summary>Filtering strength for segment 3 [0..63]</summary>
public int segment_level_segments3;
/// <summary>Size of the transparency data</summary>
public int alpha_data_size;
/// <summary>Size of the enhancement layer data</summary>
public int layer_data_size;
// lossless encoder statistics
/// <summary>bit0:predictor bit1:cross-color transform bit2:subtract-green bit3:color indexing</summary>
public Int32 lossless_features;
/// <summary>Number of precision bits of histogram</summary>
public int histogram_bits;
/// <summary>Precision bits for transform</summary>
public int transform_bits;
/// <summary>Number of bits for color cache lookup</summary>
public int cache_bits;
/// <summary>Number of color in palette, if used</summary>
public int palette_size;
/// <summary>Final lossless size</summary>
public int lossless_size;
/// <summary>Lossless header (transform, Huffman, etc) size</summary>
public int lossless_hdr_size;
/// <summary>Lossless image data size</summary>
public int lossless_data_size;
/// <summary>Padding for later use</summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
private readonly uint[] pad;
};
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPDecoderConfig
{
/// <summary>Immutable bit stream features (optional)</summary>
public WebPBitstreamFeatures input;
/// <summary>Output buffer (can point to external memory)</summary>
public WebPDecBuffer output;
/// <summary>Decoding options</summary>
public WebPDecoderOptions options;
}
/// <summary>Output buffer</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPDecBuffer
{
/// <summary>Color space</summary>
public WEBP_CSP_MODE colorspace;
/// <summary>Width of image</summary>
public int width;
/// <summary>Height of image</summary>
public int height;
/// <summary>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</summary>
public int is_external_memory;
/// <summary>Output buffer parameters</summary>
public RGBA_YUVA_Buffer u;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad1;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad2;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad3;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad4;
/// <summary>Internally allocated memory (only when is_external_memory is 0). Should not be used externally, but accessed via WebPRGBABuffer</summary>
public IntPtr private_memory;
}
/// <summary>Union of buffer parameters</summary>
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct RGBA_YUVA_Buffer
{
[FieldOffsetAttribute(0)]
public WebPRGBABuffer RGBA;
[FieldOffsetAttribute(0)]
public WebPYUVABuffer YUVA;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPYUVABuffer
{
/// <summary>Pointer to luma samples</summary>
public IntPtr y;
/// <summary>Pointer to chroma U samples</summary>
public IntPtr u;
/// <summary>Pointer to chroma V samples</summary>
public IntPtr v;
/// <summary>Pointer to alpha samples</summary>
public IntPtr a;
/// <summary>Luma stride</summary>
public int y_stride;
/// <summary>Chroma U stride</summary>
public int u_stride;
/// <summary>Chroma V stride</summary>
public int v_stride;
/// <summary>Alpha stride</summary>
public int a_stride;
/// <summary>Luma plane size</summary>
public UIntPtr y_size;
/// <summary>Chroma plane U size</summary>
public UIntPtr u_size;
/// <summary>Chroma plane V size</summary>
public UIntPtr v_size;
/// <summary>Alpha plane size</summary>
public UIntPtr a_size;
}
/// <summary>Generic structure for describing the output sample buffer</summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct WebPRGBABuffer
{
/// <summary>Pointer to RGBA samples</summary>
public IntPtr rgba;
/// <summary>Stride in bytes from one scanline to the next</summary>
public int stride;
/// <summary>Total size of the RGBA buffer</summary>
public UIntPtr size;
}
/// <summary>Decoding options</summary>
[StructLayout(LayoutKind.Sequential)]
public struct WebPDecoderOptions
{
/// <summary>If true, skip the in-loop filtering</summary>
public int bypass_filtering;
/// <summary>If true, use faster point-wise up-sampler</summary>
public int no_fancy_upsampling;
/// <summary>If true, cropping is applied _first_</summary>
public int use_cropping;
/// <summary>Left position for cropping. Will be snapped to even values</summary>
public int crop_left;
/// <summary>Top position for cropping. Will be snapped to even values</summary>
public int crop_top;
/// <summary>Width of the cropping area</summary>
public int crop_width;
/// <summary>Height of the cropping area</summary>
public int crop_height;
/// <summary>If true, scaling is applied _afterward_</summary>
public int use_scaling;
/// <summary>Final width</summary>
public int scaled_width;
/// <summary>Final height</summary>
public int scaled_height;
/// <summary>If true, use multi-threaded decoding</summary>
public int use_threads;
/// <summary>Dithering strength (0=Off, 100=full)</summary>
public int dithering_strength;
/// <summary>Flip output vertically</summary>
public int flip;
/// <summary>Alpha dithering strength in [0..100]</summary>
public int alpha_dithering_strength;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad1;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad2;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad3;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad4;
/// <summary>Padding for later use</summary>
private readonly UInt32 pad5;
};
#endregion
}