Skip to content

Commit ef200b1

Browse files
committed
pass DitherType as arg, mild palette dither
1 parent da23152 commit ef200b1

File tree

1 file changed

+90
-85
lines changed

1 file changed

+90
-85
lines changed

Qualetize.c

Lines changed: 90 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,6 @@
77
#include "Tiles.h"
88
/**************************************/
99

10-
//! Dither modes available
11-
#define DITHER_NONE ( 0) //! No dither
12-
#define DITHER_ORDERED(n) ( n) //! Ordered dithering (Kernel size: (2^n) x (2^n))
13-
#define DITHER_FLOYDSTEINBERG (-1) //! Floyd-Steinberg (diffusion)
14-
15-
//! Dither settings
16-
//! NOTE: Ordered dithering gives consistent tiled results, but Floyd-Steinberg can look nicer.
17-
//! Recommend dither level of 0.5 for ordered, and 1.0 for Floyd-Steinberg.
18-
//! NOTE: DITHER_NO_ALPHA disables dithering on the alpha channel.
19-
#define DITHER_TYPE DITHER_ORDERED(3)
20-
#define DITHER_LEVEL 0.5f
21-
#define DITHER_NO_ALPHA
22-
2310
//! When not zero, the PSNR for each channel will be displayed
2411
#define MEASURE_PSNR 1
2512

@@ -49,14 +36,32 @@ struct BGRAf_t Qualetize(
4936
int MaxTilePals,
5037
int MaxPalSize,
5138
int PalUnused,
39+
const struct BGRA8_t *BitRange,
40+
int DitherType,
5241
int ReplaceImage
5342
) {
5443
int i;
5544

5645
//! Do processing
5746
TilesData_QuantizePalettes(TilesData, Palette, MaxTilePals, MaxPalSize, PalUnused);
5847

59-
//! Convert pixels to palettized
48+
//! Reduce palette range
49+
{
50+
struct BGRAf_t DitherVal = BGRAf_FromBGRA(&(const struct BGRA8_t){1,1,1,0}, BitRange);
51+
DitherVal = BGRAf_Muli(&DitherVal, 0.25f);
52+
for(i=0;i<MaxTilePals*MaxPalSize;i++) {
53+
struct BGRAf_t p = BGRAf_FromYCoCg(&Palette[i]);
54+
55+
//! Apply dithering bias to every second colour
56+
if(i&1) p = BGRAf_Add(&p, &DitherVal);
57+
58+
struct BGRA8_t p2 = BGRA_FromBGRAf(&p, BitRange);
59+
p = BGRAf_FromBGRA(&p2, BitRange);
60+
Palette[i] = BGRAf_AsYCoCg(&p);
61+
}
62+
}
63+
64+
//! Get parameters, pointers, etc.
6065
int x, y;
6166
int ImgW = Image->Width;
6267
int ImgH = Image->Height;
@@ -68,39 +73,41 @@ struct BGRAf_t Qualetize(
6873
#endif
6974
const uint8_t *PxSrcIdx = Image->ColPal ? Image->PxIdx : NULL;
7075
const struct BGRA8_t *PxSrcBGR = Image->ColPal ? Image->ColPal : Image->PxBGR;
71-
#if DITHER_TYPE != DITHER_NONE
72-
# if DITHER_TYPE == DITHER_FLOYDSTEINBERG
73-
struct BGRAf_t *PxDiffuse = TilesData->PxData;
74-
for(y=0;y<ImgH;y++) for(x=0;x<ImgW;x++) PxDiffuse[y*ImgW+x] = (struct BGRAf_t){0,0,0,0};
75-
# else
76-
struct BGRAf_t *PaletteSpread = TilesData->PxData;
77-
for(i=0;i<MaxTilePals;i++) {
78-
//! Find the mean values of this palette
79-
int n;
80-
struct BGRAf_t Mean = (struct BGRAf_t){0,0,0,0};
81-
for(n=PalUnused;n<MaxPalSize;n++) Mean = BGRAf_Add(&Mean, &Palette[i*MaxPalSize+n]);
82-
Mean = BGRAf_Divi(&Mean, MaxPalSize-PalUnused);
83-
84-
//! Compute slopes and store to the palette spread
85-
//! NOTE: For some reason, it works better to use the square root as a weight.
86-
//! This probably gives a value somewhere between the arithmetic mean and
87-
//! the smooth-max, which should result in better quality.
88-
struct BGRAf_t Spread = {0,0,0,0}, SpreadW = {0,0,0,0};
89-
for(n=PalUnused;n<MaxPalSize;n++) {
90-
struct BGRAf_t d = BGRAf_Sub(&Palette[i*MaxPalSize+n], &Mean);
91-
d = BGRAf_Abs(&d);
92-
struct BGRAf_t w = BGRAf_Sqrt(&d);
93-
d = BGRAf_Mul(&d, &w);
94-
Spread = BGRAf_Add(&Spread, &d);
95-
SpreadW = BGRAf_Add(&SpreadW, &w);
96-
}
97-
PaletteSpread[i] = BGRAf_DivSafe(&Spread, &SpreadW, NULL);
76+
77+
//! Initialize dither patterns
78+
struct BGRAf_t *PxDiffuse = TilesData->PxData; //! DITHER_FLOYDSTEINBERG only
79+
struct BGRAf_t *PaletteSpread = TilesData->PxData; //! DITHER_ORDERED only
80+
if(DitherType != DITHER_NONE) {
81+
if(DitherType == DITHER_FLOYDSTEINBERG) {
82+
for(y=0;y<ImgH;y++) for(x=0;x<ImgW;x++) PxDiffuse[y*ImgW+x] = (struct BGRAf_t){0,0,0,0};
83+
} else for(i=0;i<MaxTilePals;i++) {
84+
//! Find the mean values of this palette
85+
int n;
86+
struct BGRAf_t Mean = (struct BGRAf_t){0,0,0,0};
87+
for(n=PalUnused;n<MaxPalSize;n++) Mean = BGRAf_Add(&Mean, &Palette[i*MaxPalSize+n]);
88+
Mean = BGRAf_Divi(&Mean, MaxPalSize-PalUnused);
89+
90+
//! Compute slopes and store to the palette spread
91+
//! NOTE: For some reason, it works better to use the square root as a weight.
92+
//! This probably gives a value somewhere between the arithmetic mean and
93+
//! the smooth-max, which should result in better quality.
94+
struct BGRAf_t Spread = {0,0,0,0}, SpreadW = {0,0,0,0};
95+
for(n=PalUnused;n<MaxPalSize;n++) {
96+
struct BGRAf_t d = BGRAf_Sub(&Palette[i*MaxPalSize+n], &Mean);
97+
d = BGRAf_Abs(&d);
98+
struct BGRAf_t w = BGRAf_Sqrt(&d);
99+
d = BGRAf_Mul(&d, &w);
100+
Spread = BGRAf_Add(&Spread, &d);
101+
SpreadW = BGRAf_Add(&SpreadW, &w);
102+
}
103+
PaletteSpread[i] = BGRAf_DivSafe(&Spread, &SpreadW, NULL);
98104
#ifdef DITHER_NO_ALPHA
99-
PaletteSpread[i].a = 0.0f;
105+
PaletteSpread[i].a = 0.0f;
100106
#endif
107+
}
101108
}
102-
# endif
103-
#endif
109+
110+
//! Convert pixels to palettized
104111
for(y=0;y<ImgH;y++) for(x=0;x<ImgW;x++) {
105112
int PalIdx = TilePalIdx[(y/TileH)*(ImgW/TileW) + (x/TileW)];
106113

@@ -113,58 +120,56 @@ struct BGRAf_t Qualetize(
113120
Px_Original = BGRAf_AsYCoCg(&Px_Original);
114121
Px = Px_Original;
115122
}
116-
#if DITHER_TYPE != DITHER_NONE
117-
# if DITHER_TYPE == DITHER_FLOYDSTEINBERG
118-
//! Adjust for diffusion error
119-
{
120-
struct BGRAf_t Dif = PxDiffuse[y*ImgW + x];
123+
124+
//! Apply dithering?
125+
if(DitherType != DITHER_NONE) {
126+
if(DitherType == DITHER_FLOYDSTEINBERG) {
127+
//! Adjust for diffusion error
128+
struct BGRAf_t Dif = PxDiffuse[y*ImgW + x];
121129
#ifdef DITHER_NO_ALPHA
122-
Dif.a = 0.0f;
130+
Dif.a = 0.0f;
123131
#endif
124-
Dif = BGRAf_Muli(&Dif, DITHER_LEVEL);
125-
Px = BGRAf_Add (&Px, &Dif);
126-
}
127-
# else
128-
//! Adjust for dither matrix
129-
{
130-
int Threshold = 0, xKey = x, yKey = x^y;
131-
int Bit = DITHER_TYPE-1; do {
132-
Threshold = Threshold*2 + (yKey & 1), yKey >>= 1; //! <- Hopefully turned into "SHR, ADC"
133-
Threshold = Threshold*2 + (xKey & 1), xKey >>= 1;
134-
} while(--Bit >= 0);
135-
float fThres = Threshold * (1.0f / (1 << (2*DITHER_TYPE))) - 0.5f;
136-
struct BGRAf_t DitherVal = BGRAf_Muli(&PaletteSpread[PalIdx], fThres*DITHER_LEVEL);
137-
Px = BGRAf_Add(&Px, &DitherVal);
132+
Dif = BGRAf_Muli(&Dif, DITHER_LEVEL);
133+
Px = BGRAf_Add (&Px, &Dif);
134+
} else {
135+
//! Adjust for dither matrix
136+
int Threshold = 0, xKey = x, yKey = x^y;
137+
int Bit = DitherType-1; do {
138+
Threshold = Threshold*2 + (yKey & 1), yKey >>= 1; //! <- Hopefully turned into "SHR, ADC"
139+
Threshold = Threshold*2 + (xKey & 1), xKey >>= 1;
140+
} while(--Bit >= 0);
141+
float fThres = Threshold * (1.0f / (1 << (2*DitherType))) - 0.5f;
142+
struct BGRAf_t DitherVal = BGRAf_Muli(&PaletteSpread[PalIdx], fThres*DITHER_LEVEL);
143+
Px = BGRAf_Add(&Px, &DitherVal);
144+
}
138145
}
139-
# endif
140-
#endif
141-
//! Find matching palette entry and store
146+
147+
//! Find matching palette entry. Store and get the error
142148
int PalCol = FindPaletteEntry(&Px, Palette + PalIdx*MaxPalSize, MaxPalSize, PalUnused);
143149
PxData[y*ImgW + x] = PalIdx*MaxPalSize + PalCol;
144-
#if MEASURE_PSNR || DITHER_TYPE == DITHER_FLOYDSTEINBERG
145150
struct BGRAf_t Error = BGRAf_Sub(&Px_Original, &Palette[PxData[y*ImgW + x]]);
146-
#endif
147-
#if DITHER_TYPE == DITHER_FLOYDSTEINBERG
151+
148152
//! Store error diffusion
149-
if(y+1 < ImgH) {
150-
if(x > 0) {
151-
struct BGRAf_t t = BGRAf_Muli(&Error, 3.0f/16);
152-
PxDiffuse[(y+1)*ImgW+(x-1)] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x-1)], &t);
153-
}
154-
if(1) {
155-
struct BGRAf_t t = BGRAf_Muli(&Error, 5.0f/16);
156-
PxDiffuse[(y+1)*ImgW+(x )] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x )], &t);
153+
if(DitherType == DITHER_FLOYDSTEINBERG) {
154+
if(y+1 < ImgH) {
155+
if(x > 0) {
156+
struct BGRAf_t t = BGRAf_Muli(&Error, 3.0f/16);
157+
PxDiffuse[(y+1)*ImgW+(x-1)] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x-1)], &t);
158+
}
159+
if(1) {
160+
struct BGRAf_t t = BGRAf_Muli(&Error, 5.0f/16);
161+
PxDiffuse[(y+1)*ImgW+(x )] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x )], &t);
162+
}
163+
if(x+1 < ImgW) {
164+
struct BGRAf_t t = BGRAf_Muli(&Error, 1.0f/16);
165+
PxDiffuse[(y+1)*ImgW+(x+1)] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x+1)], &t);
166+
}
157167
}
158168
if(x+1 < ImgW) {
159-
struct BGRAf_t t = BGRAf_Muli(&Error, 1.0f/16);
160-
PxDiffuse[(y+1)*ImgW+(x+1)] = BGRAf_Add(&PxDiffuse[(y+1)*ImgW+(x+1)], &t);
169+
struct BGRAf_t t = BGRAf_Muli(&Error, 7.0f/16);
170+
PxDiffuse[(y )*ImgW+(x+1)] = BGRAf_Add(&PxDiffuse[(y )*ImgW+(x+1)], &t);
161171
}
162172
}
163-
if(x+1 < ImgW) {
164-
struct BGRAf_t t = BGRAf_Muli(&Error, 7.0f/16);
165-
PxDiffuse[(y )*ImgW+(x+1)] = BGRAf_Add(&PxDiffuse[(y )*ImgW+(x+1)], &t);
166-
}
167-
#endif
168173
#if MEASURE_PSNR
169174
//! Accumulate squared error
170175
Error = BGRAf_Mul(&Error, &Error);

0 commit comments

Comments
 (0)