-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathunipal.c
152 lines (127 loc) · 4.26 KB
/
unipal.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* UNIPAL.C: reduce an RGB 24-bit image to 8-bit using uniform palette */
/* Coded by Trinh D.D. Nguyen, Dec 2024 */
// #define USE_GAMMA_CORRECTION
#include <stdio.h>
#include <string.h>
#ifdef USE_GAMMA_CORRECTION
#include <math.h>
#endif
#include "image.h"
#include "bitmap.h"
typedef struct cube_t {
uint32 r, g, b;
uint32 count;
} cube, cubes[256];
/* for ordered dithering */
uint8 bayerMatrix[16] = { 0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5};
/* slightly fast color clamping */
inline uint8 clamp(int n) {
n &= -(n >= 0);
return n | ((255 - n) >> 31);
}
#ifdef USE_GAMMA_CORRECTION
float gamma_value = 2.2;
/* gamma correction for an RGB triplet */
void gamma_correction(int *r, int *g, int *b, float gamma) {
float rNorm = *r / 255.0;
float gNorm = *g / 255.0;
float bNorm = *b / 255.0;
// perform corrections
rNorm = pow(rNorm, gamma);
gNorm = pow(gNorm, gamma);
bNorm = pow(bNorm, gamma);
*r = (int)(rNorm * 255.0);
*g = (int)(gNorm * 255.0);
*b = (int)(bNorm * 255.0);
}
#endif
/* fast RGB quantization */
bitmap quantize_uniform(const bitmap bmp, bool dither) {
if (!bmp) return NULL;
if (bmp->format != BMF_RGB24) return NULL;
/* create output indexed bitmap */
bitmap res = bitmap_create(bmp->width, bmp->height, BMF_INDEXED8, true);
cubes uniCubes = {0}; /* RGB cubes */
uint8 * src = bmp->data; /* pointer to source bitmap bits */
uint8 * dst = res->data; /* pointer to output bitmap bits */
/* quantization phase */
for (int y = 0; y < bmp->height; y++) {
for (int x = 0; x < bmp->width; x++) {
/* dithering if needed */
int t = dither ? (bayerMatrix[((y & 3) << 2) + (x & 3)] - 8) : 0;
int b = clamp((*src++) + (t << 1));
int g = clamp((*src++) + (t << 1));
int r = clamp((*src++) + (t << 1));
#ifdef USE_GAMMA_CORRECTION
gamma_correction(&r, &g, &b, gamma_value);
#endif
/* quantize */
uint8 k = ((r >> 5) << 5) + ((g >> 5) << 2) + (b >> 6);
(*dst++) = k;
/* preparing RGB cubes for CLUT */
uniCubes[k].r += r;
uniCubes[k].g += g;
uniCubes[k].b += b;
uniCubes[k].count++;
}
}
/* generate a uniform CLUT based on the quantized colors */
for (int i = 0; i < 256; i++)
if (uniCubes[i].count) {
res->pal[i].r = (uniCubes[i].r / uniCubes[i].count);
res->pal[i].g = (uniCubes[i].g / uniCubes[i].count);
res->pal[i].b = (uniCubes[i].b / uniCubes[i].count);
}
else {
res->pal[i].r = 0;
res->pal[i].g = 0;
res->pal[i].b = 0;
}
return res;
}
/* main program */
int main(int argc, char * argv[]) {
bitmap bmp;
bool dither = false;
char input[256] = {0}, output[256] = "output.bmp";
if (argc < 2) {
printf("Usage: unipal image.bmp [output.bmp] [-d[ither]]\n");
return -1;
}
if (argc >= 2) {
strncpy(input, argv[1], 255);
if (argc == 3) {
dither = !strcmp(argv[2], "-dither") || !strcmp(argv[2], "-d") ;
if (!dither)
strncpy(output, argv[2], 255);
}
else
if (argc > 3) {
strncpy(output, argv[2], 255);
dither = !strcmp(argv[3], "-dither") || !strcmp(argv[3], "-d") ;
}
}
printf(". Input = [%s]\n", input);
printf(". Output = [%s]\n", output);
printf(". Loading bitmap [%s]...\n", input);
if (!(bmp = bmp_load(input))) {
printf("ERROR: cannot load [%s]\n", input);
return -1;
}
printf(" - Image dimensions = %d x %d\n", bmp->width, bmp->height);
printf(". Quantizing colors (dithering: %s)...\n", dither ? "yes" : "no");
bitmap res = quantize_uniform(bmp, dither);
if (res) {
printf(". Saving output to [%s]...\n", output);
if (!bmp_save(output, &res))
printf("ERROR: cannot write output bitmap.\n");
bitmap_destroy(&res);
}
else
printf("ERROR: input bitmap must be 24-bit.\n");
bitmap_destroy(&bmp);
return 0;
}