The following is based on a "a low-cost approximation" of color difference from CompuPhase - Colour metric, but avoids the square root function (thereby measuring the square of the color difference, which is fine for comparison purposes), and scales the result to avoid loss of precision caused by integer division. It sorts an array of color tuples into order from "most red" to "least red".
The comparison makes mid-grey more red than black, and black more red than white, which seems fairly arbitrary!
#include <stdio.h>
#include <stdlib.h>
/*
* Sorts an array of RGB color values (each component from 0 to 255)
* in descending order of redness.
*
* Uses a low-cost approximation of color distance from
* <https://www.compuphase.com/cmetric.htm>. It uses a weighted Euclidean
* distance function to measure the distance between colors, where the weight
* factors depend on the mean level of "red":
*
* rmean = (c1.r + c2.r) / 2
* dr = c1.r - c2.r
* dg = c1.g - c2.g
* db = c1.b - c2.b
*
* dc = sqrt(((2 + (rmean / 256)) * dr * dr) + (4 * dg * dg) +
* ((2 + ((255 - rmean) / 256)) * db * db))
*
* Uses a modified version of the above which returns the square of the
* distance between two colors scaled by a silly amount to avoid loss of
* precision caused by integer division.
*/
struct rgb {
unsigned char r; /* red range 0..255 */
unsigned char g; /* green range 0..255 */
unsigned char b; /* blue range 0..255 */
};
/* distance squared between two colors, scaled by some silly amount. */
long color_dist_squared(const struct rgb *c1, const struct rgb *c2)
{
long rsum = c1->r + c2->r;
long dr = (long)c1->r - (long)c2->r;
long dg = (long)c1->g - (long)c2->g;
long db = (long)c1->b - (long)c2->b;
return (((1024 + rsum) * dr * dr) + (2048 * dg * dg) +
((1534 - rsum) * db * db));
}
/* distance squared from pure red, scaled by some silly amount. */
long antiredness_squared(const struct rgb *c)
{
const struct rgb pure_red = { .r = 255, .g = 0, .b = 0 };
return color_dist_squared(&pure_red, c);
}
/*
* qsort() comparison function.
* a and b point to struct rgb values.
* Returns 1 if *a is more anti-red (less red) than *b.
* Returns 0 if *a and *b are equally (anti-)red.
* Returns -1 if *a is less anti-red (more red) than *b.
*/
int compar_antiredness(const void *a, const void *b)
{
const struct rgb *ca = (const struct rgb *)a;
const struct rgb *cb = (const struct rgb *)b;
long ara = antiredness_squared(ca);
long arb = antiredness_squared(cb);
long diff = ara - arb;
return (diff > 0) - (diff < 0);
}
int main(void)
{
struct rgb colors[] = {
{ .r = 125, .g = 0, .b = 0 },
{ .r = 150, .g = 255, .b = 0 },
{ .r = 100, .g = 20, .b = 21 },
{ .r = 255, .g = 21, .b = 22 },
{ .r = 0, .g = 0, .b = 0 },
{ .r = 255, .g = 255, .b = 255 },
{ .r = 128, .g = 128, .b = 128 },
{ .r = 255, .g = 0, .b = 0 },
};
size_t num_colors = sizeof(colors) / sizeof(colors[0]);
size_t i;
printf("Unsorted colors:\n");
for (i = 0; i < num_colors; i++) {
printf("[%zu] R=%u, G=%u, B=%u\n", i, (unsigned)colors[i].r,
(unsigned)colors[i].g, (unsigned)colors[i].b);
}
printf("\n");
qsort(colors, num_colors, sizeof(colors[0]), compar_antiredness);
printf("Colors sorted from most red to least red:\n");
for (i = 0; i < num_colors; i++) {
printf("[%zu] R=%u, G=%u, B=%u\n", i, (unsigned)colors[i].r,
(unsigned)colors[i].g, (unsigned)colors[i].b);
}
return 0;
}
Output from the above:
Unsorted colors:
[0] R=125, G=0, B=0
[1] R=150, G=255, B=0
[2] R=100, G=20, B=21
[3] R=255, G=21, B=22
[4] R=0, G=0, B=0
[5] R=255, G=255, B=255
[6] R=128, G=128, B=128
[7] R=255, G=0, B=0
Colors sorted from most red to least red:
[0] R=255, G=0, B=0
[1] R=255, G=21, B=22
[2] R=125, G=0, B=0
[3] R=100, G=20, B=21
[4] R=128, G=128, B=128
[5] R=0, G=0, B=0
[6] R=150, G=255, B=0
[7] R=255, G=255, B=255
EDIT: Of course, it is just as easy to sort from most green to least green, or from most blue to least blue using the following functions:
/* distance squared from pure green, scaled by some silly amount. */
long antigreenness_squared(const struct rgb *c)
{
const struct rgb pure_green = { .r = 0, .g = 255, .b = 0 };
return color_dist_squared(&pure_green, c);
}
/*
* qsort() comparison function.
* a and b point to struct rgb values.
* Returns 1 if *a is more anti-green (less green) than *b.
* Returns 0 if *a and *b are equally (anti-)green.
* Returns -1 if *a is less anti-green (more green) than *b.
*/
int compar_antigreenness(const void *a, const void *b)
{
const struct rgb *ca = (const struct rgb *)a;
const struct rgb *cb = (const struct rgb *)b;
long aga = antigreenness_squared(ca);
long agb = antigreenness_squared(cb);
long diff = aga - agb;
return (diff > 0) - (diff < 0);
}
/* distance squared from pure blue, scaled by some silly amount. */
long antiblueness_squared(const struct rgb *c)
{
const struct rgb pure_blue = { .r = 0, .g = 0, .b = 255 };
return color_dist_squared(&pure_blue, c);
}
/*
* qsort() comparison function.
* a and b point to struct rgb values.
* Returns 1 if *a is more anti-blue (less blue) than *b.
* Returns 0 if *a and *b are equally (anti-)blue.
* Returns -1 if *a is less anti-blue (more blue) than *b.
*/
int compar_antiblueness(const void *a, const void *b)
{
const struct rgb *ca = (const struct rgb *)a;
const struct rgb *cb = (const struct rgb *)b;
long aba = antiblueness_squared(ca);
long abb = antiblueness_squared(cb);
long diff = aba - abb;
return (diff > 0) - (diff < 0);
}