diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 0f812b041..49fc820c5 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -58,7 +58,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/src/Greenshot.Addon.Confluence/Greenshot.Addon.Confluence.csproj b/src/Greenshot.Addon.Confluence/Greenshot.Addon.Confluence.csproj
index 03dace116..2ac4fd889 100644
--- a/src/Greenshot.Addon.Confluence/Greenshot.Addon.Confluence.csproj
+++ b/src/Greenshot.Addon.Confluence/Greenshot.Addon.Confluence.csproj
@@ -24,6 +24,6 @@
-
+
diff --git a/src/Greenshot.Addon.ExternalCommand/Greenshot.Addon.ExternalCommand.csproj b/src/Greenshot.Addon.ExternalCommand/Greenshot.Addon.ExternalCommand.csproj
index 2d3b55970..179ba53b6 100644
--- a/src/Greenshot.Addon.ExternalCommand/Greenshot.Addon.ExternalCommand.csproj
+++ b/src/Greenshot.Addon.ExternalCommand/Greenshot.Addon.ExternalCommand.csproj
@@ -19,6 +19,6 @@
-
+
diff --git a/src/Greenshot.Addon.InternetExplorer/Greenshot.Addon.InternetExplorer.csproj b/src/Greenshot.Addon.InternetExplorer/Greenshot.Addon.InternetExplorer.csproj
index 62ececceb..ce9c65c91 100644
--- a/src/Greenshot.Addon.InternetExplorer/Greenshot.Addon.InternetExplorer.csproj
+++ b/src/Greenshot.Addon.InternetExplorer/Greenshot.Addon.InternetExplorer.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/Greenshot.Addon.Jira/Greenshot.Addon.Jira.csproj b/src/Greenshot.Addon.Jira/Greenshot.Addon.Jira.csproj
index c5c01460d..d429e9df2 100644
--- a/src/Greenshot.Addon.Jira/Greenshot.Addon.Jira.csproj
+++ b/src/Greenshot.Addon.Jira/Greenshot.Addon.Jira.csproj
@@ -20,7 +20,7 @@
-
-
+
+
diff --git a/src/Greenshot.Addons/Core/ImageOutput.cs b/src/Greenshot.Addons/Core/ImageOutput.cs
index 2fb03ac85..49e3ce60f 100644
--- a/src/Greenshot.Addons/Core/ImageOutput.cs
+++ b/src/Greenshot.Addons/Core/ImageOutput.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -38,6 +38,7 @@ using Greenshot.Addons.Interfaces.Plugin;
using Greenshot.Core.Enums;
using Greenshot.Gfx;
using Greenshot.Gfx.Quantizer;
+using Greenshot.Gfx.Structs;
using Encoder = System.Drawing.Imaging.Encoder;
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
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)
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)
{
return disposeImage;
@@ -708,7 +709,7 @@ namespace Greenshot.Addons.Core
var isAlpha = Image.IsAlphaPixelFormat(bitmapToSave.PixelFormat);
if (outputSettings.ReduceColors || !isAlpha && CoreConfiguration.OutputFileAutoReduceColors)
{
- using var quantizer = new WuQuantizer(bitmapToSave);
+ using var quantizer = new WuQuantizer(bitmapToSave);
var colorCount = quantizer.GetColorCount();
Log.Info().WriteLine("Image with format {0} has {1} colors", bitmapToSave.PixelFormat, colorCount);
if (!outputSettings.ReduceColors && colorCount >= 256)
diff --git a/src/Greenshot.Addons/Extensions/ClipboardBitmapExtensions.cs b/src/Greenshot.Addons/Extensions/ClipboardBitmapExtensions.cs
index 183e60535..269dff9a4 100644
--- a/src/Greenshot.Addons/Extensions/ClipboardBitmapExtensions.cs
+++ b/src/Greenshot.Addons/Extensions/ClipboardBitmapExtensions.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -39,7 +39,7 @@ namespace Greenshot.Addons.Extensions
public static class ClipboardBitmapExtensions
{
private static readonly LogSource Log = new LogSource();
- private static readonly string[] SupportedBitmapFormats =
+ private static readonly string[] SupportedBitmapFormats =
{
"PNG",
"PNG+Office Art",
@@ -73,7 +73,7 @@ namespace Greenshot.Addons.Extensions
return true;
}
- return clipboardAccessToken.GetFilenames()
+ return clipboardAccessToken.GetFileNames()
.Select(filename => Path.GetExtension(filename).ToLowerInvariant())
.Intersect(SupportedExtensions)
.Any();
@@ -236,7 +236,7 @@ namespace Greenshot.Addons.Extensions
/// byte[]
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 bmpData = bitmap.NativeBitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
diff --git a/src/Greenshot.Addons/Greenshot.Addons.csproj b/src/Greenshot.Addons/Greenshot.Addons.csproj
index a0e4a67ed..b4d7933c9 100644
--- a/src/Greenshot.Addons/Greenshot.Addons.csproj
+++ b/src/Greenshot.Addons/Greenshot.Addons.csproj
@@ -20,15 +20,15 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
diff --git a/src/Greenshot.Core/Greenshot.Core.csproj b/src/Greenshot.Core/Greenshot.Core.csproj
index 283c2c896..9a50f2d80 100644
--- a/src/Greenshot.Core/Greenshot.Core.csproj
+++ b/src/Greenshot.Core/Greenshot.Core.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/src/Greenshot.Gfx/BitmapHelper.cs b/src/Greenshot.Gfx/BitmapHelper.cs
index c712f5dd8..d6dc6d110 100644
--- a/src/Greenshot.Gfx/BitmapHelper.cs
+++ b/src/Greenshot.Gfx/BitmapHelper.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -46,7 +46,7 @@ namespace Greenshot.Gfx
private static readonly LogSource Log = new LogSource();
///
- /// 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.
///
public static IDictionary StreamConverters { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
@@ -444,7 +444,7 @@ namespace Greenshot.Gfx
Matrix22 = 0,
Matrix33 = darkness
};
-
+
var shadowRectangle = new NativeRect(new NativePoint(shadowSize, shadowSize), sourceBitmap.Size);
ApplyColorMatrix(sourceBitmap, NativeRect.Empty, returnImage, shadowRectangle, maskMatrix);
@@ -755,7 +755,6 @@ namespace Greenshot.Gfx
colors += lineColorCount;
}
});
-
return colors;
}
@@ -815,7 +814,7 @@ namespace Greenshot.Gfx
return original;
}
-
+
if (width == original.Width * 2)
{
return original.Scale2X();
diff --git a/src/Greenshot.Gfx/ChunkyBitmap.cs b/src/Greenshot.Gfx/ChunkyBitmap.cs
new file mode 100644
index 000000000..409f01b15
--- /dev/null
+++ b/src/Greenshot.Gfx/ChunkyBitmap.cs
@@ -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 .
+
+using System;
+
+namespace Greenshot.Gfx
+{
+ ///
+ /// A bitmap wrapper with memory from Marshal.AllocHGlobal
+ ///
+ /// struct for the pixel information
+ public class ChunkyBitmap : UnmanagedBitmap
+ {
+
+ ///
+ /// The constructor for the UnmanagedBitmap
+ ///
+ /// int
+ /// int
+ /// float
+ /// float
+ public ChunkyBitmap(int width, int height, float horizontalPixelsPerInch = 0.96f, float verticalPixelsPerInch = 0.96f) : base(width, height, horizontalPixelsPerInch, verticalPixelsPerInch)
+ {
+ }
+
+ ///
+ /// The constructor for the UnmanagedBitmap with already initialized bits
+ ///
+ /// IntPtr to the bits, this will not be freed
+ /// int
+ /// int
+ /// float
+ /// float
+ public ChunkyBitmap(IntPtr bits, int width, int height, float horizontalPixelsPerInch = 0.96f, float verticalPixelsPerInch = 0.96f) : base(bits, width, height, horizontalPixelsPerInch, verticalPixelsPerInch)
+ {
+ }
+ }
+}
diff --git a/src/Greenshot.Gfx/Effects/ReduceColorsEffect.cs b/src/Greenshot.Gfx/Effects/ReduceColorsEffect.cs
index aa9092ba4..0f8f99957 100644
--- a/src/Greenshot.Gfx/Effects/ReduceColorsEffect.cs
+++ b/src/Greenshot.Gfx/Effects/ReduceColorsEffect.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -21,6 +21,7 @@ using System;
using System.Drawing.Drawing2D;
using Dapplo.Log;
using Greenshot.Gfx.Quantizer;
+using Greenshot.Gfx.Structs;
namespace Greenshot.Gfx.Effects
{
@@ -39,7 +40,7 @@ namespace Greenshot.Gfx.Effects
///
public IBitmapWithNativeSupport Apply(IBitmapWithNativeSupport sourceBitmap, Matrix matrix)
{
- using (var quantizer = new WuQuantizer(sourceBitmap))
+ using (var quantizer = new WuQuantizer(sourceBitmap))
{
var colorCount = quantizer.GetColorCount();
if (colorCount <= Colors)
diff --git a/src/Greenshot.Gfx/Greenshot.Gfx.csproj b/src/Greenshot.Gfx/Greenshot.Gfx.csproj
index d8d407039..d8f96e209 100644
--- a/src/Greenshot.Gfx/Greenshot.Gfx.csproj
+++ b/src/Greenshot.Gfx/Greenshot.Gfx.csproj
@@ -8,8 +8,8 @@
-
-
-
+
+
+
diff --git a/src/Greenshot.Gfx/Quantizer/WuColorCube.cs b/src/Greenshot.Gfx/Quantizer/WuColorCube.cs
index 4c59364e8..076552343 100644
--- a/src/Greenshot.Gfx/Quantizer/WuColorCube.cs
+++ b/src/Greenshot.Gfx/Quantizer/WuColorCube.cs
@@ -1,25 +1,25 @@
// 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 .
namespace Greenshot.Gfx.Quantizer
{
- internal class WuColorCube
+ internal struct WuColorCube
{
///
/// Gets or sets the red minimum.
diff --git a/src/Greenshot.Gfx/Quantizer/WuColorCubeOld.cs b/src/Greenshot.Gfx/Quantizer/WuColorCubeOld.cs
new file mode 100644
index 000000000..6e004269f
--- /dev/null
+++ b/src/Greenshot.Gfx/Quantizer/WuColorCubeOld.cs
@@ -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 .
+
+namespace Greenshot.Gfx.Quantizer
+{
+ internal class WuColorCubeOld
+ {
+ ///
+ /// Gets or sets the red minimum.
+ ///
+ /// The red minimum.
+ public int RedMinimum { get; set; }
+
+ ///
+ /// Gets or sets the red maximum.
+ ///
+ /// The red maximum.
+ public int RedMaximum { get; set; }
+
+ ///
+ /// Gets or sets the green minimum.
+ ///
+ /// The green minimum.
+ public int GreenMinimum { get; set; }
+
+ ///
+ /// Gets or sets the green maximum.
+ ///
+ /// The green maximum.
+ public int GreenMaximum { get; set; }
+
+ ///
+ /// Gets or sets the blue minimum.
+ ///
+ /// The blue minimum.
+ public int BlueMinimum { get; set; }
+
+ ///
+ /// Gets or sets the blue maximum.
+ ///
+ /// The blue maximum.
+ public int BlueMaximum { get; set; }
+
+ ///
+ /// Gets or sets the cube volume.
+ ///
+ /// The volume.
+ public int Volume { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Gfx/Quantizer/WuQuantizer.cs b/src/Greenshot.Gfx/Quantizer/WuQuantizer.cs
index 3324dc654..e8188fb48 100644
--- a/src/Greenshot.Gfx/Quantizer/WuQuantizer.cs
+++ b/src/Greenshot.Gfx/Quantizer/WuQuantizer.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -21,16 +21,16 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
-using System.Drawing.Imaging;
-using Dapplo.Log;
-using Greenshot.Gfx.FastBitmap;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Greenshot.Gfx.Structs;
namespace Greenshot.Gfx.Quantizer
{
///
/// Implementation of the WuQuantizer algorithm
///
- public class WuQuantizer : IDisposable
+ public class WuQuantizer : IDisposable where TPixel : unmanaged
{
private const int Maxcolor = 512;
private const int Red = 2;
@@ -39,7 +39,6 @@ namespace Greenshot.Gfx.Quantizer
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;
@@ -49,23 +48,25 @@ namespace Greenshot.Gfx.Quantizer
private readonly long[,,] _momentsBlue;
private readonly long[,,] _momentsGreen;
private readonly long[,,] _momentsRed;
- private readonly IBitmapWithNativeSupport _sourceBitmap;
+ private readonly UnmanagedBitmap _sourceBitmap;
private readonly long[,,] _weights;
- private int[] _blues;
- private int[] _greens;
-
- private int[] _reds;
- private IBitmapWithNativeSupport _resultBitmap;
- private int[] _sums;
-
- private byte[] _tag;
+ private ChunkyBitmap _resultBitmap;
///
/// The constructor for the WuQauntizer
///
- ///
- public WuQuantizer(IBitmapWithNativeSupport sourceBitmap)
+ /// IBitmapWithNativeSupport
+ public WuQuantizer(IBitmapWithNativeSupport sourceBitmap) : this(sourceBitmap as UnmanagedBitmap)
+ {
+
+ }
+
+ ///
+ /// The constructor for the WuQauntizer
+ ///
+ /// UnmanagedBitmap of TPixel
+ public WuQuantizer(UnmanagedBitmap sourceBitmap)
{
_sourceBitmap = sourceBitmap;
// Make sure the color count variables are reset
@@ -75,12 +76,6 @@ namespace Greenshot.Gfx.Quantizer
// creates all the cubes
_cubes = new WuColorCube[Maxcolor];
- // initializes all the cubes
- for (var cubeIndex = 0; cubeIndex < Maxcolor; cubeIndex++)
- {
- _cubes[cubeIndex] = new WuColorCube();
- }
-
// resets the reference minimums
_cubes[0].RedMinimum = 0;
_cubes[0].GreenMinimum = 0;
@@ -97,32 +92,23 @@ namespace Greenshot.Gfx.Quantizer
_momentsBlue = new long[Sidesize, Sidesize, Sidesize];
_moments = new float[Sidesize, Sidesize, Sidesize];
- var table = new int[256];
-
- for (var tableIndex = 0; tableIndex < 256; ++tableIndex)
+ Span table = stackalloc 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++)
+ _resultBitmap = new ChunkyBitmap(sourceBitmap.Width, sourceBitmap.Height);
+ for (var y = 0; y < sourceBitmap.Height; y++)
{
- for (var x = 0; x < sourceFastBitmap.Width; x++)
+ var srcPixelSpan = MemoryMarshal.Cast(_sourceBitmap[y]);
+ var destPixelSpan = _resultBitmap[y];
+ for (var x = 0; x < sourceBitmap.Width; x++)
{
- Color color;
- if (!(sourceFastBitmap is IFastBitmapWithBlend sourceFastBitmapWithBlend))
- {
- color = sourceFastBitmap.GetColorAt(x, y);
- }
- else
- {
- color = sourceFastBitmapWithBlend.GetBlendedColorAt(x, y);
- }
+ var color = srcPixelSpan[x];
+
// 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
if (!bitArray.Get(index))
{
@@ -143,10 +129,9 @@ namespace Greenshot.Gfx.Quantizer
// Store the initial "match"
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();
}
///
@@ -155,7 +140,7 @@ namespace Greenshot.Gfx.Quantizer
Dispose(true);
}
-
+
///
/// Dispose implementation
///
@@ -191,39 +176,30 @@ namespace Greenshot.Gfx.Quantizer
/// Bitmap
public IBitmapWithNativeSupport SimpleReindex()
{
- var colors = new List();
- var lookup = new Dictionary();
- 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++)
+ Span colors = stackalloc uint[256];
+ var lookup = new Dictionary();
+
+ byte colorCount = 0;
+ for (var y = 0; y < _sourceBitmap.Height; y++)
+ {
+ var srcSpan = MemoryMarshal.Cast(_sourceBitmap[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;
- 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);
+ index = lookup[color];
}
+ 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;
for (var paletteIndex = 0; paletteIndex < 256; paletteIndex++)
{
- if (paletteIndex < _colorCount)
+ if (paletteIndex < colorCount)
{
- entries[paletteIndex] = colors[paletteIndex];
+ entries[paletteIndex] = colors[paletteIndex].ToColor();
}
else
{
@@ -261,12 +237,10 @@ namespace Greenshot.Gfx.Quantizer
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];
@@ -274,7 +248,7 @@ namespace Greenshot.Gfx.Quantizer
for (var cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex)
{
// 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[cubeIndex] = _cubes[cubeIndex].Volume > 1 ? CalculateVariance(_cubes[cubeIndex]) : 0.0f;
@@ -307,16 +281,16 @@ namespace Greenshot.Gfx.Quantizer
break;
}
- var lookupRed = new int[Maxcolor];
- var lookupGreen = new int[Maxcolor];
- var lookupBlue = new int[Maxcolor];
+ Span lookupRed = stackalloc int[Maxcolor];
+ Span lookupGreen = stackalloc int[Maxcolor];
+ Span lookupBlue = stackalloc int[Maxcolor];
- _tag = new byte[Maxvolume];
+ Span tag = stackalloc byte[Maxvolume];
// pre-calculates lookup tables
for (var k = 0; k < allowedColorCount; ++k)
{
- Mark(_cubes[k], k, _tag);
+ Mark(_cubes[k], k, tag);
var weight = Volume(_cubes[k], _weights);
@@ -334,92 +308,78 @@ namespace Greenshot.Gfx.Quantizer
}
}
- _reds = new int[allowedColorCount + 1];
- _greens = new int[allowedColorCount + 1];
- _blues = new int[allowedColorCount + 1];
- _sums = new int[allowedColorCount + 1];
+ Span reds = stackalloc int[allowedColorCount + 1];
+ Span greens = stackalloc int[allowedColorCount + 1];
+ Span blues = stackalloc int[allowedColorCount + 1];
+ Span sums = stackalloc int[allowedColorCount + 1];
- Log.Info().WriteLine("Starting bitmap reconstruction...");
- using (var dest = FastBitmapFactory.Create(_resultBitmap) as FastChunkyBitmap)
+ var lookup = new Dictionary();
+ for (var y = 0; y < _sourceBitmap.Height; y++)
{
- using var src = FastBitmapFactory.Create(_sourceBitmap);
- var lookup = new Dictionary();
- for (var y = 0; y < src.Height; y++)
+ var destSpan = _resultBitmap[y];
+ var srcSpan = MemoryMarshal.Cast(_sourceBitmap[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 (src is IFastBitmapWithBlend srcBlend)
- {
- // WithoutAlpha, this makes it possible to ignore the alpha
- color = srcBlend.GetBlendedColorAt(x, y);
- }
- else
- {
- color = src.GetColorAt(x, y);
- }
+ // If not we need to find the best match
- // Check if we already matched the color
- byte bestMatch;
- if (!lookup.ContainsKey(color))
+ // First get initial match
+ bestMatch = destSpan[x];
+ 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
- bestMatch = dest.GetColorIndexAt(x, y);
- bestMatch = _tag[bestMatch];
+ var distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue;
- var bestDistance = 100000000;
- for (var lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++)
+ if (distance < bestDistance)
{
- 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;
- }
+ 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);
+ 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]++;
+
+ destSpan[x] = bestMatch;
}
}
-
// generates palette
var imagePalette = _resultBitmap.NativeBitmap.Palette;
var entries = imagePalette.Entries;
for (var paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++)
{
- if (_sums[paletteIndex] > 0)
+ if (sums[paletteIndex] > 0)
{
- _reds[paletteIndex] /= _sums[paletteIndex];
- _greens[paletteIndex] /= _sums[paletteIndex];
- _blues[paletteIndex] /= _sums[paletteIndex];
+ reds[paletteIndex] /= sums[paletteIndex];
+ greens[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;
@@ -434,11 +394,11 @@ namespace Greenshot.Gfx.Quantizer
///
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];
+ Span area = stackalloc long[Sidesize];
+ Span areaRed = stackalloc long[Sidesize];
+ Span areaGreen = stackalloc long[Sidesize];
+ Span areaBlue = stackalloc long[Sidesize];
+ var area2 = new float[Sidesize];
for (var redIndex = 1; redIndex <= Maxsideindex; ++redIndex)
{
@@ -483,10 +443,11 @@ namespace Greenshot.Gfx.Quantizer
}
}
- ///
- /// Computes the volume of the cube in a specific moment.
- ///
- private static long Volume(WuColorCube cube, long[,,] moment)
+ ///
+ /// Computes the volume of the cube in a specific moment.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static long Volume(in WuColorCube cube, long[,,] moment)
{
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
@@ -498,10 +459,11 @@ namespace Greenshot.Gfx.Quantizer
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
}
- ///
- /// Computes the volume of the cube in a specific moment. For the floating-point values.
- ///
- private static float VolumeFloat(WuColorCube cube, float[,,] moment)
+ ///
+ /// Computes the volume of the cube in a specific moment. For the floating-point values.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float VolumeFloat(in WuColorCube cube, float[,,] moment)
{
return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
@@ -513,10 +475,11 @@ namespace Greenshot.Gfx.Quantizer
moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
}
- ///
- /// Splits the cube in given position, and color direction.
- ///
- private static long Top(WuColorCube cube, int direction, int position, long[,,] moment) =>
+ ///
+ /// Splits the cube in given position, and color direction.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static long Top(in WuColorCube cube, int direction, int position, long[,,] moment) =>
direction switch
{
Red => (moment[position, cube.GreenMaximum, cube.BlueMaximum] -
@@ -537,7 +500,8 @@ namespace Greenshot.Gfx.Quantizer
///
/// Splits the cube in a given color direction at its minimum.
///
- 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
{
Red => (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
@@ -558,7 +522,7 @@ namespace Greenshot.Gfx.Quantizer
///
/// Calculates statistical variance for a given cube.
///
- private float CalculateVariance(WuColorCube cube)
+ private float CalculateVariance(in WuColorCube cube)
{
float volumeRed = Volume(cube, _momentsRed);
float volumeGreen = Volume(cube, _momentsGreen);
@@ -574,7 +538,7 @@ namespace Greenshot.Gfx.Quantizer
///
/// Finds the optimal (maximal) position for the cut.
///
- 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 bottomGreen = Bottom(cube, direction, _momentsGreen);
@@ -623,7 +587,7 @@ namespace Greenshot.Gfx.Quantizer
///
/// Cuts a cube with another one.
///
- private bool Cut(WuColorCube first, WuColorCube second)
+ private bool Cut(ref WuColorCube first, ref WuColorCube second)
{
int direction;
@@ -699,7 +663,7 @@ namespace Greenshot.Gfx.Quantizer
///
/// Marks all the tags with a given label.
///
- private static void Mark(WuColorCube cube, int label, byte[] tag)
+ private static void Mark(in WuColorCube cube, int label, Span tag)
{
for (var redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex)
{
diff --git a/src/Greenshot.Gfx/Quantizer/WuQuantizerOld.cs b/src/Greenshot.Gfx/Quantizer/WuQuantizerOld.cs
new file mode 100644
index 000000000..472d3a1c0
--- /dev/null
+++ b/src/Greenshot.Gfx/Quantizer/WuQuantizerOld.cs
@@ -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 .
+
+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
+{
+ ///
+ /// Implementation of the WuQuantizer algorithm
+ ///
+ 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;
+
+ ///
+ /// The constructor for the WuQauntizer
+ ///
+ ///
+ 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();
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+
+ ///
+ /// Dispose implementation
+ ///
+ /// bool
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing)
+ {
+ return;
+ }
+
+ if (_resultBitmap == null)
+ {
+ return;
+ }
+
+ _resultBitmap.Dispose();
+ _resultBitmap = null;
+ }
+
+ ///
+ /// Returns the number of colors
+ ///
+ /// int
+ public int GetColorCount()
+ {
+ return _colorCount;
+ }
+
+ ///
+ /// Reindex the 24/32 BPP (A)RGB image to a 8BPP
+ ///
+ /// Bitmap
+ public IBitmapWithNativeSupport SimpleReindex()
+ {
+ var colors = new List();
+ var lookup = new Dictionary();
+ 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;
+ }
+
+ ///
+ /// Get the image
+ ///
+ 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();
+ 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;
+ }
+
+ ///
+ /// Converts the histogram to a series of moments.
+ ///
+ 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];
+ }
+ }
+ }
+ }
+
+ ///
+ /// Computes the volume of the cube in a specific moment.
+ ///
+ 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];
+ }
+
+ ///
+ /// Computes the volume of the cube in a specific moment. For the floating-point values.
+ ///
+ 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];
+ }
+
+ ///
+ /// Splits the cube in given position, and color direction.
+ ///
+ 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
+ };
+
+ ///
+ /// Splits the cube in a given color direction at its minimum.
+ ///
+ 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
+ };
+
+ ///
+ /// Calculates statistical variance for a given cube.
+ ///
+ 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;
+ }
+
+ ///
+ /// Finds the optimal (maximal) position for the cut.
+ ///
+ 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;
+ }
+
+ ///
+ /// Cuts a cube with another one.
+ ///
+ 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;
+ }
+
+ ///
+ /// Marks all the tags with a given label.
+ ///
+ 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;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Gfx/Structs/Bgr32.cs b/src/Greenshot.Gfx/Structs/Bgr32.cs
index 3e4514f5b..6d3603b2f 100644
--- a/src/Greenshot.Gfx/Structs/Bgr32.cs
+++ b/src/Greenshot.Gfx/Structs/Bgr32.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -49,7 +49,7 @@ namespace Greenshot.Gfx.Structs
/// Equal
///
public static bool operator ==(Bgr32 left, Bgr32 right) => Equals(left, right);
-
+
///
/// Not Equal
///
diff --git a/src/Greenshot.Gfx/Structs/PixelExtensions.cs b/src/Greenshot.Gfx/Structs/PixelExtensions.cs
index f11a4c584..61393e1d9 100644
--- a/src/Greenshot.Gfx/Structs/PixelExtensions.cs
+++ b/src/Greenshot.Gfx/Structs/PixelExtensions.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -26,6 +26,34 @@ namespace Greenshot.Gfx.Structs
///
public static class PixelExtensions
{
+ ///
+ public static Bgr24 BackgroundBlendColor { get; set; }
+
+ ///
+ /// Make a Brga32 from the specified color
+ ///
+ /// Color
+ /// Bgr32
+ 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,
+ };
+ }
///
/// Make a Brga32 from the specified color
///
@@ -55,6 +83,27 @@ namespace Greenshot.Gfx.Structs
G = color.G,
B = color.B,
};
- }
+ }
+
+ ///
+ /// Make a color from the specified Brg32
+ ///
+ /// Bgr32
+ /// Color
+ public static Color ToColor(this Bgr32 color)
+ {
+ return Color.FromArgb(color.R, color.G, color.B);
+ }
+
+
+ ///
+ /// Make a color from the specified uint
+ ///
+ /// uint
+ /// Color
+ public static Color ToColor(this uint color)
+ {
+ return Color.FromArgb((int)color);
+ }
}
}
\ No newline at end of file
diff --git a/src/Greenshot.Gfx/UnmanagedBitmap.cs b/src/Greenshot.Gfx/UnmanagedBitmap.cs
index 8dc20f136..fd0178f36 100644
--- a/src/Greenshot.Gfx/UnmanagedBitmap.cs
+++ b/src/Greenshot.Gfx/UnmanagedBitmap.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -138,11 +138,12 @@ namespace Greenshot.Gfx
Bgr24 _ => PixelFormat.Format24bppRgb,
Bgra32 _ => PixelFormat.Format32bppArgb,
Bgr32 _ => PixelFormat.Format32bppRgb,
+ byte _ => PixelFormat.Format8bppIndexed,
_ => throw new NotSupportedException("Unknown pixel format")
};
}
}
-
+
///
/// Returns the pixel format for this Unmanaged bitmap
///
@@ -156,6 +157,7 @@ namespace Greenshot.Gfx
Bgr24 _ => PixelFormats.Bgr24,
Bgra32 _ => PixelFormats.Bgra32,
Bgr32 _ => PixelFormats.Bgr32,
+ byte _ => PixelFormats.Indexed8,
_ => throw new NotSupportedException("Unknown pixel format")
};
}
@@ -171,7 +173,7 @@ namespace Greenshot.Gfx
/// Convert this to a real bitmap
///
/// Bitmap
- public Bitmap NativeBitmap
+ public virtual Bitmap NativeBitmap
{
get
{
@@ -182,7 +184,7 @@ namespace Greenshot.Gfx
return _nativeBitmap;
}
}
-
+
///
/// Convert this to a real bitmap
///
diff --git a/src/Greenshot.PerformanceTests/GfxPerformance.cs b/src/Greenshot.PerformanceTests/GfxPerformance.cs
index 253fc05f7..290e2c9e7 100644
--- a/src/Greenshot.PerformanceTests/GfxPerformance.cs
+++ b/src/Greenshot.PerformanceTests/GfxPerformance.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -53,10 +53,31 @@ namespace Greenshot.PerformanceTests
[Benchmark]
- [Arguments(PixelFormat.Format24bppRgb)]
- [Arguments(PixelFormat.Format32bppRgb)]
- [Arguments(PixelFormat.Format32bppArgb)]
- public void WuQuantizer(PixelFormat pixelFormat)
+ //[Arguments(PixelFormat.Format24bppRgb)]
+ [Arguments(PixelFormat.Format32bppRgb, 256)]
+ [Arguments(PixelFormat.Format32bppRgb, 128)]
+ //[Arguments(PixelFormat.Format32bppArgb)]
+ public void WuQuantizer(PixelFormat pixelFormat, int maxColors)
+ {
+ var bitmap = new UnmanagedBitmap(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(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 graphics = Graphics.FromImage(bitmap.NativeBitmap))
@@ -65,12 +86,12 @@ namespace Greenshot.PerformanceTests
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
}
- var quantizer = new WuQuantizer(bitmap);
- using var quantizedImage = quantizer.GetQuantizedImage();
+ var quantizer = new WuQuantizerOld(bitmap);
+ using var quantizedImage = quantizer.GetQuantizedImage(maxColors);
quantizedImage.NativeBitmap.Save(@"quantized.png", ImageFormat.Png);
}
- [Benchmark]
+ //[Benchmark]
[Arguments(PixelFormat.Format24bppRgb)]
[Arguments(PixelFormat.Format32bppRgb)]
[Arguments(PixelFormat.Format32bppArgb)]
@@ -86,7 +107,7 @@ namespace Greenshot.PerformanceTests
bitmap.ApplyBoxBlur(10);
}
- [Benchmark]
+ //[Benchmark]
public void Blur_UnmanagedBitmap()
{
using var unmanagedBitmap = new UnmanagedBitmap(400, 400);
@@ -100,7 +121,7 @@ namespace Greenshot.PerformanceTests
unmanagedBitmap.ApplyBoxBlur(10);
}
- [Benchmark]
+ //[Benchmark]
public void Blur_Old()
{
using var bitmap = BitmapFactory.CreateEmpty(400, 400, PixelFormat.Format32bppRgb, Color.White);
@@ -113,37 +134,37 @@ namespace Greenshot.PerformanceTests
BoxBlurOld.ApplyOldBoxBlur(bitmap, 10);
}
- [Benchmark]
+ //[Benchmark]
public void Scale2x_FastBitmap()
{
ScaleX.Scale2X(_unmanagedTestBitmap).Dispose();
}
- [Benchmark]
+ //[Benchmark]
public void Scale2x_Unmanaged()
{
_unmanagedTestBitmap.Scale2X().Dispose();
}
- [Benchmark]
+ //[Benchmark]
public void Scale2x_Unmanaged_Reference()
{
_unmanagedTestBitmap.Scale2XReference().Dispose();
}
- [Benchmark]
+ //[Benchmark]
public void Scale3x_FastBitmap()
{
ScaleX.Scale3X(_unmanagedTestBitmap).Dispose();
}
- [Benchmark]
+ //[Benchmark]
public void Scale3x_Unmanaged()
{
_unmanagedTestBitmap.Scale3X().Dispose();
}
- [Benchmark]
+ //[Benchmark]
public void Scale3x_Unmanaged_Reference()
{
_unmanagedTestBitmap.Scale3XReference().Dispose();
diff --git a/src/Greenshot.PerformanceTests/Greenshot.PerformanceTests.csproj b/src/Greenshot.PerformanceTests/Greenshot.PerformanceTests.csproj
index a3d107fd6..91240097d 100644
--- a/src/Greenshot.PerformanceTests/Greenshot.PerformanceTests.csproj
+++ b/src/Greenshot.PerformanceTests/Greenshot.PerformanceTests.csproj
@@ -16,10 +16,10 @@
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/src/Greenshot.Tests/Greenshot.Tests.csproj b/src/Greenshot.Tests/Greenshot.Tests.csproj
index 1610a9611..98e055cee 100644
--- a/src/Greenshot.Tests/Greenshot.Tests.csproj
+++ b/src/Greenshot.Tests/Greenshot.Tests.csproj
@@ -28,15 +28,15 @@
-
-
+
+
-
+
-
+
runtime; build; native; contentfiles; analyzers
all
@@ -51,6 +51,9 @@
+
+ PreserveNewest
+
PreserveNewest
diff --git a/src/Greenshot.Tests/QuantizeTests.cs b/src/Greenshot.Tests/QuantizeTests.cs
index deed44eae..1aa283879 100644
--- a/src/Greenshot.Tests/QuantizeTests.cs
+++ b/src/Greenshot.Tests/QuantizeTests.cs
@@ -1,26 +1,29 @@
// 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 .
using System.Drawing;
using System.Drawing.Imaging;
+using System.IO;
using Greenshot.Gfx;
+using Greenshot.Gfx.Formats;
using Greenshot.Gfx.Quantizer;
+using Greenshot.Gfx.Structs;
using Xunit;
namespace Greenshot.Tests
@@ -30,19 +33,58 @@ namespace Greenshot.Tests
///
public class QuantizeTests
{
+ public QuantizeTests()
+ {
+ BitmapHelper.RegisterFormatReader();
+ }
+
+ [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(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]
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 pen = new SolidBrush(Color.Blue);
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(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(bitmap2);
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");
}
+
}
}
diff --git a/src/Greenshot.Tests/ScaleXTests.cs b/src/Greenshot.Tests/ScaleXTests.cs
index 9a5b05f81..82c3eb466 100644
--- a/src/Greenshot.Tests/ScaleXTests.cs
+++ b/src/Greenshot.Tests/ScaleXTests.cs
@@ -1,19 +1,19 @@
// 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 .
@@ -37,7 +37,7 @@ namespace Greenshot.Tests
{
LogSettings.RegisterDefaultLogger(LogLevels.Verbose, testOutputHelper);
}
-
+
[Fact]
public void Test_Scale2X_UnmanagedBitmap()
{
diff --git a/src/Greenshot.Tests/TestFiles/paiS7wOGKN0XA1wIaq8qHNoWQqq64wnFu3svA9Ux.jpeg b/src/Greenshot.Tests/TestFiles/paiS7wOGKN0XA1wIaq8qHNoWQqq64wnFu3svA9Ux.jpeg
new file mode 100644
index 000000000..198910cf9
Binary files /dev/null and b/src/Greenshot.Tests/TestFiles/paiS7wOGKN0XA1wIaq8qHNoWQqq64wnFu3svA9Ux.jpeg differ
diff --git a/src/Greenshot/Greenshot.csproj b/src/Greenshot/Greenshot.csproj
index b1558bdbb..024cae4c8 100644
--- a/src/Greenshot/Greenshot.csproj
+++ b/src/Greenshot/Greenshot.csproj
@@ -34,16 +34,16 @@
-
+
-
+
-
-
-
-
+
+
+
+
diff --git a/src/global.json b/src/global.json
index c3d298e9f..e6509422e 100644
--- a/src/global.json
+++ b/src/global.json
@@ -1,6 +1,7 @@
{
"sdk": {
"version": "3.1.100",
- "rollForward": "latestPatch"
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
}
}
\ No newline at end of file