1 /* 2 * Copyright (c) 2009, 2015, 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.impl.ps; 27 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import com.sun.javafx.font.FontResource; 31 import com.sun.javafx.font.FontStrike; 32 import com.sun.javafx.font.Metrics; 33 import com.sun.javafx.font.PrismFontFactory; 34 import com.sun.javafx.geom.BaseBounds; 35 import com.sun.javafx.geom.Point2D; 36 import com.sun.javafx.geom.RectBounds; 37 import com.sun.javafx.geom.Rectangle; 38 import com.sun.javafx.geom.Shape; 39 import com.sun.javafx.geom.transform.Affine2D; 40 import com.sun.javafx.geom.transform.Affine3D; 41 import com.sun.javafx.geom.transform.AffineBase; 42 import com.sun.javafx.geom.transform.BaseTransform; 43 import com.sun.javafx.geom.transform.NoninvertibleTransformException; 44 import com.sun.javafx.scene.text.GlyphList; 45 import com.sun.javafx.sg.prism.NGLightBase; 46 import com.sun.prism.BasicStroke; 47 import com.sun.prism.CompositeMode; 48 import com.sun.prism.MaskTextureGraphics; 49 import com.sun.prism.MultiTexture; 50 import com.sun.prism.PixelFormat; 51 import com.sun.prism.RTTexture; 52 import com.sun.prism.ReadbackGraphics; 53 import com.sun.prism.ReadbackRenderTarget; 54 import com.sun.prism.RenderTarget; 55 import com.sun.prism.Texture; 56 import com.sun.prism.impl.BaseGraphics; 57 import com.sun.prism.impl.GlyphCache; 58 import com.sun.prism.impl.PrismSettings; 59 import com.sun.prism.impl.VertexBuffer; 60 import com.sun.prism.impl.ps.BaseShaderContext.MaskType; 61 import com.sun.prism.impl.shape.MaskData; 62 import com.sun.prism.impl.shape.ShapeUtil; 63 import com.sun.prism.paint.Color; 64 import com.sun.prism.paint.Gradient; 65 import com.sun.prism.paint.ImagePattern; 66 import com.sun.prism.paint.LinearGradient; 67 import com.sun.prism.paint.Paint; 68 import com.sun.prism.paint.RadialGradient; 69 import com.sun.prism.ps.Shader; 70 import com.sun.prism.ps.ShaderGraphics; 71 72 public abstract class BaseShaderGraphics 73 extends BaseGraphics 74 implements ShaderGraphics, ReadbackGraphics, MaskTextureGraphics 75 { 76 private static Affine2D TEMP_TX2D = new Affine2D(); 77 private static Affine3D TEMP_TX3D = new Affine3D(); 78 79 private final BaseShaderContext context; 80 private Shader externalShader; 81 private boolean isComplexPaint; 82 private float pixelScale = 1.0f; 83 84 protected BaseShaderGraphics(BaseShaderContext context, 85 RenderTarget renderTarget) 86 { 87 super(context, renderTarget); 88 this.context = context; 89 } 90 91 BaseShaderContext getContext() { 92 return context; 93 } 94 95 boolean isComplexPaint() { 96 return isComplexPaint; 97 } 98 99 public void getPaintShaderTransform(Affine3D ret) { 100 ret.setTransform(getTransformNoClone()); 101 } 102 103 public Shader getExternalShader() { 104 return externalShader; 105 } 106 107 public void setExternalShader(Shader shader) { 108 this.externalShader = shader; 109 context.setExternalShader(this, shader); 110 } 111 112 @Override 113 public void setPaint(Paint paint) { 114 if (paint.getType().isGradient()) { 115 Gradient grad = (Gradient)paint; 116 isComplexPaint = grad.getNumStops() > PaintHelper.MULTI_MAX_FRACTIONS; 117 } else { 118 isComplexPaint = false; 119 } 120 super.setPaint(paint); 121 } 122 123 private NGLightBase lights[] = null; 124 125 public void setLights(NGLightBase lights[]) { this.lights = lights; } 126 127 public final NGLightBase[] getLights() { return this.lights; } 128 129 @Override 130 public void drawTexture(Texture tex, 131 float dx1, float dy1, float dx2, float dy2, 132 float sx1, float sy1, float sx2, float sy2) 133 { 134 // intercept MultiTexture operations 135 // FIXME: this should be pushed up to Graphics interface so that non-shader 136 // renderers can use MultiTexture too 137 if (tex instanceof MultiTexture) { 138 drawMultiTexture((MultiTexture)tex, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); 139 } else { 140 super.drawTexture(tex, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); 141 } 142 } 143 144 @Override 145 public void drawTexture3SliceH(Texture tex, 146 float dx1, float dy1, float dx2, float dy2, 147 float sx1, float sy1, float sx2, float sy2, 148 float dh1, float dh2, float sh1, float sh2) 149 { 150 if (!(tex instanceof MultiTexture)) { 151 super.drawTexture3SliceH(tex, 152 dx1, dy1, dx2, dy2, 153 sx1, sy1, sx2, sy2, 154 dh1, dh2, sh1, sh2); 155 return; 156 } 157 MultiTexture mtex = (MultiTexture) tex; 158 drawMultiTexture(mtex, dx1, dy1, dh1, dy2, sx1, sy1, sh1, sy2); 159 drawMultiTexture(mtex, dh1, dy1, dh2, dy2, sh1, sy1, sh2, sy2); 160 drawMultiTexture(mtex, dh2, dy1, dx2, dy2, sh2, sy1, sx2, sy2); 161 } 162 163 @Override 164 public void drawTexture3SliceV(Texture tex, 165 float dx1, float dy1, float dx2, float dy2, 166 float sx1, float sy1, float sx2, float sy2, 167 float dv1, float dv2, float sv1, float sv2) 168 { 169 if (!(tex instanceof MultiTexture)) { 170 super.drawTexture3SliceV(tex, 171 dx1, dy1, dx2, dy2, 172 sx1, sy1, sx2, sy2, 173 dv1, dv2, sv1, sv2); 174 return; 175 } 176 MultiTexture mtex = (MultiTexture) tex; 177 drawMultiTexture(mtex, dx1, dy1, dx2, dv1, sx1, sy1, sx2, sv1); 178 drawMultiTexture(mtex, dx1, dv1, dx2, dv2, sx1, sv1, sx2, sv2); 179 drawMultiTexture(mtex, dx1, dv2, dx2, dy2, sx1, sv2, sx2, sy2); 180 } 181 182 @Override 183 public void drawTexture9Slice(Texture tex, 184 float dx1, float dy1, float dx2, float dy2, 185 float sx1, float sy1, float sx2, float sy2, 186 float dh1, float dv1, float dh2, float dv2, 187 float sh1, float sv1, float sh2, float sv2) 188 { 189 if (!(tex instanceof MultiTexture)) { 190 super.drawTexture9Slice(tex, 191 dx1, dy1, dx2, dy2, 192 sx1, sy1, sx2, sy2, 193 dh1, dv1, dh2, dv2, 194 sh1, sv1, sh2, sv2); 195 return; 196 } 197 MultiTexture mtex = (MultiTexture) tex; 198 drawMultiTexture(mtex, dx1, dy1, dh1, dv1, sx1, sy1, sh1, sv1); 199 drawMultiTexture(mtex, dh1, dy1, dh2, dv1, sh1, sy1, sh2, sv1); 200 drawMultiTexture(mtex, dh2, dy1, dx2, dv1, sh2, sy1, sx2, sv1); 201 202 drawMultiTexture(mtex, dx1, dv1, dh1, dv2, sx1, sv1, sh1, sv2); 203 drawMultiTexture(mtex, dh1, dv1, dh2, dv2, sh1, sv1, sh2, sv2); 204 drawMultiTexture(mtex, dh2, dv1, dx2, dv2, sh2, sv1, sx2, sv2); 205 206 drawMultiTexture(mtex, dx1, dv2, dh1, dy2, sx1, sv2, sh1, sy2); 207 drawMultiTexture(mtex, dh1, dv2, dh2, dy2, sh1, sv2, sh2, sy2); 208 drawMultiTexture(mtex, dh2, dv2, dx2, dy2, sh2, sv2, sx2, sy2); 209 } 210 211 private static float calculateScaleFactor(float contentDim, float physicalDim) { 212 if (contentDim == physicalDim) { 213 return 1f; 214 } 215 // we have to subtract 1 to eliminate the "green line of death" 216 return (contentDim-1) / physicalDim; 217 } 218 219 protected void drawMultiTexture(MultiTexture tex, 220 float dx1, float dy1, float dx2, float dy2, 221 float sx1, float sy1, float sx2, float sy2) 222 { 223 // The following is safe; this method does not mutate the transform 224 BaseTransform xform = getTransformNoClone(); 225 if (isSimpleTranslate) { 226 xform = IDENT; 227 dx1 += transX; 228 dy1 += transY; 229 dx2 += transX; 230 dy2 += transY; 231 } 232 233 Texture textures[] = ((MultiTexture)tex).getTextures(); 234 Shader shader = context.validateTextureOp(this, xform, textures, tex.getPixelFormat()); 235 236 if (null == shader) { 237 // FIXME: throw exception?? We can't do anything without the shader.. 238 return; 239 } 240 241 if (tex.getPixelFormat() == PixelFormat.MULTI_YCbCr_420) { 242 Texture lumaTex = textures[PixelFormat.YCBCR_PLANE_LUMA]; 243 Texture cbTex = textures[PixelFormat.YCBCR_PLANE_CHROMABLUE]; 244 Texture crTex = textures[PixelFormat.YCBCR_PLANE_CHROMARED]; 245 246 // sampler scaling factors 247 float imgWidth = (float)tex.getContentWidth(); 248 float imgHeight = (float)tex.getContentHeight(); 249 float lumaScaleX, lumaScaleY; 250 float alphaScaleX, alphaScaleY; 251 float cbScaleX, cbScaleY; 252 float crScaleX, crScaleY; 253 254 lumaScaleX = calculateScaleFactor(imgWidth, (float)lumaTex.getPhysicalWidth()); 255 lumaScaleY = calculateScaleFactor(imgHeight, (float)lumaTex.getPhysicalHeight()); 256 257 if (textures.length > 3) { 258 Texture alphaTex = textures[PixelFormat.YCBCR_PLANE_ALPHA]; 259 alphaScaleX = calculateScaleFactor(imgWidth, (float)alphaTex.getPhysicalWidth()); 260 alphaScaleY = calculateScaleFactor(imgHeight, (float)alphaTex.getPhysicalHeight()); 261 } else { 262 alphaScaleX = alphaScaleY = 0f; 263 } 264 265 float chromaWidth = (float)Math.floor((double)imgWidth/2.0); 266 float chromaHeight = (float)Math.floor((double)imgHeight/2.0); 267 268 cbScaleX = calculateScaleFactor(chromaWidth, (float)cbTex.getPhysicalWidth()); 269 cbScaleY = calculateScaleFactor(chromaHeight, (float)cbTex.getPhysicalHeight()); 270 crScaleX = calculateScaleFactor(chromaWidth, (float)crTex.getPhysicalWidth()); 271 crScaleY = calculateScaleFactor(chromaHeight, (float)crTex.getPhysicalHeight()); 272 273 shader.setConstant("lumaAlphaScale", lumaScaleX, lumaScaleY, alphaScaleX, alphaScaleY); 274 shader.setConstant("cbCrScale", cbScaleX, cbScaleY, crScaleX, crScaleY); 275 276 float tx1 = sx1 / imgWidth; 277 float ty1 = sy1 / imgHeight; 278 float tx2 = sx2 / imgWidth; 279 float ty2 = sy2 / imgHeight; 280 281 VertexBuffer vb = context.getVertexBuffer(); 282 vb.addQuad(dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2); 283 } else { 284 // should have been caught by validateTextureOp, but just in case 285 throw new UnsupportedOperationException("Unsupported multitexture format "+tex.getPixelFormat()); 286 } 287 } 288 289 public void drawTextureRaw2(Texture src1, Texture src2, 290 float dx1, float dy1, float dx2, float dy2, 291 float t1x1, float t1y1, float t1x2, float t1y2, 292 float t2x1, float t2y1, float t2x2, float t2y2) 293 { 294 // The following is safe; this method does not mutate the transform 295 BaseTransform xform = getTransformNoClone(); 296 if (isSimpleTranslate) { 297 xform = IDENT; 298 dx1 += transX; 299 dy1 += transY; 300 dx2 += transX; 301 dy2 += transY; 302 } 303 context.validateTextureOp(this, xform, src1, src2, 304 PixelFormat.INT_ARGB_PRE); 305 306 VertexBuffer vb = context.getVertexBuffer(); 307 vb.addQuad(dx1, dy1, dx2, dy2, 308 t1x1, t1y1, t1x2, t1y2, 309 t2x1, t2y1, t2x2, t2y2); 310 } 311 312 public void drawMappedTextureRaw2(Texture src1, Texture src2, 313 float dx1, float dy1, float dx2, float dy2, 314 float t1x11, float t1y11, float t1x21, float t1y21, 315 float t1x12, float t1y12, float t1x22, float t1y22, 316 float t2x11, float t2y11, float t2x21, float t2y21, 317 float t2x12, float t2y12, float t2x22, float t2y22) 318 { 319 // The following is safe; this method does not mutate the transform 320 BaseTransform xform = getTransformNoClone(); 321 if (isSimpleTranslate) { 322 xform = IDENT; 323 dx1 += transX; 324 dy1 += transY; 325 dx2 += transX; 326 dy2 += transY; 327 } 328 context.validateTextureOp(this, xform, src1, src2, 329 PixelFormat.INT_ARGB_PRE); 330 331 VertexBuffer vb = context.getVertexBuffer(); 332 vb.addMappedQuad(dx1, dy1, dx2, dy2, 333 t1x11, t1y11, t1x21, t1y21, 334 t1x12, t1y12, t1x22, t1y22, 335 t2x11, t2y11, t2x21, t2y21, 336 t2x12, t2y12, t2x22, t2y22); 337 } 338 339 public void drawPixelsMasked(RTTexture imgtex, RTTexture masktex, 340 int dx, int dy, int dw, int dh, 341 int ix, int iy, int mx, int my) 342 { 343 if (dw <= 0 || dh <= 0) return; 344 float iw = imgtex.getPhysicalWidth(); 345 float ih = imgtex.getPhysicalHeight(); 346 float mw = masktex.getPhysicalWidth(); 347 float mh = masktex.getPhysicalHeight(); 348 float dx1 = dx; 349 float dy1 = dy; 350 float dx2 = dx + dw; 351 float dy2 = dy + dh; 352 float ix1 = ix / iw; 353 float iy1 = iy / ih; 354 float ix2 = (ix + dw) / iw; 355 float iy2 = (iy + dh) / ih; 356 float mx1 = mx / mw; 357 float my1 = my / mh; 358 float mx2 = (mx + dw) / mw; 359 float my2 = (my + dh) / mh; 360 context.validateMaskTextureOp(this, IDENT, imgtex, masktex, 361 PixelFormat.INT_ARGB_PRE); 362 VertexBuffer vb = context.getVertexBuffer(); 363 vb.addQuad(dx1, dy1, dx2, dy2, 364 ix1, iy1, ix2, iy2, 365 mx1, my1, mx2, my2); 366 } 367 368 public void maskInterpolatePixels(RTTexture imgtex, RTTexture masktex, 369 int dx, int dy, int dw, int dh, 370 int ix, int iy, int mx, int my) 371 { 372 if (dw <= 0 || dh <= 0) return; 373 float iw = imgtex.getPhysicalWidth(); 374 float ih = imgtex.getPhysicalHeight(); 375 float mw = masktex.getPhysicalWidth(); 376 float mh = masktex.getPhysicalHeight(); 377 float dx1 = dx; 378 float dy1 = dy; 379 float dx2 = dx + dw; 380 float dy2 = dy + dh; 381 float ix1 = ix / iw; 382 float iy1 = iy / ih; 383 float ix2 = (ix + dw) / iw; 384 float iy2 = (iy + dh) / ih; 385 float mx1 = mx / mw; 386 float my1 = my / mh; 387 float mx2 = (mx + dw) / mw; 388 float my2 = (my + dh) / mh; 389 CompositeMode oldmode = getCompositeMode(); 390 setCompositeMode(CompositeMode.DST_OUT); 391 context.validateTextureOp(this, IDENT, masktex, 392 PixelFormat.INT_ARGB_PRE); 393 VertexBuffer vb = context.getVertexBuffer(); 394 vb.addQuad(dx1, dy1, dx2, dy2, 395 mx1, my1, mx2, my2); 396 397 setCompositeMode(CompositeMode.ADD); 398 context.validateMaskTextureOp(this, IDENT, imgtex, masktex, 399 PixelFormat.INT_ARGB_PRE); 400 vb.addQuad(dx1, dy1, dx2, dy2, 401 ix1, iy1, ix2, iy2, 402 mx1, my1, mx2, my2); 403 404 setCompositeMode(oldmode); 405 } 406 407 private void renderWithComplexPaint(Shape shape, BasicStroke stroke, 408 float bx, float by, float bw, float bh) 409 { 410 // creating/updating the mask texture may unset the current 411 // texture used by pending vertices, so flush the vertex buffer first 412 context.flushVertexBuffer(); 413 414 // The following is safe; this method does not mutate the transform 415 BaseTransform xform = getTransformNoClone(); 416 MaskData maskData = 417 ShapeUtil.rasterizeShape(shape, stroke, getFinalClipNoClone(), xform, true, isAntialiasedShape()); 418 int maskW = maskData.getWidth(); 419 int maskH = maskData.getHeight(); 420 421 float dx1 = maskData.getOriginX(); 422 float dy1 = maskData.getOriginY(); 423 float dx2 = dx1 + maskW; 424 float dy2 = dy1 + maskH; 425 426 // Note that we could use multitexturing here (upload mask 427 // data to texture unit 0 and paint data to texture unit 1 428 // then set the texture mode to modulate) but this operation is 429 // already plenty slow and not worth optimizing at this time, 430 // so for now we will merge the mask with the paint data in software. 431 Gradient grad = (Gradient)paint; 432 TEMP_TX2D.setToTranslation(-dx1, -dy1); 433 TEMP_TX2D.concatenate(xform); 434 Texture tex = context.getGradientTexture(grad, TEMP_TX2D, 435 maskW, maskH, maskData, 436 bx, by, bw, bh); 437 438 float tx1 = 0f; 439 float ty1 = 0f; 440 float tx2 = tx1 + ((float)maskW) / tex.getPhysicalWidth(); 441 float ty2 = ty1 + ((float)maskH) / tex.getPhysicalHeight(); 442 443 // the mask has been generated in device space, so we use 444 // identity transform here 445 VertexBuffer vb = context.getVertexBuffer(); 446 context.validateTextureOp(this, IDENT, tex, null, tex.getPixelFormat()); 447 vb.addQuad(dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2); 448 tex.unlock(); 449 } 450 451 private static RectBounds TMP_BOUNDS = new RectBounds(); 452 @Override 453 protected void renderShape(Shape shape, BasicStroke stroke, 454 float bx, float by, float bw, float bh) 455 { 456 if (isComplexPaint) { 457 renderWithComplexPaint(shape, stroke, bx, by, bw, bh); 458 return; 459 } 460 461 // The following is safe; this method does not mutate the transform 462 BaseTransform xform = getTransformNoClone(); 463 MaskData maskData = 464 ShapeUtil.rasterizeShape(shape, stroke, getFinalClipNoClone(), xform, true, isAntialiasedShape()); 465 Texture maskTex = context.validateMaskTexture(maskData, false); 466 467 AffineBase paintTx; 468 if (PrismSettings.primTextureSize != 0) { 469 // the mask has been generated in device space, so we use 470 // identity transform here 471 Shader shader = 472 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE, maskTex, 473 bx, by, bw, bh); 474 475 paintTx = getPaintTextureTx(xform, shader, bx, by, bw, bh); 476 } else { 477 // the mask has been generated in device space, so we use 478 // identity transform here 479 context.validatePaintOp(this, IDENT, maskTex, bx, by, bw, bh); 480 paintTx = null; 481 } 482 483 context.updateMaskTexture(maskData, TMP_BOUNDS, false); 484 485 float dx1 = maskData.getOriginX(); 486 float dy1 = maskData.getOriginY(); 487 float dx2 = dx1 + maskData.getWidth(); 488 float dy2 = dy1 + maskData.getHeight(); 489 float tx1 = TMP_BOUNDS.getMinX(); 490 float ty1 = TMP_BOUNDS.getMinY(); 491 float tx2 = TMP_BOUNDS.getMaxX(); 492 float ty2 = TMP_BOUNDS.getMaxY(); 493 494 VertexBuffer vb = context.getVertexBuffer(); 495 vb.addQuad(dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2, paintTx); 496 497 maskTex.unlock(); 498 } 499 500 private static float getStrokeExpansionFactor(BasicStroke stroke) { 501 if (stroke.getType() == BasicStroke.TYPE_OUTER) { 502 return 1f; 503 } else if (stroke.getType() == BasicStroke.TYPE_CENTERED) { 504 return 0.5f; 505 } else { 506 return 0f; 507 } 508 } 509 510 private static final float FRINGE_FACTOR; 511 static { 512 String v = (String) AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("prism.primshaderpad")); 513 if (v == null) { 514 FRINGE_FACTOR = -0.5f; 515 } else { 516 FRINGE_FACTOR = -Float.valueOf(v); 517 System.out.println("Prism ShaderGraphics primitive shader pad = "+FRINGE_FACTOR); 518 } 519 } 520 521 private BaseTransform extract3Dremainder(BaseTransform xform) { 522 if (xform.is2D()) { 523 return IDENT; 524 } 525 TEMP_TX3D.setTransform(xform); 526 TEMP_TX2D.setTransform(xform.getMxx(), xform.getMyx(), 527 xform.getMxy(), xform.getMyy(), 528 xform.getMxt(), xform.getMyt()); 529 try { 530 TEMP_TX2D.invert(); 531 TEMP_TX3D.concatenate(TEMP_TX2D); 532 } catch (NoninvertibleTransformException ex) { 533 } 534 return TEMP_TX3D; 535 } 536 537 private void renderGeneralRoundedRect(float rx, float ry, float rw, float rh, 538 float arcw, float arch, 539 MaskType type, BasicStroke stroke) 540 { 541 // NOTE: using floats here for now, not sure if it's a problem yet... 542 float bx, by, bw, bh, ifractw, ifracth; 543 float ox, oy, wdx, wdy, hdx, hdy; 544 if (stroke == null) { 545 bx = rx; 546 by = ry; 547 bw = rw; 548 bh = rh; 549 ifractw = ifracth = 0f; 550 } else { 551 float sw = stroke.getLineWidth(); 552 float ow = getStrokeExpansionFactor(stroke) * sw; 553 bx = rx - ow; 554 by = ry - ow; 555 ow *= 2f; 556 bw = rw + ow; 557 bh = rh + ow; 558 if (arcw > 0 && arch > 0) { 559 arcw += ow; 560 arch += ow; 561 } else { 562 if (stroke.getLineJoin() == BasicStroke.JOIN_ROUND) { 563 arcw = arch = ow; 564 type = MaskType.DRAW_ROUNDRECT; 565 } else { 566 arcw = arch = 0f; 567 } 568 } 569 ifractw = (bw - sw * 2f) / bw; 570 ifracth = (bh - sw * 2f) / bh; 571 if (ifractw <= 0f || ifracth <= 0f) { 572 type = type.getFillType(); 573 } 574 } 575 576 // The following is safe; this method does not mutate the transform 577 BaseTransform xform = getTransformNoClone(); 578 BaseTransform rendertx; 579 if (isSimpleTranslate) { 580 wdx = hdy = 1f; 581 wdy = hdx = 0f; 582 ox = bx + transX; 583 oy = by + transY; 584 rendertx = IDENT; 585 } else { 586 rendertx = extract3Dremainder(xform); 587 wdx = (float)xform.getMxx(); 588 hdx = (float)xform.getMxy(); 589 wdy = (float)xform.getMyx(); 590 hdy = (float)xform.getMyy(); 591 ox = (bx * wdx) + (by * hdx) + (float)xform.getMxt(); 592 oy = (bx * wdy) + (by * hdy) + (float)xform.getMyt(); 593 } 594 595 wdx *= bw; 596 wdy *= bw; 597 hdx *= bh; 598 hdy *= bh; 599 600 float arcfractw = arcw / bw; 601 float arcfracth = arch / bh; 602 renderGeneralRoundedPgram(ox, oy, wdx, wdy, hdx, hdy, 603 arcfractw, arcfracth, ifractw, ifracth, 604 rendertx, type, rx, ry, rw, rh); 605 } 606 607 private void renderGeneralRoundedPgram(float ox, float oy, 608 float wvecx, float wvecy, 609 float hvecx, float hvecy, 610 float arcfractw, float arcfracth, 611 float ifractw, float ifracth, 612 BaseTransform rendertx, MaskType type, 613 float rx, float ry, float rw, float rh) 614 { 615 float wlen = len(wvecx, wvecy); 616 float hlen = len(hvecx, hvecy); 617 if (wlen == 0 || hlen == 0) { 618 // parallelogram has collapsed to a line or point 619 return; 620 } 621 622 // Calculate the 4 corners of the pgram in device space. 623 // Note that the UL,UR,LL,LR (Upper/Lower Left/Right) designations 624 // are virtual since the wdxy and hdxy vectors can point in any 625 // direction depending on the transform being applied. 626 float xUL = ox; 627 float yUL = oy; 628 float xUR = ox + wvecx; 629 float yUR = oy + wvecy; 630 float xLL = ox + hvecx; 631 float yLL = oy + hvecy; 632 float xLR = xUR + hvecx; 633 float yLR = yUR + hvecy; 634 635 // Calculate the unit vectors along each side of the pgram as well 636 // as the device space perpendicular dimension across the pgram 637 // (which is different than the lengths of the two pairs of sides 638 // since it is measured perpendicular to the "other" sides, not 639 // along the original sides). 640 // The perpendicular dimension is the dot product of the perpendicular 641 // of the unit vector for one pair of sides with the displacement 642 // vector for the other pair of sides. 643 // The unit vector perpendicular to (dx,dy) is given by: 644 // normx = dx / len(dx, dy); 645 // normy = dy / len(dx, dy); 646 // The (ccw) perpendicular vectors would then be: 647 // unitperpx = +normy = +dy / len; 648 // unitperpy = -normx = -dx / len; 649 // Thus the perpendicular width and height distances are: 650 // pwdist = wvec.uphvec = wvecx * (hvecy/hlen) - wvecy * (hvecx/hlen) 651 // phdist = hvec.upwvec = hvecx * (wvecy/wlen) - hvecy * (wvecx/wlen) 652 // If we factor out the divide by the lengths then we are left 653 // with numerators that are simply negations of each other: 654 // pwdist = (wvecx * hvecy - wvecy * hvecx) / hlen 655 // phdist = (hvecx * wvecy - hvecy * wvecx) / wlen 656 // Finally we multiply by 0.5 since we want the distance to the 657 // edges of the parallelgram from the center point, not across the 658 // whole pgram. And, since we want the absolute value, we can 659 // ignore the fact that the numerator is negated between the two 660 // formulas since we will just have to negate one of the 2 values 661 // afterwards anyway to make them both positive. 662 // Note that the numerators are the formula for the area of the 663 // parallelogram (without the abs() operation). Dividing by the 664 // length of one of the sides would then give the length of the 665 // perpendicular across to the other side. 666 float halfarea = (wvecx * hvecy - wvecy * hvecx) * 0.5f; 667 float pwdist = halfarea / hlen; 668 float phdist = halfarea / wlen; 669 if (pwdist < 0) pwdist = -pwdist; 670 if (phdist < 0) phdist = -phdist; 671 672 // Now we calculate the normalized unit vectors. 673 float nwvecx = wvecx / wlen; 674 float nwvecy = wvecy / wlen; 675 float nhvecx = hvecx / hlen; 676 float nhvecy = hvecy / hlen; 677 678 // Bias the parallelogram corner coordinates "outward" by half a 679 // pixel distance. 680 // The most general way to do this is to move each parallelogram 681 // edge outward by a specified distance and then find the new 682 // intersection point. 683 // The general form for the intersection of 2 lines is: 684 // line1 = (x1,y1) -> (x2,y2) 685 // line2 = (x3,y3) -> (x4,y4) 686 // t = ((x4-x3)(y1-y3) - (y4-y3)(x1-x3)) 687 // / ((y4-y3)(x2-x1) - (x4-x3)(y2-y1)) 688 // intersection point = (x1 + t*(x2-x1), y1 + t*(y2-y1)) 689 // Now consider if these lines are displaced versions of 2 690 // other lines which share a common end point (such as is the 691 // case of pushing 2 adjacent edges of the pgram outward: 692 // line1 = (xa+dx1, ya+dy1) -> (xb+dx1, yb+dy1) 693 // line2 = (xa+dx2, ya+dy2) -> (xc+dx2, yc+dy2) 694 // "x4-x3" = (xc+dx2) - (xa+dx2) = xc-xa 695 // "y4-y3" = (yc+dy2) - (ya+dy2) = yc-ya 696 // "x2-x1" = (xb+dx1) - (xa+dx1) = xb-xa 697 // "y2-y1" = (yb+dy1) - (ya+dy1) = yb-ya 698 // "y1-y3" = (y1+dy1) - (y1+dy2) = dy1 - dy2 699 // "x1-x3" = (xa+dx1) - (xa+dx2) = dx1 - dx2 700 // t = ((xc-xa)(dy1-dy2) - ((yc-ya)(dx1-dx2)) 701 // / ((yc-ya)(xb-xa) - (xc-xa)(yb-ya)) 702 // Now we need to displace these 2 lines "outward" by half a 703 // pixel distance. We will start by looking at applying a unit 704 // pixel distance and then cutting the adjustment in half (it 705 // can be seen to scale linearly when you look at the final 706 // equations). This is achieved by applying one of our unit 707 // vectors with a cw rotation to one line and the other unit 708 // vector with a ccw rotation to the other line. Ideally we 709 // would choose which to apply cw vs. ccw by the way that this 710 // corner of the pgram is turning, but as it turns out, the 711 // consequences of getting it backward is that our "t" will 712 // turn out negative (just negate all of the d[xy][12] values 713 // in the t equation above and you will see that it changes 714 // sign. Since we are calculating the new corner using the 715 // equation newx = (xa+dx1) + t*(xb-xa), we want the value of 716 // t that drives the point "away from xb,yb", in other words, 717 // we want the negative t. So, if t is positive then we want 718 // to use negative t and reverse the perpendicular offset we 719 // applied to xa: 720 // t < 0 => newx = (xa+dx) + t*(xb-xa) = xa + (dx + t*(xb-xa)) 721 // t > 0 => newx = (xa-dx) - t*(xb-xa) = xa - (dx + t*(xb-xa)) 722 // where (copying t equation from above again): 723 // t = ((xc-xa)(dy1-dy2) - ((yc-ya)(dx1-dx2)) 724 // / ((yc-ya)(xb-xa) - (xc-xa)(yb-ya)) 725 // For xa,ya = xUL,yUL and xb,yb = xUR,yUR and xc,yc = xLL,yLL: 726 // [xy]b - [xy]a = [xy]UR - [xy]UL = wvec 727 // [xy]c - [xy]a = [xy]LL - [xy]UL = hvec 728 // dx1,dy1 = +nwvecy, -nwvecx // ccw on xa->xb 729 // dx2,dy2 = -nhvecy, +nhvecx // cw on xa->xc 730 // dx1 - dx2 = +nwvecy - -nhvecy = nwvecy + nhvecy 731 // dy1 - dy2 = -nwvecx - +nhvecx = -(nwvecx + nhvecx) 732 float num = -hvecx*(nwvecx + nhvecx) - hvecy*(nwvecy + nhvecy); 733 float den = hvecy*wvecx - hvecx*wvecy; 734 float t = num / den; 735 // Negating the sign of t and multiplying by 0.5 gets us the 736 // proper sign for the offset and cuts it down to half a pixel 737 float factor = FRINGE_FACTOR * Math.signum(t); 738 float offx = (t * wvecx + nwvecy) * factor; 739 float offy = (t * wvecy - nwvecx) * factor; 740 xUL += offx; yUL += offy; 741 // x22 is offset by the reverse amounts 742 xLR -= offx; yLR -= offy; 743 // For xa,ya = xUR,yUR and xb,yb = xLR,yLR and xc,yc = xUL,yUL 744 // Note that xa,ya => xc,yc is negative of wvec 745 // [xy]b - [xy]a = [xy]LR - [xy]UR = +hvec 746 // [xy]c - [xy]a = [xy]UL - [xy]UR = -wvec 747 // dx1,dy1 = +nhvecy, -nhvecx 748 // dx2,dy2 = -(-nwvecy), +(-nwvecx) = +nwvecy, -nwvecx 749 // dx1 - dx2 = nhvecy - nwvecy 750 // dy1 - dy2 = -nhvecx - -nwvecx = nwvecx - nhvecx 751 // den = -wvecy * hvecx - -wvecx * hvecy 752 // = hvecy * wvecx - hvecx * wvecy (already computed) 753 // num = -wvecx * (nwvecx - nhvecx) - -wvecy * (nhvecy - nwvecy) 754 // = wvecy * (nhvecy - nwvecy) - wvecx * (nwvecx - nhvecx) 755 num = wvecy * (nhvecy - nwvecy) - wvecx * (nwvecx - nhvecx); 756 t = num / den; 757 factor = FRINGE_FACTOR * Math.signum(t); 758 offx = (t * hvecx + nhvecy) * factor; 759 offy = (t * hvecy - nhvecx) * factor; 760 xUR += offx; yUR += offy; 761 xLL -= offx; yLL -= offy; 762 763 // texture coordinates (uv) will be calculated using a transform 764 // by the perpendicular unit vectors so that the texture U 765 // coordinates measure our progress along the pwdist axis (i.e. 766 // perpendicular to hvec and the texture V coordinates measure 767 // our progress along phdist (perpendicular to wvec): 768 // u = x * nhvecy - y * nhvecx; 769 // v = x * nwvecy - y * nwvecx; 770 771 // We now calculate the uv paramters for the 4 corners such that 772 // uv(cx,cy) = 0,0 773 // uv(corners - center) = +/-(wlen/2), +/-(hlen/2) 774 // Note that: 775 // uv(corner - center) = uv(corner) - uv(center) 776 // Calculate the center of the parallelogram and its uv values 777 float xC = (xUL + xLR) * 0.5f; 778 float yC = (yUL + yLR) * 0.5f; 779 float uC = xC * nhvecy - yC * nhvecx; 780 float vC = xC * nwvecy - yC * nwvecx; 781 // Now use that to calculate the corner values relative to the center 782 float uUL = xUL * nhvecy - yUL * nhvecx - uC; 783 float vUL = xUL * nwvecy - yUL * nwvecx - vC; 784 float uUR = xUR * nhvecy - yUR * nhvecx - uC; 785 float vUR = xUR * nwvecy - yUR * nwvecx - vC; 786 float uLL = xLL * nhvecy - yLL * nhvecx - uC; 787 float vLL = xLL * nwvecy - yLL * nwvecx - vC; 788 float uLR = xLR * nhvecy - yLR * nhvecx - uC; 789 float vLR = xLR * nwvecy - yLR * nwvecx - vC; 790 791 // the pgram params have been calculated in device space, so we use 792 // identity transform here 793 if (type == MaskType.DRAW_ROUNDRECT || type == MaskType.FILL_ROUNDRECT) { 794 float oarcw = pwdist * arcfractw; 795 float oarch = phdist * arcfracth; 796 if (oarcw < 0.5 || oarch < 0.5) { 797 // The pgram renderer fades the entire primitive if the arc 798 // sizes fall below 0.5 pixels since the interiors act as if 799 // they are sampled at the center of the indicated circle and 800 // radii smaller than that produce less than full coverage 801 // even at the circle center. If the arcwh are less than 802 // 0.5 then the difference in area of the corner pixels 803 // compared to a PGRAM primitive of the same size is just the 804 // tiny corner cutout of area (4-PI)/16 which is less than 805 // .06 pixels. Thus, we can convert the primitive to a 806 // PGRAM without any loss of precision. 807 type = (type == MaskType.DRAW_ROUNDRECT) 808 ? MaskType.DRAW_PGRAM : MaskType.FILL_PGRAM; 809 } else { 810 float flatw = pwdist - oarcw; 811 float flath = phdist - oarch; 812 float ivalw, ivalh; 813 if (type == MaskType.DRAW_ROUNDRECT) { 814 float iwdist = pwdist * ifractw; 815 float ihdist = phdist * ifracth; 816 // First we calculate the inner arc radii and see if they 817 // are large enough to render. 818 ivalw = iwdist - flatw; 819 ivalh = ihdist - flath; 820 // As above we need to fix things if we get inner arc 821 // radii below half a pixel. We have a special shader 822 // for doing a "semi round" rect which has a round outer 823 // shell and a rectangular (pgram) inner shell... 824 if (ivalw < 0.5f || ivalh < 0.5f) { 825 // inner val is idim 826 ivalw = iwdist; 827 ivalh = ihdist; 828 type = MaskType.DRAW_SEMIROUNDRECT; 829 } else { 830 // inner val is invarcradii 831 ivalw = 1.0f / ivalw; 832 ivalh = 1.0f / ivalh; 833 } 834 } else { 835 // Not used by FILL_ROUNDRECT, but we need constant 836 // values that will not cause an unnecessary vertex 837 // buffer flush in the validateOp below. 838 ivalw = ivalh = 0f; 839 } 840 oarcw = 1.0f / oarcw; 841 oarch = 1.0f / oarch; 842 Shader shader = 843 context.validatePaintOp(this, rendertx, type, 844 rx, ry, rw, rh, 845 oarcw, oarch, 846 ivalw, ivalh, 0, 0); 847 shader.setConstant("oinvarcradii", oarcw, oarch); 848 if (type == MaskType.DRAW_ROUNDRECT) { 849 shader.setConstant("iinvarcradii", ivalw, ivalh); 850 } else if (type == MaskType.DRAW_SEMIROUNDRECT) { 851 shader.setConstant("idim", ivalw, ivalh); 852 } 853 pwdist = flatw; 854 phdist = flath; 855 } 856 } // no else here as we may have converted an RRECT to a PGRAM above 857 if (type == MaskType.DRAW_PGRAM || type == MaskType.DRAW_ELLIPSE) { 858 float idimw = pwdist * ifractw; 859 float idimh = phdist * ifracth; 860 if (type == MaskType.DRAW_ELLIPSE) { 861 if (Math.abs(pwdist - phdist) < .01) { 862 type = MaskType.DRAW_CIRCLE; 863 // The phdist and idimh parameters will not be used by this 864 // shader, but we do need the maximum single pixel coverage 865 // so we coopt them to be min(1.0, area): 866 phdist = (float) Math.min(1.0, phdist * phdist * Math.PI); 867 idimh = (float) Math.min(1.0, idimh * idimh * Math.PI); 868 } else { 869 // the ellipse drawing shader uses inverted arc dimensions 870 // to turn divides into multiplies 871 pwdist = 1.0f / pwdist; 872 phdist = 1.0f / phdist; 873 idimw = 1.0f / idimw; 874 idimh = 1.0f / idimh; 875 } 876 } 877 Shader shader = 878 context.validatePaintOp(this, rendertx, type, 879 rx, ry, rw, rh, 880 idimw, idimh, 0f, 0f, 0f, 0f); 881 shader.setConstant("idim", idimw, idimh); 882 } else if (type == MaskType.FILL_ELLIPSE) { 883 if (Math.abs(pwdist - phdist) < .01) { 884 type = MaskType.FILL_CIRCLE; 885 // The phdist parameter will not be used by this shader, 886 // but we do need the maximum single pixel contribution 887 // so we coopt the value to be min(1.0, area): 888 phdist = (float) Math.min(1.0, phdist * phdist * Math.PI); 889 } else { 890 // the ellipse filling shader uses inverted arc dimensions to 891 // turn divides into multiplies: 892 pwdist = 1.0f / pwdist; 893 phdist = 1.0f / phdist; 894 uUL *= pwdist; vUL *= phdist; 895 uUR *= pwdist; vUR *= phdist; 896 uLL *= pwdist; vLL *= phdist; 897 uLR *= pwdist; vLR *= phdist; 898 } 899 context.validatePaintOp(this, rendertx, type, rx, ry, rw, rh); 900 } else if (type == MaskType.FILL_PGRAM) { 901 context.validatePaintOp(this, rendertx, type, rx, ry, rw, rh); 902 } 903 904 context.getVertexBuffer().addMappedPgram(xUL, yUL, xUR, yUR, 905 xLL, yLL, xLR, yLR, 906 uUL, vUL, uUR, vUR, 907 uLL, vLL, uLR, vLR, 908 pwdist, phdist); 909 } 910 911 AffineBase getPaintTextureTx(BaseTransform renderTx, Shader shader, 912 float rx, float ry, float rw, float rh) 913 { 914 switch (paint.getType()) { 915 case COLOR: 916 return null; 917 case LINEAR_GRADIENT: 918 return PaintHelper.getLinearGradientTx((LinearGradient) paint, 919 shader, renderTx, 920 rx, ry, rw, rh); 921 case RADIAL_GRADIENT: 922 return PaintHelper.getRadialGradientTx((RadialGradient) paint, 923 shader, renderTx, 924 rx, ry, rw, rh); 925 case IMAGE_PATTERN: 926 return PaintHelper.getImagePatternTx(this, (ImagePattern) paint, 927 shader, renderTx, 928 rx, ry, rw, rh); 929 } 930 throw new InternalError("Unrecogized paint type: "+paint); 931 } 932 933 // The second set of rectangular bounds are for validating a 934 // proportional paint. They should be identical to the first 935 // set for a fillRect() operation, but they may be different 936 // for a vertical or horizontal drawLine() operation. 937 boolean fillPrimRect(float x, float y, float w, float h, 938 Texture rectTex, Texture wrapTex, 939 float bx, float by, float bw, float bh) 940 { 941 BaseTransform xform = getTransformNoClone(); 942 float mxx = (float) xform.getMxx(); 943 float mxy = (float) xform.getMxy(); 944 float mxt = (float) xform.getMxt(); 945 float myx = (float) xform.getMyx(); 946 float myy = (float) xform.getMyy(); 947 float myt = (float) xform.getMyt(); 948 float dxdist = len(mxx, myx); 949 float dydist = len(mxy, myy); 950 if (dxdist == 0.0f || dydist == 0.0f) { 951 // entire path has collapsed and occupies no area 952 return true; 953 } 954 float pixelw = 1.0f / dxdist; 955 float pixelh = 1.0f / dydist; 956 float x0 = x - pixelw * 0.5f; 957 float y0 = y - pixelh * 0.5f; 958 float x1 = x + w + pixelw * 0.5f; 959 float y1 = y + h + pixelh * 0.5f; 960 int cellw = (int) Math.ceil(w * dxdist - 1.0f/512.0f); 961 int cellh = (int) Math.ceil(h * dydist - 1.0f/512.0f); 962 VertexBuffer vb = context.getVertexBuffer(); 963 int max = context.getRectTextureMaxSize(); 964 if (cellw <= max && cellh <= max) { 965 float u0 = ((cellw * (cellw + 1)) / 2) - 0.5f; 966 float v0 = ((cellh * (cellh + 1)) / 2) - 0.5f; 967 float u1 = u0 + cellw + 1.0f; 968 float v1 = v0 + cellh + 1.0f; 969 u0 /= rectTex.getPhysicalWidth(); 970 v0 /= rectTex.getPhysicalHeight(); 971 u1 /= rectTex.getPhysicalWidth(); 972 v1 /= rectTex.getPhysicalHeight(); 973 if (xform.isTranslateOrIdentity()) { 974 x0 += mxt; 975 y0 += myt; 976 x1 += mxt; 977 y1 += myt; 978 xform = IDENT; 979 } else if (xform.is2D()) { 980 Shader shader = 981 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE, rectTex, 982 bx, by, bw, bh); 983 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 984 if (paintTx == null) { 985 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 986 x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 987 x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 988 x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 989 u0, v0, u1, v0, u0, v1, u1, v1, 0, 0); 990 } else { 991 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 992 x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 993 x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 994 x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 995 u0, v0, u1, v0, u0, v1, u1, v1, 996 x0, y0, x1, y1, paintTx); 997 } 998 return true; 999 } else { 1000 System.out.println("Not a 2d transform!"); 1001 mxt = myt = 0.0f; 1002 } 1003 Shader shader = 1004 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rectTex, 1005 bx, by, bw, bh); 1006 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1007 if (paintTx == null) { 1008 vb.addQuad(x0, y0, x1, y1, 1009 u0, v0, u1, v1); 1010 } else { 1011 paintTx.translate(-mxt, -myt); 1012 vb.addQuad(x0, y0, x1, y1, 1013 u0, v0, u1, v1, 1014 paintTx); 1015 } 1016 return true; 1017 } 1018 if (wrapTex == null) { 1019 return false; 1020 } 1021 float u0 = 0.5f / wrapTex.getPhysicalWidth(); 1022 float v0 = 0.5f / wrapTex.getPhysicalHeight(); 1023 float uc = (cellw * 0.5f + 1.0f) / wrapTex.getPhysicalWidth(); 1024 float vc = (cellh * 0.5f + 1.0f) / wrapTex.getPhysicalHeight(); 1025 float xc = x + w * 0.5f; 1026 float yc = y + h * 0.5f; 1027 if (xform.isTranslateOrIdentity()) { 1028 x0 += mxt; 1029 y0 += myt; 1030 xc += mxt; 1031 yc += myt; 1032 x1 += mxt; 1033 y1 += myt; 1034 xform = IDENT; 1035 } else if (xform.is2D()) { 1036 Shader shader = 1037 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE, wrapTex, 1038 bx, by, bw, bh); 1039 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1040 float mxx_x0 = mxx * x0, myx_x0 = myx * x0; 1041 float mxy_y0 = mxy * y0, myy_y0 = myy * y0; 1042 float mxx_xc = mxx * xc, myx_xc = myx * xc; 1043 float mxy_yc = mxy * yc, myy_yc = myy * yc; 1044 float mxx_x1 = mxx * x1, myx_x1 = myx * x1; 1045 float mxy_y1 = mxy * y1, myy_y1 = myy * y1; 1046 // xcc,ycc used in all 4 quads 1047 float xcc = mxx_xc + mxy_yc + mxt; 1048 float ycc = myx_xc + myy_yc + myt; 1049 // xcn, ycn and xnc, ync all used in 2 quads each 1050 float xc0 = mxx_xc + mxy_y0 + mxt; 1051 float yc0 = myx_xc + myy_y0 + myt; 1052 float x0c = mxx_x0 + mxy_yc + mxt; 1053 float y0c = myx_x0 + myy_yc + myt; 1054 float xc1 = mxx_xc + mxy_y1 + mxt; 1055 float yc1 = myx_xc + myy_y1 + myt; 1056 float x1c = mxx_x1 + mxy_yc + mxt; 1057 float y1c = myx_x1 + myy_yc + myt; 1058 // Note that all quads use same 00->c0->0c->cc coordinates for 1059 // the inner and outer uv texture coordinates regardless of the 1060 // reflection of the quad of vertex coordinates 1061 1062 if (paintTx == null) { 1063 // quad1 - 00 -> c0 -> 0c -> cc 1064 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 1065 xc0, yc0, x0c, y0c, xcc, ycc, 1066 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1067 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1068 vb.addMappedPgram(x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 1069 xc0, yc0, x1c, y1c, xcc, ycc, 1070 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1071 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1072 vb.addMappedPgram(x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 1073 xc1, yc1, x0c, y0c, xcc, ycc, 1074 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1075 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1076 vb.addMappedPgram(x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 1077 xc1, yc1, x1c, y1c, xcc, ycc, 1078 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1079 } else { 1080 // quad1 - 00 -> c0 -> 0c -> cc 1081 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 1082 xc0, yc0, x0c, y0c, xcc, ycc, 1083 u0, v0, uc, v0, u0, vc, uc, vc, 1084 x0, y0, xc, yc, paintTx); 1085 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1086 vb.addMappedPgram(x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 1087 xc0, yc0, x1c, y1c, xcc, ycc, 1088 u0, v0, uc, v0, u0, vc, uc, vc, 1089 x1, y0, xc, yc, paintTx); 1090 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1091 vb.addMappedPgram(x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 1092 xc1, yc1, x0c, y0c, xcc, ycc, 1093 u0, v0, uc, v0, u0, vc, uc, vc, 1094 x0, y1, xc, yc, paintTx); 1095 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1096 vb.addMappedPgram(x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 1097 xc1, yc1, x1c, y1c, xcc, ycc, 1098 u0, v0, uc, v0, u0, vc, uc, vc, 1099 x1, y1, xc, yc, paintTx); 1100 } 1101 return true; 1102 } else { 1103 System.out.println("Not a 2d transform!"); 1104 mxt = myt = 0; 1105 } 1106 Shader shader = 1107 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, wrapTex, 1108 bx, by, bw, bh); 1109 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1110 if (paintTx != null) { 1111 paintTx.translate(-mxt, -myt); 1112 } 1113 vb.addQuad(x0, y0, xc, yc, 1114 u0, v0, uc, vc, 1115 paintTx); 1116 vb.addQuad(x1, y0, xc, yc, 1117 u0, v0, uc, vc, 1118 paintTx); 1119 vb.addQuad(x0, y1, xc, yc, 1120 u0, v0, uc, vc, 1121 paintTx); 1122 vb.addQuad(x1, y1, xc, yc, 1123 u0, v0, uc, vc, 1124 paintTx); 1125 return true; 1126 } 1127 1128 boolean drawPrimRect(float x, float y, float w, float h) { 1129 float lw = stroke.getLineWidth(); 1130 float pad = getStrokeExpansionFactor(stroke) * lw; 1131 BaseTransform xform = getTransformNoClone(); 1132 float mxx = (float) xform.getMxx(); 1133 float mxy = (float) xform.getMxy(); 1134 float mxt = (float) xform.getMxt(); 1135 float myx = (float) xform.getMyx(); 1136 float myy = (float) xform.getMyy(); 1137 float myt = (float) xform.getMyt(); 1138 float dxdist = len(mxx, myx); 1139 float dydist = len(mxy, myy); 1140 if (dxdist == 0.0f || dydist == 0.0f) { 1141 // entire path has collapsed and occupies no area 1142 return true; 1143 } 1144 float pixelw = 1.0f / dxdist; 1145 float pixelh = 1.0f / dydist; 1146 float x0 = x - pad - pixelw * 0.5f; 1147 float y0 = y - pad - pixelh * 0.5f; 1148 float xc = x + w * 0.5f; 1149 float yc = y + h * 0.5f; 1150 float x1 = x + w + pad + pixelw * 0.5f; 1151 float y1 = y + h + pad + pixelh * 0.5f; 1152 Texture rTex = context.getWrapRectTexture(); 1153 float wscale = 1.0f / rTex.getPhysicalWidth(); 1154 float hscale = 1.0f / rTex.getPhysicalHeight(); 1155 float ou0 = 0.5f * wscale; 1156 float ov0 = 0.5f * hscale; 1157 float ouc = ((w * 0.5f + pad) * dxdist + 1.0f) * wscale; 1158 float ovc = ((h * 0.5f + pad) * dydist + 1.0f) * hscale; 1159 float offsetx = lw * dxdist * wscale; 1160 float offsety = lw * dydist * hscale; 1161 VertexBuffer vb = context.getVertexBuffer(); 1162 if (xform.isTranslateOrIdentity()) { 1163 x0 += mxt; 1164 y0 += myt; 1165 xc += mxt; 1166 yc += myt; 1167 x1 += mxt; 1168 y1 += myt; 1169 xform = IDENT; 1170 } else if (xform.is2D()) { 1171 Shader shader = 1172 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE_DIFF, 1173 rTex, x, y, w, h, 1174 offsetx, offsety, 0, 0, 0, 0); 1175 shader.setConstant("innerOffset", offsetx, offsety); 1176 AffineBase paintTx = getPaintTextureTx(IDENT, shader, x, y, w, h); 1177 float mxx_x0 = mxx * x0, myx_x0 = myx * x0; 1178 float mxy_y0 = mxy * y0, myy_y0 = myy * y0; 1179 float mxx_xc = mxx * xc, myx_xc = myx * xc; 1180 float mxy_yc = mxy * yc, myy_yc = myy * yc; 1181 float mxx_x1 = mxx * x1, myx_x1 = myx * x1; 1182 float mxy_y1 = mxy * y1, myy_y1 = myy * y1; 1183 1184 // xcc,ycc used in all 4 quads 1185 float xcc = mxx_xc + mxy_yc + mxt; 1186 float ycc = myx_xc + myy_yc + myt; 1187 // xcn, ycn and xnc, ync all used in 2 quads each 1188 float xc0 = mxx_xc + mxy_y0 + mxt; 1189 float yc0 = myx_xc + myy_y0 + myt; 1190 float x0c = mxx_x0 + mxy_yc + mxt; 1191 float y0c = myx_x0 + myy_yc + myt; 1192 float xc1 = mxx_xc + mxy_y1 + mxt; 1193 float yc1 = myx_xc + myy_y1 + myt; 1194 float x1c = mxx_x1 + mxy_yc + mxt; 1195 float y1c = myx_x1 + myy_yc + myt; 1196 // Note that all quads use same 00->c0->0c->cc coordinates for 1197 // the inner and outer uv texture coordinates regardless of the 1198 // reflection of the quad of vertex coordinates 1199 1200 if (paintTx == null) { 1201 // quad1 - 00 -> c0 -> 0c -> cc 1202 vb.addMappedPgram(mxx_x0 + mxy_y0 + mxt, myx_x0 + myy_y0 + myt, 1203 xc0, yc0, x0c, y0c, xcc, ycc, 1204 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1205 0, 0); 1206 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1207 vb.addMappedPgram(mxx_x1 + mxy_y0 + mxt, myx_x1 + myy_y0 + myt, 1208 xc0, yc0, x1c, y1c, xcc, ycc, 1209 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1210 0, 0); 1211 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1212 vb.addMappedPgram(mxx_x0 + mxy_y1 + mxt, myx_x0 + myy_y1 + myt, 1213 xc1, yc1, x0c, y0c, xcc, ycc, 1214 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1215 0, 0); 1216 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1217 vb.addMappedPgram(mxx_x1 + mxy_y1 + mxt, myx_x1 + myy_y1 + myt, 1218 xc1, yc1, x1c, y1c, xcc, ycc, 1219 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1220 0, 0); 1221 } else { 1222 // quad1 - 00 -> c0 -> 0c -> cc 1223 vb.addMappedPgram(mxx_x0 + mxy_y0 + mxt, myx_x0 + myy_y0 + myt, 1224 xc0, yc0, x0c, y0c, xcc, ycc, 1225 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1226 x0, y0, xc, yc, paintTx); 1227 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1228 vb.addMappedPgram(mxx_x1 + mxy_y0 + mxt, myx_x1 + myy_y0 + myt, 1229 xc0, yc0, x1c, y1c, xcc, ycc, 1230 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1231 x1, y0, xc, yc, paintTx); 1232 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1233 vb.addMappedPgram(mxx_x0 + mxy_y1 + mxt, myx_x0 + myy_y1 + myt, 1234 xc1, yc1, x0c, y0c, xcc, ycc, 1235 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1236 x0, y1, xc, yc, paintTx); 1237 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1238 vb.addMappedPgram(mxx_x1 + mxy_y1 + mxt, myx_x1 + myy_y1 + myt, 1239 xc1, yc1, x1c, y1c, xcc, ycc, 1240 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1241 x1, y1, xc, yc, paintTx); 1242 } 1243 rTex.unlock(); 1244 return true; 1245 } else { 1246 System.out.println("Not a 2d transform!"); 1247 mxt = myt = 0.0f; 1248 } 1249 Shader shader = 1250 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE_DIFF, 1251 rTex, x, y, w, h, 1252 offsetx, offsety, 0, 0, 0, 0); 1253 shader.setConstant("innerOffset", offsetx, offsety); 1254 AffineBase paintTx = getPaintTextureTx(IDENT, shader, x, y, w, h); 1255 if (paintTx != null) { 1256 paintTx.translate(-mxt, -myt); 1257 } 1258 vb.addQuad( x0, y0, xc, yc, 1259 ou0, ov0, ouc, ovc, 1260 paintTx); 1261 vb.addQuad( x1, y0, xc, yc, 1262 ou0, ov0, ouc, ovc, 1263 paintTx); 1264 vb.addQuad( x0, y1, xc, yc, 1265 ou0, ov0, ouc, ovc, 1266 paintTx); 1267 vb.addQuad( x1, y1, xc, yc, 1268 ou0, ov0, ouc, ovc, 1269 paintTx); 1270 rTex.unlock(); 1271 return true; 1272 } 1273 1274 boolean drawPrimDiagonal(float x1, float y1, float x2, float y2, 1275 float lw, int cap, 1276 float bx, float by, float bw, float bh) 1277 { 1278 // assert x1 != x2 && y1 != y2, otherwise caller would have 1279 // vectored us to fillPrimRect() 1280 if (stroke.getType() == BasicStroke.TYPE_CENTERED) { 1281 lw *= 0.5f; 1282 } 1283 float dx = x2 - x1; 1284 float dy = y2 - y1; 1285 float len = len(dx, dy); 1286 dx /= len; 1287 dy /= len; 1288 float ldx = dx * lw; 1289 float ldy = dy * lw; 1290 // First expand perpendicularly using (ldy, -ldx) 1291 float xUL = x1 + ldy, yUL = y1 - ldx; 1292 float xUR = x2 + ldy, yUR = y2 - ldx; 1293 float xLL = x1 - ldy, yLL = y1 + ldx; 1294 float xLR = x2 - ldy, yLR = y2 + ldx; 1295 if (cap == BasicStroke.CAP_SQUARE) { 1296 // Then add SQUARE end caps using (ldx, ldy) if needed 1297 xUL -= ldx; yUL -= ldy; 1298 xLL -= ldx; yLL -= ldy; 1299 xUR += ldx; yUR += ldy; 1300 xLR += ldx; yLR += ldy; 1301 } 1302 1303 float hdx, hdy, vdx, vdy; 1304 int cellw, cellh; 1305 BaseTransform xform = getTransformNoClone(); 1306 float mxt = (float) xform.getMxt(); 1307 float myt = (float) xform.getMyt(); 1308 if (xform.isTranslateOrIdentity()) { 1309 hdx = dx; hdy = dy; 1310 vdx = dy; vdy = -dx; 1311 cellw = (int) Math.ceil(len(xUR - xUL, yUR - yUL)); 1312 cellh = (int) Math.ceil(len(xLL - xUL, yLL - yUL)); 1313 xform = IDENT; 1314 } else if (xform.is2D()) { 1315 float mxx = (float) xform.getMxx(); 1316 float mxy = (float) xform.getMxy(); 1317 float myx = (float) xform.getMyx(); 1318 float myy = (float) xform.getMyy(); 1319 float tx, ty; 1320 tx = mxx * xUL + mxy * yUL; 1321 ty = myx * xUL + myy * yUL; 1322 xUL = tx; yUL = ty; 1323 tx = mxx * xUR + mxy * yUR; 1324 ty = myx * xUR + myy * yUR; 1325 xUR = tx; yUR = ty; 1326 tx = mxx * xLL + mxy * yLL; 1327 ty = myx * xLL + myy * yLL; 1328 xLL = tx; yLL = ty; 1329 tx = mxx * xLR + mxy * yLR; 1330 ty = myx * xLR + myy * yLR; 1331 xLR = tx; yLR = ty; 1332 // hdx, hdy are transformed unit vectors along the line 1333 hdx = mxx * dx + mxy * dy; 1334 hdy = myx * dx + myy * dy; 1335 float dlen = len(hdx, hdy); 1336 if (dlen == 0.0f) return true; 1337 hdx /= dlen; 1338 hdy /= dlen; 1339 // vdx, vdy are transformed perpendicular unit vectors 1340 // (perpendicular to the line in user space, but then transformed) 1341 vdx = mxx * dy - mxy * dx; 1342 vdy = myx * dy - myy * dx; 1343 dlen = len(vdx, vdy); 1344 if (dlen == 0.0f) return true; 1345 vdx /= dlen; 1346 vdy /= dlen; 1347 cellw = (int) Math.ceil(Math.abs((xUR - xUL) * hdx + (yUR - yUL) * hdy)); 1348 cellh = (int) Math.ceil(Math.abs((xLL - xUL) * vdx + (yLL - yUL) * vdy)); 1349 xform = IDENT; 1350 } else { 1351 System.out.println("Not a 2d transform!"); 1352 return false; 1353 } 1354 hdx *= 0.5f; 1355 hdy *= 0.5f; 1356 vdx *= 0.5f; 1357 vdy *= 0.5f; 1358 xUL = xUL + mxt + vdx - hdx; 1359 yUL = yUL + myt + vdy - hdy; 1360 xUR = xUR + mxt + vdx + hdx; 1361 yUR = yUR + myt + vdy + hdy; 1362 xLL = xLL + mxt - vdx - hdx; 1363 yLL = yLL + myt - vdy - hdy; 1364 xLR = xLR + mxt - vdx + hdx; 1365 yLR = yLR + myt - vdy + hdy; 1366 VertexBuffer vb = context.getVertexBuffer(); 1367 int cellmax = context.getRectTextureMaxSize(); 1368 if (cellh <= cellmax) { 1369 float v0 = ((cellh * (cellh + 1)) / 2) - 0.5f; 1370 float v1 = v0 + cellh + 1.0f; 1371 Texture rTex = context.getRectTexture(); 1372 v0 /= rTex.getPhysicalHeight(); 1373 v1 /= rTex.getPhysicalHeight(); 1374 if (cellw <= cellmax) { 1375 float u0 = ((cellw * (cellw + 1)) / 2) - 0.5f; 1376 float u1 = u0 + cellw + 1.0f; 1377 u0 /= rTex.getPhysicalWidth(); 1378 u1 /= rTex.getPhysicalWidth(); 1379 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1380 bx, by, bw, bh); 1381 vb.addMappedPgram(xUL, yUL, xUR, yUR, xLL, yLL, xLR, yLR, 1382 u0, v0, u1, v0, u0, v1, u1, v1, 1383 0, 0); 1384 // System.out.print("1"); System.out.flush(); 1385 rTex.unlock(); 1386 return true; 1387 } 1388 // long thin line (cellw is along the line, cellh is across it) 1389 if (cellw <= cellmax * 2 - 1) { 1390 // 2-slice the line at its midpoint. 1391 // use the cellmax,cellh cell for maximum coverage of each half 1392 // we can use at most (cellmax-0.5) per half so that we do not 1393 // see the antialias drop off on the last half pixel of the far 1394 // end of the cell. This lets us support a line of "length" up 1395 // to (cellmax-0.5 + cellmax-0.5) or (cellmax*2-1). 1396 float xUC = (xUL + xUR) * 0.5f; 1397 float yUC = (yUL + yUR) * 0.5f; 1398 float xLC = (xLL + xLR) * 0.5f; 1399 float yLC = (yLL + yLR) * 0.5f; 1400 float u0 = ((cellmax * (cellmax + 1)) / 2) - 0.5f; 1401 float u1 = u0 + 0.5f + cellw * 0.5f; 1402 u0 /= rTex.getPhysicalWidth(); 1403 u1 /= rTex.getPhysicalWidth(); 1404 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1405 bx, by, bw, bh); 1406 // first half of line x1,y1 -> midpoint 1407 vb.addMappedPgram(xUL, yUL, xUC, yUC, xLL, yLL, xLC, yLC, 1408 u0, v0, u1, v0, u0, v1, u1, v1, 1409 0, 0); 1410 // second half of line midpoint -> x2,y2 1411 vb.addMappedPgram(xUR, yUR, xUC, yUC, xLR, yLR, xLC, yLC, 1412 u0, v0, u1, v0, u0, v1, u1, v1, 1413 0, 0); 1414 // System.out.print("2"); System.out.flush(); 1415 rTex.unlock(); 1416 return true; 1417 } 1418 // Finally, 3-slice the line (left edge, huge middle, right edge) 1419 float u0 = 0.5f / rTex.getPhysicalWidth(); 1420 float u1 = 1.5f / rTex.getPhysicalWidth(); 1421 // The lower case L or R indicates "inner left" or "inner right" 1422 hdx *= 2.0f; 1423 hdy *= 2.0f; 1424 float xUl = xUL + hdx; 1425 float yUl = yUL + hdy; 1426 float xUr = xUR - hdx; 1427 float yUr = yUR - hdy; 1428 float xLl = xLL + hdx; 1429 float yLl = yLL + hdy; 1430 float xLr = xLR - hdx; 1431 float yLr = yLR - hdy; 1432 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1433 bx, by, bw, bh); 1434 // first pixel of line x1,y1 -> x1,y1+pixel 1435 vb.addMappedPgram(xUL, yUL, xUl, yUl, xLL, yLL, xLl, yLl, 1436 u0, v0, u1, v0, u0, v1, u1, v1, 1437 0, 0); 1438 // middle part of line x1,y1+pixel -> x2,y2-pixel 1439 vb.addMappedPgram(xUl, yUl, xUr, yUr, xLl, yLl, xLr, yLr, 1440 u1, v0, u1, v0, u1, v1, u1, v1, 1441 0, 0); 1442 // last part of line x2,y2-pixel -> x2,y2 1443 vb.addMappedPgram(xUr, yUr, xUR, yUR, xLr, yLr, xLR, yLR, 1444 u1, v0, u0, v0, u1, v1, u0, v1, 1445 0, 0); 1446 // System.out.print("3"); System.out.flush(); 1447 rTex.unlock(); 1448 return true; 1449 } 1450 // we could 2 and 3 slice extremely wide short lines, but they 1451 // are very rare in practice so we just jump straight to a 1452 // standard 4-slice off of the wrap-rect texture 1453 float xUC = (xUL + xUR) * 0.5f; 1454 float yUC = (yUL + yUR) * 0.5f; 1455 float xLC = (xLL + xLR) * 0.5f; 1456 float yLC = (yLL + yLR) * 0.5f; 1457 float xCL = (xUL + xLL) * 0.5f; 1458 float yCL = (yUL + yLL) * 0.5f; 1459 float xCR = (xUR + xLR) * 0.5f; 1460 float yCR = (yUR + yLR) * 0.5f; 1461 float xCC = (xUC + xLC) * 0.5f; 1462 float yCC = (yUC + yLC) * 0.5f; 1463 Texture rTex = context.getWrapRectTexture(); 1464 float u0 = 0.5f / rTex.getPhysicalWidth(); 1465 float v0 = 0.5f / rTex.getPhysicalHeight(); 1466 float uc = (cellw * 0.5f + 1.0f) / rTex.getPhysicalWidth(); 1467 float vc = (cellh * 0.5f + 1.0f) / rTex.getPhysicalHeight(); 1468 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1469 bx, by, bw, bh); 1470 vb.addMappedPgram(xUL, yUL, xUC, yUC, xCL, yCL, xCC, yCC, 1471 u0, v0, uc, v0, u0, vc, uc, vc, 1472 0, 0); 1473 vb.addMappedPgram(xUR, yUR, xUC, yUC, xCR, yCR, xCC, yCC, 1474 u0, v0, uc, v0, u0, vc, uc, vc, 1475 0, 0); 1476 vb.addMappedPgram(xLL, yLL, xLC, yLC, xCL, yCL, xCC, yCC, 1477 u0, v0, uc, v0, u0, vc, uc, vc, 1478 0, 0); 1479 vb.addMappedPgram(xLR, yLR, xLC, yLC, xCR, yCR, xCC, yCC, 1480 u0, v0, uc, v0, u0, vc, uc, vc, 1481 0, 0); 1482 // System.out.print("4"); System.out.flush(); 1483 rTex.unlock(); 1484 return true; 1485 } 1486 1487 public void fillRect(float x, float y, float w, float h) { 1488 if (w <= 0 || h <= 0) { 1489 return; 1490 } 1491 if (!isAntialiasedShape()) { 1492 fillQuad(x, y, x + w, y + h); 1493 return; 1494 } 1495 if (isComplexPaint) { 1496 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1497 renderWithComplexPaint(scratchRRect, null, x, y, w, h); 1498 return; 1499 } 1500 if (PrismSettings.primTextureSize != 0) { 1501 Texture rTex = context.getRectTexture(); 1502 Texture wTex = context.getWrapRectTexture(); 1503 boolean success = fillPrimRect(x, y, w, h, rTex, wTex, x, y, w, h); 1504 rTex.unlock(); 1505 wTex.unlock(); 1506 if (success) return; 1507 } 1508 renderGeneralRoundedRect(x, y, w, h, 0f, 0f, 1509 MaskType.FILL_PGRAM, null); 1510 } 1511 1512 public void fillEllipse(float x, float y, float w, float h) { 1513 if (w <= 0 || h <= 0) { 1514 return; 1515 } 1516 if (isComplexPaint) { 1517 scratchEllipse.setFrame(x, y, w, h); 1518 renderWithComplexPaint(scratchEllipse, null, x, y, w, h); 1519 return; 1520 } 1521 if (!isAntialiasedShape()) { 1522 scratchEllipse.setFrame(x, y, w, h); 1523 renderShape(scratchEllipse, null, x, y, w, h); 1524 return; 1525 } 1526 if (PrismSettings.primTextureSize != 0) { 1527 if (fillPrimRect(x, y, w, h, 1528 context.getOvalTexture(), 1529 null, 1530 x, y, w, h)) 1531 { 1532 return; 1533 } 1534 } 1535 renderGeneralRoundedRect(x, y, w, h, w, h, 1536 MaskType.FILL_ELLIPSE, null); 1537 } 1538 1539 public void fillRoundRect(float x, float y, float w, float h, 1540 float arcw, float arch) 1541 { 1542 arcw = Math.min(Math.abs(arcw), w); 1543 arch = Math.min(Math.abs(arch), h); 1544 1545 if (w <= 0 || h <= 0) { 1546 return; 1547 } 1548 if (isComplexPaint) { 1549 scratchRRect.setRoundRect(x, y, w, h, arcw, arch); 1550 renderWithComplexPaint(scratchRRect, null, x, y, w, h); 1551 return; 1552 } 1553 if (!isAntialiasedShape()) { 1554 scratchRRect.setRoundRect(x, y, w, h, arcw, arch); 1555 renderShape(scratchRRect, null, x, y, w, h); 1556 return; 1557 } 1558 renderGeneralRoundedRect(x, y, w, h, arcw, arch, 1559 MaskType.FILL_ROUNDRECT, null); 1560 } 1561 1562 public void fillQuad(float x1, float y1, float x2, float y2) { 1563 float bx, by, bw, bh; 1564 if (x1 <= x2) { 1565 bx = x1; 1566 bw = x2 - x1; 1567 } else { 1568 bx = x2; 1569 bw = x1 - x2; 1570 } 1571 if (y1 <= y2) { 1572 by = y1; 1573 bh = y2 - y1; 1574 } else { 1575 by = y2; 1576 bh = y1 - y2; 1577 } 1578 1579 if (isComplexPaint) { 1580 scratchRRect.setRoundRect(bx, by, bw, bh, 0, 0); 1581 renderWithComplexPaint(scratchRRect, null, bx, by, bw, bh); 1582 return; 1583 } 1584 1585 BaseTransform xform = getTransformNoClone(); 1586 if (PrismSettings.primTextureSize != 0) { 1587 float mxt, myt; 1588 if (xform.isTranslateOrIdentity()) { 1589 mxt = (float) xform.getMxt(); 1590 myt = (float) xform.getMyt(); 1591 xform = IDENT; 1592 x1 += mxt; 1593 y1 += myt; 1594 x2 += mxt; 1595 y2 += myt; 1596 } else { 1597 mxt = myt = 0.0f; 1598 } 1599 Shader shader = 1600 context.validatePaintOp(this, xform, MaskType.ALPHA_ONE, null, 1601 bx, by, bw, bh); 1602 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1603 if (paintTx != null) { 1604 paintTx.translate(-mxt, -myt); 1605 } 1606 context.getVertexBuffer().addQuad(x1, y1, x2, y2, 0, 0, 0, 0, paintTx); 1607 return; 1608 } 1609 if (isSimpleTranslate) { 1610 xform = IDENT; 1611 bx += transX; 1612 by += transY; 1613 } 1614 context.validatePaintOp(this, xform, MaskType.SOLID, bx, by, bw, bh); 1615 1616 VertexBuffer vb = context.getVertexBuffer(); 1617 vb.addQuad(bx, by, bx+bw, by+bh); 1618 } 1619 1620 private static final double SQRT_2 = Math.sqrt(2.0); 1621 private static boolean canUseStrokeShader(BasicStroke bs) { 1622 // RT-27378 1623 // TODO: Expand the cases that renderGeneralRoundRect() can handle... 1624 return (!bs.isDashed() && 1625 (bs.getType() == BasicStroke.TYPE_INNER || 1626 bs.getLineJoin() == BasicStroke.JOIN_ROUND || 1627 (bs.getLineJoin() == BasicStroke.JOIN_MITER && 1628 bs.getMiterLimit() >= SQRT_2))); 1629 } 1630 1631 public void blit(RTTexture srcTex, RTTexture dstTex, 1632 int srcX0, int srcY0, int srcX1, int srcY1, 1633 int dstX0, int dstY0, int dstX1, int dstY1) { 1634 if (dstTex == null) { 1635 context.setRenderTarget(this); 1636 } else { 1637 context.setRenderTarget((BaseGraphics)dstTex.createGraphics()); 1638 } 1639 context.blit(srcTex, dstTex, srcX0, srcY0, srcX1, srcY1, 1640 dstX0, dstY0, dstX1, dstY1); 1641 } 1642 1643 public void drawRect(float x, float y, float w, float h) { 1644 if (w < 0 || h < 0) { 1645 return; 1646 } 1647 if (w == 0 || h == 0) { 1648 drawLine(x, y, x + w, y + h); 1649 return; 1650 } 1651 if (isComplexPaint) { 1652 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1653 renderWithComplexPaint(scratchRRect, stroke, x, y, w, h); 1654 return; 1655 } 1656 if (!isAntialiasedShape()) { 1657 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1658 renderShape(scratchRRect, stroke, x, y, w, h); 1659 return; 1660 } 1661 if (canUseStrokeShader(stroke)) { 1662 if (PrismSettings.primTextureSize != 0 && 1663 stroke.getLineJoin() != BasicStroke.CAP_ROUND) 1664 { 1665 if (drawPrimRect(x, y, w, h)) { 1666 return; 1667 } 1668 } 1669 renderGeneralRoundedRect(x, y, w, h, 0f, 0f, 1670 MaskType.DRAW_PGRAM, stroke); 1671 return; 1672 } 1673 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1674 renderShape(scratchRRect, stroke, x, y, w, h); 1675 } 1676 1677 private boolean checkInnerCurvature(float arcw, float arch) { 1678 // Test to see if inner ellipse satisfies (flattening < 0.5) 1679 // otherwise it will not be approximated by a "parallel ellipse" 1680 // very well and we should just use shape rendering. 1681 1682 // RT-27378 1683 // TODO: Implement better "distance to ellipse" formulas in the shaders 1684 float inset = stroke.getLineWidth() * 1685 (1f - getStrokeExpansionFactor(stroke)); 1686 arcw -= inset; 1687 arch -= inset; 1688 // Note that if either inset arcw,h go to <= 0 then we will invoke the 1689 // fill primitive for ellipse, or the round rect primitive will 1690 // invoke its "tiny inner corner" fixes and we will be safe 1691 return (arcw <= 0 || arch <= 0 || 1692 (arcw * 2f > arch && arch * 2f > arcw)); 1693 } 1694 1695 public void drawEllipse(float x, float y, float w, float h) { 1696 if (w < 0 || h < 0) { 1697 return; 1698 } 1699 if (!isComplexPaint && !stroke.isDashed() && 1700 checkInnerCurvature(w, h) && isAntialiasedShape()) 1701 { 1702 renderGeneralRoundedRect(x, y, w, h, w, h, 1703 MaskType.DRAW_ELLIPSE, stroke); 1704 return; 1705 } 1706 scratchEllipse.setFrame(x, y, w, h); 1707 renderShape(scratchEllipse, stroke, x, y, w, h); 1708 } 1709 1710 public void drawRoundRect(float x, float y, float w, float h, 1711 float arcw, float arch) 1712 { 1713 arcw = Math.min(Math.abs(arcw), w); 1714 arch = Math.min(Math.abs(arch), h); 1715 1716 if (w < 0 || h < 0) { 1717 return; 1718 } 1719 if (!isComplexPaint && !stroke.isDashed() && 1720 checkInnerCurvature(arcw, arch) && isAntialiasedShape()) 1721 { 1722 renderGeneralRoundedRect(x, y, w, h, arcw, arch, 1723 MaskType.DRAW_ROUNDRECT, stroke); 1724 return; 1725 } 1726 scratchRRect.setRoundRect(x, y, w, h, arcw, arch); 1727 renderShape(scratchRRect, stroke, x, y, w, h); 1728 } 1729 1730 public void drawLine(float x1, float y1, float x2, float y2) { 1731 float bx, by, bw, bh; 1732 if (x1 <= x2) { 1733 bx = x1; 1734 bw = x2 - x1; 1735 } else { 1736 bx = x2; 1737 bw = x1 - x2; 1738 } 1739 if (y1 <= y2) { 1740 by = y1; 1741 bh = y2 - y1; 1742 } else { 1743 by = y2; 1744 bh = y1 - y2; 1745 } 1746 1747 // RT-27378 1748 // TODO: casting down to floats everywhere here; evaluate later 1749 // to see if this is enough precision... 1750 // TODO: stroke normalization control? 1751 if (stroke.getType() == BasicStroke.TYPE_INNER) { 1752 return; 1753 } 1754 if (isComplexPaint) { 1755 scratchLine.setLine(x1, y1, x2, y2); 1756 renderWithComplexPaint(scratchLine, stroke, bx, by, bw, bh); 1757 return; 1758 } 1759 if (!isAntialiasedShape()) { 1760 scratchLine.setLine(x1, y1, x2, y2); 1761 renderShape(scratchLine, stroke, bx, by, bw, bh); 1762 return; 1763 } 1764 int cap = stroke.getEndCap(); 1765 if (stroke.isDashed()) { 1766 // NOTE: we could construct the GeneralPath directly 1767 // for CAP_ROUND and save a lot of processing in that case... 1768 // And again, we would need to deal with dropout control... 1769 scratchLine.setLine(x1, y1, x2, y2); 1770 renderShape(scratchLine, stroke, bx, by, bw, bh); 1771 return; 1772 } 1773 float lw = stroke.getLineWidth(); 1774 if (PrismSettings.primTextureSize != 0 && 1775 cap != BasicStroke.CAP_ROUND) 1776 { 1777 float pad = lw; 1778 if (stroke.getType() == BasicStroke.TYPE_CENTERED) { 1779 pad *= 0.5f; 1780 } 1781 if (bw == 0.0f || bh == 0.0f) { 1782 float padx, pady; 1783 if (cap == BasicStroke.CAP_SQUARE) { 1784 // CAP_SQUARE pads the same on all sides 1785 padx = pady = pad; 1786 // System.out.print("S"); System.out.flush(); 1787 } else if (bw != 0.0f) { 1788 // Horizontal CAP_BUTT line - widen vertically 1789 padx = 0.0f; 1790 pady = pad; 1791 // System.out.print("H"); System.out.flush(); 1792 } else if (bh != 0.0f) { 1793 // Vertical CAP_BUTT line - widen horizontally 1794 padx = pad; 1795 pady = 0.0f; 1796 // System.out.print("V"); System.out.flush(); 1797 } else { 1798 // System.out.print("0"); System.out.flush(); 1799 // Zero length line - NOP for CAP_BUTT 1800 return; 1801 } 1802 Texture rTex = context.getRectTexture(); 1803 Texture wTex = context.getWrapRectTexture(); 1804 boolean success = fillPrimRect(bx - padx, by - pady, 1805 bw + padx + padx, bh + pady + pady, 1806 rTex, wTex, bx, by, bw, bh); 1807 rTex.unlock(); 1808 wTex.unlock(); 1809 if (success) return; 1810 } else { 1811 if (drawPrimDiagonal(x1, y1, x2, y2, lw, cap, 1812 bx, by, bw, bh)) 1813 { 1814 return; 1815 } 1816 } 1817 } 1818 // System.out.print("#"); System.out.flush(); 1819 if (stroke.getType() == BasicStroke.TYPE_OUTER) { 1820 lw *= 2f; 1821 } 1822 float dx = x2 - x1; 1823 float dy = y2 - y1; 1824 float len = len(dx, dy); 1825 float ldx, ldy; // lw length vector in direction of line in user space 1826 if (len == 0) { 1827 if (cap == BasicStroke.CAP_BUTT) { 1828 return; 1829 } 1830 ldx = lw; 1831 ldy = 0; 1832 } else { 1833 ldx = lw * dx / len; 1834 ldy = lw * dy / len; 1835 } 1836 // The following is safe; this method does not mutate the transform 1837 BaseTransform xform = getTransformNoClone(); 1838 BaseTransform rendertx; 1839 float pdx, pdy; // ldx,ldy rotated 90 in user space then transformed 1840 if (isSimpleTranslate) { 1841 double tx = xform.getMxt(); 1842 double ty = xform.getMyt(); 1843 x1 += tx; 1844 y1 += ty; 1845 x2 += tx; 1846 y2 += ty; 1847 pdx = ldy; 1848 pdy = -ldx; 1849 rendertx = IDENT; 1850 } else { 1851 rendertx = extract3Dremainder(xform); 1852 double coords[] = {x1, y1, x2, y2}; 1853 xform.transform(coords, 0, coords, 0, 2); 1854 x1 = (float)coords[0]; 1855 y1 = (float)coords[1]; 1856 x2 = (float)coords[2]; 1857 y2 = (float)coords[3]; 1858 dx = x2 - x1; 1859 dy = y2 - y1; 1860 coords[0] = ldx; 1861 coords[1] = ldy; 1862 coords[2] = ldy; 1863 coords[3] = -ldx; 1864 xform.deltaTransform(coords, 0, coords, 0, 2); 1865 ldx = (float) coords[0]; 1866 ldy = (float) coords[1]; 1867 pdx = (float) coords[2]; 1868 pdy = (float) coords[3]; 1869 } 1870 float px = x1 - pdx / 2f; 1871 float py = y1 - pdy / 2f; 1872 float arcfractw, arcfracth; 1873 MaskType type; 1874 if (cap != BasicStroke.CAP_BUTT) { 1875 px -= ldx / 2f; 1876 py -= ldy / 2f; 1877 dx += ldx; 1878 dy += ldy; 1879 if (cap == BasicStroke.CAP_ROUND) { 1880 arcfractw = len(ldx, ldy) / len(dx, dy); 1881 arcfracth = 1f; 1882 type = MaskType.FILL_ROUNDRECT; 1883 } else { 1884 arcfractw = arcfracth = 0f; 1885 type = MaskType.FILL_PGRAM; 1886 } 1887 } else { 1888 arcfractw = arcfracth = 0f; 1889 type = MaskType.FILL_PGRAM; 1890 } 1891 renderGeneralRoundedPgram(px, py, dx, dy, pdx, pdy, 1892 arcfractw, arcfracth, 0f, 0f, 1893 rendertx, type, 1894 bx, by, bw, bh); 1895 } 1896 1897 private static float len(float x, float y) { 1898 return ((x == 0f) ? Math.abs(y) 1899 : ((y == 0f) ? Math.abs(x) 1900 : (float)Math.sqrt(x * x + y * y))); 1901 } 1902 1903 private boolean lcdSampleInvalid = false; 1904 1905 public void setNodeBounds(RectBounds bounds) { 1906 nodeBounds = bounds; 1907 lcdSampleInvalid = bounds != null; 1908 } 1909 1910 private void initLCDSampleRT() { 1911 if (lcdSampleInvalid) { 1912 RectBounds textBounds = new RectBounds(); 1913 getTransformNoClone().transform(nodeBounds, textBounds); 1914 Rectangle clipRect = getClipRectNoClone(); 1915 if (clipRect != null && !clipRect.isEmpty()) { 1916 // Reduce sample area if there is any clipping bounds set 1917 textBounds.intersectWith(clipRect); 1918 } 1919 // LCD mixing with background will often lead to extra pixel at edge 1920 // thus adding a single pixel, as padding, at edges and bottom. 1921 float bx = textBounds.getMinX() - 1.0f; 1922 float by = textBounds.getMinY(); 1923 float bw = textBounds.getWidth() + 2.0f; 1924 float bh = textBounds.getHeight() + 1.0f; 1925 1926 context.validateLCDBuffer(getRenderTarget()); 1927 1928 // Create a graphics for us to render into the LCDBuffer 1929 // Note this also sets the current RenderTarget as the LCDBuffer 1930 BaseShaderGraphics bsg = (BaseShaderGraphics) context.getLCDBuffer().createGraphics(); 1931 bsg.setCompositeMode(CompositeMode.SRC); 1932 context.validateLCDOp(bsg, IDENT, (Texture) getRenderTarget(), null, true, null); 1933 1934 int srch = getRenderTarget().getPhysicalHeight(); 1935 int srcw = getRenderTarget().getPhysicalWidth(); 1936 float tx1 = bx / srcw; 1937 float ty1 = by / srch; 1938 float tx2 = (bx + bw) / srcw; 1939 float ty2 = (by + bh) / srch; 1940 1941 //sample the source RT in the following bounds and store it in the LCDBuffer RT. 1942 bsg.drawLCDBuffer(bx, by, bw, bh, tx1, ty1, tx2, ty2); 1943 context.setRenderTarget(this); 1944 } 1945 lcdSampleInvalid = false; 1946 } 1947 1948 public void drawString(GlyphList gl, FontStrike strike, float x, float y, 1949 Color selectColor, int selectStart, int selectEnd) { 1950 1951 if (isComplexPaint || 1952 paint.getType().isImagePattern() || 1953 strike.drawAsShapes()) 1954 { 1955 // FontStrike.drawAsShapes() may be true for very large font sizes 1956 // in which case so no glyph images are cached. 1957 // The Prism Text node handles such cases directly, but Webnode relies 1958 // upon Prism's drawString method, so we need to handle it here too. 1959 1960 // this is not a very optimal approach and may not hit exactly 1961 // the same pixels that we would hit in the case where the 1962 // glyph cache is used, but the complex paint case is not 1963 // common enough to warrant further optimization 1964 BaseTransform xform = BaseTransform.getTranslateInstance(x, y); 1965 Shape shape = strike.getOutline(gl, xform); 1966 fill(shape); 1967 return; 1968 } 1969 1970 BaseTransform xform = getTransformNoClone(); 1971 1972 Paint textPaint = getPaint(); 1973 Color textColor = textPaint.getType() == Paint.Type.COLOR ? 1974 (Color) textPaint : null; 1975 1976 CompositeMode blendMode = getCompositeMode(); 1977 // LCD support requires several attributes to function: 1978 // FontStrike supports LCD, SRC_OVER CompositeMode and Paint is a COLOR 1979 boolean lcdSupported = blendMode == CompositeMode.SRC_OVER && 1980 textColor != null && 1981 xform.is2D() && 1982 !getRenderTarget().isMSAA(); 1983 1984 /* If the surface can't support LCD text we need to replace an 1985 * LCD mode strike with the equivalent grey scale one. 1986 */ 1987 if (strike.getAAMode() == FontResource.AA_LCD && !lcdSupported) { 1988 FontResource fr = strike.getFontResource(); 1989 float size = strike.getSize(); 1990 BaseTransform tx = strike.getTransform(); 1991 strike = fr.getStrike(size, tx, FontResource.AA_GREYSCALE); 1992 } 1993 1994 float bx = 0f, by = 0f, bw = 0f, bh = 0f; 1995 if (paint.getType().isGradient() && ((Gradient)paint).isProportional()) { 1996 // If drawString is called directly without using setNodeBounds, 1997 // then nodeBounds is null, and we must determine the bounds based 1998 // on the str(vs. the node). 1999 RectBounds textBounds = nodeBounds; 2000 if (textBounds == null) { 2001 Metrics m = strike.getMetrics(); 2002 float pad = -m.getAscent() * 0.4f; 2003 textBounds = new RectBounds(-pad, 2004 m.getAscent(), 2005 gl.getWidth() + 2.0f *pad, 2006 m.getDescent() + m.getLineGap()); 2007 bx = x; 2008 by = y; 2009 } 2010 2011 bx += textBounds.getMinX(); 2012 by += textBounds.getMinY(); 2013 bw = textBounds.getWidth(); 2014 bh = textBounds.getHeight(); 2015 } 2016 2017 BaseBounds clip = null; 2018 Point2D p2d = new Point2D(x, y); 2019 if (isSimpleTranslate) { 2020 /* Only use clip for simple transforms so that coordinates in the 2021 * glyph list (user space) and the coordinates of the clip 2022 * (device space) can be intersected. 2023 */ 2024 clip = getFinalClipNoClone(); 2025 xform = IDENT; 2026 p2d.x += transX; 2027 p2d.y += transY; 2028 } 2029 2030 /* Cache look up needs to be on the font as rendered, including 2031 * AA mode, metrics mode, glyph transform (pt size combined with 2032 * the full graphics transform). Most of this info is expected to 2033 * be in the font, which here is close to being a full strike 2034 * description. 2035 */ 2036 GlyphCache glyphCache = context.getGlyphCache(strike); 2037 Texture cacheTex = glyphCache.getBackingStore(); 2038 2039 //Since we currently cannot support LCD text on transparant surfaces, we 2040 //verify that we are drawing to an opaque surface. 2041 if (strike.getAAMode() == FontResource.AA_LCD) { 2042 if (nodeBounds == null) { 2043 // If drawString is called directly without using 2044 // setNodeBounds then we must determine the bounds of the str, 2045 // before we render background to texture. 2046 // This is slow, but required by webnode. 2047 2048 Metrics m = strike.getMetrics(); 2049 // Ruff guess for padding, since lots of glyphs exceed advance 2050 RectBounds textBounds = 2051 new RectBounds(x - 2, 2052 y + m.getAscent(), 2053 x + 2 + gl.getWidth(), 2054 y + 1 + m.getDescent() + m.getLineGap()); 2055 2056 setNodeBounds(textBounds); 2057 initLCDSampleRT(); 2058 setNodeBounds(null); 2059 } else { 2060 initLCDSampleRT(); 2061 } 2062 float invgamma = PrismFontFactory.getLCDContrast(); 2063 float gamma = 1.0f/invgamma; 2064 textColor = new Color((float)Math.pow(textColor.getRed(), invgamma), 2065 (float)Math.pow(textColor.getGreen(), invgamma), 2066 (float)Math.pow(textColor.getBlue(), invgamma), 2067 (float)Math.pow(textColor.getAlpha(), invgamma)); 2068 if (selectColor != null) { 2069 selectColor = new Color( 2070 (float)Math.pow(selectColor.getRed(), invgamma), 2071 (float)Math.pow(selectColor.getGreen(), invgamma), 2072 (float)Math.pow(selectColor.getBlue(), invgamma), 2073 (float)Math.pow(selectColor.getAlpha(), invgamma)); 2074 } 2075 2076 // In order to handle transparency, the LCD shader need to manually 2077 // composite source with destination. Thus, SRC_OVER compositing 2078 // needs to be set to SRC, while shader is active. 2079 setCompositeMode(CompositeMode.SRC); 2080 2081 //set our 2nd LCD shader. 2082 Shader shader = context.validateLCDOp(this, IDENT, 2083 context.getLCDBuffer(), 2084 cacheTex, false, textColor); 2085 2086 float unitXCoord = 1.0f/((float)cacheTex.getPhysicalWidth()); 2087 shader.setConstant("gamma", gamma, invgamma, unitXCoord); 2088 setCompositeMode(blendMode); // Restore composite mode 2089 } else { 2090 context.validatePaintOp(this, IDENT, cacheTex, bx, by, bw, bh); 2091 } 2092 if (isSimpleTranslate) { 2093 // Applying this rounding allows for smoother text animation, 2094 // when animating simple translated text. 2095 // Asking glyph textures to be rendered at non-integral 2096 // locations produces very poor text. This doesn't solve 2097 // the problem for scaled (etc) cases, but addresses a 2098 // common case. 2099 p2d.y = Math.round(p2d.y); 2100 p2d.x = Math.round(p2d.x); 2101 } 2102 glyphCache.render(context, gl, p2d.x, p2d.y, selectStart, selectEnd, 2103 selectColor, textColor, xform, clip); 2104 } 2105 2106 //This function is used by the LCD path to render a quad into the 2107 //LCD RTT Texture. here the presentable is set as input and 2108 //sampled using texture coordinates. This is later used in a 2109 //second pass to provide the dst color as input. 2110 private void drawLCDBuffer(float bx, float by, float bw, float bh, 2111 float tx1, float ty1, float tx2, float ty2) 2112 { 2113 context.setRenderTarget(this); 2114 context.getVertexBuffer().addQuad(bx, by, bx + bw, by + bh, tx1, ty1, tx2, ty2); 2115 } 2116 2117 public boolean canReadBack() { 2118 RenderTarget rt = getRenderTarget(); 2119 return rt instanceof ReadbackRenderTarget && 2120 ((ReadbackRenderTarget) rt).getBackBuffer() != null; 2121 } 2122 2123 public RTTexture readBack(Rectangle view) { 2124 RenderTarget rt = getRenderTarget(); 2125 context.flushVertexBuffer(); 2126 context.validateLCDBuffer(rt); 2127 RTTexture lcdrtt = context.getLCDBuffer(); 2128 Texture bbtex = ((ReadbackRenderTarget) rt).getBackBuffer(); 2129 2130 float x1 = view.x; 2131 float y1 = view.y; 2132 float x2 = x1 + view.width; 2133 float y2 = y1 + view.height; 2134 2135 // Create a graphics for us to render into the LCDBuffer 2136 // Note this also sets the current RenderTarget as the LCDBuffer 2137 BaseShaderGraphics bsg = (BaseShaderGraphics) lcdrtt.createGraphics(); 2138 bsg.setCompositeMode(CompositeMode.SRC); 2139 context.validateTextureOp(bsg, IDENT, bbtex, bbtex.getPixelFormat()); 2140 2141 // sample the source RT in the following bounds and store it in the LCDBuffer RT. 2142 bsg.drawTexture(bbtex, 0, 0, view.width, view.height, x1, y1, x2, y2); 2143 context.flushVertexBuffer(); 2144 2145 // set the RenderTarget back to this. 2146 context.setRenderTarget(this); 2147 return lcdrtt; 2148 } 2149 2150 public void releaseReadBackBuffer(RTTexture rtt) { 2151 // This will be needed when we track LCD buffer locks and uses. 2152 // (See RT-29488) 2153 // context.releaseLCDBuffer(); 2154 } 2155 2156 public void setup3DRendering() { 2157 context.setRenderTarget(this); 2158 } 2159 2160 @Override 2161 public void setPixelScaleFactor(float pixelScale) { 2162 this.pixelScale = pixelScale; 2163 } 2164 2165 @Override 2166 public float getPixelScaleFactor() { 2167 return pixelScale; 2168 } 2169 2170 }