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

1976 lines
103 KiB
C#
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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);
/// <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("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();
/// <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("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 |
/// <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
}