Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/n64graphics_ci_dir/exoquant/exoquant.c
7861 views
1
/*
2
ExoQuant v0.7
3
4
Copyright (c) 2004 Dennis Ranke
5
6
Permission is hereby granted, free of charge, to any person obtaining a copy of
7
this software and associated documentation files (the "Software"), to deal in
8
the Software without restriction, including without limitation the rights to
9
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
of the Software, and to permit persons to whom the Software is furnished to do
11
so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
*/
24
25
#include "exoquant.h"
26
#include <math.h>
27
#include <stdlib.h>
28
#include <stdio.h>
29
30
#ifndef NULL
31
#define NULL (0)
32
#endif
33
34
#define SCALE_R 1.0f
35
#define SCALE_G 1.2f
36
#define SCALE_B 0.8f
37
#define SCALE_A 1.0f
38
39
exq_data *exq_init()
40
{
41
int i;
42
exq_data *pExq;
43
44
pExq = (exq_data*)malloc(sizeof(exq_data));
45
46
for(i = 0; i < EXQ_HASH_SIZE; i++)
47
pExq->pHash[i] = NULL;
48
49
pExq->numColors = 0;
50
pExq->optimized = 0;
51
pExq->transparency = 1;
52
pExq->numBitsPerChannel = 8;
53
54
return pExq;
55
}
56
57
void exq_no_transparency(exq_data *pExq)
58
{
59
pExq->transparency = 0;
60
}
61
62
void exq_free(exq_data *pExq)
63
{
64
int i;
65
exq_histogram *pCur, *pNext;
66
67
for(i = 0; i < EXQ_HASH_SIZE; i++)
68
for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pNext)
69
{
70
pNext = pCur->pNextInHash;
71
free(pCur);
72
}
73
74
free(pExq);
75
}
76
77
static unsigned int exq_make_hash(unsigned int rgba)
78
{
79
rgba -= (rgba >> 13) | (rgba << 19);
80
rgba -= (rgba >> 13) | (rgba << 19);
81
rgba -= (rgba >> 13) | (rgba << 19);
82
rgba -= (rgba >> 13) | (rgba << 19);
83
rgba -= (rgba >> 13) | (rgba << 19);
84
rgba &= EXQ_HASH_SIZE - 1;
85
return rgba;
86
}
87
88
void exq_feed(exq_data *pExq, unsigned char *pData, int nPixels)
89
{
90
int i;
91
unsigned int hash;
92
unsigned char r, g, b, a;
93
exq_histogram *pCur;
94
unsigned char channelMask = 0xff00 >> pExq->numBitsPerChannel;
95
96
for(i = 0; i < nPixels; i++)
97
{
98
r = *pData++; g = *pData++; b = *pData++; a = *pData++;
99
hash = exq_make_hash(((unsigned int)r) | (((unsigned int)g) << 8) | (((unsigned int)b) << 16) | (((unsigned int)a) << 24));
100
101
pCur = pExq->pHash[hash];
102
while(pCur != NULL && (pCur->ored != r || pCur->ogreen != g ||
103
pCur->oblue != b || pCur->oalpha != a))
104
pCur = pCur->pNextInHash;
105
106
if(pCur != NULL)
107
pCur->num++;
108
else
109
{
110
pCur = (exq_histogram*)malloc(sizeof(exq_histogram));
111
pCur->pNextInHash = pExq->pHash[hash];
112
pExq->pHash[hash] = pCur;
113
pCur->ored = r; pCur->ogreen = g; pCur->oblue = b; pCur->oalpha = a;
114
r &= channelMask; g &= channelMask; b &= channelMask;
115
pCur->color.r = r / 255.0f * SCALE_R;
116
pCur->color.g = g / 255.0f * SCALE_G;
117
pCur->color.b = b / 255.0f * SCALE_B;
118
pCur->color.a = a / 255.0f * SCALE_A;
119
120
if(pExq->transparency)
121
{
122
pCur->color.r *= pCur->color.a;
123
pCur->color.g *= pCur->color.a;
124
pCur->color.b *= pCur->color.a;
125
}
126
127
pCur->num = 1;
128
pCur->palIndex = -1;
129
pCur->ditherScale.r = pCur->ditherScale.g = pCur->ditherScale.b =
130
pCur->ditherScale.a = -1;
131
pCur->ditherIndex[0] = pCur->ditherIndex[1] = pCur->ditherIndex[2] =
132
pCur->ditherIndex[3] = -1;
133
}
134
}
135
}
136
137
void exq_quantize(exq_data *pExq, int nColors)
138
{
139
exq_quantize_ex(pExq, nColors, 0);
140
}
141
142
void exq_quantize_hq(exq_data *pExq, int nColors)
143
{
144
exq_quantize_ex(pExq, nColors, 1);
145
}
146
147
void exq_quantize_ex(exq_data *pExq, int nColors, int hq)
148
{
149
int besti;
150
exq_float beste;
151
exq_histogram *pCur, *pNext;
152
int i, j;
153
154
if(nColors > 256)
155
nColors = 256;
156
157
if(pExq->numColors == 0)
158
{
159
pExq->node[0].pHistogram = NULL;
160
for(i = 0; i < EXQ_HASH_SIZE; i++)
161
for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pCur->pNextInHash)
162
{
163
pCur->pNext = pExq->node[0].pHistogram;
164
pExq->node[0].pHistogram = pCur;
165
}
166
167
exq_sum_node(&pExq->node[0]);
168
169
pExq->numColors = 1;
170
}
171
172
for(i = pExq->numColors; i < nColors; i++)
173
{
174
beste = 0;
175
besti = 0;
176
for(j = 0; j < i; j++)
177
if(pExq->node[j].vdif >= beste)
178
{
179
beste = pExq->node[j].vdif;
180
besti = j;
181
}
182
183
// printf("node %d: %d, %f\n", besti, pExq->node[besti].num, beste);
184
185
pCur = pExq->node[besti].pHistogram;
186
pExq->node[besti].pHistogram = NULL;
187
pExq->node[i].pHistogram = NULL;
188
while(pCur != NULL && pCur != pExq->node[besti].pSplit)
189
{
190
pNext = pCur->pNext;
191
pCur->pNext = pExq->node[i].pHistogram;
192
pExq->node[i].pHistogram = pCur;
193
pCur = pNext;
194
}
195
196
while(pCur != NULL)
197
{
198
pNext = pCur->pNext;
199
pCur->pNext = pExq->node[besti].pHistogram;
200
pExq->node[besti].pHistogram = pCur;
201
pCur = pNext;
202
}
203
204
exq_sum_node(&pExq->node[besti]);
205
exq_sum_node(&pExq->node[i]);
206
207
pExq->numColors = i + 1;
208
if(hq)
209
exq_optimize_palette(pExq, 1);
210
}
211
212
pExq->optimized = 0;
213
}
214
215
exq_float exq_get_mean_error(exq_data *pExq)
216
{
217
int i, n;
218
exq_float err;
219
220
n = 0;
221
err = 0;
222
for(i = 0; i < pExq->numColors; i++)
223
{
224
n += pExq->node[i].num;
225
err += pExq->node[i].err;
226
}
227
228
return sqrt(err / n) * 256;
229
}
230
231
void exq_get_palette(exq_data *pExq, unsigned char *pPal, int nColors)
232
{
233
int i, j;
234
exq_float r, g, b, a;
235
unsigned char channelMask = 0xff00 >> pExq->numBitsPerChannel;
236
237
if(nColors > pExq->numColors)
238
nColors = pExq->numColors;
239
240
if(!pExq->optimized)
241
exq_optimize_palette(pExq, 4);
242
243
for(i = 0; i < nColors; i++)
244
{
245
r = pExq->node[i].avg.r;
246
g = pExq->node[i].avg.g;
247
b = pExq->node[i].avg.b;
248
a = pExq->node[i].avg.a;
249
250
if(pExq->transparency == 1 && a != 0)
251
{
252
r /= a; g/= a; b/= a;
253
}
254
255
pPal[0] = (unsigned char)(r / SCALE_R * 255.9f);
256
pPal[1] = (unsigned char)(g / SCALE_G * 255.9f);
257
pPal[2] = (unsigned char)(b / SCALE_B * 255.9f);
258
pPal[3] = (unsigned char)(a / SCALE_A * 255.9f);
259
260
for(j = 0; j < 3; j++)
261
pPal[j] = (pPal[j] + (1 << (8 - pExq->numBitsPerChannel)) / 2) & channelMask;
262
pPal += 4;
263
}
264
}
265
266
void exq_set_palette(exq_data *pExq, unsigned char *pPal, int nColors)
267
{
268
int i;
269
270
pExq->numColors = nColors;
271
272
for(i = 0; i < nColors; i++)
273
{
274
pExq->node[i].avg.r = *pPal++ * SCALE_R / 255.9f;
275
pExq->node[i].avg.g = *pPal++ * SCALE_G / 255.9f;
276
pExq->node[i].avg.b = *pPal++ * SCALE_B / 255.9f;
277
pExq->node[i].avg.a = *pPal++ * SCALE_A / 255.9f;
278
}
279
280
pExq->optimized = 1;
281
}
282
283
void exq_sum_node(exq_node *pNode)
284
{
285
int n, n2;
286
exq_color fsum, fsum2, vc, tmp, tmp2, sum, sum2;
287
exq_histogram *pCur;
288
exq_float isqrt, nv, v;
289
290
n = 0;
291
fsum.r = fsum.g = fsum.b = fsum.a = 0;
292
fsum2.r = fsum2.g = fsum2.b = fsum2.a = 0;
293
294
for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext)
295
{
296
n += pCur->num;
297
fsum.r += pCur->color.r * pCur->num;
298
fsum.g += pCur->color.g * pCur->num;
299
fsum.b += pCur->color.b * pCur->num;
300
fsum.a += pCur->color.a * pCur->num;
301
fsum2.r += pCur->color.r * pCur->color.r * pCur->num;
302
fsum2.g += pCur->color.g * pCur->color.g * pCur->num;
303
fsum2.b += pCur->color.b * pCur->color.b * pCur->num;
304
fsum2.a += pCur->color.a * pCur->color.a * pCur->num;
305
}
306
pNode->num = n;
307
if(n == 0)
308
{
309
pNode->vdif = 0;
310
pNode->err = 0;
311
return;
312
}
313
314
pNode->avg.r = fsum.r / n;
315
pNode->avg.g = fsum.g / n;
316
pNode->avg.b = fsum.b / n;
317
pNode->avg.a = fsum.a / n;
318
319
vc.r = fsum2.r - fsum.r * pNode->avg.r;
320
vc.g = fsum2.g - fsum.g * pNode->avg.g;
321
vc.b = fsum2.b - fsum.b * pNode->avg.b;
322
vc.a = fsum2.a - fsum.a * pNode->avg.a;
323
324
v = vc.r + vc.g + vc.b + vc.a;
325
pNode->err = v;
326
pNode->vdif = -v;
327
328
if(vc.r > vc.g && vc.r > vc.b && vc.r > vc.a)
329
exq_sort(&pNode->pHistogram, exq_sort_by_r);
330
else if(vc.g > vc.b && vc.g > vc.a)
331
exq_sort(&pNode->pHistogram, exq_sort_by_g);
332
else if(vc.b > vc.a)
333
exq_sort(&pNode->pHistogram, exq_sort_by_b);
334
else
335
exq_sort(&pNode->pHistogram, exq_sort_by_a);
336
337
pNode->dir.r = pNode->dir.g = pNode->dir.b = pNode->dir.a = 0;
338
for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext)
339
{
340
tmp.r = (pCur->color.r - pNode->avg.r) * pCur->num;
341
tmp.g = (pCur->color.g - pNode->avg.g) * pCur->num;
342
tmp.b = (pCur->color.b - pNode->avg.b) * pCur->num;
343
tmp.a = (pCur->color.a - pNode->avg.a) * pCur->num;
344
if(tmp.r * pNode->dir.r + tmp.g * pNode->dir.g +
345
tmp.b * pNode->dir.b + tmp.a * pNode->dir.a < 0)
346
{
347
tmp.r = -tmp.r;
348
tmp.g = -tmp.g;
349
tmp.b = -tmp.b;
350
tmp.a = -tmp.a;
351
}
352
pNode->dir.r += tmp.r;
353
pNode->dir.g += tmp.g;
354
pNode->dir.b += tmp.b;
355
pNode->dir.a += tmp.a;
356
}
357
isqrt = 1 / sqrt(pNode->dir.r * pNode->dir.r +
358
pNode->dir.g * pNode->dir.g + pNode->dir.b * pNode->dir.b +
359
pNode->dir.a * pNode->dir.a);
360
pNode->dir.r *= isqrt;
361
pNode->dir.g *= isqrt;
362
pNode->dir.b *= isqrt;
363
pNode->dir.a *= isqrt;
364
365
exq_sort_dir = pNode->dir;
366
exq_sort(&pNode->pHistogram, exq_sort_by_dir);
367
368
sum.r = sum.g = sum.b = sum.a = 0;
369
sum2.r = sum2.g = sum2.b = sum2.a = 0;
370
n2 = 0;
371
pNode->pSplit = pNode->pHistogram;
372
for(pCur = pNode->pHistogram; pCur != NULL; pCur = pCur->pNext)
373
{
374
if(pNode->pSplit == NULL)
375
pNode->pSplit = pCur;
376
377
n2 += pCur->num;
378
sum.r += pCur->color.r * pCur->num;
379
sum.g += pCur->color.g * pCur->num;
380
sum.b += pCur->color.b * pCur->num;
381
sum.a += pCur->color.a * pCur->num;
382
sum2.r += pCur->color.r * pCur->color.r * pCur->num;
383
sum2.g += pCur->color.g * pCur->color.g * pCur->num;
384
sum2.b += pCur->color.b * pCur->color.b * pCur->num;
385
sum2.a += pCur->color.a * pCur->color.a * pCur->num;
386
387
if(n == n2)
388
break;
389
390
tmp.r = sum2.r - sum.r*sum.r / n2;
391
tmp.g = sum2.g - sum.g*sum.g / n2;
392
tmp.b = sum2.b - sum.b*sum.b / n2;
393
tmp.a = sum2.a - sum.a*sum.a / n2;
394
tmp2.r = (fsum2.r - sum2.r) - (fsum.r-sum.r)*(fsum.r-sum.r) / (n - n2);
395
tmp2.g = (fsum2.g - sum2.g) - (fsum.g-sum.g)*(fsum.g-sum.g) / (n - n2);
396
tmp2.b = (fsum2.b - sum2.b) - (fsum.b-sum.b)*(fsum.b-sum.b) / (n - n2);
397
tmp2.a = (fsum2.a - sum2.a) - (fsum.a-sum.a)*(fsum.a-sum.a) / (n - n2);
398
399
nv = tmp.r + tmp.g + tmp.b + tmp.a + tmp2.r + tmp2.g + tmp2.b + tmp2.a;
400
if(-nv > pNode->vdif)
401
{
402
pNode->vdif = -nv;
403
pNode->pSplit = NULL;
404
}
405
}
406
407
if(pNode->pSplit == pNode->pHistogram)
408
pNode->pSplit = pNode->pSplit->pNext;
409
410
pNode->vdif += v;
411
// printf("error sum: %f, vdif: %f\n", pNode->err, pNode->vdif);
412
}
413
414
void exq_optimize_palette(exq_data *pExq, int iter)
415
{
416
int n, i, j;
417
exq_histogram *pCur;
418
419
pExq->optimized = 1;
420
421
for(n = 0; n < iter; n++)
422
{
423
for(i = 0; i < pExq->numColors; i++)
424
pExq->node[i].pHistogram = NULL;
425
426
for(i = 0; i < EXQ_HASH_SIZE; i++)
427
for(pCur = pExq->pHash[i]; pCur != NULL; pCur = pCur->pNextInHash)
428
{
429
j = exq_find_nearest_color(pExq, &pCur->color);
430
pCur->pNext = pExq->node[j].pHistogram;
431
pExq->node[j].pHistogram = pCur;
432
}
433
434
for(i = 0; i < pExq->numColors; i++)
435
exq_sum_node(&pExq->node[i]);
436
}
437
}
438
439
void exq_map_image(exq_data *pExq, int nPixels, unsigned char *pIn,
440
unsigned char *pOut)
441
{
442
int i;
443
exq_color c;
444
exq_histogram *pHist;
445
446
if(!pExq->optimized)
447
exq_optimize_palette(pExq, 4);
448
449
for(i = 0; i < nPixels; i++)
450
{
451
pHist = exq_find_histogram(pExq, pIn);
452
if(pHist != NULL && pHist->palIndex != -1)
453
{
454
*pOut++ = (unsigned char)pHist->palIndex;
455
pIn += 4;
456
}
457
else
458
{
459
c.r = *pIn++ / 255.0f * SCALE_R;
460
c.g = *pIn++ / 255.0f * SCALE_G;
461
c.b = *pIn++ / 255.0f * SCALE_B;
462
c.a = *pIn++ / 255.0f * SCALE_A;
463
464
if(pExq->transparency)
465
{
466
c.r *= c.a; c.g *= c.a; c.b *= c.a;
467
}
468
469
*pOut = exq_find_nearest_color(pExq, &c);
470
if(pHist != NULL)
471
pHist->palIndex = *pOut;
472
pOut++;
473
}
474
}
475
}
476
477
void exq_map_image_ordered(exq_data *pExq, int width, int height,
478
unsigned char *pIn, unsigned char *pOut)
479
{
480
exq_map_image_dither(pExq, width, height, pIn, pOut, 1);
481
}
482
483
void exq_map_image_random(exq_data *pExq, int nPixels,
484
unsigned char *pIn, unsigned char *pOut)
485
{
486
exq_map_image_dither(pExq, nPixels, 1, pIn, pOut, 0);
487
}
488
489
void exq_map_image_dither(exq_data *pExq, int width, int height,
490
unsigned char *pIn, unsigned char *pOut, int ordered)
491
{
492
int x, y, i, j, d;
493
exq_color p, scale, tmp;
494
exq_histogram *pHist;
495
const exq_float dither_matrix[4] = { -0.375, 0.125, 0.375, -0.125 };
496
497
if(!pExq->optimized)
498
exq_optimize_palette(pExq, 4);
499
500
for(y = 0; y < height; y++)
501
for(x = 0; x < width; x++)
502
{
503
if(ordered)
504
d = (x & 1) + (y & 1) * 2;
505
else
506
d = rand() & 3;
507
pHist = exq_find_histogram(pExq, pIn);
508
p.r = *pIn++ / 255.0f * SCALE_R;
509
p.g = *pIn++ / 255.0f * SCALE_G;
510
p.b = *pIn++ / 255.0f * SCALE_B;
511
p.a = *pIn++ / 255.0f * SCALE_A;
512
513
if(pExq->transparency)
514
{
515
p.r *= p.a; p.g *= p.a; p.b *= p.a;
516
}
517
518
if(pHist == NULL || pHist->ditherScale.r < 0)
519
{
520
i = exq_find_nearest_color(pExq, &p);
521
scale.r = pExq->node[i].avg.r - p.r;
522
scale.g = pExq->node[i].avg.g - p.g;
523
scale.b = pExq->node[i].avg.b - p.b;
524
scale.a = pExq->node[i].avg.a - p.a;
525
tmp.r = p.r - scale.r / 3;
526
tmp.g = p.g - scale.g / 3;
527
tmp.b = p.b - scale.b / 3;
528
tmp.a = p.a - scale.a / 3;
529
j = exq_find_nearest_color(pExq, &tmp);
530
if(i == j)
531
{
532
tmp.r = p.r - scale.r * 3;
533
tmp.g = p.g - scale.g * 3;
534
tmp.b = p.b - scale.b * 3;
535
tmp.a = p.a - scale.a * 3;
536
j = exq_find_nearest_color(pExq, &tmp);
537
}
538
if(i != j)
539
{
540
scale.r = (pExq->node[j].avg.r - pExq->node[i].avg.r) * 0.8f;
541
scale.g = (pExq->node[j].avg.g - pExq->node[i].avg.g) * 0.8f;
542
scale.b = (pExq->node[j].avg.b - pExq->node[i].avg.b) * 0.8f;
543
scale.a = (pExq->node[j].avg.a - pExq->node[i].avg.a) * 0.8f;
544
if(scale.r < 0) scale.r = -scale.r;
545
if(scale.g < 0) scale.g = -scale.g;
546
if(scale.b < 0) scale.b = -scale.b;
547
if(scale.a < 0) scale.a = -scale.a;
548
}
549
else
550
scale.r = scale.g = scale.b = scale.a = 0;
551
552
if(pHist != NULL)
553
{
554
pHist->ditherScale.r = scale.r;
555
pHist->ditherScale.g = scale.g;
556
pHist->ditherScale.b = scale.b;
557
pHist->ditherScale.a = scale.a;
558
}
559
}
560
else
561
{
562
scale.r = pHist->ditherScale.r;
563
scale.g = pHist->ditherScale.g;
564
scale.b = pHist->ditherScale.b;
565
scale.a = pHist->ditherScale.a;
566
}
567
568
if(pHist != NULL && pHist->ditherIndex[d] >= 0)
569
*pOut++ = (unsigned char)pHist->ditherIndex[d];
570
else
571
{
572
tmp.r = p.r + scale.r * dither_matrix[d];
573
tmp.g = p.g + scale.g * dither_matrix[d];
574
tmp.b = p.b + scale.b * dither_matrix[d];
575
tmp.a = p.a + scale.a * dither_matrix[d];
576
*pOut = exq_find_nearest_color(pExq, &tmp);
577
if(pHist != NULL)
578
pHist->ditherIndex[d] = *pOut;
579
pOut++;
580
}
581
}
582
}
583
584
exq_histogram *exq_find_histogram(exq_data *pExq, unsigned char *pCol)
585
{
586
unsigned int hash;
587
int r, g, b, a;
588
exq_histogram *pCur;
589
590
r = *pCol++; g = *pCol++; b = *pCol++; a = *pCol++;
591
hash = exq_make_hash(((unsigned int)r) | (((unsigned int)g) << 8) | (((unsigned int)b) << 16) | (((unsigned int)a) << 24));
592
593
pCur = pExq->pHash[hash];
594
while(pCur != NULL && (pCur->ored != r || pCur->ogreen != g ||
595
pCur->oblue != b || pCur->oalpha != a))
596
pCur = pCur->pNextInHash;
597
598
return pCur;
599
}
600
601
unsigned char exq_find_nearest_color(exq_data *pExq, exq_color *pColor)
602
{
603
exq_float bestv;
604
int besti, i;
605
exq_color dif;
606
607
bestv = 16;
608
besti = 0;
609
for(i = 0; i < pExq->numColors; i++)
610
{
611
dif.r = pColor->r - pExq->node[i].avg.r;
612
dif.g = pColor->g - pExq->node[i].avg.g;
613
dif.b = pColor->b - pExq->node[i].avg.b;
614
dif.a = pColor->a - pExq->node[i].avg.a;
615
if(dif.r*dif.r + dif.g*dif.g + dif.b*dif.b + dif.a*dif.a < bestv)
616
{
617
bestv = dif.r*dif.r + dif.g*dif.g + dif.b*dif.b + dif.a*dif.a;
618
besti = i;
619
}
620
}
621
622
return (unsigned char)besti;
623
}
624
625
void exq_sort(exq_histogram **ppHist, exq_float (*sortfunc)(const exq_histogram *pHist))
626
{
627
exq_histogram *pLow, *pHigh, *pCur, *pNext;
628
int n = 0;
629
exq_float sum = 0;
630
631
for(pCur = *ppHist; pCur != NULL; pCur = pCur->pNext)
632
{
633
n++;
634
sum += sortfunc(pCur);
635
}
636
637
if(n < 2)
638
return;
639
640
sum /= n;
641
642
pLow = pHigh = NULL;
643
for(pCur = *ppHist; pCur != NULL; pCur = pNext)
644
{
645
pNext = pCur->pNext;
646
if(sortfunc(pCur) < sum)
647
{
648
pCur->pNext = pLow;
649
pLow = pCur;
650
}
651
else
652
{
653
pCur->pNext = pHigh;
654
pHigh = pCur;
655
}
656
}
657
658
if(pLow == NULL)
659
{
660
*ppHist = pHigh;
661
return;
662
}
663
if(pHigh == NULL)
664
{
665
*ppHist = pLow;
666
return;
667
}
668
669
exq_sort(&pLow, sortfunc);
670
exq_sort(&pHigh, sortfunc);
671
672
*ppHist = pLow;
673
while(pLow->pNext != NULL)
674
pLow = pLow->pNext;
675
676
pLow->pNext = pHigh;
677
}
678
679
exq_float exq_sort_by_r(const exq_histogram *pHist)
680
{
681
return pHist->color.r;
682
}
683
684
exq_float exq_sort_by_g(const exq_histogram *pHist)
685
{
686
return pHist->color.g;
687
}
688
689
exq_float exq_sort_by_b(const exq_histogram *pHist)
690
{
691
return pHist->color.b;
692
}
693
694
exq_float exq_sort_by_a(const exq_histogram *pHist)
695
{
696
return pHist->color.a;
697
}
698
699
exq_color exq_sort_dir;
700
701
exq_float exq_sort_by_dir(const exq_histogram *pHist)
702
{
703
return pHist->color.r * exq_sort_dir.r +
704
pHist->color.g * exq_sort_dir.g +
705
pHist->color.b * exq_sort_dir.b +
706
pHist->color.a * exq_sort_dir.a;
707
}
708
709