using BlurHash; using System.Drawing.Imaging; namespace System.Drawing.BlurHash; public static class BlurHasher { #pragma warning disable CA1416 /// <summary> /// Encodes a picture into a BlurHash string /// </summary> /// <param name="image">The picture to encode</param> /// <param name="componentsX">The number of components used on the X-Axis for the DCT</param> /// <param name="componentsY">The number of components used on the Y-Axis for the DCT</param> /// <returns>The resulting BlurHash string</returns> public static string Encode(Image image, int componentsX, int componentsY) => Core.Encode(ConvertBitmap(image as Bitmap ?? new Bitmap(image)), componentsX, componentsY); /// <summary> /// Decodes a BlurHash string into a <c>System.Drawing.Image</c> /// </summary> /// <param name="blurhash">The blurhash string to decode</param> /// <param name="outputWidth">The desired width of the output in pixels</param> /// <param name="outputHeight">The desired height of the output in pixels</param> /// <param name="punch">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.</param> /// <returns>The decoded preview</returns> public static Image Decode(string blurhash, int outputWidth, int outputHeight, double punch = 1.0) { Pixel[,] pixelData = new Pixel[outputWidth, outputHeight]; Core.Decode(blurhash, pixelData, punch); return ConvertToBitmap(pixelData); } /// <summary> /// Converts the given bitmap to the library-independent representation used within the BlurHash-core /// </summary> /// <param name="sourceBitmap">The bitmap to encode</param> 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; } /// <summary> /// Converts the library-independent representation of pixels into a bitmap /// </summary> /// <param name="pixelData">The library-independent representation of the image</param> /// <returns>A <c>System.Drawing.Bitmap</c> in 32bpp-RGB representation</returns> 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; } }