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

/prism-j2d/src/com/sun/prism/j2d/paint/RadialGradientPaintContext.java

https://bitbucket.org/rbair/rbair-controls-8
Java | 466 lines | 193 code | 65 blank | 208 comment | 29 complexity | acfc672ed2505986db085396fe3f38ba 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.j2d.paint;
  26. import com.sun.prism.j2d.paint.MultipleGradientPaint.CycleMethod;
  27. import com.sun.prism.j2d.paint.MultipleGradientPaint.ColorSpaceType;
  28. import java.awt.Color;
  29. import java.awt.Rectangle;
  30. import java.awt.RenderingHints;
  31. import java.awt.geom.AffineTransform;
  32. import java.awt.geom.Rectangle2D;
  33. import java.awt.image.ColorModel;
  34. /**
  35. * Provides the actual implementation for the RadialGradientPaint.
  36. * This is where the pixel processing is done. A RadialGradienPaint
  37. * only supports circular gradients, but it should be possible to scale
  38. * the circle to look approximately elliptical, by means of a
  39. * gradient transform passed into the RadialGradientPaint constructor.
  40. */
  41. final class RadialGradientPaintContext extends MultipleGradientPaintContext {
  42. /** True when (focus == center). */
  43. private boolean isSimpleFocus = false;
  44. /** True when (cycleMethod == NO_CYCLE). */
  45. private boolean isNonCyclic = false;
  46. /** Radius of the outermost circle defining the 100% gradient stop. */
  47. private float radius;
  48. /** Variables representing center and focus points. */
  49. private float centerX, centerY, focusX, focusY;
  50. /** Radius of the gradient circle squared. */
  51. private float radiusSq;
  52. /** Constant part of X, Y user space coordinates. */
  53. private float constA, constB;
  54. /** Constant second order delta for simple loop. */
  55. private float gDeltaDelta;
  56. /**
  57. * This value represents the solution when focusX == X. It is called
  58. * trivial because it is easier to calculate than the general case.
  59. */
  60. private float trivial;
  61. /** Amount for offset when clamping focus. */
  62. private static final float SCALEBACK = .99f;
  63. /**
  64. * Constructor for RadialGradientPaintContext.
  65. *
  66. * @param paint the {@code RadialGradientPaint} from which this context
  67. * is created
  68. * @param cm the {@code ColorModel} that receives
  69. * the {@code Paint} data (this is used only as a hint)
  70. * @param deviceBounds the device space bounding box of the
  71. * graphics primitive being rendered
  72. * @param userBounds the user space bounding box of the
  73. * graphics primitive being rendered
  74. * @param t the {@code AffineTransform} from user
  75. * space into device space (gradientTransform should be
  76. * concatenated with this)
  77. * @param hints the hints that the context object uses to choose
  78. * between rendering alternatives
  79. * @param cx the center X coordinate in user space of the circle defining
  80. * the gradient. The last color of the gradient is mapped to
  81. * the perimeter of this circle.
  82. * @param cy the center Y coordinate in user space of the circle defining
  83. * the gradient. The last color of the gradient is mapped to
  84. * the perimeter of this circle.
  85. * @param r the radius of the circle defining the extents of the
  86. * color gradient
  87. * @param fx the X coordinate in user space to which the first color
  88. * is mapped
  89. * @param fy the Y coordinate in user space to which the first color
  90. * is mapped
  91. * @param fractions the fractions specifying the gradient distribution
  92. * @param colors the gradient colors
  93. * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
  94. * @param colorSpace which colorspace to use for interpolation,
  95. * either SRGB or LINEAR_RGB
  96. */
  97. RadialGradientPaintContext(RadialGradientPaint paint,
  98. ColorModel cm,
  99. Rectangle deviceBounds,
  100. Rectangle2D userBounds,
  101. AffineTransform t,
  102. RenderingHints hints,
  103. float cx, float cy,
  104. float r,
  105. float fx, float fy,
  106. float[] fractions,
  107. Color[] colors,
  108. CycleMethod cycleMethod,
  109. ColorSpaceType colorSpace)
  110. {
  111. super(paint, cm, deviceBounds, userBounds, t, hints,
  112. fractions, colors, cycleMethod, colorSpace);
  113. // copy some parameters
  114. centerX = cx;
  115. centerY = cy;
  116. focusX = fx;
  117. focusY = fy;
  118. radius = r;
  119. this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
  120. this.isNonCyclic = (cycleMethod == CycleMethod.NO_CYCLE);
  121. // for use in the quadractic equation
  122. radiusSq = radius * radius;
  123. float dX = focusX - centerX;
  124. float dY = focusY - centerY;
  125. double distSq = (dX * dX) + (dY * dY);
  126. // test if distance from focus to center is greater than the radius
  127. if (distSq > radiusSq * SCALEBACK) {
  128. // clamp focus to radius
  129. float scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);
  130. dX = dX * scalefactor;
  131. dY = dY * scalefactor;
  132. focusX = centerX + dX;
  133. focusY = centerY + dY;
  134. }
  135. // calculate the solution to be used in the case where X == focusX
  136. // in cyclicCircularGradientFillRaster()
  137. trivial = (float)Math.sqrt(radiusSq - (dX * dX));
  138. // constant parts of X, Y user space coordinates
  139. constA = a02 - centerX;
  140. constB = a12 - centerY;
  141. // constant second order delta for simple loop
  142. gDeltaDelta = 2 * ( a00 * a00 + a10 * a10) / radiusSq;
  143. }
  144. /**
  145. * Return a Raster containing the colors generated for the graphics
  146. * operation.
  147. *
  148. * @param x,y,w,h the area in device space for which colors are
  149. * generated.
  150. */
  151. protected void fillRaster(int pixels[], int off, int adjust,
  152. int x, int y, int w, int h)
  153. {
  154. if (isSimpleFocus && isNonCyclic && isSimpleLookup) {
  155. simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);
  156. } else {
  157. cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
  158. }
  159. }
  160. /**
  161. * This code works in the simplest of cases, where the focus == center
  162. * point, the gradient is noncyclic, and the gradient lookup method is
  163. * fast (single array index, no conversion necessary).
  164. */
  165. private void simpleNonCyclicFillRaster(int pixels[], int off, int adjust,
  166. int x, int y, int w, int h)
  167. {
  168. /* We calculate sqrt(X^2 + Y^2) relative to the radius
  169. * size to get the fraction for the color to use.
  170. *
  171. * Each step along the scanline adds (a00, a10) to (X, Y).
  172. * If we precalculate:
  173. * gRel = X^2+Y^2
  174. * for the start of the row, then for each step we need to
  175. * calculate:
  176. * gRel' = (X+a00)^2 + (Y+a10)^2
  177. * = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2
  178. * = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  179. * = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  180. * = gRel + 2*DP + SD
  181. * (where DP = dot product between X,Y and a00,a10
  182. * and SD = dot product square of the delta vector)
  183. * For the step after that we get:
  184. * gRel'' = (X+2*a00)^2 + (Y+2*a10)^2
  185. * = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2
  186. * = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)
  187. * = gRel + 4*DP + 4*SD
  188. * = gRel' + 2*DP + 3*SD
  189. * The increment changed by:
  190. * (gRel'' - gRel') - (gRel' - gRel)
  191. * = (2*DP + 3*SD) - (2*DP + SD)
  192. * = 2*SD
  193. * Note that this value depends only on the (inverse of the)
  194. * transformation matrix and so is a constant for the loop.
  195. * To make this all relative to the unit circle, we need to
  196. * divide all values as follows:
  197. * [XY] /= radius
  198. * gRel /= radiusSq
  199. * DP /= radiusSq
  200. * SD /= radiusSq
  201. */
  202. // coordinates of UL corner in "user space" relative to center
  203. float rowX = (a00*x) + (a01*y) + constA;
  204. float rowY = (a10*x) + (a11*y) + constB;
  205. // second order delta calculated in constructor
  206. float gDeltaDelta = this.gDeltaDelta;
  207. // adjust is (scan-w) of pixels array, we need (scan)
  208. adjust += w;
  209. // rgb of the 1.0 color used when the distance exceeds gradient radius
  210. int rgbclip = gradient[fastGradientArraySize];
  211. for (int j = 0; j < h; j++) {
  212. // these values depend on the coordinates of the start of the row
  213. float gRel = (rowX * rowX + rowY * rowY) / radiusSq;
  214. float gDelta = (2 * ( a00 * rowX + a10 * rowY) / radiusSq +
  215. gDeltaDelta/2);
  216. /* Use optimized loops for any cases where gRel >= 1.
  217. * We do not need to calculate sqrt(gRel) for these
  218. * values since sqrt(N>=1) == (M>=1).
  219. * Note that gRel follows a parabola which can only be < 1
  220. * for a small region around the center on each scanline. In
  221. * particular:
  222. * gDeltaDelta is always positive
  223. * gDelta is <0 until it crosses the midpoint, then >0
  224. * To the left and right of that region, it will always be
  225. * >=1 out to infinity, so we can process the line in 3
  226. * regions:
  227. * out to the left - quick fill until gRel < 1, updating gRel
  228. * in the heart - slow fraction=sqrt fill while gRel < 1
  229. * out to the right - quick fill rest of scanline, ignore gRel
  230. */
  231. int i = 0;
  232. // Quick fill for "out to the left"
  233. while (i < w && gRel >= 1.0f) {
  234. pixels[off + i] = rgbclip;
  235. gRel += gDelta;
  236. gDelta += gDeltaDelta;
  237. i++;
  238. }
  239. // Slow fill for "in the heart"
  240. while (i < w && gRel < 1.0f) {
  241. int gIndex;
  242. if (gRel <= 0) {
  243. gIndex = 0;
  244. } else {
  245. float fIndex = gRel * SQRT_LUT_SIZE;
  246. int iIndex = (int) (fIndex);
  247. float s0 = sqrtLut[iIndex];
  248. float s1 = sqrtLut[iIndex+1] - s0;
  249. fIndex = s0 + (fIndex - iIndex) * s1;
  250. gIndex = (int) (fIndex * fastGradientArraySize);
  251. }
  252. // store the color at this point
  253. pixels[off + i] = gradient[gIndex];
  254. // incremental calculation
  255. gRel += gDelta;
  256. gDelta += gDeltaDelta;
  257. i++;
  258. }
  259. // Quick fill to end of line for "out to the right"
  260. while (i < w) {
  261. pixels[off + i] = rgbclip;
  262. i++;
  263. }
  264. off += adjust;
  265. rowX += a01;
  266. rowY += a11;
  267. }
  268. }
  269. // SQRT_LUT_SIZE must be a power of 2 for the test above to work.
  270. private static final int SQRT_LUT_SIZE = (1 << 11);
  271. private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];
  272. static {
  273. for (int i = 0; i < sqrtLut.length; i++) {
  274. sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));
  275. }
  276. }
  277. /**
  278. * Fill the raster, cycling the gradient colors when a point falls outside
  279. * of the perimeter of the 100% stop circle.
  280. *
  281. * This calculation first computes the intersection point of the line
  282. * from the focus through the current point in the raster, and the
  283. * perimeter of the gradient circle.
  284. *
  285. * Then it determines the percentage distance of the current point along
  286. * that line (focus is 0%, perimeter is 100%).
  287. *
  288. * Equation of a circle centered at (a,b) with radius r:
  289. * (x-a)^2 + (y-b)^2 = r^2
  290. * Equation of a line with slope m and y-intercept b:
  291. * y = mx + b
  292. * Replacing y in the circle equation and solving using the quadratic
  293. * formula produces the following set of equations. Constant factors have
  294. * been extracted out of the inner loop.
  295. */
  296. private void cyclicCircularGradientFillRaster(int pixels[], int off,
  297. int adjust,
  298. int x, int y,
  299. int w, int h)
  300. {
  301. // constant part of the C factor of the quadratic equation
  302. final double constC =
  303. -radiusSq + (centerX * centerX) + (centerY * centerY);
  304. // coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
  305. double A, B, C;
  306. // slope and y-intercept of the focus-perimeter line
  307. double slope, yintcpt;
  308. // intersection with circle X,Y coordinate
  309. double solutionX, solutionY;
  310. // constant parts of X, Y coordinates
  311. final float constX = (a00*x) + (a01*y) + a02;
  312. final float constY = (a10*x) + (a11*y) + a12;
  313. // constants in inner loop quadratic formula
  314. final float precalc2 = 2 * centerY;
  315. final float precalc3 = -2 * centerX;
  316. // value between 0 and 1 specifying position in the gradient
  317. float g;
  318. // determinant of quadratic formula (should always be > 0)
  319. float det;
  320. // sq distance from the current point to focus
  321. float currentToFocusSq;
  322. // sq distance from the intersect point to focus
  323. float intersectToFocusSq;
  324. // temp variables for change in X,Y squared
  325. float deltaXSq, deltaYSq;
  326. // used to index pixels array
  327. int indexer = off;
  328. // incremental index change for pixels array
  329. int pixInc = w+adjust;
  330. // for every row
  331. if (trivial == 0) {
  332. // Optimization for case where we will calculate
  333. // exceptional values for nearly every pixel below.
  334. int rgb0 = indexIntoGradientsArrays(0f);
  335. for (int j = 0; j < h; j++) {
  336. for (int i = 0; i < w; i++) {
  337. pixels[indexer + i] = rgb0;
  338. }
  339. indexer += pixInc;
  340. }
  341. return;
  342. }
  343. for (int j = 0; j < h; j++) {
  344. // user space point; these are constant from column to column
  345. float X = (a01*j) + constX;
  346. float Y = (a11*j) + constY;
  347. // for every column (inner loop begins here)
  348. for (int i = 0; i < w; i++) {
  349. if (X == focusX) {
  350. // special case to avoid divide by zero
  351. solutionX = focusX;
  352. solutionY = centerY;
  353. solutionY += (Y > focusY) ? trivial : -trivial;
  354. } else {
  355. // slope and y-intercept of the focus-perimeter line
  356. slope = (Y - focusY) / (X - focusX);
  357. yintcpt = Y - (slope * X);
  358. // use the quadratic formula to calculate the
  359. // intersection point
  360. A = (slope * slope) + 1;
  361. B = precalc3 + (-2 * slope * (centerY - yintcpt));
  362. C = constC + (yintcpt* (yintcpt - precalc2));
  363. det = (float)Math.sqrt((B * B) - (4 * A * C));
  364. solutionX = -B;
  365. // choose the positive or negative root depending
  366. // on where the X coord lies with respect to the focus
  367. solutionX += (X < focusX)? -det : det;
  368. solutionX = solutionX / (2 * A); // divisor
  369. solutionY = (slope * solutionX) + yintcpt;
  370. }
  371. // Calculate the square of the distance from the current point
  372. // to the focus and the square of the distance from the
  373. // intersection point to the focus. Want the squares so we can
  374. // do 1 square root after division instead of 2 before.
  375. deltaXSq = X - focusX;
  376. deltaXSq = deltaXSq * deltaXSq;
  377. deltaYSq = Y - focusY;
  378. deltaYSq = deltaYSq * deltaYSq;
  379. currentToFocusSq = deltaXSq + deltaYSq;
  380. deltaXSq = (float)solutionX - focusX;
  381. deltaXSq = deltaXSq * deltaXSq;
  382. deltaYSq = (float)solutionY - focusY;
  383. deltaYSq = deltaYSq * deltaYSq;
  384. intersectToFocusSq = deltaXSq + deltaYSq;
  385. if (intersectToFocusSq == 0) {
  386. intersectToFocusSq =
  387. (solutionY >= focusY) ? trivial : -trivial;
  388. }
  389. // get the percentage (0-1) of the current point along the
  390. // focus-circumference line
  391. g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
  392. // store the color at this point
  393. pixels[indexer + i] = indexIntoGradientsArrays(g);
  394. // incremental change in X, Y
  395. X += a00;
  396. Y += a10;
  397. } //end inner loop
  398. indexer += pixInc;
  399. } //end outer loop
  400. }
  401. }