1 /* 2 * Copyright (c) 2012, 2014, 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 26 package com.sun.prism.sw; 27 28 import com.sun.javafx.geom.RectBounds; 29 import com.sun.javafx.geom.Shape; 30 import com.sun.javafx.geom.transform.Affine2D; 31 import com.sun.javafx.geom.transform.BaseTransform; 32 import com.sun.pisces.GradientColorMap; 33 import com.sun.pisces.PiscesRenderer; 34 import com.sun.pisces.RendererBase; 35 import com.sun.pisces.Transform6; 36 import com.sun.prism.Image; 37 import com.sun.prism.PixelFormat; 38 import com.sun.prism.Texture; 39 import com.sun.prism.impl.PrismSettings; 40 import com.sun.prism.paint.Color; 41 import com.sun.prism.paint.Gradient; 42 import com.sun.prism.paint.ImagePattern; 43 import com.sun.prism.paint.LinearGradient; 44 import com.sun.prism.paint.Paint; 45 import com.sun.prism.paint.RadialGradient; 46 import com.sun.prism.paint.Stop; 47 48 final class SWPaint { 49 50 private final SWContext context; 51 private final PiscesRenderer pr; 52 53 private final BaseTransform paintTx = new Affine2D(); 54 private final Transform6 piscesTx = new Transform6(); 55 56 private float compositeAlpha = 1.0f; 57 private float px, py, pw, ph; 58 59 SWPaint(SWContext context, PiscesRenderer pr) { 60 this.context = context; 61 this.pr = pr; 62 } 63 64 float getCompositeAlpha() { 65 return compositeAlpha; 66 } 67 68 void setCompositeAlpha(float newValue) { 69 compositeAlpha = newValue; 70 } 71 72 void setColor(Color c, float compositeAlpha) { 73 if (PrismSettings.debug) { 74 System.out.println("PR.setColor: " + c); 75 } 76 this.pr.setColor((int) (c.getRed() * 255), 77 (int) (255 * c.getGreen()), 78 (int) (255 * c.getBlue()), 79 (int) (255 * c.getAlpha() * compositeAlpha)); 80 } 81 82 void setPaintFromShape(Paint p, BaseTransform tx, Shape shape, RectBounds nodeBounds, 83 float localX, float localY, float localWidth, float localHeight) 84 { 85 this.computePaintBounds(p, shape, nodeBounds, localX, localY, localWidth, localHeight); 86 this.setPaintBeforeDraw(p, tx, px, py, pw, ph); 87 } 88 89 private void computePaintBounds(Paint p, Shape shape, RectBounds nodeBounds, 90 float localX, float localY, float localWidth, float localHeight) 91 { 92 if (p.isProportional()) { 93 if (nodeBounds != null) { 94 px = nodeBounds.getMinX(); 95 py = nodeBounds.getMinY(); 96 pw = nodeBounds.getWidth(); 97 ph = nodeBounds.getHeight(); 98 } else if (shape != null) { 99 final RectBounds bounds = shape.getBounds(); 100 px = bounds.getMinX(); 101 py = bounds.getMinY(); 102 pw = bounds.getWidth(); 103 ph = bounds.getHeight(); 104 } else { 105 px = localX; 106 py = localY; 107 pw = localWidth; 108 ph = localHeight; 109 } 110 } else { 111 px = py = pw = ph = 0; 112 } 113 } 114 115 void setPaintBeforeDraw(Paint p, BaseTransform tx, float x, float y, float width, float height) { 116 switch (p.getType()) { 117 case COLOR: 118 this.setColor((Color)p, this.compositeAlpha); 119 break; 120 case LINEAR_GRADIENT: 121 final LinearGradient lg = (LinearGradient)p; 122 if (PrismSettings.debug) { 123 System.out.println("PR.setLinearGradient: " + lg.getX1() + ", " + lg.getY1() + ", " + lg.getX2() + ", " + lg.getY2()); 124 } 125 126 paintTx.setTransform(tx); 127 SWUtils.convertToPiscesTransform(paintTx, piscesTx); 128 129 float x1 = lg.getX1(); 130 float y1 = lg.getY1(); 131 float x2 = lg.getX2(); 132 float y2 = lg.getY2(); 133 if (lg.isProportional()) { 134 x1 = x + width * x1; 135 y1 = y + height * y1; 136 x2 = x + width * x2; 137 y2 = y + height * y2; 138 } 139 this.pr.setLinearGradient((int)(SWUtils.TO_PISCES * x1), (int)(SWUtils.TO_PISCES * y1), 140 (int)(SWUtils.TO_PISCES * x2), (int)(SWUtils.TO_PISCES * y2), 141 getFractions(lg), getARGB(lg, this.compositeAlpha), getPiscesGradientCycleMethod(lg.getSpreadMethod()), piscesTx); 142 break; 143 case RADIAL_GRADIENT: 144 final RadialGradient rg = (RadialGradient)p; 145 if (PrismSettings.debug) { 146 System.out.println("PR.setRadialGradient: " + rg.getCenterX() + ", " + rg.getCenterY() + ", " + rg.getFocusAngle() + ", " + rg.getFocusDistance() + ", " + rg.getRadius()); 147 } 148 149 paintTx.setTransform(tx); 150 151 float cx = rg.getCenterX(); 152 float cy = rg.getCenterY(); 153 float r = rg.getRadius(); 154 if (rg.isProportional()) { 155 float dim = Math.min(width, height); 156 float bcx = x + width * 0.5f; 157 float bcy = y + height * 0.5f; 158 cx = bcx + (cx - 0.5f) * dim; 159 cy = bcy + (cy - 0.5f) * dim; 160 r *= dim; 161 if (width != height && width != 0.0 && height != 0.0) { 162 paintTx.deriveWithTranslation(bcx, bcy); 163 paintTx.deriveWithConcatenation(width / dim, 0, 0, height / dim, 0, 0); 164 paintTx.deriveWithTranslation(-bcx, -bcy); 165 } 166 } 167 SWUtils.convertToPiscesTransform(paintTx, piscesTx); 168 169 final float fx = (float)(cx + rg.getFocusDistance() * r * Math.cos(Math.toRadians(rg.getFocusAngle()))); 170 final float fy = (float)(cy + rg.getFocusDistance() * r * Math.sin(Math.toRadians(rg.getFocusAngle()))); 171 172 this.pr.setRadialGradient((int) (SWUtils.TO_PISCES * cx), (int) (SWUtils.TO_PISCES * cy), 173 (int) (SWUtils.TO_PISCES * fx), (int) (SWUtils.TO_PISCES * fy), (int) (SWUtils.TO_PISCES * r), 174 getFractions(rg), getARGB(rg, this.compositeAlpha), getPiscesGradientCycleMethod(rg.getSpreadMethod()), piscesTx); 175 break; 176 case IMAGE_PATTERN: 177 final ImagePattern ip = (ImagePattern)p; 178 if (ip.getImage().getPixelFormat() == PixelFormat.BYTE_ALPHA) { 179 throw new UnsupportedOperationException("Alpha image is not supported as an image pattern."); 180 } else { 181 this.computeImagePatternTransform(ip, tx, x, y, width, height); 182 final SWArgbPreTexture tex = context.validateImagePaintTexture(ip.getImage().getWidth(), ip.getImage().getHeight()); 183 tex.update(ip.getImage()); 184 if (this.compositeAlpha < 1.0f) { 185 tex.applyCompositeAlpha(this.compositeAlpha); 186 } 187 188 this.pr.setTexture(RendererBase.TYPE_INT_ARGB_PRE, tex.getDataNoClone(), 189 tex.getContentWidth(), tex.getContentHeight(), tex.getPhysicalWidth(), 190 piscesTx, 191 tex.getWrapMode() == Texture.WrapMode.REPEAT, 192 tex.hasAlpha()); 193 } 194 break; 195 default: 196 throw new IllegalArgumentException("Unknown paint type: " + p.getType()); 197 } 198 } 199 200 private static int[] getARGB(Gradient grd, float compositeAlpha) { 201 final int nstops = grd.getNumStops(); 202 final int argb[] = new int[nstops]; 203 for (int i = 0; i < nstops; i++) { 204 final Stop stop = grd.getStops().get(i); 205 final Color stopColor = stop.getColor(); 206 float alpha255 = 255 * stopColor.getAlpha() * compositeAlpha; 207 argb[i] = ((((int)(alpha255)) & 0xFF) << 24) + 208 ((((int)(alpha255 * stopColor.getRed())) & 0xFF) << 16) + 209 ((((int)(alpha255 * stopColor.getGreen())) & 0xFF) << 8) + 210 (((int)(alpha255 * stopColor.getBlue())) & 0xFF); 211 } 212 return argb; 213 } 214 215 private static int[] getFractions(Gradient grd) { 216 final int nstops = grd.getNumStops(); 217 final int fractions[] = new int[nstops]; 218 for (int i = 0; i < nstops; i++) { 219 final Stop stop = grd.getStops().get(i); 220 fractions[i] = (int)(SWUtils.TO_PISCES * stop.getOffset()); 221 } 222 return fractions; 223 } 224 225 private static int getPiscesGradientCycleMethod(final int prismCycleMethod) { 226 switch (prismCycleMethod) { 227 case Gradient.PAD: 228 return GradientColorMap.CYCLE_NONE; 229 case Gradient.REFLECT: 230 return GradientColorMap.CYCLE_REFLECT; 231 case Gradient.REPEAT: 232 return GradientColorMap.CYCLE_REPEAT; 233 } 234 return GradientColorMap.CYCLE_NONE; 235 } 236 237 Transform6 computeDrawTexturePaintTransform(BaseTransform tx, float dx1, float dy1, float dx2, float dy2, 238 float sx1, float sy1, float sx2, float sy2) 239 { 240 paintTx.setTransform(tx); 241 242 final float scaleX = computeScale(dx1, dx2, sx1, sx2); 243 final float scaleY = computeScale(dy1, dy2, sy1, sy2); 244 245 if (scaleX == 1 && scaleY == 1) { 246 paintTx.deriveWithTranslation(-Math.min(sx1, sx2) + Math.min(dx1, dx2), 247 -Math.min(sy1, sy2) + Math.min(dy1, dy2)); 248 } else { 249 paintTx.deriveWithTranslation(Math.min(dx1, dx2), Math.min(dy1, dy2)); 250 paintTx.deriveWithTranslation((scaleX >= 0) ? 0 : Math.abs(dx2 - dx1), 251 (scaleY >= 0) ? 0 : Math.abs(dy2 - dy1)); 252 paintTx.deriveWithConcatenation(scaleX, 0, 0, scaleY, 0, 0); 253 paintTx.deriveWithTranslation(-Math.min(sx1, sx2), -Math.min(sy1, sy2)); 254 } 255 256 SWUtils.convertToPiscesTransform(paintTx, piscesTx); 257 return piscesTx; 258 } 259 260 private float computeScale(float dv1, float dv2, float sv1, float sv2) { 261 final float dv_diff = dv2 - dv1; 262 float scale = dv_diff / (sv2 - sv1); 263 if (Math.abs(scale) > (Integer.MAX_VALUE >> 16)) { 264 scale = Math.signum(scale) * (Integer.MAX_VALUE >> 16); 265 } 266 return scale; 267 } 268 269 Transform6 computeSetTexturePaintTransform(Paint p, BaseTransform tx, RectBounds nodeBounds, 270 float localX, float localY, float localWidth, float localHeight) 271 { 272 this.computePaintBounds(p, null, nodeBounds, localX, localY, localWidth, localHeight); 273 274 final ImagePattern ip = (ImagePattern)p; 275 this.computeImagePatternTransform(ip, tx, px, py, pw, ph); 276 return piscesTx; 277 } 278 279 private void computeImagePatternTransform(ImagePattern ip, BaseTransform tx, float x, float y, float width, float height) { 280 final Image image = ip.getImage(); 281 if (PrismSettings.debug) { 282 System.out.println("PR.setTexturePaint: " + image); 283 System.out.println("imagePattern: x: " + ip.getX() + ", y: " + ip.getY() + 284 ", w: " + ip.getWidth() + ", h: " + ip.getHeight() + ", proportional: " + ip.isProportional()); 285 } 286 287 paintTx.setTransform(tx); 288 if (ip.isProportional()) { 289 paintTx.deriveWithConcatenation(width / image.getWidth() * ip.getWidth(), 0, 290 0, height / image.getHeight() * ip.getHeight(), 291 x + width * ip.getX(), y + height * ip.getY()); 292 } else { 293 paintTx.deriveWithConcatenation(ip.getWidth() / image.getWidth(), 0, 294 0, ip.getHeight() / image.getHeight(), 295 x + ip.getX(), y + ip.getY()); 296 } 297 SWUtils.convertToPiscesTransform(paintTx, piscesTx); 298 } 299 }