Skip to content

Commit 6525db0

Browse files
authored
Add files via upload
1 parent c5ddd35 commit 6525db0

3 files changed

Lines changed: 66 additions & 13 deletions

File tree

nQuantCpp/DblGNGQuantizer.cpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,6 @@ namespace GrowingNeuralGas
633633
}
634634
}
635635

636-
if (mDivn < .04 && PG < 1 && PG >= coeffs[0][1] && nMaxColors >= 64)
637-
enforcedDither = false;
638636
if (mDivn > .0029 && nMaxColors <= 32)
639637
enforcedDither = false;
640638

@@ -669,7 +667,7 @@ namespace GrowingNeuralGas
669667
return nearestColorIndex(pPalette, nMaxColors, argb, pos);
670668
return closestColorIndex(pPalette, nMaxColors, argb, pos);
671669
};
672-
return dither_image(pixels.data(), pPalette, nMaxColors, NearestColorIndex, hasSemiTransparency, m_transparentPixelIndex, qPixels, width, height);
670+
return dither_image(pixels.data(), pPalette, nMaxColors, NearestColorIndex, hasSemiTransparency, m_transparentPixelIndex, qPixels, width, height, saliencies, enforcedDither);
673671
}
674672

675673
UINT pixelIndex = 0;
@@ -685,16 +683,6 @@ namespace GrowingNeuralGas
685683
{
686684
const auto bitmapHeight = pixels.size() / bitmapWidth;
687685

688-
if (dither && !enforcedDither) {
689-
auto qPixels = make_unique<unsigned short[]>(pixels.size());
690-
quantize_image(pixels, pPalette, nMaxColors, qPixels.get(), bitmapWidth, bitmapHeight, dither);
691-
692-
pixelMap.clear();
693-
clear();
694-
695-
return ProcessImagePixels(pDest, qPixels.get(), m_transparentPixelIndex >= 0);
696-
}
697-
698686
if (dither) {
699687
saliencies.resize(pixels.size());
700688
auto saliencyBase = .1f;
@@ -712,6 +700,17 @@ namespace GrowingNeuralGas
712700
if (enforcedDither)
713701
enforcedDither = nMaxColors < 32 || nMaxColors > 64;
714702

703+
bool sortedByYDiff = nMaxColors >= 128 && mDivn >= .02 && (!hasAlpha() || mDivn < .18);
704+
if (dither && (sortedByYDiff || !enforcedDither)) {
705+
auto qPixels = make_unique<unsigned short[]>(pixels.size());
706+
quantize_image(pixels, pPalette, nMaxColors, qPixels.get(), bitmapWidth, bitmapHeight, dither);
707+
708+
pixelMap.clear();
709+
clear();
710+
711+
return ProcessImagePixels(pDest, qPixels.get(), m_transparentPixelIndex >= 0);
712+
}
713+
715714
auto GetColorIndex = [&](const Color& c) -> int {
716715
return GetARGBIndex(c, hasSemiTransparency, hasAlpha());
717716
};

nQuantCpp/bitmapUtilities.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// GetBitmapHeaderSize
55
//
66
#include "bitmapUtilities.h"
7+
#include <algorithm>
78

89
ULONG GetBitmapHeaderSize(LPCVOID pDib)
910
{
@@ -663,6 +664,55 @@ bool dithering_image(const ARGB* pixels, const ColorPalette* pPalette, DitherFn
663664
return true;
664665
}
665666

667+
// Standard Interleaved Gradient Noise formula by Jorge Jimenez
668+
inline float GetInterleavedGradientNoise(int x, int y)
669+
{
670+
// Returns a pseudo-random value between 0.0f and 1.0f
671+
return fmod(52.9829189f * fmod(0.06711056f * float(x) + 0.00583715f * float(y), 1.0f), 1.0f);
672+
}
673+
674+
bool dither_image(const ARGB* pixels, const ARGB* pPalette, const UINT nMaxColors, DitherFn ditherFn,
675+
const bool& hasSemiTransparency, const int& transparentPixelIndex, unsigned short* qPixels,
676+
const UINT width, const UINT height, const vector<float>& saliencies, bool enforcedDither)
677+
{
678+
// Base spread determined by palette density
679+
const float baseSpread = 255.0f / cbrt(static_cast<float>(nMaxColors));
680+
681+
for (UINT y = 0; y < height; ++y)
682+
{
683+
for (UINT x = 0; x < width; ++x)
684+
{
685+
UINT pixelIndex = y * width + x;
686+
Color c(pixels[pixelIndex]);
687+
688+
// Handle pure transparency early
689+
if (transparentPixelIndex >= 0 && c.GetA() == 0) {
690+
qPixels[pixelIndex] = static_cast<unsigned short>(transparentPixelIndex);
691+
continue;
692+
}
693+
694+
// Generate centered noise [-0.5, 0.5]
695+
float noise = GetInterleavedGradientNoise(static_cast<int>(x), static_cast<int>(y)) - 0.5f;
696+
697+
// Compute noise offset. If saliencies, modulate by pixel saliency;
698+
// otherwise, use uniform base noise intensity across the image.
699+
float weight = enforcedDither && !saliencies.empty() ? saliencies[pixelIndex] : 1.0f;
700+
float offset = noise * baseSpread * weight;
701+
702+
// Apply noise and clamp safely to RGB limits
703+
int r = clamp(static_cast<int>(c.GetR() + offset), 0, 255);
704+
int g = clamp(static_cast<int>(c.GetG() + offset), 0, 255);
705+
int b = clamp(static_cast<int>(c.GetB() + offset), 0, 255);
706+
int a = c.GetA();
707+
708+
auto noisyArgb = Color::MakeARGB(a, r, g, b);
709+
qPixels[pixelIndex] = ditherFn(pPalette, nMaxColors, noisyArgb, y + x);
710+
}
711+
}
712+
713+
return true;
714+
}
715+
666716
bool ProcessImagePixels(Bitmap* pDest, const ARGB* qPixels, const bool& hasSemiTransparency, const int& transparentPixelIndex)
667717
{
668718
UINT bpp = GetPixelFormatSize(pDest->GetPixelFormat());

nQuantCpp/bitmapUtilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ bool dither_image(const ARGB* pixels, const ARGB* pPalette, const UINT nMaxColor
7676

7777
bool dithering_image(const ARGB* pixels, const ColorPalette* pPalette, DitherFn ditherFn, const bool& hasSemiTransparency, const int& transparentPixelIndex, const UINT nMaxColors, ARGB* qPixels, const UINT width, const UINT height);
7878

79+
bool dither_image(const ARGB* pixels, const ARGB* pPalette, const UINT nMaxColors, DitherFn ditherFn,
80+
const bool& hasSemiTransparency, const int& transparentPixelIndex, unsigned short* qPixels,
81+
const UINT width, const UINT height, const vector<float>& saliencies, bool enforcedDither);
82+
7983
bool ProcessImagePixels(Bitmap* pDest, const ARGB* qPixels, const bool& hasSemiTransparency, const int& transparentPixelIndex);
8084

8185
bool ProcessImagePixels(Bitmap* pDest, const unsigned short* qPixels, const bool hasTransparent);

0 commit comments

Comments
 (0)