Working state before .NET 5 upgrade

This commit is contained in:
Robin Krom 2020-07-20 11:49:19 +02:00
commit 56ccfd4d2a
28 changed files with 1203 additions and 284 deletions

View file

@ -58,7 +58,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))"> <ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))">
<PackageReference Include="Nerdbank.GitVersioning" Version="3.0.50"> <PackageReference Include="Nerdbank.GitVersioning" Version="3.1.91">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -24,6 +24,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.Confluence" Version="0.9.7" /> <PackageReference Include="Dapplo.Confluence" Version="1.0.16" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -19,6 +19,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.0.0" /> <PackageReference Include="CliWrap" Version="3.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.Windows.Com" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Com" Version="0.11.17" />
<PackageReference Include="Unofficial.Microsoft.mshtml" Version="7.0.3300" /> <PackageReference Include="Unofficial.Microsoft.mshtml" Version="7.0.3300" />
</ItemGroup> </ItemGroup>

View file

@ -20,7 +20,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.Jira" Version="0.9.19" /> <PackageReference Include="Dapplo.Jira" Version="0.9.28" />
<PackageReference Include="DynamicData" Version="6.14.8" /> <PackageReference Include="DynamicData" Version="6.16.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -38,6 +38,7 @@ using Greenshot.Addons.Interfaces.Plugin;
using Greenshot.Core.Enums; using Greenshot.Core.Enums;
using Greenshot.Gfx; using Greenshot.Gfx;
using Greenshot.Gfx.Quantizer; using Greenshot.Gfx.Quantizer;
using Greenshot.Gfx.Structs;
using Encoder = System.Drawing.Imaging.Encoder; using Encoder = System.Drawing.Imaging.Encoder;
namespace Greenshot.Addons.Core namespace Greenshot.Addons.Core
@ -374,7 +375,7 @@ namespace Greenshot.Addons.Core
// We create a copy of the bitmap, so everything else can be disposed // We create a copy of the bitmap, so everything else can be disposed
surfaceFileStream.Position = 0; surfaceFileStream.Position = 0;
var fileImage = BitmapHelper.FromStream(surfaceFileStream, ".greenshot"); var fileImage = BitmapHelper.FromStream(surfaceFileStream, ".greenshot");
// Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
const int markerSize = 14; const int markerSize = 14;
@ -700,7 +701,7 @@ namespace Greenshot.Addons.Core
} }
} }
// check for color reduction, forced or automatically, only when the DisableReduceColors is false // check for color reduction, forced or automatically, only when the DisableReduceColors is false
if (outputSettings.DisableReduceColors || !CoreConfiguration.OutputFileAutoReduceColors && !outputSettings.ReduceColors) if (outputSettings.DisableReduceColors || !CoreConfiguration.OutputFileAutoReduceColors && !outputSettings.ReduceColors)
{ {
return disposeImage; return disposeImage;
@ -708,7 +709,7 @@ namespace Greenshot.Addons.Core
var isAlpha = Image.IsAlphaPixelFormat(bitmapToSave.PixelFormat); var isAlpha = Image.IsAlphaPixelFormat(bitmapToSave.PixelFormat);
if (outputSettings.ReduceColors || !isAlpha && CoreConfiguration.OutputFileAutoReduceColors) if (outputSettings.ReduceColors || !isAlpha && CoreConfiguration.OutputFileAutoReduceColors)
{ {
using var quantizer = new WuQuantizer(bitmapToSave); using var quantizer = new WuQuantizer<Bgra32>(bitmapToSave);
var colorCount = quantizer.GetColorCount(); var colorCount = quantizer.GetColorCount();
Log.Info().WriteLine("Image with format {0} has {1} colors", bitmapToSave.PixelFormat, colorCount); Log.Info().WriteLine("Image with format {0} has {1} colors", bitmapToSave.PixelFormat, colorCount);
if (!outputSettings.ReduceColors && colorCount >= 256) if (!outputSettings.ReduceColors && colorCount >= 256)

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -39,7 +39,7 @@ namespace Greenshot.Addons.Extensions
public static class ClipboardBitmapExtensions public static class ClipboardBitmapExtensions
{ {
private static readonly LogSource Log = new LogSource(); private static readonly LogSource Log = new LogSource();
private static readonly string[] SupportedBitmapFormats = private static readonly string[] SupportedBitmapFormats =
{ {
"PNG", "PNG",
"PNG+Office Art", "PNG+Office Art",
@ -73,7 +73,7 @@ namespace Greenshot.Addons.Extensions
return true; return true;
} }
return clipboardAccessToken.GetFilenames() return clipboardAccessToken.GetFileNames()
.Select(filename => Path.GetExtension(filename).ToLowerInvariant()) .Select(filename => Path.GetExtension(filename).ToLowerInvariant())
.Intersect(SupportedExtensions) .Intersect(SupportedExtensions)
.Any(); .Any();
@ -236,7 +236,7 @@ namespace Greenshot.Addons.Extensions
/// <returns>byte[]</returns> /// <returns>byte[]</returns>
private static byte[] BitmapToByteArray(IBitmapWithNativeSupport bitmap) private static byte[] BitmapToByteArray(IBitmapWithNativeSupport bitmap)
{ {
// Lock the bitmap's bits. // Lock the bitmap's bits.
var rect = new NativeRect(0, 0, bitmap.Width, bitmap.Height); var rect = new NativeRect(0, 0, bitmap.Width, bitmap.Height);
var bmpData = bitmap.NativeBitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); var bmpData = bitmap.NativeBitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);

View file

@ -20,15 +20,15 @@
<PackageReference Include="Dapplo.CaliburnMicro.Metro" Version="2.1.10" /> <PackageReference Include="Dapplo.CaliburnMicro.Metro" Version="2.1.10" />
<PackageReference Include="Dapplo.CaliburnMicro.Toasts" Version="2.1.10" /> <PackageReference Include="Dapplo.CaliburnMicro.Toasts" Version="2.1.10" />
<PackageReference Include="Dapplo.CaliburnMicro.Translations" Version="2.1.10" /> <PackageReference Include="Dapplo.CaliburnMicro.Translations" Version="2.1.10" />
<PackageReference Include="Dapplo.HttpExtensions" Version="0.10.9" /> <PackageReference Include="Dapplo.HttpExtensions" Version="1.0.3" />
<PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="0.10.9" /> <PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.0.3" />
<PackageReference Include="Dapplo.HttpExtensions.OAuth" Version="0.10.9" /> <PackageReference Include="Dapplo.HttpExtensions.OAuth" Version="1.0.3" />
<PackageReference Include="Dapplo.Windows.Clipboard" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Clipboard" Version="0.11.17" />
<PackageReference Include="Dapplo.Windows.Dpi" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Dpi" Version="0.11.17" />
<PackageReference Include="Dapplo.Windows.Icons" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Icons" Version="0.11.17" />
<PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" /> <PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" />
<PackageReference Include="MahApps.Metro.IconPacks" Version="3.3.0" /> <PackageReference Include="MahApps.Metro.IconPacks" Version="3.3.0" />
<PackageReference Include="Svg" Version="3.0.102" /> <PackageReference Include="Svg" Version="3.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.Windows.Icons" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Icons" Version="0.11.17" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -46,7 +46,7 @@ namespace Greenshot.Gfx
private static readonly LogSource Log = new LogSource(); private static readonly LogSource Log = new LogSource();
/// <summary> /// <summary>
/// This defines all available bitmap reader functions, registered to an "extension" is called with a stream to a IBitmap. /// This defines all available bitmap reader functions, registered to an "extension" is called with a stream to a IBitmap.
/// </summary> /// </summary>
public static IDictionary<string, IImageFormatReader> StreamConverters { get; } = new Dictionary<string, IImageFormatReader>(StringComparer.OrdinalIgnoreCase); public static IDictionary<string, IImageFormatReader> StreamConverters { get; } = new Dictionary<string, IImageFormatReader>(StringComparer.OrdinalIgnoreCase);
@ -444,7 +444,7 @@ namespace Greenshot.Gfx
Matrix22 = 0, Matrix22 = 0,
Matrix33 = darkness Matrix33 = darkness
}; };
var shadowRectangle = new NativeRect(new NativePoint(shadowSize, shadowSize), sourceBitmap.Size); var shadowRectangle = new NativeRect(new NativePoint(shadowSize, shadowSize), sourceBitmap.Size);
ApplyColorMatrix(sourceBitmap, NativeRect.Empty, returnImage, shadowRectangle, maskMatrix); ApplyColorMatrix(sourceBitmap, NativeRect.Empty, returnImage, shadowRectangle, maskMatrix);
@ -755,7 +755,6 @@ namespace Greenshot.Gfx
colors += lineColorCount; colors += lineColorCount;
} }
}); });
return colors; return colors;
} }
@ -815,7 +814,7 @@ namespace Greenshot.Gfx
return original; return original;
} }
if (width == original.Width * 2) if (width == original.Width * 2)
{ {
return original.Scale2X(); return original.Scale2X();

View file

@ -0,0 +1,54 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Greenshot.Gfx
{
/// <summary>
/// A bitmap wrapper with memory from Marshal.AllocHGlobal
/// </summary>
/// <typeparam name="TPixelLayout">struct for the pixel information</typeparam>
public class ChunkyBitmap : UnmanagedBitmap<byte>
{
/// <summary>
/// The constructor for the UnmanagedBitmap
/// </summary>
/// <param name="width">int</param>
/// <param name="height">int</param>
/// <param name="horizontalPixelsPerInch">float</param>
/// <param name="verticalPixelsPerInch">float</param>
public ChunkyBitmap(int width, int height, float horizontalPixelsPerInch = 0.96f, float verticalPixelsPerInch = 0.96f) : base(width, height, horizontalPixelsPerInch, verticalPixelsPerInch)
{
}
/// <summary>
/// The constructor for the UnmanagedBitmap with already initialized bits
/// </summary>
/// <param name="bits">IntPtr to the bits, this will not be freed</param>
/// <param name="width">int</param>
/// <param name="height">int</param>
/// <param name="horizontalPixelsPerInch">float</param>
/// <param name="verticalPixelsPerInch">float</param>
public ChunkyBitmap(IntPtr bits, int width, int height, float horizontalPixelsPerInch = 0.96f, float verticalPixelsPerInch = 0.96f) : base(bits, width, height, horizontalPixelsPerInch, verticalPixelsPerInch)
{
}
}
}

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -21,6 +21,7 @@ using System;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using Dapplo.Log; using Dapplo.Log;
using Greenshot.Gfx.Quantizer; using Greenshot.Gfx.Quantizer;
using Greenshot.Gfx.Structs;
namespace Greenshot.Gfx.Effects namespace Greenshot.Gfx.Effects
{ {
@ -39,7 +40,7 @@ namespace Greenshot.Gfx.Effects
/// <inheritdoc /> /// <inheritdoc />
public IBitmapWithNativeSupport Apply(IBitmapWithNativeSupport sourceBitmap, Matrix matrix) public IBitmapWithNativeSupport Apply(IBitmapWithNativeSupport sourceBitmap, Matrix matrix)
{ {
using (var quantizer = new WuQuantizer(sourceBitmap)) using (var quantizer = new WuQuantizer<Bgra32>(sourceBitmap))
{ {
var colorCount = quantizer.GetColorCount(); var colorCount = quantizer.GetColorCount();
if (colorCount <= Colors) if (colorCount <= Colors)

View file

@ -8,8 +8,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.Addons" Version="1.3.3" /> <PackageReference Include="Dapplo.Addons" Version="1.3.3" />
<PackageReference Include="Dapplo.Log" Version="1.3.26" /> <PackageReference Include="Dapplo.Log" Version="1.3.26" />
<PackageReference Include="Dapplo.Windows.Dpi" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Dpi" Version="0.11.17" />
<PackageReference Include="Svg" Version="3.0.102" /> <PackageReference Include="Svg" Version="3.1.1" />
<PackageReference Include="System.Memory" Version="4.5.3" /> <PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,25 +1,25 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Greenshot.Gfx.Quantizer namespace Greenshot.Gfx.Quantizer
{ {
internal class WuColorCube internal struct WuColorCube
{ {
/// <summary> /// <summary>
/// Gets or sets the red minimum. /// Gets or sets the red minimum.

View file

@ -0,0 +1,66 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace Greenshot.Gfx.Quantizer
{
internal class WuColorCubeOld
{
/// <summary>
/// Gets or sets the red minimum.
/// </summary>
/// <value>The red minimum.</value>
public int RedMinimum { get; set; }
/// <summary>
/// Gets or sets the red maximum.
/// </summary>
/// <value>The red maximum.</value>
public int RedMaximum { get; set; }
/// <summary>
/// Gets or sets the green minimum.
/// </summary>
/// <value>The green minimum.</value>
public int GreenMinimum { get; set; }
/// <summary>
/// Gets or sets the green maximum.
/// </summary>
/// <value>The green maximum.</value>
public int GreenMaximum { get; set; }
/// <summary>
/// Gets or sets the blue minimum.
/// </summary>
/// <value>The blue minimum.</value>
public int BlueMinimum { get; set; }
/// <summary>
/// Gets or sets the blue maximum.
/// </summary>
/// <value>The blue maximum.</value>
public int BlueMaximum { get; set; }
/// <summary>
/// Gets or sets the cube volume.
/// </summary>
/// <value>The volume.</value>
public int Volume { get; set; }
}
}

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -21,16 +21,16 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Runtime.CompilerServices;
using Dapplo.Log; using System.Runtime.InteropServices;
using Greenshot.Gfx.FastBitmap; using Greenshot.Gfx.Structs;
namespace Greenshot.Gfx.Quantizer namespace Greenshot.Gfx.Quantizer
{ {
/// <summary> /// <summary>
/// Implementation of the WuQuantizer algorithm /// Implementation of the WuQuantizer algorithm
/// </summary> /// </summary>
public class WuQuantizer : IDisposable public class WuQuantizer<TPixel> : IDisposable where TPixel : unmanaged
{ {
private const int Maxcolor = 512; private const int Maxcolor = 512;
private const int Red = 2; private const int Red = 2;
@ -39,7 +39,6 @@ namespace Greenshot.Gfx.Quantizer
private const int Sidesize = 33; private const int Sidesize = 33;
private const int Maxsideindex = 32; private const int Maxsideindex = 32;
private const int Maxvolume = Sidesize * Sidesize * Sidesize; private const int Maxvolume = Sidesize * Sidesize * Sidesize;
private static readonly LogSource Log = new LogSource();
// To count the colors // To count the colors
private readonly int _colorCount; private readonly int _colorCount;
@ -49,23 +48,25 @@ namespace Greenshot.Gfx.Quantizer
private readonly long[,,] _momentsBlue; private readonly long[,,] _momentsBlue;
private readonly long[,,] _momentsGreen; private readonly long[,,] _momentsGreen;
private readonly long[,,] _momentsRed; private readonly long[,,] _momentsRed;
private readonly IBitmapWithNativeSupport _sourceBitmap; private readonly UnmanagedBitmap<TPixel> _sourceBitmap;
private readonly long[,,] _weights; private readonly long[,,] _weights;
private int[] _blues; private ChunkyBitmap _resultBitmap;
private int[] _greens;
private int[] _reds;
private IBitmapWithNativeSupport _resultBitmap;
private int[] _sums;
private byte[] _tag;
/// <summary> /// <summary>
/// The constructor for the WuQauntizer /// The constructor for the WuQauntizer
/// </summary> /// </summary>
/// <param name="sourceBitmap"></param> /// <param name="sourceBitmap">IBitmapWithNativeSupport</param>
public WuQuantizer(IBitmapWithNativeSupport sourceBitmap) public WuQuantizer(IBitmapWithNativeSupport sourceBitmap) : this(sourceBitmap as UnmanagedBitmap<TPixel>)
{
}
/// <summary>
/// The constructor for the WuQauntizer
/// </summary>
/// <param name="sourceBitmap">UnmanagedBitmap of TPixel</param>
public WuQuantizer(UnmanagedBitmap<TPixel> sourceBitmap)
{ {
_sourceBitmap = sourceBitmap; _sourceBitmap = sourceBitmap;
// Make sure the color count variables are reset // Make sure the color count variables are reset
@ -75,12 +76,6 @@ namespace Greenshot.Gfx.Quantizer
// creates all the cubes // creates all the cubes
_cubes = new WuColorCube[Maxcolor]; _cubes = new WuColorCube[Maxcolor];
// initializes all the cubes
for (var cubeIndex = 0; cubeIndex < Maxcolor; cubeIndex++)
{
_cubes[cubeIndex] = new WuColorCube();
}
// resets the reference minimums // resets the reference minimums
_cubes[0].RedMinimum = 0; _cubes[0].RedMinimum = 0;
_cubes[0].GreenMinimum = 0; _cubes[0].GreenMinimum = 0;
@ -97,32 +92,23 @@ namespace Greenshot.Gfx.Quantizer
_momentsBlue = new long[Sidesize, Sidesize, Sidesize]; _momentsBlue = new long[Sidesize, Sidesize, Sidesize];
_moments = new float[Sidesize, Sidesize, Sidesize]; _moments = new float[Sidesize, Sidesize, Sidesize];
var table = new int[256]; Span<int> table = stackalloc int[256];
for (var tableIndex = 0; tableIndex < 256; ++tableIndex)
for (var tableIndex = 0; tableIndex < 256; ++tableIndex)
{ {
table[tableIndex] = tableIndex * tableIndex; table[tableIndex] = tableIndex * tableIndex;
} }
// Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage _resultBitmap = new ChunkyBitmap(sourceBitmap.Width, sourceBitmap.Height);
using var sourceFastBitmap = FastBitmapFactory.Create(sourceBitmap); for (var y = 0; y < sourceBitmap.Height; y++)
sourceFastBitmap.Lock();
using var destinationFastBitmap = FastBitmapFactory.CreateEmpty(sourceBitmap.Size, PixelFormat.Format8bppIndexed, Color.White) as FastChunkyBitmap;
for (var y = 0; y < sourceFastBitmap.Height; y++)
{ {
for (var x = 0; x < sourceFastBitmap.Width; x++) var srcPixelSpan = MemoryMarshal.Cast<TPixel, Bgra32>(_sourceBitmap[y]);
var destPixelSpan = _resultBitmap[y];
for (var x = 0; x < sourceBitmap.Width; x++)
{ {
Color color; var color = srcPixelSpan[x];
if (!(sourceFastBitmap is IFastBitmapWithBlend sourceFastBitmapWithBlend))
{
color = sourceFastBitmap.GetColorAt(x, y);
}
else
{
color = sourceFastBitmapWithBlend.GetBlendedColorAt(x, y);
}
// To count the colors // To count the colors
var index = color.ToArgb() & 0x00ffffff; var index = (color.B << 16) | (color.G << 8) | color.R;
// Check if we already have this color // Check if we already have this color
if (!bitArray.Get(index)) if (!bitArray.Get(index))
{ {
@ -143,10 +129,9 @@ namespace Greenshot.Gfx.Quantizer
// Store the initial "match" // Store the initial "match"
var paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue; var paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue;
destinationFastBitmap.SetColorIndexAt(x, y, (byte) (paletteIndex & 0xff)); destPixelSpan[x] = (byte)(paletteIndex & 0xff);
} }
} }
_resultBitmap = destinationFastBitmap.UnlockAndReturnBitmap();
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -155,7 +140,7 @@ namespace Greenshot.Gfx.Quantizer
Dispose(true); Dispose(true);
} }
/// <summary> /// <summary>
/// Dispose implementation /// Dispose implementation
/// </summary> /// </summary>
@ -191,39 +176,30 @@ namespace Greenshot.Gfx.Quantizer
/// <returns>Bitmap</returns> /// <returns>Bitmap</returns>
public IBitmapWithNativeSupport SimpleReindex() public IBitmapWithNativeSupport SimpleReindex()
{ {
var colors = new List<Color>(); Span<uint> colors = stackalloc uint[256];
var lookup = new Dictionary<Color, byte>(); var lookup = new Dictionary<uint, byte>();
using (var bbbDest = FastBitmapFactory.Create(_resultBitmap) as FastChunkyBitmap)
{ byte colorCount = 0;
bbbDest.Lock(); for (var y = 0; y < _sourceBitmap.Height; y++)
using var bbbSrc = FastBitmapFactory.Create(_sourceBitmap); {
bbbSrc.Lock(); var srcSpan = MemoryMarshal.Cast<TPixel, uint>(_sourceBitmap[y]);
for (var y = 0; y < bbbSrc.Height; y++) var destSpan = _resultBitmap[y];
for (var x = 0; x < _sourceBitmap.Width; x++)
{ {
for (var x = 0; x < bbbSrc.Width; x++) var color = srcSpan[x];
byte index;
if (lookup.ContainsKey(color))
{ {
Color color; index = lookup[color];
if (bbbSrc is IFastBitmapWithBlend bbbSrcBlend)
{
color = bbbSrcBlend.GetBlendedColorAt(x, y);
}
else
{
color = bbbSrc.GetColorAt(x, y);
}
byte index;
if (lookup.ContainsKey(color))
{
index = lookup[color];
}
else
{
colors.Add(color);
index = (byte) (colors.Count - 1);
lookup.Add(color, index);
}
bbbDest.SetColorIndexAt(x, y, index);
} }
else
{
colors[colorCount] = color;
index = colorCount;
colorCount++;
lookup.Add(color, index);
}
destSpan[x] = index;
} }
} }
@ -232,9 +208,9 @@ namespace Greenshot.Gfx.Quantizer
var entries = imagePalette.Entries; var entries = imagePalette.Entries;
for (var paletteIndex = 0; paletteIndex < 256; paletteIndex++) for (var paletteIndex = 0; paletteIndex < 256; paletteIndex++)
{ {
if (paletteIndex < _colorCount) if (paletteIndex < colorCount)
{ {
entries[paletteIndex] = colors[paletteIndex]; entries[paletteIndex] = colors[paletteIndex].ToColor();
} }
else else
{ {
@ -261,12 +237,10 @@ namespace Greenshot.Gfx.Quantizer
if (_colorCount < allowedColorCount) if (_colorCount < allowedColorCount)
{ {
// Simple logic to reduce to 8 bit // Simple logic to reduce to 8 bit
Log.Info().WriteLine("Colors in the image are already less as whished for, using simple copy to indexed image, no quantizing needed!");
return SimpleReindex(); return SimpleReindex();
} }
// preprocess the colors // preprocess the colors
CalculateMoments(); CalculateMoments();
Log.Info().WriteLine("Calculated the moments...");
var next = 0; var next = 0;
var volumeVariance = new float[Maxcolor]; var volumeVariance = new float[Maxcolor];
@ -274,7 +248,7 @@ namespace Greenshot.Gfx.Quantizer
for (var cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex) for (var cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex)
{ {
// if cut is possible; make it // if cut is possible; make it
if (Cut(_cubes[next], _cubes[cubeIndex])) if (Cut(ref _cubes[next], ref _cubes[cubeIndex]))
{ {
volumeVariance[next] = _cubes[next].Volume > 1 ? CalculateVariance(_cubes[next]) : 0.0f; volumeVariance[next] = _cubes[next].Volume > 1 ? CalculateVariance(_cubes[next]) : 0.0f;
volumeVariance[cubeIndex] = _cubes[cubeIndex].Volume > 1 ? CalculateVariance(_cubes[cubeIndex]) : 0.0f; volumeVariance[cubeIndex] = _cubes[cubeIndex].Volume > 1 ? CalculateVariance(_cubes[cubeIndex]) : 0.0f;
@ -307,16 +281,16 @@ namespace Greenshot.Gfx.Quantizer
break; break;
} }
var lookupRed = new int[Maxcolor]; Span<int> lookupRed = stackalloc int[Maxcolor];
var lookupGreen = new int[Maxcolor]; Span<int> lookupGreen = stackalloc int[Maxcolor];
var lookupBlue = new int[Maxcolor]; Span<int> lookupBlue = stackalloc int[Maxcolor];
_tag = new byte[Maxvolume]; Span<byte> tag = stackalloc byte[Maxvolume];
// pre-calculates lookup tables // pre-calculates lookup tables
for (var k = 0; k < allowedColorCount; ++k) for (var k = 0; k < allowedColorCount; ++k)
{ {
Mark(_cubes[k], k, _tag); Mark(_cubes[k], k, tag);
var weight = Volume(_cubes[k], _weights); var weight = Volume(_cubes[k], _weights);
@ -334,92 +308,78 @@ namespace Greenshot.Gfx.Quantizer
} }
} }
_reds = new int[allowedColorCount + 1]; Span<int> reds = stackalloc int[allowedColorCount + 1];
_greens = new int[allowedColorCount + 1]; Span<int> greens = stackalloc int[allowedColorCount + 1];
_blues = new int[allowedColorCount + 1]; Span<int> blues = stackalloc int[allowedColorCount + 1];
_sums = new int[allowedColorCount + 1]; Span<int> sums = stackalloc int[allowedColorCount + 1];
Log.Info().WriteLine("Starting bitmap reconstruction...");
using (var dest = FastBitmapFactory.Create(_resultBitmap) as FastChunkyBitmap) var lookup = new Dictionary<Bgra32, byte>();
for (var y = 0; y < _sourceBitmap.Height; y++)
{ {
using var src = FastBitmapFactory.Create(_sourceBitmap); var destSpan = _resultBitmap[y];
var lookup = new Dictionary<Color, byte>(); var srcSpan = MemoryMarshal.Cast<TPixel, Bgra32>(_sourceBitmap[y]);
for (var y = 0; y < src.Height; y++) for (var x = 0; x < _sourceBitmap.Width; x++)
{ {
for (var x = 0; x < src.Width; x++) var color = srcSpan[x];
// Check if we already matched the color
byte bestMatch;
if (!lookup.ContainsKey(color))
{ {
Color color; // If not we need to find the best match
if (src is IFastBitmapWithBlend srcBlend)
{
// WithoutAlpha, this makes it possible to ignore the alpha
color = srcBlend.GetBlendedColorAt(x, y);
}
else
{
color = src.GetColorAt(x, y);
}
// Check if we already matched the color // First get initial match
byte bestMatch; bestMatch = destSpan[x];
if (!lookup.ContainsKey(color)) bestMatch = tag[bestMatch];
var bestDistance = 100000000;
for (var lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++)
{ {
// If not we need to find the best match var foundRed = lookupRed[lookupIndex];
var foundGreen = lookupGreen[lookupIndex];
var foundBlue = lookupBlue[lookupIndex];
var deltaRed = color.R - foundRed;
var deltaGreen = color.G - foundGreen;
var deltaBlue = color.B - foundBlue;
// First get initial match var distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
bestMatch = dest.GetColorIndexAt(x, y);
bestMatch = _tag[bestMatch];
var bestDistance = 100000000; if (distance < bestDistance)
for (var lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++)
{ {
var foundRed = lookupRed[lookupIndex]; bestDistance = distance;
var foundGreen = lookupGreen[lookupIndex]; bestMatch = (byte) lookupIndex;
var foundBlue = lookupBlue[lookupIndex];
var deltaRed = color.R - foundRed;
var deltaGreen = color.G - foundGreen;
var deltaBlue = color.B - foundBlue;
var distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
if (distance < bestDistance)
{
bestDistance = distance;
bestMatch = (byte) lookupIndex;
}
} }
lookup.Add(color, bestMatch);
} }
else lookup.Add(color, bestMatch);
{
// Already matched, so we just use the lookup
bestMatch = lookup[color];
}
_reds[bestMatch] += color.R;
_greens[bestMatch] += color.G;
_blues[bestMatch] += color.B;
_sums[bestMatch]++;
dest.SetColorIndexAt(x, y, bestMatch);
} }
else
{
// Already matched, so we just use the lookup
bestMatch = lookup[color];
}
reds[bestMatch] += color.R;
greens[bestMatch] += color.G;
blues[bestMatch] += color.B;
sums[bestMatch]++;
destSpan[x] = bestMatch;
} }
} }
// generates palette // generates palette
var imagePalette = _resultBitmap.NativeBitmap.Palette; var imagePalette = _resultBitmap.NativeBitmap.Palette;
var entries = imagePalette.Entries; var entries = imagePalette.Entries;
for (var paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++) for (var paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++)
{ {
if (_sums[paletteIndex] > 0) if (sums[paletteIndex] > 0)
{ {
_reds[paletteIndex] /= _sums[paletteIndex]; reds[paletteIndex] /= sums[paletteIndex];
_greens[paletteIndex] /= _sums[paletteIndex]; greens[paletteIndex] /= sums[paletteIndex];
_blues[paletteIndex] /= _sums[paletteIndex]; blues[paletteIndex] /= sums[paletteIndex];
} }
entries[paletteIndex] = Color.FromArgb(255, _reds[paletteIndex], _greens[paletteIndex], _blues[paletteIndex]); entries[paletteIndex] = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]);
} }
_resultBitmap.NativeBitmap.Palette = imagePalette; _resultBitmap.NativeBitmap.Palette = imagePalette;
@ -434,11 +394,11 @@ namespace Greenshot.Gfx.Quantizer
/// </summary> /// </summary>
private void CalculateMoments() private void CalculateMoments()
{ {
var area = new long[Sidesize]; Span<long> area = stackalloc long[Sidesize];
var areaRed = new long[Sidesize]; Span<long> areaRed = stackalloc long[Sidesize];
var areaGreen = new long[Sidesize]; Span<long> areaGreen = stackalloc long[Sidesize];
var areaBlue = new long[Sidesize]; Span<long> areaBlue = stackalloc long[Sidesize];
var area2 = new float[Sidesize]; var area2 = new float[Sidesize];
for (var redIndex = 1; redIndex <= Maxsideindex; ++redIndex) for (var redIndex = 1; redIndex <= Maxsideindex; ++redIndex)
{ {
@ -483,10 +443,11 @@ namespace Greenshot.Gfx.Quantizer
} }
} }
/// <summary> /// <summary>
/// Computes the volume of the cube in a specific moment. /// Computes the volume of the cube in a specific moment.
/// </summary> /// </summary>
private static long Volume(WuColorCube cube, long[,,] moment) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long Volume(in WuColorCube cube, long[,,] moment)
{ {
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
@ -498,10 +459,11 @@ namespace Greenshot.Gfx.Quantizer
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
} }
/// <summary> /// <summary>
/// Computes the volume of the cube in a specific moment. For the floating-point values. /// Computes the volume of the cube in a specific moment. For the floating-point values.
/// </summary> /// </summary>
private static float VolumeFloat(WuColorCube cube, float[,,] moment) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float VolumeFloat(in WuColorCube cube, float[,,] moment)
{ {
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
@ -513,10 +475,11 @@ namespace Greenshot.Gfx.Quantizer
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
} }
/// <summary> /// <summary>
/// Splits the cube in given position, and color direction. /// Splits the cube in given position, and color direction.
/// </summary> /// </summary>
private static long Top(WuColorCube cube, int direction, int position, long[,,] moment) => [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long Top(in WuColorCube cube, int direction, int position, long[,,] moment) =>
direction switch direction switch
{ {
Red => (moment[position, cube.GreenMaximum, cube.BlueMaximum] - Red => (moment[position, cube.GreenMaximum, cube.BlueMaximum] -
@ -537,7 +500,8 @@ namespace Greenshot.Gfx.Quantizer
/// <summary> /// <summary>
/// Splits the cube in a given color direction at its minimum. /// Splits the cube in a given color direction at its minimum.
/// </summary> /// </summary>
private static long Bottom(WuColorCube cube, int direction, long[,,] moment) => [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long Bottom(in WuColorCube cube, int direction, long[,,] moment) =>
direction switch direction switch
{ {
Red => (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + Red => (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
@ -558,7 +522,7 @@ namespace Greenshot.Gfx.Quantizer
/// <summary> /// <summary>
/// Calculates statistical variance for a given cube. /// Calculates statistical variance for a given cube.
/// </summary> /// </summary>
private float CalculateVariance(WuColorCube cube) private float CalculateVariance(in WuColorCube cube)
{ {
float volumeRed = Volume(cube, _momentsRed); float volumeRed = Volume(cube, _momentsRed);
float volumeGreen = Volume(cube, _momentsGreen); float volumeGreen = Volume(cube, _momentsGreen);
@ -574,7 +538,7 @@ namespace Greenshot.Gfx.Quantizer
/// <summary> /// <summary>
/// Finds the optimal (maximal) position for the cut. /// Finds the optimal (maximal) position for the cut.
/// </summary> /// </summary>
private float Maximize(WuColorCube cube, int direction, int first, int last, int[] cut, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight) private float Maximize(in WuColorCube cube, int direction, int first, int last, int[] cut, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight)
{ {
var bottomRed = Bottom(cube, direction, _momentsRed); var bottomRed = Bottom(cube, direction, _momentsRed);
var bottomGreen = Bottom(cube, direction, _momentsGreen); var bottomGreen = Bottom(cube, direction, _momentsGreen);
@ -623,7 +587,7 @@ namespace Greenshot.Gfx.Quantizer
/// <summary> /// <summary>
/// Cuts a cube with another one. /// Cuts a cube with another one.
/// </summary> /// </summary>
private bool Cut(WuColorCube first, WuColorCube second) private bool Cut(ref WuColorCube first, ref WuColorCube second)
{ {
int direction; int direction;
@ -699,7 +663,7 @@ namespace Greenshot.Gfx.Quantizer
/// <summary> /// <summary>
/// Marks all the tags with a given label. /// Marks all the tags with a given label.
/// </summary> /// </summary>
private static void Mark(WuColorCube cube, int label, byte[] tag) private static void Mark(in WuColorCube cube, int label, Span<byte> tag)
{ {
for (var redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex) for (var redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex)
{ {

View file

@ -0,0 +1,716 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using Dapplo.Log;
using Greenshot.Gfx.FastBitmap;
namespace Greenshot.Gfx.Quantizer
{
/// <summary>
/// Implementation of the WuQuantizer algorithm
/// </summary>
public class WuQuantizerOld : IDisposable
{
private const int Maxcolor = 512;
private const int Red = 2;
private const int Green = 1;
private const int Blue = 0;
private const int Sidesize = 33;
private const int Maxsideindex = 32;
private const int Maxvolume = Sidesize * Sidesize * Sidesize;
private static readonly LogSource Log = new LogSource();
// To count the colors
private readonly int _colorCount;
private readonly WuColorCubeOld[] _cubes;
private readonly float[,,] _moments;
private readonly long[,,] _momentsBlue;
private readonly long[,,] _momentsGreen;
private readonly long[,,] _momentsRed;
private readonly IBitmapWithNativeSupport _sourceBitmap;
private readonly long[,,] _weights;
private int[] _blues;
private int[] _greens;
private int[] _reds;
private IBitmapWithNativeSupport _resultBitmap;
private int[] _sums;
private byte[] _tag;
/// <summary>
/// The constructor for the WuQauntizer
/// </summary>
/// <param name="sourceBitmap"></param>
public WuQuantizerOld(IBitmapWithNativeSupport sourceBitmap)
{
_sourceBitmap = sourceBitmap;
// Make sure the color count variables are reset
var bitArray = new BitArray((int) Math.Pow(2, 24));
_colorCount = 0;
// creates all the cubes
_cubes = new WuColorCubeOld[Maxcolor];
// initializes all the cubes
for (var cubeIndex = 0; cubeIndex < Maxcolor; cubeIndex++)
{
_cubes[cubeIndex] = new WuColorCubeOld();
}
// resets the reference minimums
_cubes[0].RedMinimum = 0;
_cubes[0].GreenMinimum = 0;
_cubes[0].BlueMinimum = 0;
// resets the reference maximums
_cubes[0].RedMaximum = Maxsideindex;
_cubes[0].GreenMaximum = Maxsideindex;
_cubes[0].BlueMaximum = Maxsideindex;
_weights = new long[Sidesize, Sidesize, Sidesize];
_momentsRed = new long[Sidesize, Sidesize, Sidesize];
_momentsGreen = new long[Sidesize, Sidesize, Sidesize];
_momentsBlue = new long[Sidesize, Sidesize, Sidesize];
_moments = new float[Sidesize, Sidesize, Sidesize];
var table = new int[256];
for (var tableIndex = 0; tableIndex < 256; ++tableIndex)
{
table[tableIndex] = tableIndex * tableIndex;
}
// Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage
using var sourceFastBitmap = FastBitmapFactory.Create(sourceBitmap);
sourceFastBitmap.Lock();
using var destinationFastBitmap = FastBitmapFactory.CreateEmpty(sourceBitmap.Size, PixelFormat.Format8bppIndexed, Color.White) as FastChunkyBitmap;
for (var y = 0; y < sourceFastBitmap.Height; y++)
{
for (var x = 0; x < sourceFastBitmap.Width; x++)
{
Color color;
if (!(sourceFastBitmap is IFastBitmapWithBlend sourceFastBitmapWithBlend))
{
color = sourceFastBitmap.GetColorAt(x, y);
}
else
{
color = sourceFastBitmapWithBlend.GetBlendedColorAt(x, y);
}
// To count the colors
var index = color.ToArgb() & 0x00ffffff;
// Check if we already have this color
if (!bitArray.Get(index))
{
// If not, add 1 to the single colors
_colorCount++;
bitArray.Set(index, true);
}
var indexRed = (color.R >> 3) + 1;
var indexGreen = (color.G >> 3) + 1;
var indexBlue = (color.B >> 3) + 1;
_weights[indexRed, indexGreen, indexBlue]++;
_momentsRed[indexRed, indexGreen, indexBlue] += color.R;
_momentsGreen[indexRed, indexGreen, indexBlue] += color.G;
_momentsBlue[indexRed, indexGreen, indexBlue] += color.B;
_moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B];
// Store the initial "match"
var paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue;
destinationFastBitmap.SetColorIndexAt(x, y, (byte) (paletteIndex & 0xff));
}
}
_resultBitmap = destinationFastBitmap.UnlockAndReturnBitmap();
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Dispose implementation
/// </summary>
/// <param name="disposing">bool</param>
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
if (_resultBitmap == null)
{
return;
}
_resultBitmap.Dispose();
_resultBitmap = null;
}
/// <summary>
/// Returns the number of colors
/// </summary>
/// <returns>int</returns>
public int GetColorCount()
{
return _colorCount;
}
/// <summary>
/// Reindex the 24/32 BPP (A)RGB image to a 8BPP
/// </summary>
/// <returns>Bitmap</returns>
public IBitmapWithNativeSupport SimpleReindex()
{
var colors = new List<Color>();
var lookup = new Dictionary<Color, byte>();
using (var bbbDest = FastBitmapFactory.Create(_resultBitmap) as FastChunkyBitmap)
{
bbbDest.Lock();
using var bbbSrc = FastBitmapFactory.Create(_sourceBitmap);
bbbSrc.Lock();
for (var y = 0; y < bbbSrc.Height; y++)
{
for (var x = 0; x < bbbSrc.Width; x++)
{
Color color;
if (bbbSrc is IFastBitmapWithBlend bbbSrcBlend)
{
color = bbbSrcBlend.GetBlendedColorAt(x, y);
}
else
{
color = bbbSrc.GetColorAt(x, y);
}
byte index;
if (lookup.ContainsKey(color))
{
index = lookup[color];
}
else
{
colors.Add(color);
index = (byte) (colors.Count - 1);
lookup.Add(color, index);
}
bbbDest.SetColorIndexAt(x, y, index);
}
}
}
// generates palette
var imagePalette = _resultBitmap.NativeBitmap.Palette;
var entries = imagePalette.Entries;
for (var paletteIndex = 0; paletteIndex < 256; paletteIndex++)
{
if (paletteIndex < _colorCount)
{
entries[paletteIndex] = colors[paletteIndex];
}
else
{
entries[paletteIndex] = Color.Black;
}
}
_resultBitmap.NativeBitmap.Palette = imagePalette;
// Make sure the bitmap is not disposed, as we return it.
var tmpBitmap = _resultBitmap;
_resultBitmap = null;
return tmpBitmap;
}
/// <summary>
/// Get the image
/// </summary>
public IBitmapWithNativeSupport GetQuantizedImage(int allowedColorCount = 256)
{
if (allowedColorCount > 256)
{
throw new ArgumentOutOfRangeException(nameof(allowedColorCount), "Quantizing muss be done to get less than 256 colors");
}
if (_colorCount < allowedColorCount)
{
// Simple logic to reduce to 8 bit
Log.Info().WriteLine("Colors in the image are already less as whished for, using simple copy to indexed image, no quantizing needed!");
return SimpleReindex();
}
// preprocess the colors
CalculateMoments();
Log.Info().WriteLine("Calculated the moments...");
var next = 0;
var volumeVariance = new float[Maxcolor];
// processes the cubes
for (var cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex)
{
// if cut is possible; make it
if (Cut(_cubes[next], _cubes[cubeIndex]))
{
volumeVariance[next] = _cubes[next].Volume > 1 ? CalculateVariance(_cubes[next]) : 0.0f;
volumeVariance[cubeIndex] = _cubes[cubeIndex].Volume > 1 ? CalculateVariance(_cubes[cubeIndex]) : 0.0f;
}
else
{
// the cut was not possible, revert the index
volumeVariance[next] = 0.0f;
cubeIndex--;
}
next = 0;
var temp = volumeVariance[0];
for (var index = 1; index <= cubeIndex; ++index)
{
if (volumeVariance[index] <= temp)
{
continue;
}
temp = volumeVariance[index];
next = index;
}
if (temp > 0.0)
{
continue;
}
allowedColorCount = cubeIndex + 1;
break;
}
var lookupRed = new int[Maxcolor];
var lookupGreen = new int[Maxcolor];
var lookupBlue = new int[Maxcolor];
_tag = new byte[Maxvolume];
// pre-calculates lookup tables
for (var k = 0; k < allowedColorCount; ++k)
{
Mark(_cubes[k], k, _tag);
var weight = Volume(_cubes[k], _weights);
if (weight > 0)
{
lookupRed[k] = (int) (Volume(_cubes[k], _momentsRed) / weight);
lookupGreen[k] = (int) (Volume(_cubes[k], _momentsGreen) / weight);
lookupBlue[k] = (int) (Volume(_cubes[k], _momentsBlue) / weight);
}
else
{
lookupRed[k] = 0;
lookupGreen[k] = 0;
lookupBlue[k] = 0;
}
}
_reds = new int[allowedColorCount + 1];
_greens = new int[allowedColorCount + 1];
_blues = new int[allowedColorCount + 1];
_sums = new int[allowedColorCount + 1];
Log.Info().WriteLine("Starting bitmap reconstruction...");
using (var dest = FastBitmapFactory.Create(_resultBitmap) as FastChunkyBitmap)
{
using var src = FastBitmapFactory.Create(_sourceBitmap);
var lookup = new Dictionary<Color, byte>();
for (var y = 0; y < src.Height; y++)
{
for (var x = 0; x < src.Width; x++)
{
Color color;
if (src is IFastBitmapWithBlend srcBlend)
{
// WithoutAlpha, this makes it possible to ignore the alpha
color = srcBlend.GetBlendedColorAt(x, y);
}
else
{
color = src.GetColorAt(x, y);
}
// Check if we already matched the color
byte bestMatch;
if (!lookup.ContainsKey(color))
{
// If not we need to find the best match
// First get initial match
bestMatch = dest.GetColorIndexAt(x, y);
bestMatch = _tag[bestMatch];
var bestDistance = 100000000;
for (var lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++)
{
var foundRed = lookupRed[lookupIndex];
var foundGreen = lookupGreen[lookupIndex];
var foundBlue = lookupBlue[lookupIndex];
var deltaRed = color.R - foundRed;
var deltaGreen = color.G - foundGreen;
var deltaBlue = color.B - foundBlue;
var distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
if (distance < bestDistance)
{
bestDistance = distance;
bestMatch = (byte) lookupIndex;
}
}
lookup.Add(color, bestMatch);
}
else
{
// Already matched, so we just use the lookup
bestMatch = lookup[color];
}
_reds[bestMatch] += color.R;
_greens[bestMatch] += color.G;
_blues[bestMatch] += color.B;
_sums[bestMatch]++;
dest.SetColorIndexAt(x, y, bestMatch);
}
}
}
// generates palette
var imagePalette = _resultBitmap.NativeBitmap.Palette;
var entries = imagePalette.Entries;
for (var paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++)
{
if (_sums[paletteIndex] > 0)
{
_reds[paletteIndex] /= _sums[paletteIndex];
_greens[paletteIndex] /= _sums[paletteIndex];
_blues[paletteIndex] /= _sums[paletteIndex];
}
entries[paletteIndex] = Color.FromArgb(255, _reds[paletteIndex], _greens[paletteIndex], _blues[paletteIndex]);
}
_resultBitmap.NativeBitmap.Palette = imagePalette;
// Make sure the bitmap is not disposed, as we return it.
var tmpBitmap = _resultBitmap;
_resultBitmap = null;
return tmpBitmap;
}
/// <summary>
/// Converts the histogram to a series of moments.
/// </summary>
private void CalculateMoments()
{
var area = new long[Sidesize];
var areaRed = new long[Sidesize];
var areaGreen = new long[Sidesize];
var areaBlue = new long[Sidesize];
var area2 = new float[Sidesize];
for (var redIndex = 1; redIndex <= Maxsideindex; ++redIndex)
{
for (var index = 0; index <= Maxsideindex; ++index)
{
area[index] = 0;
areaRed[index] = 0;
areaGreen[index] = 0;
areaBlue[index] = 0;
area2[index] = 0;
}
for (var greenIndex = 1; greenIndex <= Maxsideindex; ++greenIndex)
{
long line = 0;
long lineRed = 0;
long lineGreen = 0;
long lineBlue = 0;
var line2 = 0.0f;
for (var blueIndex = 1; blueIndex <= Maxsideindex; ++blueIndex)
{
line += _weights[redIndex, greenIndex, blueIndex];
lineRed += _momentsRed[redIndex, greenIndex, blueIndex];
lineGreen += _momentsGreen[redIndex, greenIndex, blueIndex];
lineBlue += _momentsBlue[redIndex, greenIndex, blueIndex];
line2 += _moments[redIndex, greenIndex, blueIndex];
area[blueIndex] += line;
areaRed[blueIndex] += lineRed;
areaGreen[blueIndex] += lineGreen;
areaBlue[blueIndex] += lineBlue;
area2[blueIndex] += line2;
_weights[redIndex, greenIndex, blueIndex] = _weights[redIndex - 1, greenIndex, blueIndex] + area[blueIndex];
_momentsRed[redIndex, greenIndex, blueIndex] = _momentsRed[redIndex - 1, greenIndex, blueIndex] + areaRed[blueIndex];
_momentsGreen[redIndex, greenIndex, blueIndex] = _momentsGreen[redIndex - 1, greenIndex, blueIndex] + areaGreen[blueIndex];
_momentsBlue[redIndex, greenIndex, blueIndex] = _momentsBlue[redIndex - 1, greenIndex, blueIndex] + areaBlue[blueIndex];
_moments[redIndex, greenIndex, blueIndex] = _moments[redIndex - 1, greenIndex, blueIndex] + area2[blueIndex];
}
}
}
}
/// <summary>
/// Computes the volume of the cube in a specific moment.
/// </summary>
private static long Volume(WuColorCubeOld cube, long[,,] moment)
{
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
}
/// <summary>
/// Computes the volume of the cube in a specific moment. For the floating-point values.
/// </summary>
private static float VolumeFloat(WuColorCubeOld cube, float[,,] moment)
{
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
}
/// <summary>
/// Splits the cube in given position, and color direction.
/// </summary>
private static long Top(WuColorCubeOld cube, int direction, int position, long[,,] moment) =>
direction switch
{
Red => (moment[position, cube.GreenMaximum, cube.BlueMaximum] -
moment[position, cube.GreenMaximum, cube.BlueMinimum] -
moment[position, cube.GreenMinimum, cube.BlueMaximum] +
moment[position, cube.GreenMinimum, cube.BlueMinimum]),
Green => (moment[cube.RedMaximum, position, cube.BlueMaximum] -
moment[cube.RedMaximum, position, cube.BlueMinimum] -
moment[cube.RedMinimum, position, cube.BlueMaximum] +
moment[cube.RedMinimum, position, cube.BlueMinimum]),
Blue => (moment[cube.RedMaximum, cube.GreenMaximum, position] -
moment[cube.RedMaximum, cube.GreenMinimum, position] -
moment[cube.RedMinimum, cube.GreenMaximum, position] +
moment[cube.RedMinimum, cube.GreenMinimum, position]),
_ => 0
};
/// <summary>
/// Splits the cube in a given color direction at its minimum.
/// </summary>
private static long Bottom(WuColorCubeOld cube, int direction, long[,,] moment) =>
direction switch
{
Red => (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]),
Green => (-moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]),
Blue => (-moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]),
_ => 0
};
/// <summary>
/// Calculates statistical variance for a given cube.
/// </summary>
private float CalculateVariance(WuColorCubeOld cube)
{
float volumeRed = Volume(cube, _momentsRed);
float volumeGreen = Volume(cube, _momentsGreen);
float volumeBlue = Volume(cube, _momentsBlue);
var volumeMoment = VolumeFloat(cube, _moments);
float volumeWeight = Volume(cube, _weights);
var distance = volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue;
return volumeMoment - distance / volumeWeight;
}
/// <summary>
/// Finds the optimal (maximal) position for the cut.
/// </summary>
private float Maximize(WuColorCubeOld cube, int direction, int first, int last, int[] cut, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight)
{
var bottomRed = Bottom(cube, direction, _momentsRed);
var bottomGreen = Bottom(cube, direction, _momentsGreen);
var bottomBlue = Bottom(cube, direction, _momentsBlue);
var bottomWeight = Bottom(cube, direction, _weights);
var result = 0.0f;
cut[0] = -1;
for (var position = first; position < last; ++position)
{
// determines the cube cut at a certain position
var halfRed = bottomRed + Top(cube, direction, position, _momentsRed);
var halfGreen = bottomGreen + Top(cube, direction, position, _momentsGreen);
var halfBlue = bottomBlue + Top(cube, direction, position, _momentsBlue);
var halfWeight = bottomWeight + Top(cube, direction, position, _weights);
// the cube cannot be cut at bottom (this would lead to empty cube)
if (halfWeight != 0)
{
var halfDistance = (float) halfRed * halfRed + (float) halfGreen * halfGreen + (float) halfBlue * halfBlue;
var temp = halfDistance / halfWeight;
halfRed = wholeRed - halfRed;
halfGreen = wholeGreen - halfGreen;
halfBlue = wholeBlue - halfBlue;
halfWeight = wholeWeight - halfWeight;
if (halfWeight != 0)
{
halfDistance = (float) halfRed * halfRed + (float) halfGreen * halfGreen + (float) halfBlue * halfBlue;
temp += halfDistance / halfWeight;
if (temp > result)
{
result = temp;
cut[0] = position;
}
}
}
}
return result;
}
/// <summary>
/// Cuts a cube with another one.
/// </summary>
private bool Cut(WuColorCubeOld first, WuColorCubeOld second)
{
int direction;
int[] cutRed = {0};
int[] cutGreen = {0};
int[] cutBlue = {0};
var wholeRed = Volume(first, _momentsRed);
var wholeGreen = Volume(first, _momentsGreen);
var wholeBlue = Volume(first, _momentsBlue);
var wholeWeight = Volume(first, _weights);
var maxRed = Maximize(first, Red, first.RedMinimum + 1, first.RedMaximum, cutRed, wholeRed, wholeGreen, wholeBlue, wholeWeight);
var maxGreen = Maximize(first, Green, first.GreenMinimum + 1, first.GreenMaximum, cutGreen, wholeRed, wholeGreen, wholeBlue, wholeWeight);
var maxBlue = Maximize(first, Blue, first.BlueMinimum + 1, first.BlueMaximum, cutBlue, wholeRed, wholeGreen, wholeBlue, wholeWeight);
if (maxRed >= maxGreen && maxRed >= maxBlue)
{
direction = Red;
// cannot split empty cube
if (cutRed[0] < 0)
{
return false;
}
}
else
{
if (maxGreen >= maxRed && maxGreen >= maxBlue)
{
direction = Green;
}
else
{
direction = Blue;
}
}
second.RedMaximum = first.RedMaximum;
second.GreenMaximum = first.GreenMaximum;
second.BlueMaximum = first.BlueMaximum;
// cuts in a certain direction
switch (direction)
{
case Red:
second.RedMinimum = first.RedMaximum = cutRed[0];
second.GreenMinimum = first.GreenMinimum;
second.BlueMinimum = first.BlueMinimum;
break;
case Green:
second.GreenMinimum = first.GreenMaximum = cutGreen[0];
second.RedMinimum = first.RedMinimum;
second.BlueMinimum = first.BlueMinimum;
break;
case Blue:
second.BlueMinimum = first.BlueMaximum = cutBlue[0];
second.RedMinimum = first.RedMinimum;
second.GreenMinimum = first.GreenMinimum;
break;
}
// determines the volumes after cut
first.Volume = (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
second.Volume = (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);
// the cut was successfull
return true;
}
/// <summary>
/// Marks all the tags with a given label.
/// </summary>
private static void Mark(WuColorCubeOld cube, int label, byte[] tag)
{
for (var redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex)
{
for (var greenIndex = cube.GreenMinimum + 1; greenIndex <= cube.GreenMaximum; ++greenIndex)
{
for (var blueIndex = cube.BlueMinimum + 1; blueIndex <= cube.BlueMaximum; ++blueIndex)
{
tag[(redIndex << 10) + (redIndex << 6) + redIndex + (greenIndex << 5) + greenIndex + blueIndex] = (byte) label;
}
}
}
}
}
}

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -49,7 +49,7 @@ namespace Greenshot.Gfx.Structs
/// Equal /// Equal
/// </summary> /// </summary>
public static bool operator ==(Bgr32 left, Bgr32 right) => Equals(left, right); public static bool operator ==(Bgr32 left, Bgr32 right) => Equals(left, right);
/// <summary> /// <summary>
/// Not Equal /// Not Equal
/// </summary> /// </summary>

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -26,6 +26,34 @@ namespace Greenshot.Gfx.Structs
/// </summary> /// </summary>
public static class PixelExtensions public static class PixelExtensions
{ {
/// <inheritdoc />
public static Bgr24 BackgroundBlendColor { get; set; }
/// <summary>
/// Make a Brga32 from the specified color
/// </summary>
/// <param name="color">Color</param>
/// <returns>Bgr32</returns>
public static Bgra32 BlendedColor(this Bgra32 color)
{
if (color.A == 255)
{
return color;
}
// As the request is to get without alpha, we blend.
var rem = 255 - color.A;
var red = (byte)((color.R * color.A + BackgroundBlendColor.R * rem) / 255);
var green = (byte)((color.G * color.A + BackgroundBlendColor.G * rem) / 255);
var blue = (byte)((color.B * color.A + BackgroundBlendColor.B * rem) / 255);
return new Bgra32
{
A = 255,
R = red,
G = green,
B = blue,
};
}
/// <summary> /// <summary>
/// Make a Brga32 from the specified color /// Make a Brga32 from the specified color
/// </summary> /// </summary>
@ -55,6 +83,27 @@ namespace Greenshot.Gfx.Structs
G = color.G, G = color.G,
B = color.B, B = color.B,
}; };
} }
/// <summary>
/// Make a color from the specified Brg32
/// </summary>
/// <param name="color">Bgr32</param>
/// <returns>Color</returns>
public static Color ToColor(this Bgr32 color)
{
return Color.FromArgb(color.R, color.G, color.B);
}
/// <summary>
/// Make a color from the specified uint
/// </summary>
/// <param name="color">uint</param>
/// <returns>Color</returns>
public static Color ToColor(this uint color)
{
return Color.FromArgb((int)color);
}
} }
} }

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -138,11 +138,12 @@ namespace Greenshot.Gfx
Bgr24 _ => PixelFormat.Format24bppRgb, Bgr24 _ => PixelFormat.Format24bppRgb,
Bgra32 _ => PixelFormat.Format32bppArgb, Bgra32 _ => PixelFormat.Format32bppArgb,
Bgr32 _ => PixelFormat.Format32bppRgb, Bgr32 _ => PixelFormat.Format32bppRgb,
byte _ => PixelFormat.Format8bppIndexed,
_ => throw new NotSupportedException("Unknown pixel format") _ => throw new NotSupportedException("Unknown pixel format")
}; };
} }
} }
/// <summary> /// <summary>
/// Returns the pixel format for this Unmanaged bitmap /// Returns the pixel format for this Unmanaged bitmap
/// </summary> /// </summary>
@ -156,6 +157,7 @@ namespace Greenshot.Gfx
Bgr24 _ => PixelFormats.Bgr24, Bgr24 _ => PixelFormats.Bgr24,
Bgra32 _ => PixelFormats.Bgra32, Bgra32 _ => PixelFormats.Bgra32,
Bgr32 _ => PixelFormats.Bgr32, Bgr32 _ => PixelFormats.Bgr32,
byte _ => PixelFormats.Indexed8,
_ => throw new NotSupportedException("Unknown pixel format") _ => throw new NotSupportedException("Unknown pixel format")
}; };
} }
@ -171,7 +173,7 @@ namespace Greenshot.Gfx
/// Convert this to a real bitmap /// Convert this to a real bitmap
/// </summary> /// </summary>
/// <returns>Bitmap</returns> /// <returns>Bitmap</returns>
public Bitmap NativeBitmap public virtual Bitmap NativeBitmap
{ {
get get
{ {
@ -182,7 +184,7 @@ namespace Greenshot.Gfx
return _nativeBitmap; return _nativeBitmap;
} }
} }
/// <summary> /// <summary>
/// Convert this to a real bitmap /// Convert this to a real bitmap
/// </summary> /// </summary>

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -53,10 +53,31 @@ namespace Greenshot.PerformanceTests
[Benchmark] [Benchmark]
[Arguments(PixelFormat.Format24bppRgb)] //[Arguments(PixelFormat.Format24bppRgb)]
[Arguments(PixelFormat.Format32bppRgb)] [Arguments(PixelFormat.Format32bppRgb, 256)]
[Arguments(PixelFormat.Format32bppArgb)] [Arguments(PixelFormat.Format32bppRgb, 128)]
public void WuQuantizer(PixelFormat pixelFormat) //[Arguments(PixelFormat.Format32bppArgb)]
public void WuQuantizer(PixelFormat pixelFormat, int maxColors)
{
var bitmap = new UnmanagedBitmap<Bgr32>(400, 400);
bitmap.Span.Fill(Color.White.FromColor());
using (var graphics = Graphics.FromImage(bitmap.NativeBitmap))
{
using var pen = new SolidBrush(Color.Blue);
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
}
var quantizer = new WuQuantizer<Bgra32>(bitmap);
using var quantizedImage = quantizer.GetQuantizedImage(maxColors);
quantizedImage.NativeBitmap.Save(@"quantized.png", ImageFormat.Png);
}
[Benchmark]
//[Arguments(PixelFormat.Format24bppRgb)]
[Arguments(PixelFormat.Format32bppRgb, 256)]
[Arguments(PixelFormat.Format32bppRgb, 128)]
//[Arguments(PixelFormat.Format32bppArgb)]
public void WuQuantizerOld(PixelFormat pixelFormat, int maxColors)
{ {
using var bitmap = BitmapFactory.CreateEmpty(400, 400, pixelFormat, Color.White); using var bitmap = BitmapFactory.CreateEmpty(400, 400, pixelFormat, Color.White);
using (var graphics = Graphics.FromImage(bitmap.NativeBitmap)) using (var graphics = Graphics.FromImage(bitmap.NativeBitmap))
@ -65,12 +86,12 @@ namespace Greenshot.PerformanceTests
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340)); graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
} }
var quantizer = new WuQuantizer(bitmap); var quantizer = new WuQuantizerOld(bitmap);
using var quantizedImage = quantizer.GetQuantizedImage(); using var quantizedImage = quantizer.GetQuantizedImage(maxColors);
quantizedImage.NativeBitmap.Save(@"quantized.png", ImageFormat.Png); quantizedImage.NativeBitmap.Save(@"quantized.png", ImageFormat.Png);
} }
[Benchmark] //[Benchmark]
[Arguments(PixelFormat.Format24bppRgb)] [Arguments(PixelFormat.Format24bppRgb)]
[Arguments(PixelFormat.Format32bppRgb)] [Arguments(PixelFormat.Format32bppRgb)]
[Arguments(PixelFormat.Format32bppArgb)] [Arguments(PixelFormat.Format32bppArgb)]
@ -86,7 +107,7 @@ namespace Greenshot.PerformanceTests
bitmap.ApplyBoxBlur(10); bitmap.ApplyBoxBlur(10);
} }
[Benchmark] //[Benchmark]
public void Blur_UnmanagedBitmap() public void Blur_UnmanagedBitmap()
{ {
using var unmanagedBitmap = new UnmanagedBitmap<Bgr32>(400, 400); using var unmanagedBitmap = new UnmanagedBitmap<Bgr32>(400, 400);
@ -100,7 +121,7 @@ namespace Greenshot.PerformanceTests
unmanagedBitmap.ApplyBoxBlur(10); unmanagedBitmap.ApplyBoxBlur(10);
} }
[Benchmark] //[Benchmark]
public void Blur_Old() public void Blur_Old()
{ {
using var bitmap = BitmapFactory.CreateEmpty(400, 400, PixelFormat.Format32bppRgb, Color.White); using var bitmap = BitmapFactory.CreateEmpty(400, 400, PixelFormat.Format32bppRgb, Color.White);
@ -113,37 +134,37 @@ namespace Greenshot.PerformanceTests
BoxBlurOld.ApplyOldBoxBlur(bitmap, 10); BoxBlurOld.ApplyOldBoxBlur(bitmap, 10);
} }
[Benchmark] //[Benchmark]
public void Scale2x_FastBitmap() public void Scale2x_FastBitmap()
{ {
ScaleX.Scale2X(_unmanagedTestBitmap).Dispose(); ScaleX.Scale2X(_unmanagedTestBitmap).Dispose();
} }
[Benchmark] //[Benchmark]
public void Scale2x_Unmanaged() public void Scale2x_Unmanaged()
{ {
_unmanagedTestBitmap.Scale2X().Dispose(); _unmanagedTestBitmap.Scale2X().Dispose();
} }
[Benchmark] //[Benchmark]
public void Scale2x_Unmanaged_Reference() public void Scale2x_Unmanaged_Reference()
{ {
_unmanagedTestBitmap.Scale2XReference().Dispose(); _unmanagedTestBitmap.Scale2XReference().Dispose();
} }
[Benchmark] //[Benchmark]
public void Scale3x_FastBitmap() public void Scale3x_FastBitmap()
{ {
ScaleX.Scale3X(_unmanagedTestBitmap).Dispose(); ScaleX.Scale3X(_unmanagedTestBitmap).Dispose();
} }
[Benchmark] //[Benchmark]
public void Scale3x_Unmanaged() public void Scale3x_Unmanaged()
{ {
_unmanagedTestBitmap.Scale3X().Dispose(); _unmanagedTestBitmap.Scale3X().Dispose();
} }
[Benchmark] //[Benchmark]
public void Scale3x_Unmanaged_Reference() public void Scale3x_Unmanaged_Reference()
{ {
_unmanagedTestBitmap.Scale3XReference().Dispose(); _unmanagedTestBitmap.Scale3XReference().Dispose();

View file

@ -16,10 +16,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" /> <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="CommandLineParser" Version="2.7.82" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.2" /> <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageReference Include="SharpAvi" Version="2.1.2" /> <PackageReference Include="SharpAvi" Version="2.1.2" />
<PackageReference Include="System.Memory" Version="4.5.3" /> <PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -28,15 +28,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" /> <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="CommandLineParser" Version="2.7.82" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Dapplo.Log.XUnit" Version="1.3.26" /> <PackageReference Include="Dapplo.Log.XUnit" Version="1.3.26" />
<PackageReference Include="Dapplo.Windows" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows" Version="0.11.17" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" /> <PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" /> <PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.core" Version="2.4.1" /> <PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
@ -51,6 +51,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="TestFiles\paiS7wOGKN0XA1wIaq8qHNoWQqq64wnFu3svA9Ux.jpeg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestFiles\scroll-result.png"> <None Update="TestFiles\scroll-result.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View file

@ -1,26 +1,29 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using Greenshot.Gfx; using Greenshot.Gfx;
using Greenshot.Gfx.Formats;
using Greenshot.Gfx.Quantizer; using Greenshot.Gfx.Quantizer;
using Greenshot.Gfx.Structs;
using Xunit; using Xunit;
namespace Greenshot.Tests namespace Greenshot.Tests
@ -30,19 +33,58 @@ namespace Greenshot.Tests
/// </summary> /// </summary>
public class QuantizeTests public class QuantizeTests
{ {
public QuantizeTests()
{
BitmapHelper.RegisterFormatReader<GenericGdiFormatReader>();
}
[Fact]
public void Test_WuQuantizer_128()
{
var bitmap = BitmapHelper.LoadBitmap(@"TestFiles\paiS7wOGKN0XA1wIaq8qHNoWQqq64wnFu3svA9Ux.jpeg");
var quantizerOld = new WuQuantizerOld(bitmap);
using var quantizedImageOld = quantizerOld.GetQuantizedImage(128);
quantizedImageOld.NativeBitmap.Save(@"quantized1.png", ImageFormat.Png);
var quantizer = new WuQuantizer<Bgra32>(bitmap);
using var quantizedImage = quantizer.GetQuantizedImage(128);
quantizedImage.NativeBitmap.Save(@"quantized2.png", ImageFormat.Png);
Assert.Equal(quantizerOld.GetColorCount(), quantizer.GetColorCount());
FileAssert.AreEqual("quantized1.png", "quantized2.png");
File.Delete("quantized1.png");
File.Delete("quantized2.png");
}
[Fact] [Fact]
public void Test_WuQuantizer() public void Test_WuQuantizer()
{ {
using var bitmap = BitmapFactory.CreateEmpty(400, 400, PixelFormat.Format24bppRgb, Color.White); using var bitmap = BitmapFactory.CreateEmpty(400, 400, PixelFormat.Format32bppArgb, Color.White);
using (var graphics = Graphics.FromImage(bitmap.NativeBitmap)) using (var graphics = Graphics.FromImage(bitmap.NativeBitmap))
{ {
using var pen = new SolidBrush(Color.Blue); using var pen = new SolidBrush(Color.Blue);
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340)); graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
} }
var quantizer = new WuQuantizer(bitmap); var quantizerOld = new WuQuantizerOld(bitmap);
using var quantizedImageOld = quantizerOld.GetQuantizedImage();
quantizedImageOld.NativeBitmap.Save(@"quantized1.png", ImageFormat.Png);
var bitmap2 = new UnmanagedBitmap<Bgr32>(400, 400);
bitmap2.Span.Fill(Color.White.FromColor());
using (var graphics = Graphics.FromImage(bitmap2.NativeBitmap))
{
using var pen = new SolidBrush(Color.Blue);
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
}
var quantizer = new WuQuantizer<Bgra32>(bitmap2);
using var quantizedImage = quantizer.GetQuantizedImage(); using var quantizedImage = quantizer.GetQuantizedImage();
quantizedImage.NativeBitmap.Save(@"quantized.png", ImageFormat.Png); quantizedImage.NativeBitmap.Save(@"quantized2.png", ImageFormat.Png);
FileAssert.AreEqual("quantized1.png", "quantized2.png");
} }
} }
} }

View file

@ -1,19 +1,19 @@
// Greenshot - a free and open source screenshot tool // Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
// //
// For more information see: http://getgreenshot.org/ // For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or // the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
@ -37,7 +37,7 @@ namespace Greenshot.Tests
{ {
LogSettings.RegisterDefaultLogger<XUnitLogger>(LogLevels.Verbose, testOutputHelper); LogSettings.RegisterDefaultLogger<XUnitLogger>(LogLevels.Verbose, testOutputHelper);
} }
[Fact] [Fact]
public void Test_Scale2X_UnmanagedBitmap() public void Test_Scale2X_UnmanagedBitmap()
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -34,16 +34,16 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.7.82" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Dapplo.CaliburnMicro.Dapp" Version="2.1.10" /> <PackageReference Include="Dapplo.CaliburnMicro.Dapp" Version="2.1.10" />
<PackageReference Include="Dapplo.Log.LogFile" Version="1.3.26" /> <PackageReference Include="Dapplo.Log.LogFile" Version="1.3.26" />
<PackageReference Include="Dapplo.Log.Loggers" Version="1.3.26" /> <PackageReference Include="Dapplo.Log.Loggers" Version="1.3.26" />
<PackageReference Include="Dapplo.Windows.Multimedia" Version="0.11.15" /> <PackageReference Include="Dapplo.Windows.Multimedia" Version="0.11.17" />
<PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" /> <PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" />
<PackageReference Include="sqlite-net-pcl" Version="1.6.292" /> <PackageReference Include="sqlite-net-pcl" Version="1.7.335" />
<PackageReference Include="Svg" Version="3.0.102" /> <PackageReference Include="Svg" Version="3.1.1" />
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="3.0.0" /> <PackageReference Include="Microsoft.AppCenter.Analytics" Version="3.3.0" />
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="3.0.0" /> <PackageReference Include="Microsoft.AppCenter.Crashes" Version="3.3.0" />
<TrimmerRootAssembly Include="System.Diagnostics.Debug" /> <TrimmerRootAssembly Include="System.Diagnostics.Debug" />
</ItemGroup> </ItemGroup>

View file

@ -1,6 +1,7 @@
{ {
"sdk": { "sdk": {
"version": "3.1.100", "version": "3.1.100",
"rollForward": "latestPatch" "rollForward": "latestMajor",
"allowPrerelease": true
} }
} }