You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

345 lines
9.5 KiB

  1. #include "config.h"
  2. #include "common.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <math.h>
  7. #include <pipi.h>
  8. #define X 3
  9. #define Y 4
  10. #define A 5
  11. #define BRIGHT(x) (0.299*(x)[0] + 0.587*(x)[1] + 0.114*(x)[2])
  12. #define MAXCOLORS 16
  13. #define STEPS 256
  14. #define EPSILON (0.000001)
  15. typedef struct
  16. {
  17. double pts[STEPS + 1][MAXCOLORS * (MAXCOLORS - 1) / 2][6];
  18. int hullsize[STEPS + 1];
  19. double bary[STEPS + 1][3];
  20. }
  21. hull_t;
  22. static double const y[3] = { .299, .587, .114 };
  23. static double u[3], v[3];
  24. static int ylen;
  25. /*
  26. * Find two base vectors for the chrominance planes.
  27. */
  28. static void init_uv(void)
  29. {
  30. double tmp;
  31. ylen = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
  32. u[0] = y[1];
  33. u[1] = -y[0];
  34. u[2] = 0;
  35. tmp = sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
  36. u[0] /= tmp; u[1] /= tmp; u[2] /= tmp;
  37. v[0] = y[1] * u[2] - y[2] * u[1];
  38. v[1] = y[2] * u[0] - y[0] * u[2];
  39. v[2] = y[0] * u[1] - y[1] * u[0];
  40. tmp = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  41. v[0] /= tmp; v[1] /= tmp; v[2] /= tmp;
  42. }
  43. /*
  44. * Compute the convex hull of a given palette.
  45. */
  46. static hull_t *compute_hull(int ncolors, double const *palette)
  47. {
  48. hull_t *ret = malloc(sizeof(hull_t));
  49. double tmp;
  50. int i, j;
  51. double pal[ncolors][3];
  52. for(i = 0; i < ncolors; i++)
  53. {
  54. pal[i][0] = palette[i * 3];
  55. pal[i][1] = palette[i * 3 + 1];
  56. pal[i][2] = palette[i * 3 + 2];
  57. }
  58. /*
  59. * 1. Find the darkest and lightest colours
  60. */
  61. double *dark = NULL, *light = NULL;
  62. double min = 1.0, max = 0.0;
  63. for(i = 0; i < ncolors; i++)
  64. {
  65. double p = BRIGHT(pal[i]);
  66. if(p < min)
  67. {
  68. dark = pal[i];
  69. min = p;
  70. }
  71. if(p > max)
  72. {
  73. light = pal[i];
  74. max = p;
  75. }
  76. }
  77. double gray[3];
  78. gray[0] = light[0] - dark[0];
  79. gray[1] = light[1] - dark[1];
  80. gray[2] = light[2] - dark[2];
  81. /*
  82. * 3. Browse the grey axis and do stuff
  83. */
  84. int n;
  85. for(n = 0; n <= STEPS; n++)
  86. {
  87. double pts[ncolors * (ncolors - 1) / 2][6];
  88. double ptmp[6];
  89. #define SWAP(p1,p2) do { memcpy(ptmp, p1, sizeof(ptmp)); \
  90. memcpy(p1, p2, sizeof(ptmp)); \
  91. memcpy(p2, ptmp, sizeof(ptmp)); } while(0)
  92. double t = n * 1.0 / STEPS;
  93. int npts = 0;
  94. double p0[3];
  95. p0[0] = dark[0] + t * gray[0];
  96. p0[1] = dark[1] + t * gray[1];
  97. p0[2] = dark[2] + t * gray[2];
  98. /*
  99. * 3.1. Find all edges that intersect the t.y + (u,v) plane
  100. */
  101. for(i = 0; i < ncolors; i++)
  102. {
  103. double k1[3];
  104. k1[0] = pal[i][0] - p0[0];
  105. k1[1] = pal[i][1] - p0[1];
  106. k1[2] = pal[i][2] - p0[2];
  107. tmp = sqrt(k1[0] * k1[0] + k1[1] * k1[1] + k1[2] * k1[2]);
  108. /* If k1.y > t.y.y, we don't want this point */
  109. double yk1 = y[0] * k1[0] + y[1] * k1[1] + y[2] * k1[2];
  110. if(yk1 > t * ylen * ylen + EPSILON)
  111. continue;
  112. for(j = 0; j < ncolors; j++)
  113. {
  114. if(i == j)
  115. continue;
  116. double k2[3];
  117. k2[0] = pal[j][0] - p0[0];
  118. k2[1] = pal[j][1] - p0[1];
  119. k2[2] = pal[j][2] - p0[2];
  120. tmp = sqrt(k2[0] * k2[0] + k2[1] * k2[1] + k2[2] * k2[2]);
  121. /* If k2.y < t.y.y, we don't want this point */
  122. double yk2 = y[0] * k2[0] + y[1] * k2[1] + y[2] * k2[2];
  123. if(yk2 < t * ylen * ylen - EPSILON)
  124. continue;
  125. if(yk2 < yk1)
  126. continue;
  127. double s = yk1 == yk2 ?
  128. 0.5 : (t * ylen * ylen - yk1) / (yk2 - yk1);
  129. pts[npts][0] = p0[0] + k1[0] + s * (k2[0] - k1[0]);
  130. pts[npts][1] = p0[1] + k1[1] + s * (k2[1] - k1[1]);
  131. pts[npts][2] = p0[2] + k1[2] + s * (k2[2] - k1[2]);
  132. npts++;
  133. }
  134. }
  135. /*
  136. * 3.2. Find the barycentre of these points' convex hull. We use
  137. * the Graham Scan technique.
  138. */
  139. /* Make our problem a 2-D problem. */
  140. for(i = 0; i < npts; i++)
  141. {
  142. pts[i][X] = (pts[i][0] - p0[0]) * u[0]
  143. + (pts[i][1] - p0[1]) * u[1]
  144. + (pts[i][2] - p0[2]) * u[2];
  145. pts[i][Y] = (pts[i][0] - p0[0]) * v[0]
  146. + (pts[i][1] - p0[1]) * v[1]
  147. + (pts[i][2] - p0[2]) * v[2];
  148. }
  149. /* Find the leftmost point */
  150. int left = -1;
  151. tmp = 10.;
  152. for(i = 0; i < npts; i++)
  153. if(pts[i][X] < tmp)
  154. {
  155. left = i;
  156. tmp = pts[i][X];
  157. }
  158. SWAP(pts[0], pts[left]);
  159. /* Sort the remaining points radially around pts[0] */
  160. for(i = 1; i < npts; i++)
  161. for(j = 1; j < npts - i; j++)
  162. if((pts[j][X] - pts[0][X]) * (pts[j + 1][Y] - pts[0][Y])
  163. < (pts[j + 1][X] - pts[0][X]) * (pts[j][Y] - pts[0][Y]))
  164. SWAP(pts[j], pts[j + 1]);
  165. /* Remove points not in the convex hull */
  166. for(i = 2; i < npts; /* */)
  167. {
  168. if(i < 2)
  169. {
  170. i++;
  171. continue;
  172. }
  173. if((pts[i - 1][X] - pts[i - 2][X]) * (pts[i][Y] - pts[i - 2][Y])
  174. < (pts[i][X] - pts[i - 2][X]) * (pts[i - 1][Y] - pts[i - 2][Y]))
  175. {
  176. for(j = i - 1; j < npts - 1; j++)
  177. SWAP(pts[j], pts[j + 1]);
  178. npts--;
  179. }
  180. else
  181. i++;
  182. }
  183. /* Compute the barycentre coordinates */
  184. double ctx = 0., cty = 0., weight = 0.;
  185. for(i = 2; i < npts; i++)
  186. {
  187. double abx = pts[i - 1][X] - pts[0][X];
  188. double aby = pts[i - 1][Y] - pts[0][Y];
  189. double acx = pts[i][X] - pts[0][X];
  190. double acy = pts[i][Y] - pts[0][Y];
  191. double sqarea = (abx * abx + aby * aby) * (acx * acx + acy * acy)
  192. - (abx * acx + aby * acy) * (abx * acx + aby * acy);
  193. if(sqarea <= 0.)
  194. continue;
  195. double area = sqrt(sqarea);
  196. ctx += area * (abx + acx) / 3;
  197. cty += area * (aby + acy) / 3;
  198. weight += area;
  199. }
  200. if(weight > EPSILON)
  201. {
  202. ctx = pts[0][X] + ctx / weight;
  203. cty = pts[0][Y] + cty / weight;
  204. }
  205. else
  206. {
  207. int right = -1;
  208. tmp = -10.;
  209. for(i = 0; i < npts; i++)
  210. if(pts[i][X] > tmp)
  211. {
  212. right = i;
  213. tmp = pts[i][X];
  214. }
  215. ctx = 0.5 * (pts[0][X] + pts[right][X]);
  216. cty = 0.5 * (pts[0][Y] + pts[right][Y]);
  217. }
  218. /*
  219. * 3.3. Store the barycentre and convex hull information.
  220. */
  221. ret->bary[n][0] = p0[0] + ctx * u[0] + cty * v[0];
  222. ret->bary[n][1] = p0[1] + ctx * u[1] + cty * v[1];
  223. ret->bary[n][2] = p0[2] + ctx * u[2] + cty * v[2];
  224. //if(ncolors == 6) printf("%i %g %g %g\n", n, ret->bary[n][0], ret->bary[n][1], ret->bary[n][2]);
  225. for(i = 0; i < npts; i++)
  226. {
  227. ret->pts[n][i][0] = pts[i][0];
  228. ret->pts[n][i][1] = pts[i][1];
  229. ret->pts[n][i][2] = pts[i][2];
  230. }
  231. ret->hullsize[n] = npts;
  232. }
  233. return ret;
  234. }
  235. int main(int argc, char *argv[])
  236. {
  237. static double const rgbpal[] =
  238. {
  239. 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
  240. 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
  241. };
  242. static double const mypal[] =
  243. {
  244. 0.8, 0.0, 0.0, /* red */
  245. 0.0, 0.8, 0.0, /* green */
  246. 0.0, 0.0, 0.6, /* blue */
  247. 1.0, 1.0, 1.0, /* white */
  248. 0.9, 0.9, 0.0, /* yellow */
  249. 0.8, 0.4, 0.0, /* orange */
  250. };
  251. int i, j;
  252. init_uv();
  253. hull_t *rgbhull = compute_hull(8, rgbpal);
  254. hull_t *myhull = compute_hull(6, mypal);
  255. /*
  256. * 4. Load image and change its palette.
  257. */
  258. pipi_image_t *src = pipi_load(argv[1]);
  259. pipi_pixels_t *srcp = pipi_getpixels(src, PIPI_PIXELS_RGBA_F);
  260. float *srcdata = (float *)srcp->pixels;
  261. int w = srcp->w, h = srcp->h;
  262. pipi_image_t *dst = pipi_new(w, h);
  263. pipi_pixels_t *dstp = pipi_getpixels(dst, PIPI_PIXELS_RGBA_F);
  264. float *dstdata = (float *)dstp->pixels;
  265. for(j = 0; j < h; j++)
  266. for(i = 0; i < w; i++)
  267. {
  268. double p[3];
  269. /* FIXME: Imlib fucks up the RGB order. */
  270. p[2] = srcdata[4 * (j * w + i)];
  271. p[1] = srcdata[4 * (j * w + i) + 1];
  272. p[0] = srcdata[4 * (j * w + i) + 2];
  273. double gray = BRIGHT(p);
  274. double dx = (p[0] - gray * y[0]) * u[0]
  275. + (p[1] - gray * y[1]) * u[1]
  276. + (p[2] - gray * y[2]) * u[2];
  277. double dy = (p[0] - gray * y[0]) * v[0]
  278. + (p[1] - gray * y[1]) * v[1]
  279. + (p[2] - gray * y[2]) * v[2];
  280. dstdata[4 * (j * w + i)] = myhull->bary[(int)(gray * STEPS)][2]
  281. + dx * u[2] * .3 + dy * v[2] * .3;
  282. dstdata[4 * (j * w + i) + 1] = myhull->bary[(int)(gray * STEPS)][1]
  283. + dx * u[1] * .3 + dy * v[1] * .3;
  284. dstdata[4 * (j * w + i) + 2] = myhull->bary[(int)(gray * STEPS)][0]
  285. + dx * u[0] * .3 + dy * v[0] * .3;
  286. }
  287. pipi_save(dst, argv[2]);
  288. return 0;
  289. }