PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/prism-common/src/com/sun/prism/impl/paint/RadialGradientContext.java

https://bitbucket.org/rbair/rbair-controls-8
Java | 430 lines | 169 code | 65 blank | 196 comment | 23 complexity | d2bb5275769646bb1b52098ef01d8f96 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, GPL-2.0, LGPL-2.0
  1. /*
  2. * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Oracle designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Oracle in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22. * or visit www.oracle.com if you need additional information or have any
  23. * questions.
  24. */
  25. package com.sun.prism.impl.paint;
  26. import com.sun.javafx.geom.transform.BaseTransform;
  27. import com.sun.prism.paint.Color;
  28. import com.sun.prism.paint.Gradient;
  29. import com.sun.prism.paint.RadialGradient;
  30. /**
  31. * Provides the actual implementation for the RadialGradientPaint.
  32. * This is where the pixel processing is done. A RadialGradienPaint
  33. * only supports circular gradients, but it should be possible to scale
  34. * the circle to look approximately elliptical, by means of a
  35. * gradient transform passed into the RadialGradientPaint constructor.
  36. */
  37. final class RadialGradientContext extends MultipleGradientContext {
  38. /** True when (focus == center). */
  39. private boolean isSimpleFocus = false;
  40. /** True when (cycleMethod == NO_CYCLE). */
  41. private boolean isNonCyclic = false;
  42. /** Radius of the outermost circle defining the 100% gradient stop. */
  43. private float radius;
  44. /** Variables representing center and focus points. */
  45. private float centerX, centerY, focusX, focusY;
  46. /** Radius of the gradient circle squared. */
  47. private float radiusSq;
  48. /** Constant part of X, Y user space coordinates. */
  49. private float constA, constB;
  50. /** Constant second order delta for simple loop. */
  51. private float gDeltaDelta;
  52. /**
  53. * This value represents the solution when focusX == X. It is called
  54. * trivial because it is easier to calculate than the general case.
  55. */
  56. private float trivial;
  57. /** Amount for offset when clamping focus. */
  58. private static final float SCALEBACK = .99f;
  59. /**
  60. * Constructor for RadialGradientContext.
  61. *
  62. * @param paint the {@code RadialGradientPaint} from which this context
  63. * is created
  64. * @param t the {@code AffineTransform} from user
  65. * space into device space (gradientTransform should be
  66. * concatenated with this)
  67. * @param cx the center X coordinate in user space of the circle defining
  68. * the gradient. The last color of the gradient is mapped to
  69. * the perimeter of this circle.
  70. * @param cy the center Y coordinate in user space of the circle defining
  71. * the gradient. The last color of the gradient is mapped to
  72. * the perimeter of this circle.
  73. * @param r the radius of the circle defining the extents of the
  74. * color gradient
  75. * @param fx the X coordinate in user space to which the first color
  76. * is mapped
  77. * @param fy the Y coordinate in user space to which the first color
  78. * is mapped
  79. * @param fractions the fractions specifying the gradient distribution
  80. * @param colors the gradient colors
  81. * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
  82. */
  83. RadialGradientContext(RadialGradient paint,
  84. BaseTransform t,
  85. float cx, float cy,
  86. float r,
  87. float fx, float fy,
  88. float[] fractions,
  89. Color[] colors,
  90. int cycleMethod)
  91. {
  92. super(paint, t, fractions, colors, cycleMethod);
  93. // copy some parameters
  94. centerX = cx;
  95. centerY = cy;
  96. focusX = fx;
  97. focusY = fy;
  98. radius = r;
  99. this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
  100. this.isNonCyclic = (cycleMethod == Gradient.PAD);
  101. // for use in the quadractic equation
  102. radiusSq = radius * radius;
  103. float dX = focusX - centerX;
  104. float dY = focusY - centerY;
  105. double distSq = (dX * dX) + (dY * dY);
  106. // test if distance from focus to center is greater than the radius
  107. if (distSq > radiusSq * SCALEBACK) {
  108. // clamp focus to radius
  109. float scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);
  110. dX = dX * scalefactor;
  111. dY = dY * scalefactor;
  112. focusX = centerX + dX;
  113. focusY = centerY + dY;
  114. }
  115. // calculate the solution to be used in the case where X == focusX
  116. // in cyclicCircularGradientFillRaster()
  117. trivial = (float)Math.sqrt(radiusSq - (dX * dX));
  118. // constant parts of X, Y user space coordinates
  119. constA = a02 - centerX;
  120. constB = a12 - centerY;
  121. // constant second order delta for simple loop
  122. gDeltaDelta = 2 * ( a00 * a00 + a10 * a10) / radiusSq;
  123. }
  124. /**
  125. * Return a Raster containing the colors generated for the graphics
  126. * operation.
  127. *
  128. * @param x,y,w,h the area in device space for which colors are
  129. * generated.
  130. */
  131. protected void fillRaster(int pixels[], int off, int adjust,
  132. int x, int y, int w, int h)
  133. {
  134. if (isSimpleFocus && isNonCyclic && isSimpleLookup) {
  135. simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);
  136. } else {
  137. cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
  138. }
  139. }
  140. /**
  141. * This code works in the simplest of cases, where the focus == center
  142. * point, the gradient is noncyclic, and the gradient lookup method is
  143. * fast (single array index, no conversion necessary).
  144. */
  145. private void simpleNonCyclicFillRaster(int pixels[], int off, int adjust,
  146. int x, int y, int w, int h)
  147. {
  148. /* We calculate sqrt(X^2 + Y^2) relative to the radius
  149. * size to get the fraction for the color to use.
  150. *
  151. * Each step along the scanline adds (a00, a10) to (X, Y).
  152. * If we precalculate:
  153. * gRel = X^2+Y^2
  154. * for the start of the row, then for each step we need to
  155. * calculate:
  156. * gRel' = (X+a00)^2 + (Y+a10)^2
  157. * = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2
  158. * = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  159. * = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  160. * = gRel + 2*DP + SD
  161. * (where DP = dot product between X,Y and a00,a10
  162. * and SD = dot product square of the delta vector)
  163. * For the step after that we get:
  164. * gRel'' = (X+2*a00)^2 + (Y+2*a10)^2
  165. * = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2
  166. * = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)
  167. * = gRel + 4*DP + 4*SD
  168. * = gRel' + 2*DP + 3*SD
  169. * The increment changed by:
  170. * (gRel'' - gRel') - (gRel' - gRel)
  171. * = (2*DP + 3*SD) - (2*DP + SD)
  172. * = 2*SD
  173. * Note that this value depends only on the (inverse of the)
  174. * transformation matrix and so is a constant for the loop.
  175. * To make this all relative to the unit circle, we need to
  176. * divide all values as follows:
  177. * [XY] /= radius
  178. * gRel /= radiusSq
  179. * DP /= radiusSq
  180. * SD /= radiusSq
  181. */
  182. // coordinates of UL corner in "user space" relative to center
  183. float rowX = (a00*x) + (a01*y) + constA;
  184. float rowY = (a10*x) + (a11*y) + constB;
  185. // second order delta calculated in constructor
  186. float gDeltaDelta = this.gDeltaDelta;
  187. // adjust is (scan-w) of pixels array, we need (scan)
  188. adjust += w;
  189. // rgb of the 1.0 color used when the distance exceeds gradient radius
  190. int rgbclip = gradient[fastGradientArraySize];
  191. for (int j = 0; j < h; j++) {
  192. // these values depend on the coordinates of the start of the row
  193. float gRel = (rowX * rowX + rowY * rowY) / radiusSq;
  194. float gDelta = (2 * ( a00 * rowX + a10 * rowY) / radiusSq +
  195. gDeltaDelta/2);
  196. /* Use optimized loops for any cases where gRel >= 1.
  197. * We do not need to calculate sqrt(gRel) for these
  198. * values since sqrt(N>=1) == (M>=1).
  199. * Note that gRel follows a parabola which can only be < 1
  200. * for a small region around the center on each scanline. In
  201. * particular:
  202. * gDeltaDelta is always positive
  203. * gDelta is <0 until it crosses the midpoint, then >0
  204. * To the left and right of that region, it will always be
  205. * >=1 out to infinity, so we can process the line in 3
  206. * regions:
  207. * out to the left - quick fill until gRel < 1, updating gRel
  208. * in the heart - slow fraction=sqrt fill while gRel < 1
  209. * out to the right - quick fill rest of scanline, ignore gRel
  210. */
  211. int i = 0;
  212. // Quick fill for "out to the left"
  213. while (i < w && gRel >= 1.0f) {
  214. pixels[off + i] = rgbclip;
  215. gRel += gDelta;
  216. gDelta += gDeltaDelta;
  217. i++;
  218. }
  219. // Slow fill for "in the heart"
  220. while (i < w && gRel < 1.0f) {
  221. int gIndex;
  222. if (gRel <= 0) {
  223. gIndex = 0;
  224. } else {
  225. float fIndex = gRel * SQRT_LUT_SIZE;
  226. int iIndex = (int) (fIndex);
  227. float s0 = sqrtLut[iIndex];
  228. float s1 = sqrtLut[iIndex+1] - s0;
  229. fIndex = s0 + (fIndex - iIndex) * s1;
  230. gIndex = (int) (fIndex * fastGradientArraySize);
  231. }
  232. // store the color at this point
  233. pixels[off + i] = gradient[gIndex];
  234. // incremental calculation
  235. gRel += gDelta;
  236. gDelta += gDeltaDelta;
  237. i++;
  238. }
  239. // Quick fill to end of line for "out to the right"
  240. while (i < w) {
  241. pixels[off + i] = rgbclip;
  242. i++;
  243. }
  244. off += adjust;
  245. rowX += a01;
  246. rowY += a11;
  247. }
  248. }
  249. // SQRT_LUT_SIZE must be a power of 2 for the test above to work.
  250. private static final int SQRT_LUT_SIZE = (1 << 11);
  251. private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];
  252. static {
  253. for (int i = 0; i < sqrtLut.length; i++) {
  254. sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));
  255. }
  256. }
  257. /**
  258. * Fill the raster, cycling the gradient colors when a point falls outside
  259. * of the perimeter of the 100% stop circle.
  260. *
  261. * This calculation first computes the intersection point of the line
  262. * from the focus through the current point in the raster, and the
  263. * perimeter of the gradient circle.
  264. *
  265. * Then it determines the percentage distance of the current point along
  266. * that line (focus is 0%, perimeter is 100%).
  267. *
  268. * Equation of a circle centered at (a,b) with radius r:
  269. * (x-a)^2 + (y-b)^2 = r^2
  270. * Equation of a line with slope m and y-intercept b:
  271. * y = mx + b
  272. * Replacing y in the circle equation and solving using the quadratic
  273. * formula produces the following set of equations. Constant factors have
  274. * been extracted out of the inner loop.
  275. */
  276. private void cyclicCircularGradientFillRaster(int pixels[], int off,
  277. int adjust,
  278. int x, int y,
  279. int w, int h)
  280. {
  281. // constant part of the C factor of the quadratic equation
  282. final double constC =
  283. -radiusSq + (centerX * centerX) + (centerY * centerY);
  284. // coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
  285. double A, B, C;
  286. // slope and y-intercept of the focus-perimeter line
  287. double slope, yintcpt;
  288. // intersection with circle X,Y coordinate
  289. double solutionX, solutionY;
  290. // constant parts of X, Y coordinates
  291. final float constX = (a00*x) + (a01*y) + a02;
  292. final float constY = (a10*x) + (a11*y) + a12;
  293. // constants in inner loop quadratic formula
  294. final float precalc2 = 2 * centerY;
  295. final float precalc3 = -2 * centerX;
  296. // value between 0 and 1 specifying position in the gradient
  297. float g;
  298. // determinant of quadratic formula (should always be > 0)
  299. float det;
  300. // sq distance from the current point to focus
  301. float currentToFocusSq;
  302. // sq distance from the intersect point to focus
  303. float intersectToFocusSq;
  304. // temp variables for change in X,Y squared
  305. float deltaXSq, deltaYSq;
  306. // used to index pixels array
  307. int indexer = off;
  308. // incremental index change for pixels array
  309. int pixInc = w+adjust;
  310. // for every row
  311. for (int j = 0; j < h; j++) {
  312. // user space point; these are constant from column to column
  313. float X = (a01*j) + constX;
  314. float Y = (a11*j) + constY;
  315. // for every column (inner loop begins here)
  316. for (int i = 0; i < w; i++) {
  317. if (X == focusX) {
  318. // special case to avoid divide by zero
  319. solutionX = focusX;
  320. solutionY = centerY;
  321. solutionY += (Y > focusY) ? trivial : -trivial;
  322. } else {
  323. // slope and y-intercept of the focus-perimeter line
  324. slope = (Y - focusY) / (X - focusX);
  325. yintcpt = Y - (slope * X);
  326. // use the quadratic formula to calculate the
  327. // intersection point
  328. A = (slope * slope) + 1;
  329. B = precalc3 + (-2 * slope * (centerY - yintcpt));
  330. C = constC + (yintcpt* (yintcpt - precalc2));
  331. det = (float)Math.sqrt((B * B) - (4 * A * C));
  332. solutionX = -B;
  333. // choose the positive or negative root depending
  334. // on where the X coord lies with respect to the focus
  335. solutionX += (X < focusX)? -det : det;
  336. solutionX = solutionX / (2 * A); // divisor
  337. solutionY = (slope * solutionX) + yintcpt;
  338. }
  339. // Calculate the square of the distance from the current point
  340. // to the focus and the square of the distance from the
  341. // intersection point to the focus. Want the squares so we can
  342. // do 1 square root after division instead of 2 before.
  343. deltaXSq = X - focusX;
  344. deltaXSq = deltaXSq * deltaXSq;
  345. deltaYSq = Y - focusY;
  346. deltaYSq = deltaYSq * deltaYSq;
  347. currentToFocusSq = deltaXSq + deltaYSq;
  348. deltaXSq = (float)solutionX - focusX;
  349. deltaXSq = deltaXSq * deltaXSq;
  350. deltaYSq = (float)solutionY - focusY;
  351. deltaYSq = deltaYSq * deltaYSq;
  352. intersectToFocusSq = deltaXSq + deltaYSq;
  353. // get the percentage (0-1) of the current point along the
  354. // focus-circumference line
  355. g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
  356. // store the color at this point
  357. pixels[indexer + i] = indexIntoGradientsArrays(g);
  358. // incremental change in X, Y
  359. X += a00;
  360. Y += a10;
  361. } //end inner loop
  362. indexer += pixInc;
  363. } //end outer loop
  364. }
  365. }