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