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; } }