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