1 /* 2 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.prism.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((PrivilegedAction) () -> System.getProperty("prism.primshaderpad")); 514 if (v == null) { 515 FRINGE_FACTOR = -0.5f; 516 } else { 517 FRINGE_FACTOR = -Float.valueOf(v); 518 System.out.println("Prism ShaderGraphics primitive shader pad = "+FRINGE_FACTOR); 519 } 520 } 521 522 private BaseTransform extract3Dremainder(BaseTransform xform) { 523 if (xform.is2D()) { 524 return IDENT; 525 } 526 TEMP_TX3D.setTransform(xform); 527 TEMP_TX2D.setTransform(xform.getMxx(), xform.getMyx(), 528 xform.getMxy(), xform.getMyy(), 529 xform.getMxt(), xform.getMyt()); 530 try { 531 TEMP_TX2D.invert(); 532 TEMP_TX3D.concatenate(TEMP_TX2D); 533 } catch (NoninvertibleTransformException ex) { 534 } 535 return TEMP_TX3D; 536 } 537 538 private void renderGeneralRoundedRect(float rx, float ry, float rw, float rh, 539 float arcw, float arch, 540 MaskType type, BasicStroke stroke) 541 { 542 // NOTE: using floats here for now, not sure if it's a problem yet... 543 float bx, by, bw, bh, ifractw, ifracth; 544 float ox, oy, wdx, wdy, hdx, hdy; 545 if (stroke == null) { 546 bx = rx; 547 by = ry; 548 bw = rw; 549 bh = rh; 550 ifractw = ifracth = 0f; 551 } else { 552 float sw = stroke.getLineWidth(); 553 float ow = getStrokeExpansionFactor(stroke) * sw; 554 bx = rx - ow; 555 by = ry - ow; 556 ow *= 2f; 557 bw = rw + ow; 558 bh = rh + ow; 559 if (arcw > 0 && arch > 0) { 560 arcw += ow; 561 arch += ow; 562 } else { 563 if (stroke.getLineJoin() == BasicStroke.JOIN_ROUND) { 564 arcw = arch = ow; 565 type = MaskType.DRAW_ROUNDRECT; 566 } else { 567 arcw = arch = 0f; 568 } 569 } 570 ifractw = (bw - sw * 2f) / bw; 571 ifracth = (bh - sw * 2f) / bh; 572 if (ifractw <= 0f || ifracth <= 0f) { 573 type = type.getFillType(); 574 } 575 } 576 577 // The following is safe; this method does not mutate the transform 578 BaseTransform xform = getTransformNoClone(); 579 BaseTransform rendertx; 580 if (isSimpleTranslate) { 581 wdx = hdy = 1f; 582 wdy = hdx = 0f; 583 ox = bx + transX; 584 oy = by + transY; 585 rendertx = IDENT; 586 } else { 587 rendertx = extract3Dremainder(xform); 588 wdx = (float)xform.getMxx(); 589 hdx = (float)xform.getMxy(); 590 wdy = (float)xform.getMyx(); 591 hdy = (float)xform.getMyy(); 592 ox = (bx * wdx) + (by * hdx) + (float)xform.getMxt(); 593 oy = (bx * wdy) + (by * hdy) + (float)xform.getMyt(); 594 } 595 596 wdx *= bw; 597 wdy *= bw; 598 hdx *= bh; 599 hdy *= bh; 600 601 float arcfractw = arcw / bw; 602 float arcfracth = arch / bh; 603 renderGeneralRoundedPgram(ox, oy, wdx, wdy, hdx, hdy, 604 arcfractw, arcfracth, ifractw, ifracth, 605 rendertx, type, rx, ry, rw, rh); 606 } 607 608 private void renderGeneralRoundedPgram(float ox, float oy, 609 float wvecx, float wvecy, 610 float hvecx, float hvecy, 611 float arcfractw, float arcfracth, 612 float ifractw, float ifracth, 613 BaseTransform rendertx, MaskType type, 614 float rx, float ry, float rw, float rh) 615 { 616 float wlen = len(wvecx, wvecy); 617 float hlen = len(hvecx, hvecy); 618 if (wlen == 0 || hlen == 0) { 619 // parallelogram has collapsed to a line or point 620 return; 621 } 622 623 // Calculate the 4 corners of the pgram in device space. 624 // Note that the UL,UR,LL,LR (Upper/Lower Left/Right) designations 625 // are virtual since the wdxy and hdxy vectors can point in any 626 // direction depending on the transform being applied. 627 float xUL = ox; 628 float yUL = oy; 629 float xUR = ox + wvecx; 630 float yUR = oy + wvecy; 631 float xLL = ox + hvecx; 632 float yLL = oy + hvecy; 633 float xLR = xUR + hvecx; 634 float yLR = yUR + hvecy; 635 636 // Calculate the unit vectors along each side of the pgram as well 637 // as the device space perpendicular dimension across the pgram 638 // (which is different than the lengths of the two pairs of sides 639 // since it is measured perpendicular to the "other" sides, not 640 // along the original sides). 641 // The perpendicular dimension is the dot product of the perpendicular 642 // of the unit vector for one pair of sides with the displacement 643 // vector for the other pair of sides. 644 // The unit vector perpendicular to (dx,dy) is given by: 645 // normx = dx / len(dx, dy); 646 // normy = dy / len(dx, dy); 647 // The (ccw) perpendicular vectors would then be: 648 // unitperpx = +normy = +dy / len; 649 // unitperpy = -normx = -dx / len; 650 // Thus the perpendicular width and height distances are: 651 // pwdist = wvec.uphvec = wvecx * (hvecy/hlen) - wvecy * (hvecx/hlen) 652 // phdist = hvec.upwvec = hvecx * (wvecy/wlen) - hvecy * (wvecx/wlen) 653 // If we factor out the divide by the lengths then we are left 654 // with numerators that are simply negations of each other: 655 // pwdist = (wvecx * hvecy - wvecy * hvecx) / hlen 656 // phdist = (hvecx * wvecy - hvecy * wvecx) / wlen 657 // Finally we multiply by 0.5 since we want the distance to the 658 // edges of the parallelgram from the center point, not across the 659 // whole pgram. And, since we want the absolute value, we can 660 // ignore the fact that the numerator is negated between the two 661 // formulas since we will just have to negate one of the 2 values 662 // afterwards anyway to make them both positive. 663 // Note that the numerators are the formula for the area of the 664 // parallelogram (without the abs() operation). Dividing by the 665 // length of one of the sides would then give the length of the 666 // perpendicular across to the other side. 667 float halfarea = (wvecx * hvecy - wvecy * hvecx) * 0.5f; 668 float pwdist = halfarea / hlen; 669 float phdist = halfarea / wlen; 670 if (pwdist < 0) pwdist = -pwdist; 671 if (phdist < 0) phdist = -phdist; 672 673 // Now we calculate the normalized unit vectors. 674 float nwvecx = wvecx / wlen; 675 float nwvecy = wvecy / wlen; 676 float nhvecx = hvecx / hlen; 677 float nhvecy = hvecy / hlen; 678 679 // Bias the parallelogram corner coordinates "outward" by half a 680 // pixel distance. 681 // The most general way to do this is to move each parallelogram 682 // edge outward by a specified distance and then find the new 683 // intersection point. 684 // The general form for the intersection of 2 lines is: 685 // line1 = (x1,y1) -> (x2,y2) 686 // line2 = (x3,y3) -> (x4,y4) 687 // t = ((x4-x3)(y1-y3) - (y4-y3)(x1-x3)) 688 // / ((y4-y3)(x2-x1) - (x4-x3)(y2-y1)) 689 // intersection point = (x1 + t*(x2-x1), y1 + t*(y2-y1)) 690 // Now consider if these lines are displaced versions of 2 691 // other lines which share a common end point (such as is the 692 // case of pushing 2 adjacent edges of the pgram outward: 693 // line1 = (xa+dx1, ya+dy1) -> (xb+dx1, yb+dy1) 694 // line2 = (xa+dx2, ya+dy2) -> (xc+dx2, yc+dy2) 695 // "x4-x3" = (xc+dx2) - (xa+dx2) = xc-xa 696 // "y4-y3" = (yc+dy2) - (ya+dy2) = yc-ya 697 // "x2-x1" = (xb+dx1) - (xa+dx1) = xb-xa 698 // "y2-y1" = (yb+dy1) - (ya+dy1) = yb-ya 699 // "y1-y3" = (y1+dy1) - (y1+dy2) = dy1 - dy2 700 // "x1-x3" = (xa+dx1) - (xa+dx2) = dx1 - dx2 701 // t = ((xc-xa)(dy1-dy2) - ((yc-ya)(dx1-dx2)) 702 // / ((yc-ya)(xb-xa) - (xc-xa)(yb-ya)) 703 // Now we need to displace these 2 lines "outward" by half a 704 // pixel distance. We will start by looking at applying a unit 705 // pixel distance and then cutting the adjustment in half (it 706 // can be seen to scale linearly when you look at the final 707 // equations). This is achieved by applying one of our unit 708 // vectors with a cw rotation to one line and the other unit 709 // vector with a ccw rotation to the other line. Ideally we 710 // would choose which to apply cw vs. ccw by the way that this 711 // corner of the pgram is turning, but as it turns out, the 712 // consequences of getting it backward is that our "t" will 713 // turn out negative (just negate all of the d[xy][12] values 714 // in the t equation above and you will see that it changes 715 // sign. Since we are calculating the new corner using the 716 // equation newx = (xa+dx1) + t*(xb-xa), we want the value of 717 // t that drives the point "away from xb,yb", in other words, 718 // we want the negative t. So, if t is positive then we want 719 // to use negative t and reverse the perpendicular offset we 720 // applied to xa: 721 // t < 0 => newx = (xa+dx) + t*(xb-xa) = xa + (dx + t*(xb-xa)) 722 // t > 0 => newx = (xa-dx) - t*(xb-xa) = xa - (dx + t*(xb-xa)) 723 // where (copying t equation from above again): 724 // t = ((xc-xa)(dy1-dy2) - ((yc-ya)(dx1-dx2)) 725 // / ((yc-ya)(xb-xa) - (xc-xa)(yb-ya)) 726 // For xa,ya = xUL,yUL and xb,yb = xUR,yUR and xc,yc = xLL,yLL: 727 // [xy]b - [xy]a = [xy]UR - [xy]UL = wvec 728 // [xy]c - [xy]a = [xy]LL - [xy]UL = hvec 729 // dx1,dy1 = +nwvecy, -nwvecx // ccw on xa->xb 730 // dx2,dy2 = -nhvecy, +nhvecx // cw on xa->xc 731 // dx1 - dx2 = +nwvecy - -nhvecy = nwvecy + nhvecy 732 // dy1 - dy2 = -nwvecx - +nhvecx = -(nwvecx + nhvecx) 733 float num = -hvecx*(nwvecx + nhvecx) - hvecy*(nwvecy + nhvecy); 734 float den = hvecy*wvecx - hvecx*wvecy; 735 float t = num / den; 736 // Negating the sign of t and multiplying by 0.5 gets us the 737 // proper sign for the offset and cuts it down to half a pixel 738 float factor = FRINGE_FACTOR * Math.signum(t); 739 float offx = (t * wvecx + nwvecy) * factor; 740 float offy = (t * wvecy - nwvecx) * factor; 741 xUL += offx; yUL += offy; 742 // x22 is offset by the reverse amounts 743 xLR -= offx; yLR -= offy; 744 // For xa,ya = xUR,yUR and xb,yb = xLR,yLR and xc,yc = xUL,yUL 745 // Note that xa,ya => xc,yc is negative of wvec 746 // [xy]b - [xy]a = [xy]LR - [xy]UR = +hvec 747 // [xy]c - [xy]a = [xy]UL - [xy]UR = -wvec 748 // dx1,dy1 = +nhvecy, -nhvecx 749 // dx2,dy2 = -(-nwvecy), +(-nwvecx) = +nwvecy, -nwvecx 750 // dx1 - dx2 = nhvecy - nwvecy 751 // dy1 - dy2 = -nhvecx - -nwvecx = nwvecx - nhvecx 752 // den = -wvecy * hvecx - -wvecx * hvecy 753 // = hvecy * wvecx - hvecx * wvecy (already computed) 754 // num = -wvecx * (nwvecx - nhvecx) - -wvecy * (nhvecy - nwvecy) 755 // = wvecy * (nhvecy - nwvecy) - wvecx * (nwvecx - nhvecx) 756 num = wvecy * (nhvecy - nwvecy) - wvecx * (nwvecx - nhvecx); 757 t = num / den; 758 factor = FRINGE_FACTOR * Math.signum(t); 759 offx = (t * hvecx + nhvecy) * factor; 760 offy = (t * hvecy - nhvecx) * factor; 761 xUR += offx; yUR += offy; 762 xLL -= offx; yLL -= offy; 763 764 // texture coordinates (uv) will be calculated using a transform 765 // by the perpendicular unit vectors so that the texture U 766 // coordinates measure our progress along the pwdist axis (i.e. 767 // perpendicular to hvec and the texture V coordinates measure 768 // our progress along phdist (perpendicular to wvec): 769 // u = x * nhvecy - y * nhvecx; 770 // v = x * nwvecy - y * nwvecx; 771 772 // We now calculate the uv paramters for the 4 corners such that 773 // uv(cx,cy) = 0,0 774 // uv(corners - center) = +/-(wlen/2), +/-(hlen/2) 775 // Note that: 776 // uv(corner - center) = uv(corner) - uv(center) 777 // Calculate the center of the parallelogram and its uv values 778 float xC = (xUL + xLR) * 0.5f; 779 float yC = (yUL + yLR) * 0.5f; 780 float uC = xC * nhvecy - yC * nhvecx; 781 float vC = xC * nwvecy - yC * nwvecx; 782 // Now use that to calculate the corner values relative to the center 783 float uUL = xUL * nhvecy - yUL * nhvecx - uC; 784 float vUL = xUL * nwvecy - yUL * nwvecx - vC; 785 float uUR = xUR * nhvecy - yUR * nhvecx - uC; 786 float vUR = xUR * nwvecy - yUR * nwvecx - vC; 787 float uLL = xLL * nhvecy - yLL * nhvecx - uC; 788 float vLL = xLL * nwvecy - yLL * nwvecx - vC; 789 float uLR = xLR * nhvecy - yLR * nhvecx - uC; 790 float vLR = xLR * nwvecy - yLR * nwvecx - vC; 791 792 // the pgram params have been calculated in device space, so we use 793 // identity transform here 794 if (type == MaskType.DRAW_ROUNDRECT || type == MaskType.FILL_ROUNDRECT) { 795 float oarcw = pwdist * arcfractw; 796 float oarch = phdist * arcfracth; 797 if (oarcw < 0.5 || oarch < 0.5) { 798 // The pgram renderer fades the entire primitive if the arc 799 // sizes fall below 0.5 pixels since the interiors act as if 800 // they are sampled at the center of the indicated circle and 801 // radii smaller than that produce less than full coverage 802 // even at the circle center. If the arcwh are less than 803 // 0.5 then the difference in area of the corner pixels 804 // compared to a PGRAM primitive of the same size is just the 805 // tiny corner cutout of area (4-PI)/16 which is less than 806 // .06 pixels. Thus, we can convert the primitive to a 807 // PGRAM without any loss of precision. 808 type = (type == MaskType.DRAW_ROUNDRECT) 809 ? MaskType.DRAW_PGRAM : MaskType.FILL_PGRAM; 810 } else { 811 float flatw = pwdist - oarcw; 812 float flath = phdist - oarch; 813 float ivalw, ivalh; 814 if (type == MaskType.DRAW_ROUNDRECT) { 815 float iwdist = pwdist * ifractw; 816 float ihdist = phdist * ifracth; 817 // First we calculate the inner arc radii and see if they 818 // are large enough to render. 819 ivalw = iwdist - flatw; 820 ivalh = ihdist - flath; 821 // As above we need to fix things if we get inner arc 822 // radii below half a pixel. We have a special shader 823 // for doing a "semi round" rect which has a round outer 824 // shell and a rectangular (pgram) inner shell... 825 if (ivalw < 0.5f || ivalh < 0.5f) { 826 // inner val is idim 827 ivalw = iwdist; 828 ivalh = ihdist; 829 type = MaskType.DRAW_SEMIROUNDRECT; 830 } else { 831 // inner val is invarcradii 832 ivalw = 1.0f / ivalw; 833 ivalh = 1.0f / ivalh; 834 } 835 } else { 836 // Not used by FILL_ROUNDRECT, but we need constant 837 // values that will not cause an unnecessary vertex 838 // buffer flush in the validateOp below. 839 ivalw = ivalh = 0f; 840 } 841 oarcw = 1.0f / oarcw; 842 oarch = 1.0f / oarch; 843 Shader shader = 844 context.validatePaintOp(this, rendertx, type, 845 rx, ry, rw, rh, 846 oarcw, oarch, 847 ivalw, ivalh, 0, 0); 848 shader.setConstant("oinvarcradii", oarcw, oarch); 849 if (type == MaskType.DRAW_ROUNDRECT) { 850 shader.setConstant("iinvarcradii", ivalw, ivalh); 851 } else if (type == MaskType.DRAW_SEMIROUNDRECT) { 852 shader.setConstant("idim", ivalw, ivalh); 853 } 854 pwdist = flatw; 855 phdist = flath; 856 } 857 } // no else here as we may have converted an RRECT to a PGRAM above 858 if (type == MaskType.DRAW_PGRAM || type == MaskType.DRAW_ELLIPSE) { 859 float idimw = pwdist * ifractw; 860 float idimh = phdist * ifracth; 861 if (type == MaskType.DRAW_ELLIPSE) { 862 if (Math.abs(pwdist - phdist) < .01) { 863 type = MaskType.DRAW_CIRCLE; 864 // The phdist and idimh parameters will not be used by this 865 // shader, but we do need the maximum single pixel coverage 866 // so we coopt them to be min(1.0, area): 867 phdist = (float) Math.min(1.0, phdist * phdist * Math.PI); 868 idimh = (float) Math.min(1.0, idimh * idimh * Math.PI); 869 } else { 870 // the ellipse drawing shader uses inverted arc dimensions 871 // to turn divides into multiplies 872 pwdist = 1.0f / pwdist; 873 phdist = 1.0f / phdist; 874 idimw = 1.0f / idimw; 875 idimh = 1.0f / idimh; 876 } 877 } 878 Shader shader = 879 context.validatePaintOp(this, rendertx, type, 880 rx, ry, rw, rh, 881 idimw, idimh, 0f, 0f, 0f, 0f); 882 shader.setConstant("idim", idimw, idimh); 883 } else if (type == MaskType.FILL_ELLIPSE) { 884 if (Math.abs(pwdist - phdist) < .01) { 885 type = MaskType.FILL_CIRCLE; 886 // The phdist parameter will not be used by this shader, 887 // but we do need the maximum single pixel contribution 888 // so we coopt the value to be min(1.0, area): 889 phdist = (float) Math.min(1.0, phdist * phdist * Math.PI); 890 } else { 891 // the ellipse filling shader uses inverted arc dimensions to 892 // turn divides into multiplies: 893 pwdist = 1.0f / pwdist; 894 phdist = 1.0f / phdist; 895 uUL *= pwdist; vUL *= phdist; 896 uUR *= pwdist; vUR *= phdist; 897 uLL *= pwdist; vLL *= phdist; 898 uLR *= pwdist; vLR *= phdist; 899 } 900 context.validatePaintOp(this, rendertx, type, rx, ry, rw, rh); 901 } else if (type == MaskType.FILL_PGRAM) { 902 context.validatePaintOp(this, rendertx, type, rx, ry, rw, rh); 903 } 904 905 context.getVertexBuffer().addMappedPgram(xUL, yUL, xUR, yUR, 906 xLL, yLL, xLR, yLR, 907 uUL, vUL, uUR, vUR, 908 uLL, vLL, uLR, vLR, 909 pwdist, phdist); 910 } 911 912 AffineBase getPaintTextureTx(BaseTransform renderTx, Shader shader, 913 float rx, float ry, float rw, float rh) 914 { 915 switch (paint.getType()) { 916 case COLOR: 917 return null; 918 case LINEAR_GRADIENT: 919 return PaintHelper.getLinearGradientTx((LinearGradient) paint, 920 shader, renderTx, 921 rx, ry, rw, rh); 922 case RADIAL_GRADIENT: 923 return PaintHelper.getRadialGradientTx((RadialGradient) paint, 924 shader, renderTx, 925 rx, ry, rw, rh); 926 case IMAGE_PATTERN: 927 return PaintHelper.getImagePatternTx(this, (ImagePattern) paint, 928 shader, renderTx, 929 rx, ry, rw, rh); 930 } 931 throw new InternalError("Unrecogized paint type: "+paint); 932 } 933 934 // The second set of rectangular bounds are for validating a 935 // proportional paint. They should be identical to the first 936 // set for a fillRect() operation, but they may be different 937 // for a vertical or horizontal drawLine() operation. 938 boolean fillPrimRect(float x, float y, float w, float h, 939 Texture rectTex, Texture wrapTex, 940 float bx, float by, float bw, float bh) 941 { 942 BaseTransform xform = getTransformNoClone(); 943 float mxx = (float) xform.getMxx(); 944 float mxy = (float) xform.getMxy(); 945 float mxt = (float) xform.getMxt(); 946 float myx = (float) xform.getMyx(); 947 float myy = (float) xform.getMyy(); 948 float myt = (float) xform.getMyt(); 949 float dxdist = len(mxx, myx); 950 float dydist = len(mxy, myy); 951 if (dxdist == 0.0f || dydist == 0.0f) { 952 // entire path has collapsed and occupies no area 953 return true; 954 } 955 float pixelw = 1.0f / dxdist; 956 float pixelh = 1.0f / dydist; 957 float x0 = x - pixelw * 0.5f; 958 float y0 = y - pixelh * 0.5f; 959 float x1 = x + w + pixelw * 0.5f; 960 float y1 = y + h + pixelh * 0.5f; 961 int cellw = (int) Math.ceil(w * dxdist - 1.0f/512.0f); 962 int cellh = (int) Math.ceil(h * dydist - 1.0f/512.0f); 963 VertexBuffer vb = context.getVertexBuffer(); 964 int max = context.getRectTextureMaxSize(); 965 if (cellw <= max && cellh <= max) { 966 float u0 = ((cellw * (cellw + 1)) / 2) - 0.5f; 967 float v0 = ((cellh * (cellh + 1)) / 2) - 0.5f; 968 float u1 = u0 + cellw + 1.0f; 969 float v1 = v0 + cellh + 1.0f; 970 u0 /= rectTex.getPhysicalWidth(); 971 v0 /= rectTex.getPhysicalHeight(); 972 u1 /= rectTex.getPhysicalWidth(); 973 v1 /= rectTex.getPhysicalHeight(); 974 if (xform.isTranslateOrIdentity()) { 975 x0 += mxt; 976 y0 += myt; 977 x1 += mxt; 978 y1 += myt; 979 xform = IDENT; 980 } else if (xform.is2D()) { 981 Shader shader = 982 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE, rectTex, 983 bx, by, bw, bh); 984 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 985 if (paintTx == null) { 986 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 987 x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 988 x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 989 x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 990 u0, v0, u1, v0, u0, v1, u1, v1, 0, 0); 991 } else { 992 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 993 x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 994 x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 995 x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 996 u0, v0, u1, v0, u0, v1, u1, v1, 997 x0, y0, x1, y1, paintTx); 998 } 999 return true; 1000 } else { 1001 System.out.println("Not a 2d transform!"); 1002 mxt = myt = 0.0f; 1003 } 1004 Shader shader = 1005 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rectTex, 1006 bx, by, bw, bh); 1007 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1008 if (paintTx == null) { 1009 vb.addQuad(x0, y0, x1, y1, 1010 u0, v0, u1, v1); 1011 } else { 1012 paintTx.translate(-mxt, -myt); 1013 vb.addQuad(x0, y0, x1, y1, 1014 u0, v0, u1, v1, 1015 paintTx); 1016 } 1017 return true; 1018 } 1019 if (wrapTex == null) { 1020 return false; 1021 } 1022 float u0 = 0.5f / wrapTex.getPhysicalWidth(); 1023 float v0 = 0.5f / wrapTex.getPhysicalHeight(); 1024 float uc = (cellw * 0.5f + 1.0f) / wrapTex.getPhysicalWidth(); 1025 float vc = (cellh * 0.5f + 1.0f) / wrapTex.getPhysicalHeight(); 1026 float xc = x + w * 0.5f; 1027 float yc = y + h * 0.5f; 1028 if (xform.isTranslateOrIdentity()) { 1029 x0 += mxt; 1030 y0 += myt; 1031 xc += mxt; 1032 yc += myt; 1033 x1 += mxt; 1034 y1 += myt; 1035 xform = IDENT; 1036 } else if (xform.is2D()) { 1037 Shader shader = 1038 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE, wrapTex, 1039 bx, by, bw, bh); 1040 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1041 float mxx_x0 = mxx * x0, myx_x0 = myx * x0; 1042 float mxy_y0 = mxy * y0, myy_y0 = myy * y0; 1043 float mxx_xc = mxx * xc, myx_xc = myx * xc; 1044 float mxy_yc = mxy * yc, myy_yc = myy * yc; 1045 float mxx_x1 = mxx * x1, myx_x1 = myx * x1; 1046 float mxy_y1 = mxy * y1, myy_y1 = myy * y1; 1047 // xcc,ycc used in all 4 quads 1048 float xcc = mxx_xc + mxy_yc + mxt; 1049 float ycc = myx_xc + myy_yc + myt; 1050 // xcn, ycn and xnc, ync all used in 2 quads each 1051 float xc0 = mxx_xc + mxy_y0 + mxt; 1052 float yc0 = myx_xc + myy_y0 + myt; 1053 float x0c = mxx_x0 + mxy_yc + mxt; 1054 float y0c = myx_x0 + myy_yc + myt; 1055 float xc1 = mxx_xc + mxy_y1 + mxt; 1056 float yc1 = myx_xc + myy_y1 + myt; 1057 float x1c = mxx_x1 + mxy_yc + mxt; 1058 float y1c = myx_x1 + myy_yc + myt; 1059 // Note that all quads use same 00->c0->0c->cc coordinates for 1060 // the inner and outer uv texture coordinates regardless of the 1061 // reflection of the quad of vertex coordinates 1062 1063 if (paintTx == null) { 1064 // quad1 - 00 -> c0 -> 0c -> cc 1065 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 1066 xc0, yc0, x0c, y0c, xcc, ycc, 1067 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1068 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1069 vb.addMappedPgram(x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 1070 xc0, yc0, x1c, y1c, xcc, ycc, 1071 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1072 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1073 vb.addMappedPgram(x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 1074 xc1, yc1, x0c, y0c, xcc, ycc, 1075 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1076 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1077 vb.addMappedPgram(x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 1078 xc1, yc1, x1c, y1c, xcc, ycc, 1079 u0, v0, uc, v0, u0, vc, uc, vc, 0, 0); 1080 } else { 1081 // quad1 - 00 -> c0 -> 0c -> cc 1082 vb.addMappedPgram(x0 * mxx + y0 * mxy + mxt, x0 * myx + y0 * myy + myt, 1083 xc0, yc0, x0c, y0c, xcc, ycc, 1084 u0, v0, uc, v0, u0, vc, uc, vc, 1085 x0, y0, xc, yc, paintTx); 1086 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1087 vb.addMappedPgram(x1 * mxx + y0 * mxy + mxt, x1 * myx + y0 * myy + myt, 1088 xc0, yc0, x1c, y1c, xcc, ycc, 1089 u0, v0, uc, v0, u0, vc, uc, vc, 1090 x1, y0, xc, yc, paintTx); 1091 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1092 vb.addMappedPgram(x0 * mxx + y1 * mxy + mxt, x0 * myx + y1 * myy + myt, 1093 xc1, yc1, x0c, y0c, xcc, ycc, 1094 u0, v0, uc, v0, u0, vc, uc, vc, 1095 x0, y1, xc, yc, paintTx); 1096 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1097 vb.addMappedPgram(x1 * mxx + y1 * mxy + mxt, x1 * myx + y1 * myy + myt, 1098 xc1, yc1, x1c, y1c, xcc, ycc, 1099 u0, v0, uc, v0, u0, vc, uc, vc, 1100 x1, y1, xc, yc, paintTx); 1101 } 1102 return true; 1103 } else { 1104 System.out.println("Not a 2d transform!"); 1105 mxt = myt = 0; 1106 } 1107 Shader shader = 1108 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, wrapTex, 1109 bx, by, bw, bh); 1110 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1111 if (paintTx != null) { 1112 paintTx.translate(-mxt, -myt); 1113 } 1114 vb.addQuad(x0, y0, xc, yc, 1115 u0, v0, uc, vc, 1116 paintTx); 1117 vb.addQuad(x1, y0, xc, yc, 1118 u0, v0, uc, vc, 1119 paintTx); 1120 vb.addQuad(x0, y1, xc, yc, 1121 u0, v0, uc, vc, 1122 paintTx); 1123 vb.addQuad(x1, y1, xc, yc, 1124 u0, v0, uc, vc, 1125 paintTx); 1126 return true; 1127 } 1128 1129 boolean drawPrimRect(float x, float y, float w, float h) { 1130 float lw = stroke.getLineWidth(); 1131 float pad = getStrokeExpansionFactor(stroke) * lw; 1132 BaseTransform xform = getTransformNoClone(); 1133 float mxx = (float) xform.getMxx(); 1134 float mxy = (float) xform.getMxy(); 1135 float mxt = (float) xform.getMxt(); 1136 float myx = (float) xform.getMyx(); 1137 float myy = (float) xform.getMyy(); 1138 float myt = (float) xform.getMyt(); 1139 float dxdist = len(mxx, myx); 1140 float dydist = len(mxy, myy); 1141 if (dxdist == 0.0f || dydist == 0.0f) { 1142 // entire path has collapsed and occupies no area 1143 return true; 1144 } 1145 float pixelw = 1.0f / dxdist; 1146 float pixelh = 1.0f / dydist; 1147 float x0 = x - pad - pixelw * 0.5f; 1148 float y0 = y - pad - pixelh * 0.5f; 1149 float xc = x + w * 0.5f; 1150 float yc = y + h * 0.5f; 1151 float x1 = x + w + pad + pixelw * 0.5f; 1152 float y1 = y + h + pad + pixelh * 0.5f; 1153 Texture rTex = context.getWrapRectTexture(); 1154 float wscale = 1.0f / rTex.getPhysicalWidth(); 1155 float hscale = 1.0f / rTex.getPhysicalHeight(); 1156 float ou0 = 0.5f * wscale; 1157 float ov0 = 0.5f * hscale; 1158 float ouc = ((w * 0.5f + pad) * dxdist + 1.0f) * wscale; 1159 float ovc = ((h * 0.5f + pad) * dydist + 1.0f) * hscale; 1160 float offsetx = lw * dxdist * wscale; 1161 float offsety = lw * dydist * hscale; 1162 VertexBuffer vb = context.getVertexBuffer(); 1163 if (xform.isTranslateOrIdentity()) { 1164 x0 += mxt; 1165 y0 += myt; 1166 xc += mxt; 1167 yc += myt; 1168 x1 += mxt; 1169 y1 += myt; 1170 xform = IDENT; 1171 } else if (xform.is2D()) { 1172 Shader shader = 1173 context.validatePaintOp(this, IDENT, MaskType.ALPHA_TEXTURE_DIFF, 1174 rTex, x, y, w, h, 1175 offsetx, offsety, 0, 0, 0, 0); 1176 shader.setConstant("innerOffset", offsetx, offsety); 1177 AffineBase paintTx = getPaintTextureTx(IDENT, shader, x, y, w, h); 1178 float mxx_x0 = mxx * x0, myx_x0 = myx * x0; 1179 float mxy_y0 = mxy * y0, myy_y0 = myy * y0; 1180 float mxx_xc = mxx * xc, myx_xc = myx * xc; 1181 float mxy_yc = mxy * yc, myy_yc = myy * yc; 1182 float mxx_x1 = mxx * x1, myx_x1 = myx * x1; 1183 float mxy_y1 = mxy * y1, myy_y1 = myy * y1; 1184 1185 // xcc,ycc used in all 4 quads 1186 float xcc = mxx_xc + mxy_yc + mxt; 1187 float ycc = myx_xc + myy_yc + myt; 1188 // xcn, ycn and xnc, ync all used in 2 quads each 1189 float xc0 = mxx_xc + mxy_y0 + mxt; 1190 float yc0 = myx_xc + myy_y0 + myt; 1191 float x0c = mxx_x0 + mxy_yc + mxt; 1192 float y0c = myx_x0 + myy_yc + myt; 1193 float xc1 = mxx_xc + mxy_y1 + mxt; 1194 float yc1 = myx_xc + myy_y1 + myt; 1195 float x1c = mxx_x1 + mxy_yc + mxt; 1196 float y1c = myx_x1 + myy_yc + myt; 1197 // Note that all quads use same 00->c0->0c->cc coordinates for 1198 // the inner and outer uv texture coordinates regardless of the 1199 // reflection of the quad of vertex coordinates 1200 1201 if (paintTx == null) { 1202 // quad1 - 00 -> c0 -> 0c -> cc 1203 vb.addMappedPgram(mxx_x0 + mxy_y0 + mxt, myx_x0 + myy_y0 + myt, 1204 xc0, yc0, x0c, y0c, xcc, ycc, 1205 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1206 0, 0); 1207 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1208 vb.addMappedPgram(mxx_x1 + mxy_y0 + mxt, myx_x1 + myy_y0 + myt, 1209 xc0, yc0, x1c, y1c, xcc, ycc, 1210 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1211 0, 0); 1212 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1213 vb.addMappedPgram(mxx_x0 + mxy_y1 + mxt, myx_x0 + myy_y1 + myt, 1214 xc1, yc1, x0c, y0c, xcc, ycc, 1215 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1216 0, 0); 1217 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1218 vb.addMappedPgram(mxx_x1 + mxy_y1 + mxt, myx_x1 + myy_y1 + myt, 1219 xc1, yc1, x1c, y1c, xcc, ycc, 1220 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1221 0, 0); 1222 } else { 1223 // quad1 - 00 -> c0 -> 0c -> cc 1224 vb.addMappedPgram(mxx_x0 + mxy_y0 + mxt, myx_x0 + myy_y0 + myt, 1225 xc0, yc0, x0c, y0c, xcc, ycc, 1226 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1227 x0, y0, xc, yc, paintTx); 1228 // quad2 - 10 -> c0 -> 1c -> cc (reflect quad1 around x=c) 1229 vb.addMappedPgram(mxx_x1 + mxy_y0 + mxt, myx_x1 + myy_y0 + myt, 1230 xc0, yc0, x1c, y1c, xcc, ycc, 1231 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1232 x1, y0, xc, yc, paintTx); 1233 // quad3 - 01 -> c1 -> 0c -> cc (reflect quad1 around y=c) 1234 vb.addMappedPgram(mxx_x0 + mxy_y1 + mxt, myx_x0 + myy_y1 + myt, 1235 xc1, yc1, x0c, y0c, xcc, ycc, 1236 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1237 x0, y1, xc, yc, paintTx); 1238 // quad4 - 11 -> c1 -> 1c -> cc (reflect quad1 around x=c and y=c) 1239 vb.addMappedPgram(mxx_x1 + mxy_y1 + mxt, myx_x1 + myy_y1 + myt, 1240 xc1, yc1, x1c, y1c, xcc, ycc, 1241 ou0, ov0, ouc, ov0, ou0, ovc, ouc, ovc, 1242 x1, y1, xc, yc, paintTx); 1243 } 1244 rTex.unlock(); 1245 return true; 1246 } else { 1247 System.out.println("Not a 2d transform!"); 1248 mxt = myt = 0.0f; 1249 } 1250 Shader shader = 1251 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE_DIFF, 1252 rTex, x, y, w, h, 1253 offsetx, offsety, 0, 0, 0, 0); 1254 shader.setConstant("innerOffset", offsetx, offsety); 1255 AffineBase paintTx = getPaintTextureTx(IDENT, shader, x, y, w, h); 1256 if (paintTx != null) { 1257 paintTx.translate(-mxt, -myt); 1258 } 1259 vb.addQuad( x0, y0, xc, yc, 1260 ou0, ov0, ouc, ovc, 1261 paintTx); 1262 vb.addQuad( x1, y0, xc, yc, 1263 ou0, ov0, ouc, ovc, 1264 paintTx); 1265 vb.addQuad( x0, y1, xc, yc, 1266 ou0, ov0, ouc, ovc, 1267 paintTx); 1268 vb.addQuad( x1, y1, xc, yc, 1269 ou0, ov0, ouc, ovc, 1270 paintTx); 1271 rTex.unlock(); 1272 return true; 1273 } 1274 1275 boolean drawPrimDiagonal(float x1, float y1, float x2, float y2, 1276 float lw, int cap, 1277 float bx, float by, float bw, float bh) 1278 { 1279 // assert x1 != x2 && y1 != y2, otherwise caller would have 1280 // vectored us to fillPrimRect() 1281 if (stroke.getType() == BasicStroke.TYPE_CENTERED) { 1282 lw *= 0.5f; 1283 } 1284 float dx = x2 - x1; 1285 float dy = y2 - y1; 1286 float len = len(dx, dy); 1287 dx /= len; 1288 dy /= len; 1289 float ldx = dx * lw; 1290 float ldy = dy * lw; 1291 // First expand perpendicularly using (ldy, -ldx) 1292 float xUL = x1 + ldy, yUL = y1 - ldx; 1293 float xUR = x2 + ldy, yUR = y2 - ldx; 1294 float xLL = x1 - ldy, yLL = y1 + ldx; 1295 float xLR = x2 - ldy, yLR = y2 + ldx; 1296 if (cap == BasicStroke.CAP_SQUARE) { 1297 // Then add SQUARE end caps using (ldx, ldy) if needed 1298 xUL -= ldx; yUL -= ldy; 1299 xLL -= ldx; yLL -= ldy; 1300 xUR += ldx; yUR += ldy; 1301 xLR += ldx; yLR += ldy; 1302 } 1303 1304 float hdx, hdy, vdx, vdy; 1305 int cellw, cellh; 1306 BaseTransform xform = getTransformNoClone(); 1307 float mxt = (float) xform.getMxt(); 1308 float myt = (float) xform.getMyt(); 1309 if (xform.isTranslateOrIdentity()) { 1310 hdx = dx; hdy = dy; 1311 vdx = dy; vdy = -dx; 1312 cellw = (int) Math.ceil(len(xUR - xUL, yUR - yUL)); 1313 cellh = (int) Math.ceil(len(xLL - xUL, yLL - yUL)); 1314 xform = IDENT; 1315 } else if (xform.is2D()) { 1316 float mxx = (float) xform.getMxx(); 1317 float mxy = (float) xform.getMxy(); 1318 float myx = (float) xform.getMyx(); 1319 float myy = (float) xform.getMyy(); 1320 float tx, ty; 1321 tx = mxx * xUL + mxy * yUL; 1322 ty = myx * xUL + myy * yUL; 1323 xUL = tx; yUL = ty; 1324 tx = mxx * xUR + mxy * yUR; 1325 ty = myx * xUR + myy * yUR; 1326 xUR = tx; yUR = ty; 1327 tx = mxx * xLL + mxy * yLL; 1328 ty = myx * xLL + myy * yLL; 1329 xLL = tx; yLL = ty; 1330 tx = mxx * xLR + mxy * yLR; 1331 ty = myx * xLR + myy * yLR; 1332 xLR = tx; yLR = ty; 1333 // hdx, hdy are transformed unit vectors along the line 1334 hdx = mxx * dx + mxy * dy; 1335 hdy = myx * dx + myy * dy; 1336 float dlen = len(hdx, hdy); 1337 if (dlen == 0.0f) return true; 1338 hdx /= dlen; 1339 hdy /= dlen; 1340 // vdx, vdy are transformed perpendicular unit vectors 1341 // (perpendicular to the line in user space, but then transformed) 1342 vdx = mxx * dy - mxy * dx; 1343 vdy = myx * dy - myy * dx; 1344 dlen = len(vdx, vdy); 1345 if (dlen == 0.0f) return true; 1346 vdx /= dlen; 1347 vdy /= dlen; 1348 cellw = (int) Math.ceil(Math.abs((xUR - xUL) * hdx + (yUR - yUL) * hdy)); 1349 cellh = (int) Math.ceil(Math.abs((xLL - xUL) * vdx + (yLL - yUL) * vdy)); 1350 xform = IDENT; 1351 } else { 1352 System.out.println("Not a 2d transform!"); 1353 return false; 1354 } 1355 hdx *= 0.5f; 1356 hdy *= 0.5f; 1357 vdx *= 0.5f; 1358 vdy *= 0.5f; 1359 xUL = xUL + mxt + vdx - hdx; 1360 yUL = yUL + myt + vdy - hdy; 1361 xUR = xUR + mxt + vdx + hdx; 1362 yUR = yUR + myt + vdy + hdy; 1363 xLL = xLL + mxt - vdx - hdx; 1364 yLL = yLL + myt - vdy - hdy; 1365 xLR = xLR + mxt - vdx + hdx; 1366 yLR = yLR + myt - vdy + hdy; 1367 VertexBuffer vb = context.getVertexBuffer(); 1368 int cellmax = context.getRectTextureMaxSize(); 1369 if (cellh <= cellmax) { 1370 float v0 = ((cellh * (cellh + 1)) / 2) - 0.5f; 1371 float v1 = v0 + cellh + 1.0f; 1372 Texture rTex = context.getRectTexture(); 1373 v0 /= rTex.getPhysicalHeight(); 1374 v1 /= rTex.getPhysicalHeight(); 1375 if (cellw <= cellmax) { 1376 float u0 = ((cellw * (cellw + 1)) / 2) - 0.5f; 1377 float u1 = u0 + cellw + 1.0f; 1378 u0 /= rTex.getPhysicalWidth(); 1379 u1 /= rTex.getPhysicalWidth(); 1380 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1381 bx, by, bw, bh); 1382 vb.addMappedPgram(xUL, yUL, xUR, yUR, xLL, yLL, xLR, yLR, 1383 u0, v0, u1, v0, u0, v1, u1, v1, 1384 0, 0); 1385 // System.out.print("1"); System.out.flush(); 1386 rTex.unlock(); 1387 return true; 1388 } 1389 // long thin line (cellw is along the line, cellh is across it) 1390 if (cellw <= cellmax * 2 - 1) { 1391 // 2-slice the line at its midpoint. 1392 // use the cellmax,cellh cell for maximum coverage of each half 1393 // we can use at most (cellmax-0.5) per half so that we do not 1394 // see the antialias drop off on the last half pixel of the far 1395 // end of the cell. This lets us support a line of "length" up 1396 // to (cellmax-0.5 + cellmax-0.5) or (cellmax*2-1). 1397 float xUC = (xUL + xUR) * 0.5f; 1398 float yUC = (yUL + yUR) * 0.5f; 1399 float xLC = (xLL + xLR) * 0.5f; 1400 float yLC = (yLL + yLR) * 0.5f; 1401 float u0 = ((cellmax * (cellmax + 1)) / 2) - 0.5f; 1402 float u1 = u0 + 0.5f + cellw * 0.5f; 1403 u0 /= rTex.getPhysicalWidth(); 1404 u1 /= rTex.getPhysicalWidth(); 1405 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1406 bx, by, bw, bh); 1407 // first half of line x1,y1 -> midpoint 1408 vb.addMappedPgram(xUL, yUL, xUC, yUC, xLL, yLL, xLC, yLC, 1409 u0, v0, u1, v0, u0, v1, u1, v1, 1410 0, 0); 1411 // second half of line midpoint -> x2,y2 1412 vb.addMappedPgram(xUR, yUR, xUC, yUC, xLR, yLR, xLC, yLC, 1413 u0, v0, u1, v0, u0, v1, u1, v1, 1414 0, 0); 1415 // System.out.print("2"); System.out.flush(); 1416 rTex.unlock(); 1417 return true; 1418 } 1419 // Finally, 3-slice the line (left edge, huge middle, right edge) 1420 float u0 = 0.5f / rTex.getPhysicalWidth(); 1421 float u1 = 1.5f / rTex.getPhysicalWidth(); 1422 // The lower case L or R indicates "inner left" or "inner right" 1423 hdx *= 2.0f; 1424 hdy *= 2.0f; 1425 float xUl = xUL + hdx; 1426 float yUl = yUL + hdy; 1427 float xUr = xUR - hdx; 1428 float yUr = yUR - hdy; 1429 float xLl = xLL + hdx; 1430 float yLl = yLL + hdy; 1431 float xLr = xLR - hdx; 1432 float yLr = yLR - hdy; 1433 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1434 bx, by, bw, bh); 1435 // first pixel of line x1,y1 -> x1,y1+pixel 1436 vb.addMappedPgram(xUL, yUL, xUl, yUl, xLL, yLL, xLl, yLl, 1437 u0, v0, u1, v0, u0, v1, u1, v1, 1438 0, 0); 1439 // middle part of line x1,y1+pixel -> x2,y2-pixel 1440 vb.addMappedPgram(xUl, yUl, xUr, yUr, xLl, yLl, xLr, yLr, 1441 u1, v0, u1, v0, u1, v1, u1, v1, 1442 0, 0); 1443 // last part of line x2,y2-pixel -> x2,y2 1444 vb.addMappedPgram(xUr, yUr, xUR, yUR, xLr, yLr, xLR, yLR, 1445 u1, v0, u0, v0, u1, v1, u0, v1, 1446 0, 0); 1447 // System.out.print("3"); System.out.flush(); 1448 rTex.unlock(); 1449 return true; 1450 } 1451 // we could 2 and 3 slice extremely wide short lines, but they 1452 // are very rare in practice so we just jump straight to a 1453 // standard 4-slice off of the wrap-rect texture 1454 float xUC = (xUL + xUR) * 0.5f; 1455 float yUC = (yUL + yUR) * 0.5f; 1456 float xLC = (xLL + xLR) * 0.5f; 1457 float yLC = (yLL + yLR) * 0.5f; 1458 float xCL = (xUL + xLL) * 0.5f; 1459 float yCL = (yUL + yLL) * 0.5f; 1460 float xCR = (xUR + xLR) * 0.5f; 1461 float yCR = (yUR + yLR) * 0.5f; 1462 float xCC = (xUC + xLC) * 0.5f; 1463 float yCC = (yUC + yLC) * 0.5f; 1464 Texture rTex = context.getWrapRectTexture(); 1465 float u0 = 0.5f / rTex.getPhysicalWidth(); 1466 float v0 = 0.5f / rTex.getPhysicalHeight(); 1467 float uc = (cellw * 0.5f + 1.0f) / rTex.getPhysicalWidth(); 1468 float vc = (cellh * 0.5f + 1.0f) / rTex.getPhysicalHeight(); 1469 context.validatePaintOp(this, xform, MaskType.ALPHA_TEXTURE, rTex, 1470 bx, by, bw, bh); 1471 vb.addMappedPgram(xUL, yUL, xUC, yUC, xCL, yCL, xCC, yCC, 1472 u0, v0, uc, v0, u0, vc, uc, vc, 1473 0, 0); 1474 vb.addMappedPgram(xUR, yUR, xUC, yUC, xCR, yCR, xCC, yCC, 1475 u0, v0, uc, v0, u0, vc, uc, vc, 1476 0, 0); 1477 vb.addMappedPgram(xLL, yLL, xLC, yLC, xCL, yCL, xCC, yCC, 1478 u0, v0, uc, v0, u0, vc, uc, vc, 1479 0, 0); 1480 vb.addMappedPgram(xLR, yLR, xLC, yLC, xCR, yCR, xCC, yCC, 1481 u0, v0, uc, v0, u0, vc, uc, vc, 1482 0, 0); 1483 // System.out.print("4"); System.out.flush(); 1484 rTex.unlock(); 1485 return true; 1486 } 1487 1488 public void fillRect(float x, float y, float w, float h) { 1489 if (w <= 0 || h <= 0) { 1490 return; 1491 } 1492 if (isComplexPaint) { 1493 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1494 renderWithComplexPaint(scratchRRect, null, x, y, w, h); 1495 return; 1496 } 1497 if (PrismSettings.primTextureSize != 0) { 1498 Texture rTex = context.getRectTexture(); 1499 Texture wTex = context.getWrapRectTexture(); 1500 boolean success = fillPrimRect(x, y, w, h, rTex, wTex, x, y, w, h); 1501 rTex.unlock(); 1502 wTex.unlock(); 1503 if (success) return; 1504 } 1505 renderGeneralRoundedRect(x, y, w, h, 0f, 0f, 1506 MaskType.FILL_PGRAM, null); 1507 } 1508 1509 public void fillEllipse(float x, float y, float w, float h) { 1510 if (w <= 0 || h <= 0) { 1511 return; 1512 } 1513 if (isComplexPaint) { 1514 scratchEllipse.setFrame(x, y, w, h); 1515 renderWithComplexPaint(scratchEllipse, null, x, y, w, h); 1516 return; 1517 } 1518 if (PrismSettings.primTextureSize != 0) { 1519 if (fillPrimRect(x, y, w, h, 1520 context.getOvalTexture(), 1521 null, 1522 x, y, w, h)) 1523 { 1524 return; 1525 } 1526 } 1527 renderGeneralRoundedRect(x, y, w, h, w, h, 1528 MaskType.FILL_ELLIPSE, null); 1529 } 1530 1531 public void fillRoundRect(float x, float y, float w, float h, 1532 float arcw, float arch) 1533 { 1534 arcw = Math.min(Math.abs(arcw), w); 1535 arch = Math.min(Math.abs(arch), h); 1536 1537 if (w <= 0 || h <= 0) { 1538 return; 1539 } 1540 if (isComplexPaint) { 1541 scratchRRect.setRoundRect(x, y, w, h, arcw, arch); 1542 renderWithComplexPaint(scratchRRect, null, x, y, w, h); 1543 return; 1544 } 1545 renderGeneralRoundedRect(x, y, w, h, arcw, arch, 1546 MaskType.FILL_ROUNDRECT, null); 1547 } 1548 1549 public void fillQuad(float x1, float y1, float x2, float y2) { 1550 float bx, by, bw, bh; 1551 if (x1 <= x2) { 1552 bx = x1; 1553 bw = x2 - x1; 1554 } else { 1555 bx = x2; 1556 bw = x1 - x2; 1557 } 1558 if (y1 <= y2) { 1559 by = y1; 1560 bh = y2 - y1; 1561 } else { 1562 by = y2; 1563 bh = y1 - y2; 1564 } 1565 1566 if (isComplexPaint) { 1567 scratchRRect.setRoundRect(bx, by, bw, bh, 0, 0); 1568 renderWithComplexPaint(scratchRRect, null, bx, by, bw, bh); 1569 return; 1570 } 1571 1572 BaseTransform xform = getTransformNoClone(); 1573 if (PrismSettings.primTextureSize != 0) { 1574 float mxt, myt; 1575 if (xform.isTranslateOrIdentity()) { 1576 mxt = (float) xform.getMxt(); 1577 myt = (float) xform.getMyt(); 1578 xform = IDENT; 1579 x1 += mxt; 1580 y1 += myt; 1581 x2 += mxt; 1582 y2 += myt; 1583 } else { 1584 mxt = myt = 0.0f; 1585 } 1586 Shader shader = 1587 context.validatePaintOp(this, xform, MaskType.ALPHA_ONE, null, 1588 bx, by, bw, bh); 1589 AffineBase paintTx = getPaintTextureTx(IDENT, shader, bx, by, bw, bh); 1590 if (paintTx != null) { 1591 paintTx.translate(-mxt, -myt); 1592 } 1593 context.getVertexBuffer().addQuad(x1, y1, x2, y2, 0, 0, 0, 0, paintTx); 1594 return; 1595 } 1596 if (isSimpleTranslate) { 1597 xform = IDENT; 1598 bx += transX; 1599 by += transY; 1600 } 1601 context.validatePaintOp(this, xform, MaskType.SOLID, bx, by, bw, bh); 1602 1603 VertexBuffer vb = context.getVertexBuffer(); 1604 vb.addQuad(bx, by, bx+bw, by+bh); 1605 } 1606 1607 @Override 1608 public void fillTriangles(VertexBuffer tris, int numVerts, 1609 float bx, float by, float bw, float bh) 1610 { 1611 if (isComplexPaint) { 1612 throw new AssertionError("fillTriangles() not supported for complex paints"); 1613 } 1614 1615 BaseTransform xform = getTransformNoClone(); 1616 context.validatePaintOp(this, xform, MaskType.SOLID, bx, by, bw, bh); 1617 1618 VertexBuffer vb = context.getVertexBuffer(); 1619 vb.addVerts(tris, numVerts); 1620 } 1621 1622 void fillCubicCurves(VertexBuffer tris, int numVerts, 1623 float bx, float by, float bw, float bh) 1624 { 1625 if (isComplexPaint) { 1626 throw new AssertionError("fillCubicCurves() not supported for complex paints"); 1627 } 1628 1629 BaseTransform xform = getTransformNoClone(); 1630 context.validatePaintOp(this, xform, MaskType.FILL_CUBICCURVE, bx, by, bw, bh); 1631 1632 VertexBuffer vb = context.getVertexBuffer(); 1633 vb.addVerts(tris, numVerts); 1634 } 1635 1636 private static final double SQRT_2 = Math.sqrt(2.0); 1637 private static boolean canUseStrokeShader(BasicStroke bs) { 1638 // RT-27378 1639 // TODO: Expand the cases that renderGeneralRoundRect() can handle... 1640 return (!bs.isDashed() && 1641 (bs.getType() == BasicStroke.TYPE_INNER || 1642 bs.getLineJoin() == BasicStroke.JOIN_ROUND || 1643 (bs.getLineJoin() == BasicStroke.JOIN_MITER && 1644 bs.getMiterLimit() >= SQRT_2))); 1645 } 1646 1647 public void blit(RTTexture srcTex, RTTexture dstTex, 1648 int srcX0, int srcY0, int srcX1, int srcY1, 1649 int dstX0, int dstY0, int dstX1, int dstY1) { 1650 if (dstTex == null) { 1651 context.setRenderTarget(this); 1652 } else { 1653 context.setRenderTarget((BaseGraphics)dstTex.createGraphics()); 1654 } 1655 context.blit(srcTex, dstTex, srcX0, srcY0, srcX1, srcY1, 1656 dstX0, dstY0, dstX1, dstY1); 1657 } 1658 1659 public void drawRect(float x, float y, float w, float h) { 1660 if (w < 0 || h < 0) { 1661 return; 1662 } 1663 if (w == 0 || h == 0) { 1664 drawLine(x, y, x + w, y + h); 1665 return; 1666 } 1667 if (isComplexPaint) { 1668 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1669 renderWithComplexPaint(scratchRRect, stroke, x, y, w, h); 1670 return; 1671 } 1672 if (canUseStrokeShader(stroke)) { 1673 if (PrismSettings.primTextureSize != 0 && 1674 stroke.getLineJoin() != BasicStroke.CAP_ROUND) 1675 { 1676 if (drawPrimRect(x, y, w, h)) { 1677 return; 1678 } 1679 } 1680 renderGeneralRoundedRect(x, y, w, h, 0f, 0f, 1681 MaskType.DRAW_PGRAM, stroke); 1682 return; 1683 } 1684 scratchRRect.setRoundRect(x, y, w, h, 0, 0); 1685 renderShape(scratchRRect, stroke, x, y, w, h); 1686 } 1687 1688 private boolean checkInnerCurvature(float arcw, float arch) { 1689 // Test to see if inner ellipse satisfies (flattening < 0.5) 1690 // otherwise it will not be approximated by a "parallel ellipse" 1691 // very well and we should just use shape rendering. 1692 1693 // RT-27378 1694 // TODO: Implement better "distance to ellipse" formulas in the shaders 1695 float inset = stroke.getLineWidth() * 1696 (1f - getStrokeExpansionFactor(stroke)); 1697 arcw -= inset; 1698 arch -= inset; 1699 // Note that if either inset arcw,h go to <= 0 then we will invoke the 1700 // fill primitive for ellipse, or the round rect primitive will 1701 // invoke its "tiny inner corner" fixes and we will be safe 1702 return (arcw <= 0 || arch <= 0 || 1703 (arcw * 2f > arch && arch * 2f > arcw)); 1704 } 1705 1706 public void drawEllipse(float x, float y, float w, float h) { 1707 if (w < 0 || h < 0) { 1708 return; 1709 } 1710 if (!isComplexPaint && !stroke.isDashed() && 1711 checkInnerCurvature(w, h)) 1712 { 1713 renderGeneralRoundedRect(x, y, w, h, w, h, 1714 MaskType.DRAW_ELLIPSE, stroke); 1715 return; 1716 } 1717 scratchEllipse.setFrame(x, y, w, h); 1718 renderShape(scratchEllipse, stroke, x, y, w, h); 1719 } 1720 1721 public void drawRoundRect(float x, float y, float w, float h, 1722 float arcw, float arch) 1723 { 1724 arcw = Math.min(Math.abs(arcw), w); 1725 arch = Math.min(Math.abs(arch), h); 1726 1727 if (w < 0 || h < 0) { 1728 return; 1729 } 1730 if (!isComplexPaint && !stroke.isDashed() && 1731 checkInnerCurvature(arcw, arch)) 1732 { 1733 renderGeneralRoundedRect(x, y, w, h, arcw, arch, 1734 MaskType.DRAW_ROUNDRECT, stroke); 1735 return; 1736 } 1737 scratchRRect.setRoundRect(x, y, w, h, arcw, arch); 1738 renderShape(scratchRRect, stroke, x, y, w, h); 1739 } 1740 1741 public void drawLine(float x1, float y1, float x2, float y2) { 1742 float bx, by, bw, bh; 1743 if (x1 <= x2) { 1744 bx = x1; 1745 bw = x2 - x1; 1746 } else { 1747 bx = x2; 1748 bw = x1 - x2; 1749 } 1750 if (y1 <= y2) { 1751 by = y1; 1752 bh = y2 - y1; 1753 } else { 1754 by = y2; 1755 bh = y1 - y2; 1756 } 1757 1758 // RT-27378 1759 // TODO: casting down to floats everywhere here; evaluate later 1760 // to see if this is enough precision... 1761 // TODO: stroke normalization control? 1762 if (stroke.getType() == BasicStroke.TYPE_INNER) { 1763 return; 1764 } 1765 if (isComplexPaint) { 1766 scratchLine.setLine(x1, y1, x2, y2); 1767 renderWithComplexPaint(scratchLine, stroke, bx, by, bw, bh); 1768 return; 1769 } 1770 int cap = stroke.getEndCap(); 1771 if (stroke.isDashed()) { 1772 // NOTE: we could construct the GeneralPath directly 1773 // for CAP_ROUND and save a lot of processing in that case... 1774 // And again, we would need to deal with dropout control... 1775 scratchLine.setLine(x1, y1, x2, y2); 1776 renderShape(scratchLine, stroke, bx, by, bw, bh); 1777 return; 1778 } 1779 float lw = stroke.getLineWidth(); 1780 if (PrismSettings.primTextureSize != 0 && 1781 cap != BasicStroke.CAP_ROUND) 1782 { 1783 float pad = lw; 1784 if (stroke.getType() == BasicStroke.TYPE_CENTERED) { 1785 pad *= 0.5f; 1786 } 1787 if (bw == 0.0f || bh == 0.0f) { 1788 float padx, pady; 1789 if (cap == BasicStroke.CAP_SQUARE) { 1790 // CAP_SQUARE pads the same on all sides 1791 padx = pady = pad; 1792 // System.out.print("S"); System.out.flush(); 1793 } else if (bw != 0.0f) { 1794 // Horizontal CAP_BUTT line - widen vertically 1795 padx = 0.0f; 1796 pady = pad; 1797 // System.out.print("H"); System.out.flush(); 1798 } else if (bh != 0.0f) { 1799 // Vertical CAP_BUTT line - widen horizontally 1800 padx = pad; 1801 pady = 0.0f; 1802 // System.out.print("V"); System.out.flush(); 1803 } else { 1804 // System.out.print("0"); System.out.flush(); 1805 // Zero length line - NOP for CAP_BUTT 1806 return; 1807 } 1808 Texture rTex = context.getRectTexture(); 1809 Texture wTex = context.getWrapRectTexture(); 1810 boolean success = fillPrimRect(bx - padx, by - pady, 1811 bw + padx + padx, bh + pady + pady, 1812 rTex, wTex, bx, by, bw, bh); 1813 rTex.unlock(); 1814 wTex.unlock(); 1815 if (success) return; 1816 } else { 1817 if (drawPrimDiagonal(x1, y1, x2, y2, lw, cap, 1818 bx, by, bw, bh)) 1819 { 1820 return; 1821 } 1822 } 1823 } 1824 // System.out.print("#"); System.out.flush(); 1825 if (stroke.getType() == BasicStroke.TYPE_OUTER) { 1826 lw *= 2f; 1827 } 1828 float dx = x2 - x1; 1829 float dy = y2 - y1; 1830 float len = len(dx, dy); 1831 float ldx, ldy; // lw length vector in direction of line in user space 1832 if (len == 0) { 1833 if (cap == BasicStroke.CAP_BUTT) { 1834 return; 1835 } 1836 ldx = lw; 1837 ldy = 0; 1838 } else { 1839 ldx = lw * dx / len; 1840 ldy = lw * dy / len; 1841 } 1842 // The following is safe; this method does not mutate the transform 1843 BaseTransform xform = getTransformNoClone(); 1844 BaseTransform rendertx; 1845 float pdx, pdy; // ldx,ldy rotated 90 in user space then transformed 1846 if (isSimpleTranslate) { 1847 double tx = xform.getMxt(); 1848 double ty = xform.getMyt(); 1849 x1 += tx; 1850 y1 += ty; 1851 x2 += tx; 1852 y2 += ty; 1853 pdx = ldy; 1854 pdy = -ldx; 1855 rendertx = IDENT; 1856 } else { 1857 rendertx = extract3Dremainder(xform); 1858 double coords[] = {x1, y1, x2, y2}; 1859 xform.transform(coords, 0, coords, 0, 2); 1860 x1 = (float)coords[0]; 1861 y1 = (float)coords[1]; 1862 x2 = (float)coords[2]; 1863 y2 = (float)coords[3]; 1864 dx = x2 - x1; 1865 dy = y2 - y1; 1866 coords[0] = ldx; 1867 coords[1] = ldy; 1868 coords[2] = ldy; 1869 coords[3] = -ldx; 1870 xform.deltaTransform(coords, 0, coords, 0, 2); 1871 ldx = (float) coords[0]; 1872 ldy = (float) coords[1]; 1873 pdx = (float) coords[2]; 1874 pdy = (float) coords[3]; 1875 } 1876 float px = x1 - pdx / 2f; 1877 float py = y1 - pdy / 2f; 1878 float arcfractw, arcfracth; 1879 MaskType type; 1880 if (cap != BasicStroke.CAP_BUTT) { 1881 px -= ldx / 2f; 1882 py -= ldy / 2f; 1883 dx += ldx; 1884 dy += ldy; 1885 if (cap == BasicStroke.CAP_ROUND) { 1886 arcfractw = len(ldx, ldy) / len(dx, dy); 1887 arcfracth = 1f; 1888 type = MaskType.FILL_ROUNDRECT; 1889 } else { 1890 arcfractw = arcfracth = 0f; 1891 type = MaskType.FILL_PGRAM; 1892 } 1893 } else { 1894 arcfractw = arcfracth = 0f; 1895 type = MaskType.FILL_PGRAM; 1896 } 1897 renderGeneralRoundedPgram(px, py, dx, dy, pdx, pdy, 1898 arcfractw, arcfracth, 0f, 0f, 1899 rendertx, type, 1900 bx, by, bw, bh); 1901 } 1902 1903 private static float len(float x, float y) { 1904 return ((x == 0f) ? Math.abs(y) 1905 : ((y == 0f) ? Math.abs(x) 1906 : (float)Math.sqrt(x * x + y * y))); 1907 } 1908 1909 private boolean lcdSampleInvalid = false; 1910 1911 public void setNodeBounds(RectBounds bounds) { 1912 nodeBounds = bounds; 1913 lcdSampleInvalid = bounds != null; 1914 } 1915 1916 private void initLCDSampleRT() { 1917 if (lcdSampleInvalid) { 1918 RectBounds textBounds = new RectBounds(); 1919 getTransformNoClone().transform(nodeBounds, textBounds); 1920 Rectangle clipRect = getClipRectNoClone(); 1921 if (clipRect != null && !clipRect.isEmpty()) { 1922 // Reduce sample area if there is any clipping bounds set 1923 textBounds.intersectWith(clipRect); 1924 } 1925 // LCD mixing with background will often lead to extra pixel at edge 1926 // thus adding a single pixel, as padding, at edges and bottom. 1927 float bx = textBounds.getMinX() - 1.0f; 1928 float by = textBounds.getMinY(); 1929 float bw = textBounds.getWidth() + 2.0f; 1930 float bh = textBounds.getHeight() + 1.0f; 1931 1932 context.validateLCDBuffer(getRenderTarget()); 1933 1934 // Create a graphics for us to render into the LCDBuffer 1935 // Note this also sets the current RenderTarget as the LCDBuffer 1936 BaseShaderGraphics bsg = (BaseShaderGraphics) context.getLCDBuffer().createGraphics(); 1937 bsg.setCompositeMode(CompositeMode.SRC); 1938 context.validateLCDOp(bsg, IDENT, (Texture) getRenderTarget(), null, true, null); 1939 1940 int srch = getRenderTarget().getPhysicalHeight(); 1941 int srcw = getRenderTarget().getPhysicalWidth(); 1942 float tx1 = bx / srcw; 1943 float ty1 = by / srch; 1944 float tx2 = (bx + bw) / srcw; 1945 float ty2 = (by + bh) / srch; 1946 1947 //sample the source RT in the following bounds and store it in the LCDBuffer RT. 1948 bsg.drawLCDBuffer(bx, by, bw, bh, tx1, ty1, tx2, ty2); 1949 context.setRenderTarget(this); 1950 } 1951 lcdSampleInvalid = false; 1952 } 1953 1954 public void drawString(GlyphList gl, FontStrike strike, float x, float y, 1955 Color selectColor, int selectStart, int selectEnd) { 1956 1957 if (isComplexPaint || 1958 paint.getType().isImagePattern() || 1959 strike.drawAsShapes()) 1960 { 1961 // FontStrike.drawAsShapes() may be true for very large font sizes 1962 // in which case so no glyph images are cached. 1963 // The Prism Text node handles such cases directly, but Webnode relies 1964 // upon Prism's drawString method, so we need to handle it here too. 1965 1966 // this is not a very optimal approach and may not hit exactly 1967 // the same pixels that we would hit in the case where the 1968 // glyph cache is used, but the complex paint case is not 1969 // common enough to warrant further optimization 1970 BaseTransform xform = BaseTransform.getTranslateInstance(x, y); 1971 Shape shape = strike.getOutline(gl, xform); 1972 fill(shape); 1973 return; 1974 } 1975 1976 BaseTransform xform = getTransformNoClone(); 1977 1978 Paint textPaint = getPaint(); 1979 Color textColor = textPaint.getType() == Paint.Type.COLOR ? 1980 (Color) textPaint : null; 1981 1982 CompositeMode blendMode = getCompositeMode(); 1983 // LCD support requires several attributes to function: 1984 // FontStrike supports LCD, SRC_OVER CompositeMode and Paint is a COLOR 1985 boolean lcdSupported = blendMode == CompositeMode.SRC_OVER && 1986 textColor != null && 1987 xform.is2D() && 1988 !getRenderTarget().isAntiAliasing(); 1989 1990 /* If the surface can't support LCD text we need to replace an 1991 * LCD mode strike with the equivalent grey scale one. 1992 */ 1993 if (strike.getAAMode() == FontResource.AA_LCD && !lcdSupported) { 1994 FontResource fr = strike.getFontResource(); 1995 float size = strike.getSize(); 1996 BaseTransform tx = strike.getTransform(); 1997 strike = fr.getStrike(size, tx, FontResource.AA_GREYSCALE); 1998 } 1999 2000 float bx = 0f, by = 0f, bw = 0f, bh = 0f; 2001 if (paint.getType().isGradient() && ((Gradient)paint).isProportional()) { 2002 // If drawString is called directly without using setNodeBounds, 2003 // then nodeBounds is null, and we must determine the bounds based 2004 // on the str(vs. the node). 2005 RectBounds textBounds = nodeBounds; 2006 if (textBounds == null) { 2007 Metrics m = strike.getMetrics(); 2008 float pad = -m.getAscent() * 0.4f; 2009 textBounds = new RectBounds(-pad, 2010 m.getAscent(), 2011 gl.getWidth() + 2.0f *pad, 2012 m.getDescent() + m.getLineGap()); 2013 bx = x; 2014 by = y; 2015 } 2016 2017 bx += textBounds.getMinX(); 2018 by += textBounds.getMinY(); 2019 bw = textBounds.getWidth(); 2020 bh = textBounds.getHeight(); 2021 } 2022 2023 BaseBounds clip = null; 2024 Point2D p2d = new Point2D(x, y); 2025 if (isSimpleTranslate) { 2026 /* Only use clip for simple transforms so that coordinates in the 2027 * glyph list (user space) and the coordinates of the clip 2028 * (device space) can be intersected. 2029 */ 2030 clip = getFinalClipNoClone(); 2031 xform = IDENT; 2032 p2d.x += transX; 2033 p2d.y += transY; 2034 } 2035 2036 /* Cache look up needs to be on the font as rendered, including 2037 * AA mode, metrics mode, glyph transform (pt size combined with 2038 * the full graphics transform). Most of this info is expected to 2039 * be in the font, which here is close to being a full strike 2040 * description. 2041 */ 2042 GlyphCache glyphCache = context.getGlyphCache(strike); 2043 Texture cacheTex = glyphCache.getBackingStore(); 2044 2045 //Since we currently cannot support LCD text on transparant surfaces, we 2046 //verify that we are drawing to an opaque surface. 2047 if (strike.getAAMode() == FontResource.AA_LCD) { 2048 if (nodeBounds == null) { 2049 // If drawString is called directly without using 2050 // setNodeBounds then we must determine the bounds of the str, 2051 // before we render background to texture. 2052 // This is slow, but required by webnode. 2053 2054 Metrics m = strike.getMetrics(); 2055 // Ruff guess for padding, since lots of glyphs exceed advance 2056 RectBounds textBounds = 2057 new RectBounds(x - 2, 2058 y + m.getAscent(), 2059 x + 2 + gl.getWidth(), 2060 y + 1 + m.getDescent() + m.getLineGap()); 2061 2062 setNodeBounds(textBounds); 2063 initLCDSampleRT(); 2064 setNodeBounds(null); 2065 } else { 2066 initLCDSampleRT(); 2067 } 2068 float invgamma = PrismFontFactory.getLCDContrast(); 2069 float gamma = 1.0f/invgamma; 2070 textColor = new Color((float)Math.pow(textColor.getRed(), invgamma), 2071 (float)Math.pow(textColor.getGreen(), invgamma), 2072 (float)Math.pow(textColor.getBlue(), invgamma), 2073 (float)Math.pow(textColor.getAlpha(), invgamma)); 2074 if (selectColor != null) { 2075 selectColor = new Color( 2076 (float)Math.pow(selectColor.getRed(), invgamma), 2077 (float)Math.pow(selectColor.getGreen(), invgamma), 2078 (float)Math.pow(selectColor.getBlue(), invgamma), 2079 (float)Math.pow(selectColor.getAlpha(), invgamma)); 2080 } 2081 2082 // In order to handle transparency, the LCD shader need to manually 2083 // composite source with destination. Thus, SRC_OVER compositing 2084 // needs to be set to SRC, while shader is active. 2085 setCompositeMode(CompositeMode.SRC); 2086 2087 //set our 2nd LCD shader. 2088 Shader shader = context.validateLCDOp(this, IDENT, 2089 context.getLCDBuffer(), 2090 cacheTex, false, textColor); 2091 2092 float unitXCoord = 1.0f/((float)cacheTex.getPhysicalWidth()); 2093 shader.setConstant("gamma", gamma, invgamma, unitXCoord); 2094 setCompositeMode(blendMode); // Restore composite mode 2095 } else { 2096 context.validatePaintOp(this, IDENT, cacheTex, bx, by, bw, bh); 2097 } 2098 if (isSimpleTranslate) { 2099 // Applying this rounding allows for smoother text animation, 2100 // when animating simple translated text. 2101 // Asking glyph textures to be rendered at non-integral 2102 // locations produces very poor text. This doesn't solve 2103 // the problem for scaled (etc) cases, but addresses a 2104 // common case. 2105 p2d.y = Math.round(p2d.y); 2106 p2d.x = Math.round(p2d.x); 2107 } 2108 glyphCache.render(context, gl, p2d.x, p2d.y, selectStart, selectEnd, 2109 selectColor, textColor, xform, clip); 2110 } 2111 2112 //This function is used by the LCD path to render a quad into the 2113 //LCD RTT Texture. here the presentable is set as input and 2114 //sampled using texture coordinates. This is later used in a 2115 //second pass to provide the dst color as input. 2116 private void drawLCDBuffer(float bx, float by, float bw, float bh, 2117 float tx1, float ty1, float tx2, float ty2) 2118 { 2119 context.setRenderTarget(this); 2120 context.getVertexBuffer().addQuad(bx, by, bx + bw, by + bh, tx1, ty1, tx2, ty2); 2121 } 2122 2123 public boolean canReadBack() { 2124 RenderTarget rt = getRenderTarget(); 2125 return rt instanceof ReadbackRenderTarget && 2126 ((ReadbackRenderTarget) rt).getBackBuffer() != null; 2127 } 2128 2129 public RTTexture readBack(Rectangle view) { 2130 RenderTarget rt = getRenderTarget(); 2131 context.flushVertexBuffer(); 2132 context.validateLCDBuffer(rt); 2133 RTTexture lcdrtt = context.getLCDBuffer(); 2134 Texture bbtex = ((ReadbackRenderTarget) rt).getBackBuffer(); 2135 2136 float x1 = view.x; 2137 float y1 = view.y; 2138 float x2 = x1 + view.width; 2139 float y2 = y1 + view.height; 2140 2141 // Create a graphics for us to render into the LCDBuffer 2142 // Note this also sets the current RenderTarget as the LCDBuffer 2143 BaseShaderGraphics bsg = (BaseShaderGraphics) lcdrtt.createGraphics(); 2144 bsg.setCompositeMode(CompositeMode.SRC); 2145 context.validateTextureOp(bsg, IDENT, bbtex, bbtex.getPixelFormat()); 2146 2147 // sample the source RT in the following bounds and store it in the LCDBuffer RT. 2148 bsg.drawTexture(bbtex, 0, 0, view.width, view.height, x1, y1, x2, y2); 2149 context.flushVertexBuffer(); 2150 2151 // set the RenderTarget back to this. 2152 context.setRenderTarget(this); 2153 return lcdrtt; 2154 } 2155 2156 public void releaseReadBackBuffer(RTTexture rtt) { 2157 // This will be needed when we track LCD buffer locks and uses. 2158 // (See RT-29488) 2159 // context.releaseLCDBuffer(); 2160 } 2161 2162 public void setup3DRendering() { 2163 context.setRenderTarget(this); 2164 } 2165 }