1 /*
   2  * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.prism.impl;
  27 
  28 import com.sun.glass.ui.Screen;
  29 import com.sun.javafx.geom.Ellipse2D;
  30 import com.sun.javafx.geom.Line2D;
  31 import com.sun.javafx.geom.RectBounds;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.RoundRectangle2D;
  34 import com.sun.javafx.geom.Shape;
  35 import com.sun.javafx.geom.transform.Affine3D;
  36 import com.sun.javafx.geom.transform.BaseTransform;
  37 import com.sun.javafx.sg.prism.NGCamera;
  38 import com.sun.javafx.sg.prism.NodePath;
  39 import com.sun.prism.BasicStroke;
  40 import com.sun.prism.CompositeMode;
  41 import com.sun.prism.PixelFormat;
  42 import com.sun.prism.RectShadowGraphics;
  43 import com.sun.prism.RenderTarget;
  44 import com.sun.prism.ResourceFactory;
  45 import com.sun.prism.Texture;
  46 import com.sun.prism.paint.Color;
  47 import com.sun.prism.paint.Paint;
  48 
  49 public abstract class BaseGraphics implements RectShadowGraphics {
  50 
  51     private static final BasicStroke DEFAULT_STROKE =
  52         new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f);
  53     private static final Paint DEFAULT_PAINT = Color.WHITE;
  54 
  55     protected static final RoundRectangle2D scratchRRect = new RoundRectangle2D();
  56     protected static final Ellipse2D scratchEllipse = new Ellipse2D();
  57     protected static final Line2D scratchLine = new Line2D();
  58     protected static final BaseTransform IDENT = BaseTransform.IDENTITY_TRANSFORM;
  59 
  60     // TODO: initialize transform lazily to avoid creating garbage... (RT-27422)
  61     private final Affine3D transform3D = new Affine3D();
  62     private NGCamera camera = NGCamera.INSTANCE;
  63     private RectBounds devClipRect;
  64     private RectBounds finalClipRect;
  65     protected RectBounds nodeBounds = null;
  66     private Rectangle clipRect;
  67     private int clipRectIndex;
  68     private boolean hasPreCullingBits = false;
  69     private float extraAlpha = 1f;
  70     private CompositeMode compMode;
  71     private boolean antialiasedShape = true;
  72     private boolean depthBuffer = false;
  73     private boolean depthTest = false;
  74     protected Paint paint = DEFAULT_PAINT;
  75     protected BasicStroke stroke = DEFAULT_STROKE;
  76 
  77     protected boolean isSimpleTranslate = true;
  78     protected float transX;
  79     protected float transY;
  80 
  81     private final BaseContext context;
  82     private final RenderTarget renderTarget;
  83     private boolean state3D = false;
  84     private float pixelScaleX = 1.0f;
  85     private float pixelScaleY = 1.0f;
  86 
  87     protected BaseGraphics(BaseContext context, RenderTarget target) {
  88         this.context = context;
  89         this.renderTarget = target;
  90         devClipRect = new RectBounds(0, 0,
  91                                      target.getContentWidth(),
  92                                      target.getContentHeight());
  93         finalClipRect = new RectBounds(devClipRect);
  94         compMode = CompositeMode.SRC_OVER;
  95         if (context != null) {
  96             // RT-27422
  97             // TODO: Ideally we wouldn't need this step here and would
  98             // instead call some method prior to making any OpenGL calls
  99             // to ensure that there is a current context.  We're getting
 100             // closer to that ideal in that we call validate*Op() before
 101             // every graphics operation (which in turn calls the
 102             // setRenderTarget() method), but there are still some cases
 103             // remaining where this doesn't happen (e.g. texture creation).
 104             // So for the time being this blanket call to setRenderTarget()
 105             // is better than nothing...
 106             context.setRenderTarget(this);
 107         }
 108     }
 109 
 110     protected NGCamera getCamera() {
 111         return camera;
 112     }
 113 
 114     public RenderTarget getRenderTarget() {
 115         return renderTarget;
 116     }
 117 
 118     @Override
 119     public void setState3D(boolean flag) {
 120         this.state3D = flag;
 121     }
 122 
 123     @Override
 124     public boolean isState3D() {
 125         return state3D;
 126     }
 127 
 128     public Screen getAssociatedScreen() {
 129         return context.getAssociatedScreen();
 130     }
 131 
 132     public ResourceFactory getResourceFactory() {
 133         return context.getResourceFactory();
 134     }
 135 
 136     public BaseTransform getTransformNoClone() {
 137         return transform3D;
 138     }
 139 
 140     public void setTransform(BaseTransform transform) {
 141         if (transform == null) {
 142             transform3D.setToIdentity();
 143         } else {
 144             transform3D.setTransform(transform);
 145         }
 146         validateTransformAndPaint();
 147     }
 148 
 149     public void setTransform(double m00, double m10,
 150                              double m01, double m11,
 151                              double m02, double m12)
 152     {
 153         transform3D.setTransform(m00, m10, m01, m11, m02, m12);
 154         validateTransformAndPaint();
 155     }
 156 
 157     public void setTransform3D(double mxx, double mxy, double mxz, double mxt,
 158                                double myx, double myy, double myz, double myt,
 159                                double mzx, double mzy, double mzz, double mzt)
 160     {
 161         transform3D.setTransform(mxx, mxy, mxz, mxt,
 162                                  myx, myy, myz, myt,
 163                                  mzx, mzy, mzz, mzt);
 164         validateTransformAndPaint();
 165     }
 166 
 167     public void transform(BaseTransform transform) {
 168         transform3D.concatenate(transform);
 169         validateTransformAndPaint();
 170     }
 171 
 172     public void translate(float tx, float ty) {
 173         if (tx != 0f || ty != 0f) {
 174             transform3D.translate(tx, ty);
 175             validateTransformAndPaint();
 176         }
 177     }
 178 
 179     public void translate(float tx, float ty, float tz) {
 180         if (tx != 0f || ty != 0f || tz != 0f) {
 181             transform3D.translate(tx, ty, tz);
 182             validateTransformAndPaint();
 183         }
 184     }
 185 
 186     public void scale(float sx, float sy) {
 187         if (sx != 1f || sy != 1f) {
 188             transform3D.scale(sx, sy);
 189             validateTransformAndPaint();
 190         }
 191     }
 192 
 193     public void scale(float sx, float sy, float sz) {
 194         if (sx != 1f || sy != 1f || sz != 1f) {
 195             transform3D.scale(sx, sy, sz);
 196             validateTransformAndPaint();
 197         }
 198     }
 199 
 200     public void setClipRectIndex(int index) {
 201         this.clipRectIndex = index;
 202     }
 203     public int getClipRectIndex() {
 204         return this.clipRectIndex;
 205     }
 206 
 207     public void setHasPreCullingBits(boolean hasBits) {
 208         this.hasPreCullingBits = hasBits;
 209     }
 210 
 211     public boolean hasPreCullingBits() {
 212         return hasPreCullingBits;
 213     }
 214 
 215     private NodePath renderRoot;
 216     @Override
 217     public final void setRenderRoot(NodePath root) {
 218         this.renderRoot = root;
 219     }
 220 
 221     @Override
 222     public final NodePath getRenderRoot() {
 223         return renderRoot;
 224     }
 225 
 226     private void validateTransformAndPaint() {
 227         if (transform3D.isTranslateOrIdentity() &&
 228             paint.getType() == Paint.Type.COLOR)
 229         {
 230             // RT-27422
 231             // TODO: we could probably extend this to include
 232             // proportional paints in addition to simple colors...
 233             isSimpleTranslate = true;
 234             transX = (float)transform3D.getMxt();
 235             transY = (float)transform3D.getMyt();
 236         } else {
 237             isSimpleTranslate = false;
 238             transX = 0f;
 239             transY = 0f;
 240         }
 241     }
 242 
 243     public NGCamera getCameraNoClone() {
 244         return camera;
 245     }
 246 
 247     public void setDepthTest(boolean depthTest) {
 248         this.depthTest = depthTest;
 249     }
 250 
 251     public boolean isDepthTest() {
 252         return depthTest;
 253     }
 254 
 255     public void setDepthBuffer(boolean depthBuffer) {
 256         this.depthBuffer = depthBuffer;
 257     }
 258 
 259     public boolean isDepthBuffer() {
 260         return depthBuffer;
 261     }
 262 
 263     // If true use fragment shader that does alpha testing (i.e. discard if alpha == 0.0)
 264     // Currently it is required when depth testing is in use.
 265     public boolean isAlphaTestShader() {
 266         return (PrismSettings.forceAlphaTestShader || (isDepthTest() && isDepthBuffer()));
 267     }
 268 
 269     public void setAntialiasedShape(boolean aa) {
 270         antialiasedShape = aa;
 271     }
 272 
 273     public boolean isAntialiasedShape() {
 274         return antialiasedShape;
 275     }
 276 
 277     @Override
 278     public void setPixelScaleFactors(float pixelScaleX, float pixelScaleY) {
 279         this.pixelScaleX = pixelScaleX;
 280         this.pixelScaleY = pixelScaleY;
 281     }
 282 
 283     @Override
 284     public float getPixelScaleFactorX() {
 285         return pixelScaleX;
 286     }
 287 
 288     @Override
 289     public float getPixelScaleFactorY() {
 290         return pixelScaleY;
 291     }
 292 
 293     public void setCamera(NGCamera camera) {
 294         this.camera = camera;
 295     }
 296 
 297     public Rectangle getClipRect() {
 298         return (clipRect != null) ? new Rectangle(clipRect) : null;
 299     }
 300 
 301     public Rectangle getClipRectNoClone() {
 302         return clipRect;
 303     }
 304 
 305     public RectBounds getFinalClipNoClone() {
 306         return finalClipRect;
 307     }
 308 
 309     public void setClipRect(Rectangle clipRect) {
 310         this.finalClipRect.setBounds(devClipRect);
 311         if (clipRect == null) {
 312             this.clipRect = null;
 313         } else {
 314             this.clipRect = new Rectangle(clipRect);
 315             this.finalClipRect.intersectWith(clipRect);
 316         }
 317     }
 318 
 319     public float getExtraAlpha() {
 320         return extraAlpha;
 321     }
 322 
 323     public void setExtraAlpha(float extraAlpha) {
 324         this.extraAlpha = extraAlpha;
 325     }
 326 
 327     public CompositeMode getCompositeMode() {
 328         return compMode;
 329     }
 330 
 331     public void setCompositeMode(CompositeMode compMode) {
 332         this.compMode = compMode;
 333     }
 334 
 335     public Paint getPaint() {
 336         return paint;
 337     }
 338 
 339     public void setPaint(Paint paint) {
 340         this.paint = paint;
 341         validateTransformAndPaint();
 342     }
 343 
 344     public BasicStroke getStroke() {
 345         return stroke;
 346     }
 347 
 348     public void setStroke(BasicStroke stroke) {
 349         this.stroke = stroke;
 350     }
 351 
 352     public void clear() {
 353         clear(Color.TRANSPARENT);
 354     }
 355 
 356     protected abstract void renderShape(Shape shape, BasicStroke stroke,
 357                                         float bx, float by, float bw, float bh);
 358 
 359     public void fill(Shape shape) {
 360         float bx = 0f, by = 0f, bw = 0f, bh = 0f;
 361         if (paint.isProportional()) {
 362             if (nodeBounds != null) {
 363                 bx = nodeBounds.getMinX();
 364                 by = nodeBounds.getMinY();
 365                 bw = nodeBounds.getWidth();
 366                 bh = nodeBounds.getHeight();
 367             } else {
 368                 float[] bbox = {
 369                     Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
 370                     Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
 371                 };
 372                 Shape.accumulate(bbox, shape, BaseTransform.IDENTITY_TRANSFORM);
 373                 bx = bbox[0];
 374                 by = bbox[1];
 375                 bw = bbox[2] - bx;
 376                 bh = bbox[3] - by;
 377             }
 378         }
 379         renderShape(shape, null, bx, by, bw, bh);
 380     }
 381 
 382     public void draw(Shape shape) {
 383         float bx = 0f, by = 0f, bw = 0f, bh = 0f;
 384         if (paint.isProportional()) {
 385             if (nodeBounds != null) {
 386                 bx = nodeBounds.getMinX();
 387                 by = nodeBounds.getMinY();
 388                 bw = nodeBounds.getWidth();
 389                 bh = nodeBounds.getHeight();
 390             } else {
 391                 float[] bbox = {
 392                     Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
 393                     Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
 394                 };
 395                 Shape.accumulate(bbox, shape, BaseTransform.IDENTITY_TRANSFORM);
 396                 bx = bbox[0];
 397                 by = bbox[1];
 398                 bw = bbox[2] - bx;
 399                 bh = bbox[3] - by;
 400             }
 401         }
 402         renderShape(shape, stroke, bx, by, bw, bh);
 403     }
 404 
 405     @Override
 406     public void drawTexture(Texture tex, float x, float y, float w, float h) {
 407         drawTexture(tex,
 408                     x, y, x+w, y+h,
 409                     0, 0, w, h);
 410     }
 411 
 412     @Override
 413     public void drawTexture(Texture tex,
 414                             float dx1, float dy1, float dx2, float dy2,
 415                             float sx1, float sy1, float sx2, float sy2)
 416     {
 417         BaseTransform xform = isSimpleTranslate ? IDENT : getTransformNoClone();
 418         PixelFormat format = tex.getPixelFormat();
 419         if (format == PixelFormat.BYTE_ALPHA) {
 420             // Note that we treat this as a paint operation, using the
 421             // given texture as the alpha mask; perhaps it would be better
 422             // to treat this as a separate operation from drawTexture(), but
 423             // overloading drawTexture() seems like an equally valid option.
 424             context.validatePaintOp(this, xform, tex, dx1, dy1, dx2-dx1, dy2-dy1);
 425         } else {
 426             context.validateTextureOp(this, xform, tex, format);
 427         }
 428         if (isSimpleTranslate) {
 429             // The validatePaintOp bounds above needed to use the original
 430             // coordinates (prior to any translation below) for relative
 431             // paint processing.
 432             dx1 += transX;
 433             dy1 += transY;
 434             dx2 += transX;
 435             dy2 += transY;
 436         }
 437 
 438         float pw = tex.getPhysicalWidth();
 439         float ph = tex.getPhysicalHeight();
 440         float cx1 = tex.getContentX();
 441         float cy1 = tex.getContentY();
 442         float tx1 = (cx1 + sx1) / pw;
 443         float ty1 = (cy1 + sy1) / ph;
 444         float tx2 = (cx1 + sx2) / pw;
 445         float ty2 = (cy1 + sy2) / ph;
 446 
 447         VertexBuffer vb = context.getVertexBuffer();
 448         if (context.isSuperShaderEnabled()) {
 449             vb.addSuperQuad(dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2, false);
 450         } else {
 451             vb.addQuad(dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2);
 452         }
 453     }
 454 
 455     @Override
 456     public void drawTexture3SliceH(Texture tex,
 457                                    float dx1, float dy1, float dx2, float dy2,
 458                                    float sx1, float sy1, float sx2, float sy2,
 459                                    float dh1, float dh2, float sh1, float sh2)
 460     {
 461         BaseTransform xform = isSimpleTranslate ? IDENT : getTransformNoClone();
 462         PixelFormat format = tex.getPixelFormat();
 463         if (format == PixelFormat.BYTE_ALPHA) {
 464             // Note that we treat this as a paint operation, using the
 465             // given texture as the alpha mask; perhaps it would be better
 466             // to treat this as a separate operation from drawTexture(), but
 467             // overloading drawTexture() seems like an equally valid option.
 468             context.validatePaintOp(this, xform, tex, dx1, dy1, dx2-dx1, dy2-dy1);
 469         } else {
 470             context.validateTextureOp(this, xform, tex, format);
 471         }
 472         if (isSimpleTranslate) {
 473             // The validatePaintOp bounds above needed to use the original
 474             // coordinates (prior to any translation below) for relative
 475             // paint processing.
 476             dx1 += transX;
 477             dy1 += transY;
 478             dx2 += transX;
 479             dy2 += transY;
 480             dh1 += transX;
 481          // dv1 += transY;
 482             dh2 += transX;
 483          // dv2 += transY;
 484         }
 485 
 486         float pw = tex.getPhysicalWidth();
 487         float ph = tex.getPhysicalHeight();
 488         float cx1 = tex.getContentX();
 489         float cy1 = tex.getContentY();
 490         float tx1 = (cx1 + sx1) / pw;
 491         float ty1 = (cy1 + sy1) / ph;
 492         float tx2 = (cx1 + sx2) / pw;
 493         float ty2 = (cy1 + sy2) / ph;
 494         float th1 = (cx1 + sh1) / pw;
 495      // float tv1 = (cy1 + sv1) / ph;
 496         float th2 = (cx1 + sh2) / pw;
 497      // float tv2 = (cy1 + sv2) / ph;
 498 
 499         VertexBuffer vb = context.getVertexBuffer();
 500         if (context.isSuperShaderEnabled()) {
 501             vb.addSuperQuad(dx1, dy1, dh1, dy2, tx1, ty1, th1, ty2, false);
 502             vb.addSuperQuad(dh1, dy1, dh2, dy2, th1, ty1, th2, ty2, false);
 503             vb.addSuperQuad(dh2, dy1, dx2, dy2, th2, ty1, tx2, ty2, false);
 504         } else {
 505             vb.addQuad(dx1, dy1, dh1, dy2, tx1, ty1, th1, ty2);
 506             vb.addQuad(dh1, dy1, dh2, dy2, th1, ty1, th2, ty2);
 507             vb.addQuad(dh2, dy1, dx2, dy2, th2, ty1, tx2, ty2);
 508         }
 509     }
 510 
 511     @Override
 512     public void drawTexture3SliceV(Texture tex,
 513                                    float dx1, float dy1, float dx2, float dy2,
 514                                    float sx1, float sy1, float sx2, float sy2,
 515                                    float dv1, float dv2, float sv1, float sv2)
 516     {
 517         BaseTransform xform = isSimpleTranslate ? IDENT : getTransformNoClone();
 518         PixelFormat format = tex.getPixelFormat();
 519         if (format == PixelFormat.BYTE_ALPHA) {
 520             // Note that we treat this as a paint operation, using the
 521             // given texture as the alpha mask; perhaps it would be better
 522             // to treat this as a separate operation from drawTexture(), but
 523             // overloading drawTexture() seems like an equally valid option.
 524             context.validatePaintOp(this, xform, tex, dx1, dy1, dx2-dx1, dy2-dy1);
 525         } else {
 526             context.validateTextureOp(this, xform, tex, format);
 527         }
 528         if (isSimpleTranslate) {
 529             // The validatePaintOp bounds above needed to use the original
 530             // coordinates (prior to any translation below) for relative
 531             // paint processing.
 532             dx1 += transX;
 533             dy1 += transY;
 534             dx2 += transX;
 535             dy2 += transY;
 536          // dh1 += transX;
 537             dv1 += transY;
 538          // dh2 += transX;
 539             dv2 += transY;
 540         }
 541 
 542         float pw = tex.getPhysicalWidth();
 543         float ph = tex.getPhysicalHeight();
 544         float cx1 = tex.getContentX();
 545         float cy1 = tex.getContentY();
 546         float tx1 = (cx1 + sx1) / pw;
 547         float ty1 = (cy1 + sy1) / ph;
 548         float tx2 = (cx1 + sx2) / pw;
 549         float ty2 = (cy1 + sy2) / ph;
 550      // float th1 = (cx1 + sh1) / pw;
 551         float tv1 = (cy1 + sv1) / ph;
 552      // float th2 = (cx1 + sh2) / pw;
 553         float tv2 = (cy1 + sv2) / ph;
 554 
 555         VertexBuffer vb = context.getVertexBuffer();
 556         if (context.isSuperShaderEnabled()) {
 557             vb.addSuperQuad(dx1, dy1, dx2, dv1, tx1, ty1, tx2, tv1, false);
 558             vb.addSuperQuad(dx1, dv1, dx2, dv2, tx1, tv1, tx2, tv2, false);
 559             vb.addSuperQuad(dx1, dv2, dx2, dy2, tx1, tv2, tx2, ty2, false);
 560         } else {
 561             vb.addQuad(dx1, dy1, dx2, dv1, tx1, ty1, tx2, tv1);
 562             vb.addQuad(dx1, dv1, dx2, dv2, tx1, tv1, tx2, tv2);
 563             vb.addQuad(dx1, dv2, dx2, dy2, tx1, tv2, tx2, ty2);
 564         }
 565     }
 566 
 567     @Override
 568     public void drawTexture9Slice(Texture tex,
 569                                   float dx1, float dy1, float dx2, float dy2,
 570                                   float sx1, float sy1, float sx2, float sy2,
 571                                   float dh1, float dv1, float dh2, float dv2,
 572                                   float sh1, float sv1, float sh2, float sv2)
 573     {
 574         BaseTransform xform = isSimpleTranslate ? IDENT : getTransformNoClone();
 575         PixelFormat format = tex.getPixelFormat();
 576         if (format == PixelFormat.BYTE_ALPHA) {
 577             // Note that we treat this as a paint operation, using the
 578             // given texture as the alpha mask; perhaps it would be better
 579             // to treat this as a separate operation from drawTexture(), but
 580             // overloading drawTexture() seems like an equally valid option.
 581             context.validatePaintOp(this, xform, tex, dx1, dy1, dx2-dx1, dy2-dy1);
 582         } else {
 583             context.validateTextureOp(this, xform, tex, format);
 584         }
 585         if (isSimpleTranslate) {
 586             // The validatePaintOp bounds above needed to use the original
 587             // coordinates (prior to any translation below) for relative
 588             // paint processing.
 589             dx1 += transX;
 590             dy1 += transY;
 591             dx2 += transX;
 592             dy2 += transY;
 593             dh1 += transX;
 594             dv1 += transY;
 595             dh2 += transX;
 596             dv2 += transY;
 597         }
 598 
 599         float pw = tex.getPhysicalWidth();
 600         float ph = tex.getPhysicalHeight();
 601         float cx1 = tex.getContentX();
 602         float cy1 = tex.getContentY();
 603         float tx1 = (cx1 + sx1) / pw;
 604         float ty1 = (cy1 + sy1) / ph;
 605         float tx2 = (cx1 + sx2) / pw;
 606         float ty2 = (cy1 + sy2) / ph;
 607         float th1 = (cx1 + sh1) / pw;
 608         float tv1 = (cy1 + sv1) / ph;
 609         float th2 = (cx1 + sh2) / pw;
 610         float tv2 = (cy1 + sv2) / ph;
 611 
 612         VertexBuffer vb = context.getVertexBuffer();
 613         if (context.isSuperShaderEnabled()) {
 614             vb.addSuperQuad(dx1, dy1, dh1, dv1, tx1, ty1, th1, tv1, false);
 615             vb.addSuperQuad(dh1, dy1, dh2, dv1, th1, ty1, th2, tv1, false);
 616             vb.addSuperQuad(dh2, dy1, dx2, dv1, th2, ty1, tx2, tv1, false);
 617 
 618             vb.addSuperQuad(dx1, dv1, dh1, dv2, tx1, tv1, th1, tv2, false);
 619             vb.addSuperQuad(dh1, dv1, dh2, dv2, th1, tv1, th2, tv2, false);
 620             vb.addSuperQuad(dh2, dv1, dx2, dv2, th2, tv1, tx2, tv2, false);
 621 
 622             vb.addSuperQuad(dx1, dv2, dh1, dy2, tx1, tv2, th1, ty2, false);
 623             vb.addSuperQuad(dh1, dv2, dh2, dy2, th1, tv2, th2, ty2, false);
 624             vb.addSuperQuad(dh2, dv2, dx2, dy2, th2, tv2, tx2, ty2, false);
 625         } else {
 626             vb.addQuad(dx1, dy1, dh1, dv1, tx1, ty1, th1, tv1);
 627             vb.addQuad(dh1, dy1, dh2, dv1, th1, ty1, th2, tv1);
 628             vb.addQuad(dh2, dy1, dx2, dv1, th2, ty1, tx2, tv1);
 629 
 630             vb.addQuad(dx1, dv1, dh1, dv2, tx1, tv1, th1, tv2);
 631             vb.addQuad(dh1, dv1, dh2, dv2, th1, tv1, th2, tv2);
 632             vb.addQuad(dh2, dv1, dx2, dv2, th2, tv1, tx2, tv2);
 633 
 634             vb.addQuad(dx1, dv2, dh1, dy2, tx1, tv2, th1, ty2);
 635             vb.addQuad(dh1, dv2, dh2, dy2, th1, tv2, th2, ty2);
 636             vb.addQuad(dh2, dv2, dx2, dy2, th2, tv2, tx2, ty2);
 637         }
 638     }
 639 
 640     public void drawTextureVO(Texture tex,
 641                               float topopacity, float botopacity,
 642                               float dx1, float dy1, float dx2, float dy2,
 643                               float sx1, float sy1, float sx2, float sy2)
 644     {
 645         BaseTransform xform = isSimpleTranslate ? IDENT : getTransformNoClone();
 646         PixelFormat format = tex.getPixelFormat();
 647         if (format == PixelFormat.BYTE_ALPHA) {
 648             // Note that we treat this as a paint operation, using the
 649             // given texture as the alpha mask; perhaps it would be better
 650             // to treat this as a separate operation from drawTexture(), but
 651             // overloading drawTexture() seems like an equally valid option.
 652             context.validatePaintOp(this, xform, tex, dx1, dy1, dx2-dx1, dy2-dy1);
 653         } else {
 654             context.validateTextureOp(this, xform, tex, format);
 655         }
 656         if (isSimpleTranslate) {
 657             // The validatePaintOp bounds above needed to use the original
 658             // coordinates (prior to any translation below) for relative
 659             // paint processing.
 660             dx1 += transX;
 661             dy1 += transY;
 662             dx2 += transX;
 663             dy2 += transY;
 664         }
 665 
 666         float tw = tex.getPhysicalWidth();
 667         float th = tex.getPhysicalHeight();
 668         float cx1 = tex.getContentX();
 669         float cy1 = tex.getContentY();
 670         float tx1 = (cx1 + sx1) / tw;
 671         float ty1 = (cy1 + sy1) / th;
 672         float tx2 = (cx1 + sx2) / tw;
 673         float ty2 = (cy1 + sy2) / th;
 674 
 675         VertexBuffer vb = context.getVertexBuffer();
 676         if (topopacity == 1f && botopacity == 1f) {
 677             vb.addQuad(dx1, dy1, dx2, dy2,
 678                        tx1, ty1, tx2, ty2);
 679         } else {
 680             topopacity *= getExtraAlpha();
 681             botopacity *= getExtraAlpha();
 682             vb.addQuadVO(topopacity, botopacity,
 683                          dx1, dy1, dx2, dy2,
 684                          tx1, ty1, tx2, ty2);
 685         }
 686     }
 687 
 688     public void drawTextureRaw(Texture tex,
 689                                float dx1, float dy1, float dx2, float dy2,
 690                                float tx1, float ty1, float tx2, float ty2)
 691     {
 692         // Capture the original bounds (prior to any translation below),
 693         // which will be needed in the mask case.
 694         // NOTE: note that we currently assume (here and throughout this
 695         // method) that dx1<=dx2 and dy1<=dy2; the scenegraph does not rely
 696         // on flipping behavior, but this method will need to be fixed if
 697         // that assumption becomes invalid...
 698         float bx = dx1;
 699         float by = dy1;
 700         float bw = dx2 - dx1;
 701         float bh = dy2 - dy1;
 702 
 703         // The following is safe; this method does not mutate the transform
 704         BaseTransform xform = getTransformNoClone();
 705         if (isSimpleTranslate) {
 706             xform = IDENT;
 707             dx1 += transX;
 708             dy1 += transY;
 709             dx2 += transX;
 710             dy2 += transY;
 711         }
 712 
 713         PixelFormat format = tex.getPixelFormat();
 714         if (format == PixelFormat.BYTE_ALPHA) {
 715             // Note that we treat this as a paint operation, using the
 716             // given texture as the alpha mask; perhaps it would be better
 717             // to treat this as a separate operation from drawTexture(), but
 718             // overloading drawTexture() seems like an equally valid option.
 719             context.validatePaintOp(this, xform, tex, bx, by, bw, bh);
 720         } else {
 721             context.validateTextureOp(this, xform, tex, format);
 722         }
 723 
 724         VertexBuffer vb = context.getVertexBuffer();
 725         vb.addQuad(dx1, dy1, dx2, dy2,
 726                    tx1, ty1, tx2, ty2);
 727     }
 728 
 729     public void drawMappedTextureRaw(Texture tex,
 730                                      float dx1, float dy1, float dx2, float dy2,
 731                                      float tx11, float ty11, float tx21, float ty21,
 732                                      float tx12, float ty12, float tx22, float ty22)
 733     {
 734         // Capture the original bounds (prior to any translation below),
 735         // which will be needed in the mask case.
 736         // NOTE: note that we currently assume (here and throughout this
 737         // method) that dx1<=dx2 and dy1<=dy2; the scenegraph does not rely
 738         // on flipping behavior, but this method will need to be fixed if
 739         // that assumption becomes invalid...
 740         float bx = dx1;
 741         float by = dy1;
 742         float bw = dx2 - dx1;
 743         float bh = dy2 - dy1;
 744 
 745         // The following is safe; this method does not mutate the transform
 746         BaseTransform xform = getTransformNoClone();
 747         if (isSimpleTranslate) {
 748             xform = IDENT;
 749             dx1 += transX;
 750             dy1 += transY;
 751             dx2 += transX;
 752             dy2 += transY;
 753         }
 754 
 755         PixelFormat format = tex.getPixelFormat();
 756         if (format == PixelFormat.BYTE_ALPHA) {
 757             // Note that we treat this as a paint operation, using the
 758             // given texture as the alpha mask; perhaps it would be better
 759             // to treat this as a separate operation from drawTexture(), but
 760             // overloading drawTexture() seems like an equally valid option.
 761             context.validatePaintOp(this, xform, tex, bx, by, bw, bh);
 762         } else {
 763             context.validateTextureOp(this, xform, tex, format);
 764         }
 765 
 766         VertexBuffer vb = context.getVertexBuffer();
 767         vb.addMappedQuad(dx1, dy1, dx2, dy2,
 768                          tx11, ty11, tx21, ty21,
 769                          tx12, ty12, tx22, ty22);
 770     }
 771 
 772 }