Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Blurhash-CSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blurhash-System.Drawing.Com
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blurhash-System.Drawing.Common.Test", "Blurhash-System.Drawing.Common.Test\Blurhash-System.Drawing.Common.Test.csproj", "{C72CB808-7AF6-451C-94D1-4D34F787DE70}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ImageSharp", "ImageSharp", "{836DA7C1-6ED5-46D8-91D7-C8C421769712}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blurhash.ImageSharp", "Blurhash.ImageSharp\Blurhash.ImageSharp.csproj", "{51ADA589-035D-4EC8-91F4-2CD7D899EC3B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -57,6 +61,10 @@ Global
{C72CB808-7AF6-451C-94D1-4D34F787DE70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C72CB808-7AF6-451C-94D1-4D34F787DE70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C72CB808-7AF6-451C-94D1-4D34F787DE70}.Release|Any CPU.Build.0 = Release|Any CPU
{51ADA589-035D-4EC8-91F4-2CD7D899EC3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51ADA589-035D-4EC8-91F4-2CD7D899EC3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51ADA589-035D-4EC8-91F4-2CD7D899EC3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51ADA589-035D-4EC8-91F4-2CD7D899EC3B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -68,6 +76,7 @@ Global
{3931F97A-6F7B-41C8-8EB0-6632C5989783} = {1F0D4320-FCC3-4566-8411-A331684876F2}
{93979E1C-CDD4-4B1B-9096-A0CAEB176551} = {1E9AAF8F-5723-4B38-BE3E-ADB0F0CB9188}
{C72CB808-7AF6-451C-94D1-4D34F787DE70} = {1E9AAF8F-5723-4B38-BE3E-ADB0F0CB9188}
{51ADA589-035D-4EC8-91F4-2CD7D899EC3B} = {836DA7C1-6ED5-46D8-91D7-C8C421769712}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BAAE36B0-9BF1-46D3-BA1F-051172483AE0}
Expand Down
15 changes: 15 additions & 0 deletions Blurhash.ImageSharp/Blurhash.ImageSharp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Blurhash.Core\Blurhash.Core.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0007" />
</ItemGroup>

</Project>
49 changes: 49 additions & 0 deletions Blurhash.ImageSharp/Decoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using System.Net.Mime;
using Blurhash.Core;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

namespace Blurhash.ImageSharp
{
public class Decoder : CoreDecoder
{
/// <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 Image<Rgb24> Decode(string blurhash, int outputWidth, int outputHeight, double punch = 1.0)
{
var pixelData = base.CoreDecode(blurhash, outputWidth, outputHeight, punch);
return ConvertToBitmap(pixelData);
}

/// <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>
internal static Image<Rgb24> ConvertToBitmap(Blurhash.Core.Pixel[,] pixelData)
{
var width = pixelData.GetLength(0);
var height = pixelData.GetLength(1);

var data = Enumerable.Range(0, height)
.SelectMany(y => Enumerable.Range(0, width).Select(x => (x, y)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not a for loop? This linq expression is a perf killer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was copied from another implementation, I'll expand it, as I think perf is key if it's to be used in a Xamarin app for example, which is my primary use-case.

.Select(tuple => pixelData[tuple.x, tuple.y])
.SelectMany(pixel => new byte[]
{
(byte) MathUtils.LinearTosRgb(pixel.Red), (byte) MathUtils.LinearTosRgb(pixel.Green),
(byte) MathUtils.LinearTosRgb(pixel.Blue)
})
.ToArray();

return Image.LoadPixelData<Rgb24>(data.AsSpan(), width, height);
}
}
}
71 changes: 71 additions & 0 deletions Blurhash.ImageSharp/Encoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Blurhash.Core;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

namespace Blurhash.ImageSharp
{
public class Encoder : CoreEncoder
{
/// <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 string Encode(Image<Rgb24> image, int componentsX, int componentsY)
{
return CoreEncode(ConvertBitmap(image), componentsX, componentsY);
}

/// <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 string Encode(Image<Rgba32> image, int componentsX, int componentsY)
{
return CoreEncode(ConvertBitmap(image), componentsX, componentsY);
}

/// <summary>
/// Converts the given bitmap to the library-independent representation used within the Blurhash-core
/// </summary>
/// <param name="sourceBitmap">The bitmap to encode</param>
internal static Pixel[,] ConvertBitmap<T>(Image<T> sourceBitmap) where T : struct, IPixel<T>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the implementation, you should consume Image<Rgb24> or use proper conversion with PixelOperations<T>.ToRgb24(...).

{
var width = sourceBitmap.Width;
var height = sourceBitmap.Height;
var bytesPerPixel = sourceBitmap.PixelType.BitsPerPixel / 8;
var stride = width * 3;

var result = new Pixel[width, height];

for (int y = 0; y < height; y++)
{
var rgbValues = MemoryMarshal.AsBytes(sourceBitmap.GetPixelRowSpan(y));

var index = stride;

for (var x = 0; x < width; x++)
{
result[x, y].Red = MathUtils.SRgbToLinear(rgbValues[index]);
result[x, y].Green = MathUtils.SRgbToLinear(rgbValues[index + 1]);
result[x, y].Blue = MathUtils.SRgbToLinear(rgbValues[index + 2]);
index += bytesPerPixel;
}
}

return result;
}
}
}