ImageMath.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. ** Copyright 2005 Huxtable.com. All rights reserved.
  3. */
  4. package com.cvte.blurfilter;
  5. /**
  6. * A class containing static math methods useful for image processing.
  7. */
  8. public class ImageMath {
  9. public final static float PI = (float)Math.PI;
  10. public final static float HALF_PI = (float)Math.PI/2.0f;
  11. public final static float QUARTER_PI = (float)Math.PI/4.0f;
  12. public final static float TWO_PI = (float)Math.PI*2.0f;
  13. /**
  14. * Apply a bias to a number in the unit interval, moving numbers towards 0 or 1
  15. * according to the bias parameter.
  16. * @param a the number to bias
  17. * @param b the bias parameter. 0.5 means no change, smaller values bias towards 0, larger towards 1.
  18. * @return the output value
  19. */
  20. public static float bias(float a, float b) {
  21. // return (float)Math.pow(a, Math.log(b) / Math.log(0.5));
  22. return a/((1.0f/b-2)*(1.0f-a)+1);
  23. }
  24. /**
  25. * A variant of the gamma function.
  26. * @param a the number to apply gain to
  27. * @param b the gain parameter. 0.5 means no change, smaller values reduce gain, larger values increase gain.
  28. * @return the output value
  29. */
  30. public static float gain(float a, float b) {
  31. /*
  32. float p = (float)Math.log(1.0 - b) / (float)Math.log(0.5);
  33. if (a < .001)
  34. return 0.0f;
  35. else if (a > .999)
  36. return 1.0f;
  37. if (a < 0.5)
  38. return (float)Math.pow(2 * a, p) / 2;
  39. else
  40. return 1.0f - (float)Math.pow(2 * (1. - a), p) / 2;
  41. */
  42. float c = (1.0f/b-2.0f) * (1.0f-2.0f*a);
  43. if (a < 0.5)
  44. return a/(c+1.0f);
  45. else
  46. return (c-a)/(c-1.0f);
  47. }
  48. /**
  49. * The step function. Returns 0 below a threshold, 1 above.
  50. * @param a the threshold position
  51. * @param x the input parameter
  52. * @return the output value - 0 or 1
  53. */
  54. public static float step(float a, float x) {
  55. return (x < a) ? 0.0f : 1.0f;
  56. }
  57. /**
  58. * The pulse function. Returns 1 between two thresholds, 0 outside.
  59. * @param a the lower threshold position
  60. * @param b the upper threshold position
  61. * @param x the input parameter
  62. * @return the output value - 0 or 1
  63. */
  64. public static float pulse(float a, float b, float x) {
  65. return (x < a || x >= b) ? 0.0f : 1.0f;
  66. }
  67. /**
  68. * A smoothed pulse function. A cubic function is used to smooth the step between two thresholds.
  69. * @param a1 the lower threshold position for the start of the pulse
  70. * @param a2 the upper threshold position for the start of the pulse
  71. * @param b1 the lower threshold position for the end of the pulse
  72. * @param b2 the upper threshold position for the end of the pulse
  73. * @param x the input parameter
  74. * @return the output value
  75. */
  76. public static float smoothPulse(float a1, float a2, float b1, float b2, float x) {
  77. if (x < a1 || x >= b2)
  78. return 0;
  79. if (x >= a2) {
  80. if (x < b1)
  81. return 1.0f;
  82. x = (x - b1) / (b2 - b1);
  83. return 1.0f - (x*x * (3.0f - 2.0f*x));
  84. }
  85. x = (x - a1) / (a2 - a1);
  86. return x*x * (3.0f - 2.0f*x);
  87. }
  88. /**
  89. * A smoothed step function. A cubic function is used to smooth the step between two thresholds.
  90. * @param a the lower threshold position
  91. * @param b the upper threshold position
  92. * @param x the input parameter
  93. * @return the output value
  94. */
  95. public static float smoothStep(float a, float b, float x) {
  96. if (x < a)
  97. return 0;
  98. if (x >= b)
  99. return 1;
  100. x = (x - a) / (b - a);
  101. return x*x * (3 - 2*x);
  102. }
  103. /**
  104. * A "circle up" function. Returns y on a unit circle given 1-x. Useful for forming bevels.
  105. * @param x the input parameter in the range 0..1
  106. * @return the output value
  107. */
  108. public static float circleUp(float x) {
  109. x = 1-x;
  110. return (float)Math.sqrt(1-x*x);
  111. }
  112. /**
  113. * A "circle down" function. Returns 1-y on a unit circle given x. Useful for forming bevels.
  114. * @param x the input parameter in the range 0..1
  115. * @return the output value
  116. */
  117. public static float circleDown(float x) {
  118. return 1.0f-(float)Math.sqrt(1-x*x);
  119. }
  120. /**
  121. * Clamp a value to an interval.
  122. * @param a the lower clamp threshold
  123. * @param b the upper clamp threshold
  124. * @param x the input parameter
  125. * @return the clamped value
  126. */
  127. public static float clamp(float x, float a, float b) {
  128. return (x < a) ? a : (x > b) ? b : x;
  129. }
  130. /**
  131. * Clamp a value to an interval.
  132. * @param a the lower clamp threshold
  133. * @param b the upper clamp threshold
  134. * @param x the input parameter
  135. * @return the clamped value
  136. */
  137. public static int clamp(int x, int a, int b) {
  138. return (x < a) ? a : (x > b) ? b : x;
  139. }
  140. /**
  141. * Return a mod b. This differs from the % operator with respect to negative numbers.
  142. * @param a the dividend
  143. * @param b the divisor
  144. * @return a mod b
  145. */
  146. public static double mod(double a, double b) {
  147. int n = (int)(a/b);
  148. a -= n*b;
  149. if (a < 0)
  150. return a + b;
  151. return a;
  152. }
  153. /**
  154. * Return a mod b. This differs from the % operator with respect to negative numbers.
  155. * @param a the dividend
  156. * @param b the divisor
  157. * @return a mod b
  158. */
  159. public static float mod(float a, float b) {
  160. int n = (int)(a/b);
  161. a -= n*b;
  162. if (a < 0)
  163. return a + b;
  164. return a;
  165. }
  166. /**
  167. * Return a mod b. This differs from the % operator with respect to negative numbers.
  168. * @param a the dividend
  169. * @param b the divisor
  170. * @return a mod b
  171. */
  172. public static int mod(int a, int b) {
  173. int n = a/b;
  174. a -= n*b;
  175. if (a < 0)
  176. return a + b;
  177. return a;
  178. }
  179. /**
  180. * The triangle function. Returns a repeating triangle shape in the range 0..1 with wavelength 1.0
  181. * @param x the input parameter
  182. * @return the output value
  183. */
  184. public static float triangle(float x) {
  185. float r = mod(x, 1.0f);
  186. return 2.0f*(r < 0.5 ? r : 1-r);
  187. }
  188. /**
  189. * Linear interpolation.
  190. * @param t the interpolation parameter
  191. * @param a the lower interpolation range
  192. * @param b the upper interpolation range
  193. * @return the interpolated value
  194. */
  195. public static float lerp(float t, float a, float b) {
  196. return a + t * (b - a);
  197. }
  198. /**
  199. * Linear interpolation.
  200. * @param t the interpolation parameter
  201. * @param a the lower interpolation range
  202. * @param b the upper interpolation range
  203. * @return the interpolated value
  204. */
  205. public static int lerp(float t, int a, int b) {
  206. return (int)(a + t * (b - a));
  207. }
  208. /**
  209. * Linear interpolation of ARGB values.
  210. * @param t the interpolation parameter
  211. * @param rgb1 the lower interpolation range
  212. * @param rgb2 the upper interpolation range
  213. * @return the interpolated value
  214. */
  215. public static int mixColors(float t, int rgb1, int rgb2) {
  216. int a1 = (rgb1 >> 24) & 0xff;
  217. int r1 = (rgb1 >> 16) & 0xff;
  218. int g1 = (rgb1 >> 8) & 0xff;
  219. int b1 = rgb1 & 0xff;
  220. int a2 = (rgb2 >> 24) & 0xff;
  221. int r2 = (rgb2 >> 16) & 0xff;
  222. int g2 = (rgb2 >> 8) & 0xff;
  223. int b2 = rgb2 & 0xff;
  224. a1 = lerp(t, a1, a2);
  225. r1 = lerp(t, r1, r2);
  226. g1 = lerp(t, g1, g2);
  227. b1 = lerp(t, b1, b2);
  228. return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
  229. }
  230. /**
  231. * Bilinear interpolation of ARGB values.
  232. * @param x the X interpolation parameter 0..1
  233. * @param y the y interpolation parameter 0..1
  234. * @param rgb array of four ARGB values in the order NW, NE, SW, SE
  235. * @return the interpolated value
  236. */
  237. public static int bilinearInterpolate(float x, float y, int[] p) {
  238. float m0, m1;
  239. int a0 = (p[0] >> 24) & 0xff;
  240. int r0 = (p[0] >> 16) & 0xff;
  241. int g0 = (p[0] >> 8) & 0xff;
  242. int b0 = p[0] & 0xff;
  243. int a1 = (p[1] >> 24) & 0xff;
  244. int r1 = (p[1] >> 16) & 0xff;
  245. int g1 = (p[1] >> 8) & 0xff;
  246. int b1 = p[1] & 0xff;
  247. int a2 = (p[2] >> 24) & 0xff;
  248. int r2 = (p[2] >> 16) & 0xff;
  249. int g2 = (p[2] >> 8) & 0xff;
  250. int b2 = p[2] & 0xff;
  251. int a3 = (p[3] >> 24) & 0xff;
  252. int r3 = (p[3] >> 16) & 0xff;
  253. int g3 = (p[3] >> 8) & 0xff;
  254. int b3 = p[3] & 0xff;
  255. float cx = 1.0f-x;
  256. float cy = 1.0f-y;
  257. m0 = cx * a0 + x * a1;
  258. m1 = cx * a2 + x * a3;
  259. int a = (int)(cy * m0 + y * m1);
  260. m0 = cx * r0 + x * r1;
  261. m1 = cx * r2 + x * r3;
  262. int r = (int)(cy * m0 + y * m1);
  263. m0 = cx * g0 + x * g1;
  264. m1 = cx * g2 + x * g3;
  265. int g = (int)(cy * m0 + y * m1);
  266. m0 = cx * b0 + x * b1;
  267. m1 = cx * b2 + x * b3;
  268. int b = (int)(cy * m0 + y * m1);
  269. return (a << 24) | (r << 16) | (g << 8) | b;
  270. }
  271. /**
  272. * Return the NTSC gray level of an RGB value.
  273. * @param rgb1 the input pixel
  274. * @return the gray level (0-255)
  275. */
  276. public static int brightnessNTSC(int rgb) {
  277. int r = (rgb >> 16) & 0xff;
  278. int g = (rgb >> 8) & 0xff;
  279. int b = rgb & 0xff;
  280. return (int)(r*0.299f + g*0.587f + b*0.114f);
  281. }
  282. // Catmull-Rom splines
  283. private final static float m00 = -0.5f;
  284. private final static float m01 = 1.5f;
  285. private final static float m02 = -1.5f;
  286. private final static float m03 = 0.5f;
  287. private final static float m10 = 1.0f;
  288. private final static float m11 = -2.5f;
  289. private final static float m12 = 2.0f;
  290. private final static float m13 = -0.5f;
  291. private final static float m20 = -0.5f;
  292. private final static float m21 = 0.0f;
  293. private final static float m22 = 0.5f;
  294. private final static float m23 = 0.0f;
  295. private final static float m30 = 0.0f;
  296. private final static float m31 = 1.0f;
  297. private final static float m32 = 0.0f;
  298. private final static float m33 = 0.0f;
  299. /**
  300. * Compute a Catmull-Rom spline.
  301. * @param x the input parameter
  302. * @param numKnots the number of knots in the spline
  303. * @param knots the array of knots
  304. * @return the spline value
  305. */
  306. public static float spline(float x, int numKnots, float[] knots) {
  307. int span;
  308. int numSpans = numKnots - 3;
  309. float k0, k1, k2, k3;
  310. float c0, c1, c2, c3;
  311. if (numSpans < 1)
  312. throw new IllegalArgumentException("Too few knots in spline");
  313. x = clamp(x, 0, 1) * numSpans;
  314. span = (int)x;
  315. if (span > numKnots-4)
  316. span = numKnots-4;
  317. x -= span;
  318. k0 = knots[span];
  319. k1 = knots[span+1];
  320. k2 = knots[span+2];
  321. k3 = knots[span+3];
  322. c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3;
  323. c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3;
  324. c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3;
  325. c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3;
  326. return ((c3*x + c2)*x + c1)*x + c0;
  327. }
  328. /**
  329. * Compute a Catmull-Rom spline, but with variable knot spacing.
  330. * @param x the input parameter
  331. * @param numKnots the number of knots in the spline
  332. * @param xknots the array of knot x values
  333. * @param yknots the array of knot y values
  334. * @return the spline value
  335. */
  336. public static float spline(float x, int numKnots, int[] xknots, int[] yknots) {
  337. int span;
  338. int numSpans = numKnots - 3;
  339. float k0, k1, k2, k3;
  340. float c0, c1, c2, c3;
  341. if (numSpans < 1)
  342. throw new IllegalArgumentException("Too few knots in spline");
  343. for (span = 0; span < numSpans; span++)
  344. if (xknots[span+1] > x)
  345. break;
  346. if (span > numKnots-3)
  347. span = numKnots-3;
  348. float t = (float)(x-xknots[span]) / (xknots[span+1]-xknots[span]);
  349. span--;
  350. if (span < 0) {
  351. span = 0;
  352. t = 0;
  353. }
  354. k0 = yknots[span];
  355. k1 = yknots[span+1];
  356. k2 = yknots[span+2];
  357. k3 = yknots[span+3];
  358. c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3;
  359. c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3;
  360. c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3;
  361. c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3;
  362. return ((c3*t + c2)*t + c1)*t + c0;
  363. }
  364. /**
  365. * Compute a Catmull-Rom spline for RGB values.
  366. * @param x the input parameter
  367. * @param numKnots the number of knots in the spline
  368. * @param knots the array of knots
  369. * @return the spline value
  370. */
  371. public static int colorSpline(float x, int numKnots, int[] knots) {
  372. int span;
  373. int numSpans = numKnots - 3;
  374. float k0, k1, k2, k3;
  375. float c0, c1, c2, c3;
  376. if (numSpans < 1)
  377. throw new IllegalArgumentException("Too few knots in spline");
  378. x = clamp(x, 0, 1) * numSpans;
  379. span = (int)x;
  380. if (span > numKnots-4)
  381. span = numKnots-4;
  382. x -= span;
  383. int v = 0;
  384. for (int i = 0; i < 4; i++) {
  385. int shift = i * 8;
  386. k0 = (knots[span] >> shift) & 0xff;
  387. k1 = (knots[span+1] >> shift) & 0xff;
  388. k2 = (knots[span+2] >> shift) & 0xff;
  389. k3 = (knots[span+3] >> shift) & 0xff;
  390. c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3;
  391. c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3;
  392. c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3;
  393. c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3;
  394. int n = (int)(((c3*x + c2)*x + c1)*x + c0);
  395. if (n < 0)
  396. n = 0;
  397. else if (n > 255)
  398. n = 255;
  399. v |= n << shift;
  400. }
  401. return v;
  402. }
  403. /**
  404. * Compute a Catmull-Rom spline for RGB values, but with variable knot spacing.
  405. * @param x the input parameter
  406. * @param numKnots the number of knots in the spline
  407. * @param xknots the array of knot x values
  408. * @param yknots the array of knot y values
  409. * @return the spline value
  410. */
  411. public static int colorSpline(int x, int numKnots, int[] xknots, int[] yknots) {
  412. int span;
  413. int numSpans = numKnots - 3;
  414. float k0, k1, k2, k3;
  415. float c0, c1, c2, c3;
  416. if (numSpans < 1)
  417. throw new IllegalArgumentException("Too few knots in spline");
  418. for (span = 0; span < numSpans; span++)
  419. if (xknots[span+1] > x)
  420. break;
  421. if (span > numKnots-3)
  422. span = numKnots-3;
  423. float t = (float)(x-xknots[span]) / (xknots[span+1]-xknots[span]);
  424. span--;
  425. if (span < 0) {
  426. span = 0;
  427. t = 0;
  428. }
  429. int v = 0;
  430. for (int i = 0; i < 4; i++) {
  431. int shift = i * 8;
  432. k0 = (yknots[span] >> shift) & 0xff;
  433. k1 = (yknots[span+1] >> shift) & 0xff;
  434. k2 = (yknots[span+2] >> shift) & 0xff;
  435. k3 = (yknots[span+3] >> shift) & 0xff;
  436. c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3;
  437. c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3;
  438. c1 = m20*k0 + m21*k1 + m22*k2 + m23*k3;
  439. c0 = m30*k0 + m31*k1 + m32*k2 + m33*k3;
  440. int n = (int)(((c3*t + c2)*t + c1)*t + c0);
  441. if (n < 0)
  442. n = 0;
  443. else if (n > 255)
  444. n = 255;
  445. v |= n << shift;
  446. }
  447. return v;
  448. }
  449. /**
  450. * An implementation of Fant's resampling algorithm.
  451. * @param source the source pixels
  452. * @param dest the destination pixels
  453. * @param length the length of the scanline to resample
  454. * @param offset the start offset into the arrays
  455. * @param stride the offset between pixels in consecutive rows
  456. * @param out an array of output positions for each pixel
  457. */
  458. public static void resample(int[] source, int[] dest, int length, int offset, int stride, float[] out) {
  459. int i, j;
  460. float intensity;
  461. float sizfac;
  462. float inSegment;
  463. float outSegment;
  464. int a, r, g, b, nextA, nextR, nextG, nextB;
  465. float aSum, rSum, gSum, bSum;
  466. float[] in;
  467. int srcIndex = offset;
  468. int destIndex = offset;
  469. int lastIndex = source.length;
  470. int rgb;
  471. in = new float[length+1];
  472. i = 0;
  473. for (j = 0; j < length; j++) {
  474. while (out[i+1] < j)
  475. i++;
  476. in[j] = i + (float) (j - out[i]) / (out[i + 1] - out[i]);
  477. }
  478. in[length] = length;
  479. inSegment = 1.0f;
  480. outSegment = in[1];
  481. sizfac = outSegment;
  482. aSum = rSum = gSum = bSum = 0.0f;
  483. rgb = source[srcIndex];
  484. a = (rgb >> 24) & 0xff;
  485. r = (rgb >> 16) & 0xff;
  486. g = (rgb >> 8) & 0xff;
  487. b = rgb & 0xff;
  488. srcIndex += stride;
  489. rgb = source[srcIndex];
  490. nextA = (rgb >> 24) & 0xff;
  491. nextR = (rgb >> 16) & 0xff;
  492. nextG = (rgb >> 8) & 0xff;
  493. nextB = rgb & 0xff;
  494. srcIndex += stride;
  495. i = 1;
  496. while (i < length) {
  497. float aIntensity = inSegment * a + (1.0f - inSegment) * nextA;
  498. float rIntensity = inSegment * r + (1.0f - inSegment) * nextR;
  499. float gIntensity = inSegment * g + (1.0f - inSegment) * nextG;
  500. float bIntensity = inSegment * b + (1.0f - inSegment) * nextB;
  501. if (inSegment < outSegment) {
  502. aSum += (aIntensity * inSegment);
  503. rSum += (rIntensity * inSegment);
  504. gSum += (gIntensity * inSegment);
  505. bSum += (bIntensity * inSegment);
  506. outSegment -= inSegment;
  507. inSegment = 1.0f;
  508. a = nextA;
  509. r = nextR;
  510. g = nextG;
  511. b = nextB;
  512. if (srcIndex < lastIndex)
  513. rgb = source[srcIndex];
  514. nextA = (rgb >> 24) & 0xff;
  515. nextR = (rgb >> 16) & 0xff;
  516. nextG = (rgb >> 8) & 0xff;
  517. nextB = rgb & 0xff;
  518. srcIndex += stride;
  519. } else {
  520. aSum += (aIntensity * outSegment);
  521. rSum += (rIntensity * outSegment);
  522. gSum += (gIntensity * outSegment);
  523. bSum += (bIntensity * outSegment);
  524. dest[destIndex] =
  525. ((int)Math.min(aSum/sizfac, 255) << 24) |
  526. ((int)Math.min(rSum/sizfac, 255) << 16) |
  527. ((int)Math.min(gSum/sizfac, 255) << 8) |
  528. (int)Math.min(bSum/sizfac, 255);
  529. destIndex += stride;
  530. rSum = gSum = bSum = 0.0f;
  531. inSegment -= outSegment;
  532. outSegment = in[i+1] - in[i];
  533. sizfac = outSegment;
  534. i++;
  535. }
  536. }
  537. }
  538. }