colors

extract colors from pictures
git clone git://git.2f30.org/colors
Log | Files | Refs | README | LICENSE

hexsort.c (4790B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <stdint.h>
      5 #include <unistd.h>
      6 #include <string.h>
      7 
      8 /* magic to return the ratio between two numbers */
      9 #define RATIO(a,b) (((a)>(b) ? 1.0*(b)/(a) : ((a) > 0 ? 1.0*(a)/(b) : 0)))
     10 
     11 struct color_t {
     12 	char   hex[8];
     13 	int    rgb[3];
     14 	double val;
     15 	struct color_t *next;
     16 };
     17 
     18 struct color_t *head = NULL;
     19 struct color_t **sorted = NULL;
     20 struct color_t **messed = NULL;
     21 
     22 void
     23 usage(char *argv0) {
     24 	fprintf(stderr, "usage: %s [mask]\n", argv0);
     25 }
     26 
     27 /*
     28  * converts an hexadecimal representation of a color into a 3 dimensionnal
     29  * array (RGB decomposition)
     30  */
     31 void
     32 hex2rgb(char *hex, int *rgb)
     33 {
     34 	int i;
     35 	char tmp[2];
     36 	for (i = 0; i < 3; i++) {
     37 		strncpy(tmp, hex + 1 + 2 * i, 2);
     38 		rgb[i] = strtol(tmp, NULL, 16);
     39 	}
     40 }
     41 
     42 /*
     43  * Returns the "ratio" of the negative color for an RGB representation.
     44  *
     45  * For mask 4 (100)b which is red. It will return the purity ratio of its
     46  * negative color: cyan (#00ffff). For example:
     47  *
     48  * #ff2d2d  255,45,45  45/45 = 1.0 (this is a "full" cyan)
     49  * #ff3c1e  255,6°,30  60/30 = 0.5 (this is a "half" cyan)
     50  */
     51 double
     52 color_ratio(int *rgb, uint8_t mask)
     53 {
     54 	switch(mask) {
     55 	case 4: return RATIO(rgb[1],rgb[2]);
     56 	case 2: return RATIO(rgb[0],rgb[2]);
     57 	case 1: return RATIO(rgb[0],rgb[1]);
     58 	case 3: return RATIO(rgb[1],rgb[2]);
     59 	case 5: return RATIO(rgb[0],rgb[2]);
     60 	case 6: return RATIO(rgb[0],rgb[1]);
     61 	}
     62 	return 0;
     63 }
     64 
     65 /*
     66  * Calculate a score for the dominance of the color given by `mask` (3 bit
     67  * representation of the RGB color, eg: 101 is magenta).
     68  * This will calculate the score of the color regarding the mask, and return
     69  * this value for comparison with other colors. "pure" refers to the color
     70  * represented by the mask, "negative" refers to the opposite of this color.
     71  *
     72  * We differentiate 3 types of masks:
     73  * + 100, 010, 001
     74  * + 110, 101, 011
     75  * + 111, 000
     76  *
     77  * First group will be the pure color, divided by the negative ratio.
     78  * Second group will be the pure color multiplied by the pure ratio.
     79  * Last group is just a plain sum of all colors.
     80  *
     81  * (Check function color_ratio() for an explanation on the ratio)
     82  */
     83 double
     84 color_dominant(int *rgb, uint8_t mask)
     85 {
     86 	double pure = 0, negative = 0, ratio = 0;
     87 
     88 	int prgb[3];
     89 	int nrgb[3];
     90 
     91 	/* get the score of the current value */
     92 	prgb[0] = rgb[0] * ((mask & 4)>>2);
     93 	prgb[1] = rgb[1] * ((mask & 2)>>1);
     94 	prgb[2] = rgb[2] * ((mask & 1)>>0);
     95 
     96 	/* score of the negative value */
     97 	nrgb[0] = rgb[0] * (((mask ^ 4)>>2)&1);
     98 	nrgb[1] = rgb[1] * (((mask ^ 2)>>1)&1);
     99 	nrgb[2] = rgb[2] * (((mask ^ 1)>>0)&1);
    100 
    101 	/*
    102 	 * calculate the average of either pure or negative color depending on
    103 	 * the mask's value
    104 	 */
    105 	switch (mask) {
    106 	case 1:
    107 	case 2:
    108 	case 4:
    109 		pure     =  prgb[0] + prgb[1] + prgb[2];
    110 		negative = (nrgb[0] + nrgb[1] + nrgb[2])/2;
    111 		ratio    = 1 + negative * color_ratio(nrgb, mask);
    112 		return ((pure - negative) / ratio);
    113 	case 3:
    114 	case 5:
    115 	case 6:
    116 		pure     = (prgb[0] + prgb[1] + prgb[2])/2;
    117 		negative = nrgb[0] + nrgb[1] + nrgb[2];
    118 		ratio    = 1 + pure * color_ratio(prgb, mask);
    119 		return ((pure - negative) * ratio);
    120 	}
    121 
    122 	pure     = prgb[0] + prgb[1] + prgb[2];
    123 	negative = nrgb[0] + nrgb[1] + nrgb[2];
    124 	return pure - negative;
    125 }
    126 
    127 /* create a color node, and add it to the list */
    128 struct color_t *
    129 color_new(char *hex, uint8_t mask)
    130 {
    131 	struct color_t *new = NULL;
    132 
    133 	new = malloc(sizeof(struct color_t));
    134 	if (new == NULL)
    135 		return NULL;
    136 
    137 	strncpy(new->hex, hex, 8);
    138 	hex2rgb(hex, new->rgb);
    139 	new->val = color_dominant(new->rgb, mask);
    140 	return new;
    141 }
    142 
    143 /*
    144  * Takes an unsorted list of colors as an argument, and sort it depending on
    145  * the mask value.
    146  * The mask is a 3 bit representation of the RGB composition you want to use to
    147  * sort colors, eg mask 011 will return the brightess cyan first, and darkest
    148  * red last.
    149  */
    150 struct color_t *
    151 color_sort(struct color_t *cur, struct color_t *new, uint8_t mask)
    152 {
    153 	if (cur == NULL) {
    154 		new->next = NULL;
    155 	} else {
    156 		if (new->val <= cur->val) {
    157 			cur->next = color_sort(cur->next, new, mask);
    158 			return cur;
    159 		} else {
    160 			new->next = cur;
    161 		}
    162 	}
    163 	return new;
    164 }
    165 
    166 /*
    167  * print the content of our list in the format:
    168  * <HEX>	<RGB>	<SCORE>
    169  */
    170 void
    171 color_print(struct color_t *node)
    172 {
    173 	struct color_t *tmp = NULL;
    174 	for (tmp=node; tmp; tmp=tmp->next) {
    175 		printf("%s\t%d,%d,%d\t%f\n",
    176 				tmp->hex,
    177 				tmp->rgb[0],
    178 				tmp->rgb[1],
    179 				tmp->rgb[2],
    180 				tmp->val );
    181 	}
    182 }
    183 
    184 int
    185 main(int argc, char *argv[])
    186 {
    187 	char hex[8];
    188 	uint8_t mask = 7;
    189 
    190 	/* print whitest by default */
    191 	if (argc > 1) {
    192 		if (strncmp(argv[1], "-h", 2) == 0)
    193 			usage(argv[0]);
    194 		else
    195 			mask = atoi(argv[1]);
    196 	}
    197 
    198 	while (fgets(hex, 8, stdin)) {
    199 		if (hex[0] == '#') {
    200 			head = color_sort(head, color_new(hex, mask), mask);
    201 		}
    202 	}
    203 	color_print(head);
    204 	return 0;
    205 }