using BlurHash;
using System.Drawing.Imaging;
namespace System.Drawing.BlurHash;
public static class BlurHasher
{
#pragma warning disable CA1416
///
/// Encodes a picture into a BlurHash string
///
/// The picture to encode
/// The number of components used on the X-Axis for the DCT
/// The number of components used on the Y-Axis for the DCT
/// The resulting BlurHash string
public static ReadOnlySpan Encode(Image image, int componentsX, int componentsY) =>
Core.Encode(ConvertBitmap(image as Bitmap ?? new Bitmap(image)), componentsX, componentsY);
///
/// Decodes a BlurHash string into a System.Drawing.Image
///
/// The blurHash string to decode
/// The desired width of the output in pixels
/// The desired height of the output in pixels
/// A value that affects the contrast of the decoded image. 1 means normal, smaller values will make the effect more subtle, and larger values will make it stronger.
/// The decoded preview
public static Image Decode(ReadOnlySpan blurHash, int outputWidth, int outputHeight, double punch = 1.0)
{
Pixel[,] pixelData = new Pixel[outputWidth, outputHeight];
Core.Decode(blurHash, pixelData, punch);
return ConvertToBitmap(pixelData);
}
///
/// Converts the given bitmap to the library-independent representation used within the BlurHash-core
///
/// The bitmap to encode
public static unsafe Pixel[,] ConvertBitmap(Image sourceBitmap)
{
int width = sourceBitmap.Width;
int height = sourceBitmap.Height;
using Bitmap temporaryBitmap = new(width, height, PixelFormat.Format24bppRgb);
using (Graphics graphics = Graphics.FromImage(temporaryBitmap))
{
graphics.DrawImageUnscaled(sourceBitmap, 0, 0);
}
// Lock the bitmap's bits.
BitmapData bmpData = temporaryBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, temporaryBitmap.PixelFormat);
// Get the address of the first line.
nint ptr = bmpData.Scan0;
Pixel[,] result = new Pixel[width, height];
byte* rgb = (byte*)ptr.ToPointer();
for (int y = 0; y < height; y++)
{
int index = bmpData.Stride * y;
for (int x = 0; x < width; x++)
{
ref Pixel res = ref result[x, y];
res.Blue = MathUtils.SRgbToLinear(rgb[index++]);
res.Green = MathUtils.SRgbToLinear(rgb[index++]);
res.Red = MathUtils.SRgbToLinear(rgb[index++]);
}
}
temporaryBitmap.UnlockBits(bmpData);
return result;
}
///
/// Converts the library-independent representation of pixels into a bitmap
///
/// The library-independent representation of the image
/// A System.Drawing.Bitmap in 32bpp-RGB representation
public static unsafe Bitmap ConvertToBitmap(Pixel[,] pixelData)
{
int width = pixelData.GetLength(0);
int height = pixelData.GetLength(1);
byte[] data = new byte[width * height * 4];
int index = 0;
for (int yPixel = 0; yPixel < height; yPixel++)
for (int xPixel = 0; xPixel < width; xPixel++)
{
Pixel pixel = pixelData[xPixel, yPixel];
data[index++] = (byte)MathUtils.LinearTosRgb(pixel.Blue);
data[index++] = (byte)MathUtils.LinearTosRgb(pixel.Green);
data[index++] = (byte)MathUtils.LinearTosRgb(pixel.Red);
data[index++] = 0;
}
Bitmap bmp;
fixed (byte* ptr = data)
{
bmp = new Bitmap(width, height, width * 4, PixelFormat.Format32bppRgb, new IntPtr(ptr));
}
return bmp;
}
}