1 /*
   2  * Copyright (c) 2011, 2018, 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.sw;
  27 
  28 import com.sun.glass.ui.Screen;
  29 import com.sun.javafx.font.FontResource;
  30 import com.sun.javafx.font.FontStrike;
  31 import com.sun.javafx.font.Glyph;
  32 import com.sun.javafx.font.Metrics;
  33 import com.sun.javafx.font.PrismFontFactory;
  34 import com.sun.javafx.geom.Ellipse2D;
  35 import com.sun.javafx.geom.Line2D;
  36 import com.sun.javafx.geom.Point2D;
  37 import com.sun.javafx.geom.RectBounds;
  38 import com.sun.javafx.geom.Rectangle;
  39 import com.sun.javafx.geom.RoundRectangle2D;
  40 import com.sun.javafx.geom.Shape;
  41 import com.sun.javafx.geom.transform.Affine2D;
  42 import com.sun.javafx.geom.transform.BaseTransform;
  43 import com.sun.javafx.geom.transform.GeneralTransform3D;
  44 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
  45 import com.sun.javafx.scene.text.GlyphList;
  46 import com.sun.javafx.sg.prism.NGCamera;
  47 import com.sun.javafx.sg.prism.NGLightBase;
  48 import com.sun.javafx.sg.prism.NodePath;
  49 import com.sun.pisces.GradientColorMap;
  50 import com.sun.pisces.PiscesRenderer;
  51 import com.sun.pisces.RendererBase;
  52 import com.sun.pisces.Transform6;
  53 import com.sun.prism.BasicStroke;
  54 import com.sun.prism.CompositeMode;
  55 import com.sun.prism.Graphics;
  56 import com.sun.prism.PixelFormat;
  57 import com.sun.prism.RTTexture;
  58 import com.sun.prism.ReadbackGraphics;
  59 import com.sun.prism.RenderTarget;
  60 import com.sun.prism.Texture;
  61 import com.sun.prism.impl.PrismSettings;
  62 import com.sun.prism.paint.Color;
  63 import com.sun.prism.paint.ImagePattern;
  64 import com.sun.prism.paint.Paint;
  65 
  66 final class SWGraphics implements ReadbackGraphics {
  67 
  68     private static final BasicStroke DEFAULT_STROKE =
  69         new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f);
  70     private static final Paint DEFAULT_PAINT = Color.WHITE;
  71 
  72     private final PiscesRenderer pr;
  73     private final SWContext context;
  74     private final SWRTTexture target;
  75     private final SWPaint swPaint;
  76 
  77     private final BaseTransform tx = new Affine2D();
  78 
  79     private CompositeMode compositeMode = CompositeMode.SRC_OVER;
  80 
  81     private Rectangle clip;
  82     private final Rectangle finalClip = new Rectangle();
  83     private RectBounds nodeBounds;
  84 
  85     private int clipRectIndex;
  86 
  87     private Paint paint = DEFAULT_PAINT;
  88     private BasicStroke stroke = DEFAULT_STROKE;
  89 
  90     private Ellipse2D ellipse2d;
  91     private Line2D line2d;
  92     private RoundRectangle2D rect2d;
  93 
  94     private boolean antialiasedShape = true;
  95     private boolean hasPreCullingBits = false;
  96     private float pixelScaleX = 1.0f;
  97     private float pixelScaleY = 1.0f;
  98 
  99     private NodePath renderRoot;
 100     @Override
 101     public void setRenderRoot(NodePath root) {
 102         this.renderRoot = root;
 103     }
 104 
 105     @Override
 106     public NodePath getRenderRoot() {
 107         return renderRoot;
 108     }
 109 
 110     public SWGraphics(SWRTTexture target, SWContext context, PiscesRenderer pr) {
 111         this.target = target;
 112         this.context = context;
 113         this.pr = pr;
 114         this.swPaint = new SWPaint(context, pr);
 115 
 116         this.setClipRect(null);
 117     }
 118 
 119     public RenderTarget getRenderTarget() {
 120         return target;
 121     }
 122 
 123     public SWResourceFactory getResourceFactory() {
 124         return target.getResourceFactory();
 125     }
 126 
 127     public Screen getAssociatedScreen() {
 128         return target.getAssociatedScreen();
 129     }
 130 
 131     public void sync() {
 132     }
 133 
 134     public BaseTransform getTransformNoClone() {
 135         if (PrismSettings.debug) {
 136             System.out.println("+ getTransformNoClone " + this + "; tr: " + tx);
 137         }
 138         return tx;
 139     }
 140 
 141     public void setTransform(BaseTransform xform) {
 142         if (xform == null) {
 143             xform = BaseTransform.IDENTITY_TRANSFORM;
 144         }
 145         if (PrismSettings.debug) {
 146             System.out.println("+ setTransform " + this + "; tr: " + xform);
 147         }
 148         tx.setTransform(xform);
 149     }
 150 
 151     public void setTransform(double m00, double m10,
 152                              double m01, double m11,
 153                              double m02, double m12) {
 154         tx.restoreTransform(m00, m10, m01, m11, m02, m12);
 155         if (PrismSettings.debug) {
 156             System.out.println("+ restoreTransform " + this + "; tr: " + tx);
 157         }
 158     }
 159 
 160     public void setTransform3D(double mxx, double mxy, double mxz, double mxt,
 161                                double myx, double myy, double myz, double myt,
 162                                double mzx, double mzy, double mzz, double mzt) {
 163         if (mxz != 0.0 || myz != 0.0 ||
 164             mzx != 0.0 || mzy != 0.0 || mzz != 1.0 || mzt != 0.0)
 165         {
 166             throw new UnsupportedOperationException("3D transforms not supported.");
 167         }
 168         setTransform(mxx, myx, mxy, myy, mxt, myt);
 169     }
 170 
 171     public void transform(BaseTransform xform) {
 172         if (PrismSettings.debug) {
 173             System.out.println("+ concatTransform " + this + "; tr: " + xform);
 174         }
 175         tx.deriveWithConcatenation(xform);
 176     }
 177 
 178     public void translate(float tx, float ty) {
 179         if (PrismSettings.debug) {
 180             System.out.println("+ concat translate " + this + "; tx: " + tx + "; ty: " + ty);
 181         }
 182         this.tx.deriveWithTranslation(tx, ty);
 183     }
 184 
 185     public void translate(float tx, float ty, float tz) {
 186         throw new UnsupportedOperationException("translate3D: unimp");
 187     }
 188 
 189     public void scale(float sx, float sy) {
 190         if (PrismSettings.debug) {
 191             System.out.println("+ concat scale " + this + "; sx: " + sx + "; sy: " + sy);
 192         }
 193         tx.deriveWithConcatenation(sx, 0, 0, sy, 0, 0);
 194     }
 195 
 196     public void scale(float sx, float sy, float sz) {
 197         throw new UnsupportedOperationException("scale3D: unimp");
 198     }
 199 
 200     public void setCamera(NGCamera camera) {
 201     }
 202 
 203     public void setPerspectiveTransform(GeneralTransform3D transform) {
 204     }
 205 
 206     public NGCamera getCameraNoClone() {
 207         throw new UnsupportedOperationException("getCameraNoClone: unimp");
 208     }
 209 
 210     public void setDepthTest(boolean depthTest) { }
 211 
 212     public boolean isDepthTest() {
 213         return false;
 214     }
 215 
 216     public void setDepthBuffer(boolean depthBuffer) { }
 217 
 218     public boolean isDepthBuffer() {
 219         return false;
 220     }
 221 
 222     public boolean isAlphaTestShader() {
 223         if (PrismSettings.verbose && PrismSettings.forceAlphaTestShader) {
 224             System.out.println("SW pipe doesn't support shader with alpha testing");
 225         }
 226         return false;
 227     }
 228 
 229     public void setAntialiasedShape(boolean aa) {
 230         antialiasedShape = aa;
 231     }
 232 
 233     public boolean isAntialiasedShape() {
 234         return antialiasedShape;
 235     }
 236 
 237     public Rectangle getClipRect() {
 238         return (clip == null) ? null : new Rectangle(clip);
 239     }
 240 
 241     public Rectangle getClipRectNoClone() {
 242         return clip;
 243     }
 244 
 245     public RectBounds getFinalClipNoClone() {
 246         return finalClip.toRectBounds();
 247     }
 248 
 249     public void setClipRect(Rectangle clipRect) {
 250         finalClip.setBounds(target.getDimensions());
 251         if (clipRect == null) {
 252             if (PrismSettings.debug) {
 253                 System.out.println("+ PR.resetClip");
 254             }
 255             clip = null;
 256         } else {
 257             if (PrismSettings.debug) {
 258                 System.out.println("+ PR.setClip: " + clipRect);
 259             }
 260             finalClip.intersectWith(clipRect);
 261             clip = new Rectangle(clipRect);
 262         }
 263         pr.setClip(finalClip.x, finalClip.y, finalClip.width, finalClip.height);
 264     }
 265 
 266     public void setHasPreCullingBits(boolean hasBits) {
 267         this.hasPreCullingBits = hasBits;
 268     }
 269 
 270     public boolean hasPreCullingBits() {
 271         return this.hasPreCullingBits;
 272     }
 273 
 274     public int getClipRectIndex() {
 275         return clipRectIndex;
 276     }
 277 
 278     public void setClipRectIndex(int index) {
 279         if (PrismSettings.debug) {
 280             System.out.println("+ PR.setClipRectIndex: " + index);
 281         }
 282         clipRectIndex = index;
 283     }
 284 
 285     public float getExtraAlpha() {
 286         return swPaint.getCompositeAlpha();
 287     }
 288 
 289     public void setExtraAlpha(float extraAlpha) {
 290         if (PrismSettings.debug) {
 291             System.out.println("PR.setCompositeAlpha, value: " + extraAlpha);
 292         }
 293         swPaint.setCompositeAlpha(extraAlpha);
 294     }
 295 
 296     public Paint getPaint() {
 297         return paint;
 298     }
 299 
 300     public void setPaint(Paint paint) {
 301         this.paint = paint;
 302     }
 303 
 304 
 305 
 306     public BasicStroke getStroke() {
 307         return stroke;
 308     }
 309 
 310     public void setStroke(BasicStroke stroke) {
 311         this.stroke = stroke;
 312     }
 313 
 314     public CompositeMode getCompositeMode() {
 315         return compositeMode;
 316     }
 317 
 318     public void setCompositeMode(CompositeMode mode) {
 319         this.compositeMode = mode;
 320 
 321         int piscesComp;
 322         switch (mode) {
 323             case CLEAR:
 324                 piscesComp = RendererBase.COMPOSITE_CLEAR;
 325                 if (PrismSettings.debug) {
 326                     System.out.println("PR.setCompositeRule - CLEAR");
 327                 }
 328                 break;
 329             case SRC:
 330                 piscesComp = RendererBase.COMPOSITE_SRC;
 331                 if (PrismSettings.debug) {
 332                     System.out.println("PR.setCompositeRule - SRC");
 333                 }
 334                 break;
 335             case SRC_OVER:
 336                 piscesComp = RendererBase.COMPOSITE_SRC_OVER;
 337                 if (PrismSettings.debug) {
 338                     System.out.println("PR.setCompositeRule - SRC_OVER");
 339                 }
 340                 break;
 341             default:
 342                 throw new InternalError("Unrecognized composite mode: "+mode);
 343         }
 344         this.pr.setCompositeRule(piscesComp);
 345     }
 346 
 347     public void setNodeBounds(RectBounds bounds) {
 348         if (PrismSettings.debug) {
 349             System.out.println("+ SWG.setNodeBounds: " + bounds);
 350         }
 351         nodeBounds = bounds;
 352     }
 353 
 354     public void clear() {
 355         this.clear(Color.TRANSPARENT);
 356     }
 357 
 358     /**
 359      * Clears the current {@code RenderTarget} with the given {@code Color}.
 360      * Note that this operation is affected by the current clip rectangle,
 361      * if set.  To clear the entire surface, call {@code setClipRect(null)}
 362      * prior to calling {@code clear()}.
 363      */
 364     public void clear(Color color) {
 365         if (PrismSettings.debug) {
 366             System.out.println("+ PR.clear: " + color);
 367         }
 368         this.swPaint.setColor(color, 1f);
 369         pr.clearRect(0, 0, target.getPhysicalWidth(), target.getPhysicalHeight());
 370         getRenderTarget().setOpaque(color.isOpaque());
 371     }
 372 
 373     /**
 374      * Clears the region represented by the given quad with transparent pixels.
 375      * Note that this operation is affected by the current clip rectangle,
 376      * if set, as well as the current transform (the quad is specified in
 377      * user space).  Also note that unlike the {@code clear()} methods, this
 378      * method does not attempt to clear the depth buffer.
 379      */
 380     public void clearQuad(float x1, float y1, float x2, float y2) {
 381         final CompositeMode cm = this.compositeMode;
 382         final Paint p = this.paint;
 383         this.setCompositeMode(CompositeMode.SRC);
 384         this.setPaint(Color.TRANSPARENT);
 385         this.fillQuad(x1, y1, x2, y2);
 386         this.setCompositeMode(cm);
 387         this.setPaint(p);
 388     }
 389 
 390     public void fill(Shape shape) {
 391         if (PrismSettings.debug) {
 392             System.out.println("+ fill(Shape)");
 393         }
 394         paintShape(shape, null, this.tx);
 395     }
 396 
 397     public void fillQuad(float x1, float y1, float x2, float y2) {
 398         if (PrismSettings.debug) {
 399             System.out.println("+ SWG.fillQuad");
 400         }
 401         this.fillRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1));
 402     }
 403 
 404     public void fillRect(float x, float y, float width, float height) {
 405         if (PrismSettings.debug) {
 406             System.out.printf("+ SWG.fillRect, x: %f, y: %f, w: %f, h: %f\n", x, y, width, height);
 407         }
 408         if (tx.getMxy() == 0 && tx.getMyx() == 0) {
 409             if (PrismSettings.debug) {
 410                 System.out.println("GR: " + this);
 411                 System.out.println("target: " + target + " t.w: " + target.getPhysicalWidth() + ", t.h: " + target.getPhysicalHeight() +
 412                         ", t.dims: " + target.getDimensions());
 413                 System.out.println("Tx: " + tx);
 414                 System.out.println("Clip: " + finalClip);
 415                 System.out.println("Composite rule: " + compositeMode);
 416             }
 417 
 418             final Point2D p1 = new Point2D(x, y);
 419             final Point2D p2 = new Point2D(x + width, y + height);
 420             tx.transform(p1, p1);
 421             tx.transform(p2, p2);
 422 
 423             if (this.paint.getType() == Paint.Type.IMAGE_PATTERN) {
 424                 // we can call pr.drawImage(...) directly
 425                 final ImagePattern ip = (ImagePattern)this.paint;
 426                 if (ip.getImage().getPixelFormat() == PixelFormat.BYTE_ALPHA) {
 427                     throw new UnsupportedOperationException("Alpha image is not supported as an image pattern.");
 428                 } else {
 429                     final Transform6 piscesTx = swPaint.computeSetTexturePaintTransform(this.paint, this.tx, this.nodeBounds, x, y, width, height);
 430                     final SWArgbPreTexture tex = context.validateImagePaintTexture(ip.getImage().getWidth(), ip.getImage().getHeight());
 431                     tex.update(ip.getImage());
 432 
 433                     final float compositeAlpha = swPaint.getCompositeAlpha();
 434                     final int imageMode;
 435                     if (compositeAlpha == 1f) {
 436                         imageMode = RendererBase.IMAGE_MODE_NORMAL;
 437                     } else {
 438                         imageMode = RendererBase.IMAGE_MODE_MULTIPLY;
 439                         this.pr.setColor(255, 255, 255, (int)(255 * compositeAlpha));
 440                     }
 441 
 442                     this.pr.drawImage(RendererBase.TYPE_INT_ARGB_PRE, imageMode,
 443                             tex.getDataNoClone(), tex.getContentWidth(), tex.getContentHeight(),
 444                             tex.getOffset(), tex.getPhysicalWidth(),
 445                             piscesTx,
 446                             tex.getWrapMode() == Texture.WrapMode.REPEAT,
 447                             tex.getLinearFiltering(),
 448                             (int)(Math.min(p1.x, p2.x) * SWUtils.TO_PISCES), (int)(Math.min(p1.y, p2.y) * SWUtils.TO_PISCES),
 449                             (int)(Math.abs(p2.x - p1.x) * SWUtils.TO_PISCES), (int)(Math.abs(p2.y - p1.y) * SWUtils.TO_PISCES),
 450                             RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 451                             RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 452                             0, 0, tex.getContentWidth()-1, tex.getContentHeight()-1,
 453                             tex.hasAlpha());
 454                 }
 455             } else {
 456                 swPaint.setPaintFromShape(this.paint, this.tx, null, this.nodeBounds, x, y, width, height);
 457                 this.pr.fillRect((int)(Math.min(p1.x, p2.x) * SWUtils.TO_PISCES), (int)(Math.min(p1.y, p2.y) * SWUtils.TO_PISCES),
 458                         (int)(Math.abs(p2.x - p1.x) * SWUtils.TO_PISCES), (int)(Math.abs(p2.y - p1.y) * SWUtils.TO_PISCES));
 459             }
 460         } else {
 461             this.fillRoundRect(x, y, width, height, 0, 0);
 462         }
 463     }
 464 
 465     public void fillRoundRect(float x, float y, float width, float height,
 466                               float arcw, float arch) {
 467         if (PrismSettings.debug) {
 468             System.out.println("+ SWG.fillRoundRect");
 469         }
 470         this.paintRoundRect(x, y, width, height, arcw, arch, null);
 471     }
 472 
 473     public void fillEllipse(float x, float y, float width, float height) {
 474         if (PrismSettings.debug) {
 475             System.out.println("+ SWG.fillEllipse");
 476         }
 477         this.paintEllipse(x, y, width, height, null);
 478     }
 479 
 480     public void draw(Shape shape) {
 481         if (PrismSettings.debug) {
 482             System.out.println("+ draw(Shape)");
 483         }
 484         paintShape(shape, this.stroke, this.tx);
 485     }
 486 
 487     private void paintShape(Shape shape, BasicStroke st, BaseTransform tr) {
 488         if (this.finalClip.isEmpty()) {
 489             if (PrismSettings.debug) {
 490                 System.out.println("Final clip is empty: not rendering the shape: " + shape);
 491             }
 492             return;
 493         }
 494         swPaint.setPaintFromShape(this.paint, this.tx, shape, this.nodeBounds, 0,0,0,0);
 495         this.paintShapePaintAlreadySet(shape, st, tr);
 496     }
 497 
 498     private void paintShapePaintAlreadySet(Shape shape, BasicStroke st, BaseTransform tr) {
 499         if (this.finalClip.isEmpty()) {
 500             if (PrismSettings.debug) {
 501                 System.out.println("Final clip is empty: not rendering the shape: " + shape);
 502             }
 503             return;
 504         }
 505 
 506         if (PrismSettings.debug) {
 507             System.out.println("GR: " + this);
 508             System.out.println("target: " + target + " t.w: " + target.getPhysicalWidth() + ", t.h: " + target.getPhysicalHeight() +
 509                     ", t.dims: " + target.getDimensions());
 510             System.out.println("Shape: " + shape);
 511             System.out.println("Stroke: " + st);
 512             System.out.println("Tx: " + tr);
 513             System.out.println("Clip: " + finalClip);
 514             System.out.println("Composite rule: " + compositeMode);
 515         }
 516         context.renderShape(this.pr, shape, st, tr, this.finalClip, isAntialiasedShape());
 517     }
 518 
 519     private void paintRoundRect(float x, float y, float width, float height, float arcw, float arch, BasicStroke st) {
 520         if (rect2d == null) {
 521             rect2d = new RoundRectangle2D(x, y, width, height, arcw, arch);
 522         } else {
 523             rect2d.setRoundRect(x, y, width, height, arcw, arch);
 524         }
 525         paintShape(this.rect2d, st, this.tx);
 526     }
 527 
 528     private void paintEllipse(float x, float y, float width, float height, BasicStroke st) {
 529         if (ellipse2d == null) {
 530             ellipse2d = new Ellipse2D(x, y, width, height);
 531         } else {
 532             ellipse2d.setFrame(x, y, width, height);
 533         }
 534         paintShape(this.ellipse2d, st, this.tx);
 535     }
 536 
 537     public void drawLine(float x1, float y1, float x2, float y2) {
 538         if (PrismSettings.debug) {
 539             System.out.println("+ drawLine");
 540         }
 541         if (line2d == null) {
 542             line2d = new Line2D(x1, y1, x2, y2);
 543         } else {
 544             line2d.setLine(x1, y1, x2, y2);
 545         }
 546         paintShape(this.line2d, this.stroke, this.tx);
 547     }
 548 
 549     public void drawRect(float x, float y, float width, float height) {
 550         if (PrismSettings.debug) {
 551             System.out.println("+ SWG.drawRect");
 552         }
 553         this.drawRoundRect(x, y, width, height, 0, 0);
 554     }
 555 
 556     public void drawRoundRect(float x, float y, float width, float height,
 557                               float arcw, float arch) {
 558         if (PrismSettings.debug) {
 559             System.out.println("+ SWG.drawRoundRect");
 560         }
 561         this.paintRoundRect(x, y, width, height, arcw, arch, stroke);
 562     }
 563 
 564     public void drawEllipse(float x, float y, float width, float height) {
 565         if (PrismSettings.debug) {
 566             System.out.println("+ SWG.drawEllipse");
 567         }
 568         this.paintEllipse(x, y, width, height, stroke);
 569     }
 570 
 571     public void drawString(GlyphList gl, FontStrike strike, float x, float y,
 572                            Color selectColor, int selectStart, int selectEnd) {
 573 
 574         if (PrismSettings.debug) {
 575             System.out.println("+ SWG.drawGlyphList, gl.Count: " + gl.getGlyphCount() +
 576                     ", x: " + x + ", y: " + y +
 577                     ", selectStart: " + selectStart + ", selectEnd: " + selectEnd);
 578         }
 579 
 580         final float bx, by, bw, bh;
 581         if (paint.isProportional()) {
 582             if (nodeBounds != null) {
 583                 bx = nodeBounds.getMinX();
 584                 by = nodeBounds.getMinY();
 585                 bw = nodeBounds.getWidth();
 586                 bh = nodeBounds.getHeight();
 587             } else {
 588                 Metrics m = strike.getMetrics();
 589                 bx = 0;
 590                 by = m.getAscent();
 591                 bw = gl.getWidth();
 592                 bh = m.getLineHeight();
 593             }
 594         } else {
 595             bx = by = bw = bh = 0;
 596         }
 597 
 598         final boolean drawAsMasks = tx.isTranslateOrIdentity() && (!strike.drawAsShapes());
 599         final boolean doLCDText = drawAsMasks &&
 600                 (strike.getAAMode() == FontResource.AA_LCD) &&
 601                 getRenderTarget().isOpaque() &&
 602                 (this.paint.getType() == Paint.Type.COLOR) &&
 603                 tx.is2D();
 604         BaseTransform glyphTx = null;
 605 
 606         if (doLCDText) {
 607             this.pr.setLCDGammaCorrection(1f / PrismFontFactory.getLCDContrast());
 608         } else if (drawAsMasks) {
 609             final FontResource fr = strike.getFontResource();
 610             final float origSize = strike.getSize();
 611             final BaseTransform origTx = strike.getTransform();
 612             strike = fr.getStrike(origSize, origTx, FontResource.AA_GREYSCALE);
 613         } else {
 614             glyphTx = new Affine2D();
 615         }
 616 
 617         if (selectColor == null) {
 618             swPaint.setPaintBeforeDraw(this.paint, this.tx, bx, by, bw, bh);
 619             for (int i = 0; i < gl.getGlyphCount(); i++) {
 620                 this.drawGlyph(strike, gl, i, glyphTx, drawAsMasks, x, y);
 621             }
 622         } else {
 623             for (int i = 0; i < gl.getGlyphCount(); i++) {
 624                 final int offset = gl.getCharOffset(i);
 625                 final boolean selected = selectStart <= offset && offset < selectEnd;
 626                 swPaint.setPaintBeforeDraw(selected ? selectColor : this.paint, this.tx, bx, by, bw, bh);
 627                 this.drawGlyph(strike, gl, i, glyphTx, drawAsMasks, x, y);
 628             }
 629         }
 630     }
 631 
 632     private void drawGlyph(FontStrike strike, GlyphList gl, int idx, BaseTransform glyphTx,
 633                            boolean drawAsMasks, float x, float y)
 634     {
 635 
 636         final Glyph g = strike.getGlyph(gl.getGlyphCode(idx));
 637         if (drawAsMasks) {
 638             final Point2D pt = new Point2D((float)(x + tx.getMxt() + gl.getPosX(idx)),
 639                                            (float)(y + tx.getMyt() + gl.getPosY(idx)));
 640             int subPixel = strike.getQuantizedPosition(pt);
 641             final byte pixelData[] = g.getPixelData(subPixel);
 642             if (pixelData != null) {
 643                 final int intPosX = g.getOriginX() + (int)pt.x;
 644                 final int intPosY = g.getOriginY() + (int)pt.y;
 645                 if (g.isLCDGlyph()) {
 646                     this.pr.fillLCDAlphaMask(pixelData, intPosX, intPosY,
 647                             g.getWidth(), g.getHeight(),
 648                             0, g.getWidth());
 649                 } else {
 650                     this.pr.fillAlphaMask(pixelData, intPosX, intPosY,
 651                             g.getWidth(), g.getHeight(),
 652                             0, g.getWidth());
 653                 }
 654             }
 655         } else {
 656             Shape shape = g.getShape();
 657             if (shape != null) {
 658                 glyphTx.setTransform(tx);
 659                 glyphTx.deriveWithTranslation(x + gl.getPosX(idx), y + gl.getPosY(idx));
 660                 this.paintShapePaintAlreadySet(shape, null, glyphTx);
 661             }
 662         }
 663     }
 664 
 665     public void drawTexture(Texture tex, float x, float y, float w, float h) {
 666         if (PrismSettings.debug) {
 667             System.out.printf("+ drawTexture1, x: %f, y: %f, w: %f, h: %f\n", x, y, w, h);
 668         }
 669         this.drawTexture(tex, x, y, x + w, y + h, 0, 0, w, h);
 670     }
 671 
 672     public void drawTexture(Texture tex,
 673                             float dx1, float dy1, float dx2, float dy2,
 674                             float sx1, float sy1, float sx2, float sy2)
 675     {
 676         this.drawTexture(tex, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
 677                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 678                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 679     }
 680 
 681     private void drawTexture(Texture tex,
 682                              float dx1, float dy1, float dx2, float dy2,
 683                              float sx1, float sy1, float sx2, float sy2,
 684                              int lEdge, int rEdge, int tEdge, int bEdge) {
 685         final int imageMode;
 686         final float compositeAlpha = swPaint.getCompositeAlpha();
 687         if (compositeAlpha == 1f) {
 688             imageMode = RendererBase.IMAGE_MODE_NORMAL;
 689         } else {
 690             imageMode = RendererBase.IMAGE_MODE_MULTIPLY;
 691             this.pr.setColor(255, 255, 255, (int)(255 * compositeAlpha));
 692         }
 693         this.drawTexture(tex, imageMode, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, lEdge, rEdge, tEdge, bEdge);
 694     }
 695 
 696     private void drawTexture(Texture tex, int imageMode,
 697                             float dx1, float dy1, float dx2, float dy2,
 698                             float sx1, float sy1, float sx2, float sy2,
 699                             int lEdge, int rEdge, int tEdge, int bEdge) {
 700         if (PrismSettings.debug) {
 701             System.out.println("+ drawTexture: " + tex + ", imageMode: " + imageMode +
 702                     ", tex.w: " + tex.getPhysicalWidth() + ", tex.h: " + tex.getPhysicalHeight() +
 703                     ", tex.cw: " + tex.getContentWidth() + ", tex.ch: " + tex.getContentHeight());
 704             System.out.println("target: " + target + " t.w: " + target.getPhysicalWidth() + ", t.h: " + target.getPhysicalHeight() +
 705                     ", t.dims: " + target.getDimensions());
 706             System.out.println("GR: " + this);
 707             System.out.println("dx1:" + dx1 + " dy1:" + dy1 + " dx2:" + dx2 + " dy2:" + dy2);
 708             System.out.println("sx1:" + sx1 + " sy1:" + sy1 + " sx2:" + sx2 + " sy2:" + sy2);
 709             System.out.println("Clip: " + finalClip);
 710             System.out.println("Composite rule: " + compositeMode);
 711         }
 712 
 713         final SWArgbPreTexture swTex = (SWArgbPreTexture) tex;
 714         int data[] = swTex.getDataNoClone();
 715 
 716         final RectBounds srcBBox = new RectBounds(Math.min(dx1, dx2), Math.min(dy1, dy2),
 717                 Math.max(dx1, dx2), Math.max(dy1, dy2));
 718         final RectBounds dstBBox = new RectBounds();
 719         tx.transform(srcBBox, dstBBox);
 720 
 721         final Transform6 piscesTx = swPaint.computeDrawTexturePaintTransform(this.tx,
 722                 dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
 723 
 724         if (PrismSettings.debug) {
 725             System.out.println("tx: " + tx);
 726             System.out.println("piscesTx: " + piscesTx);
 727 
 728             System.out.println("srcBBox: " + srcBBox);
 729             System.out.println("dstBBox: " + dstBBox);
 730         }
 731 
 732         // texture coordinates range
 733         final int txMin = Math.max(0, SWUtils.fastFloor(Math.min(sx1, sx2)));
 734         final int tyMin = Math.max(0, SWUtils.fastFloor(Math.min(sy1, sy2)));
 735         final int txMax = Math.min(tex.getContentWidth() - 1, SWUtils.fastCeil(Math.max(sx1, sx2)) - 1);
 736         final int tyMax = Math.min(tex.getContentHeight() - 1, SWUtils.fastCeil(Math.max(sy1, sy2)) - 1);
 737 
 738         this.pr.drawImage(RendererBase.TYPE_INT_ARGB_PRE, imageMode,
 739                 data, tex.getContentWidth(), tex.getContentHeight(),
 740                 swTex.getOffset(), tex.getPhysicalWidth(),
 741                 piscesTx,
 742                 tex.getWrapMode() == Texture.WrapMode.REPEAT,
 743                 tex.getLinearFiltering(),
 744                 (int)(SWUtils.TO_PISCES * dstBBox.getMinX()), (int)(SWUtils.TO_PISCES * dstBBox.getMinY()),
 745                 (int)(SWUtils.TO_PISCES * dstBBox.getWidth()), (int)(SWUtils.TO_PISCES * dstBBox.getHeight()),
 746                 lEdge, rEdge, tEdge, bEdge,
 747                 txMin, tyMin, txMax, tyMax,
 748                 swTex.hasAlpha());
 749 
 750         if (PrismSettings.debug) {
 751             System.out.println("* drawTexture, DONE");
 752         }
 753     }
 754 
 755     @Override
 756     public void drawTexture3SliceH(Texture tex,
 757                                    float dx1, float dy1, float dx2, float dy2,
 758                                    float sx1, float sy1, float sx2, float sy2,
 759                                    float dh1, float dh2, float sh1, float sh2)
 760     {
 761         drawTexture(tex, dx1, dy1, dh1, dy2, sx1, sy1, sh1, sy2,
 762                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD,
 763                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 764         drawTexture(tex, dh1, dy1, dh2, dy2, sh1, sy1, sh2, sy2,
 765                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD,
 766                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 767         drawTexture(tex, dh2, dy1, dx2, dy2, sh2, sy1, sx2, sy2,
 768                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 769                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 770     }
 771 
 772     @Override
 773     public void drawTexture3SliceV(Texture tex,
 774                                    float dx1, float dy1, float dx2, float dy2,
 775                                    float sx1, float sy1, float sx2, float sy2,
 776                                    float dv1, float dv2, float sv1, float sv2)
 777     {
 778         drawTexture(tex, dx1, dy1, dx2, dv1, sx1, sy1, sx2, sv1,
 779                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 780                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD);
 781         drawTexture(tex, dx1, dv1, dx2, dv2, sx1, sv1, sx2, sv2,
 782                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 783                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD);
 784         drawTexture(tex, dx1, dv2, dx2, dy2, sx1, sv2, sx2, sy2,
 785                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 786                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 787     }
 788 
 789     @Override
 790     public void drawTexture9Slice(Texture tex,
 791                                   float dx1, float dy1, float dx2, float dy2,
 792                                   float sx1, float sy1, float sx2, float sy2,
 793                                   float dh1, float dv1, float dh2, float dv2,
 794                                   float sh1, float sv1, float sh2, float sv2)
 795     {
 796         drawTexture(tex, dx1, dy1, dh1, dv1, sx1, sy1, sh1, sv1,
 797                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD,
 798                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD);
 799         drawTexture(tex, dh1, dy1, dh2, dv1, sh1, sy1, sh2, sv1,
 800                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD,
 801                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD);
 802         drawTexture(tex, dh2, dy1, dx2, dv1, sh2, sy1, sx2, sv1,
 803                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 804                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD);
 805 
 806         drawTexture(tex, dx1, dv1, dh1, dv2, sx1, sv1, sh1, sv2,
 807                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD,
 808                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD);
 809         drawTexture(tex, dh1, dv1, dh2, dv2, sh1, sv1, sh2, sv2,
 810                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD,
 811                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD);
 812         drawTexture(tex, dh2, dv1, dx2, dv2, sh2, sv1, sx2, sv2,
 813                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 814                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD);
 815 
 816         drawTexture(tex, dx1, dv2, dh1, dy2, sx1, sv2, sh1, sy2,
 817                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_PAD,
 818                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 819         drawTexture(tex, dh1, dv2, dh2, dy2, sh1, sv2, sh2, sy2,
 820                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_PAD,
 821                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 822         drawTexture(tex, dh2, dv2, dx2, dy2, sh2, sv2, sx2, sy2,
 823                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 824                 RendererBase.IMAGE_FRAC_EDGE_TRIM, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 825     }
 826 
 827     public void drawTextureVO(Texture tex,
 828                               float topopacity, float botopacity,
 829                               float dx1, float dy1, float dx2, float dy2,
 830                               float sx1, float sy1, float sx2, float sy2)
 831     {
 832         if (PrismSettings.debug) {
 833             System.out.println("* drawTextureVO");
 834         }
 835         final int[] fractions = { 0x0000, 0x10000 };
 836         final int[] argb = { 0xffffff | (((int)(topopacity * 255)) << 24),
 837                              0xffffff | (((int)(botopacity * 255)) << 24) };
 838         final Transform6 t6 = new Transform6();
 839         SWUtils.convertToPiscesTransform(this.tx, t6);
 840         this.pr.setLinearGradient(0, (int)(SWUtils.TO_PISCES * dy1), 0, (int)(SWUtils.TO_PISCES * dy2), fractions, argb,
 841                                   GradientColorMap.CYCLE_NONE, t6);
 842         this.drawTexture(tex, RendererBase.IMAGE_MODE_MULTIPLY, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
 843                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP,
 844                 RendererBase.IMAGE_FRAC_EDGE_KEEP, RendererBase.IMAGE_FRAC_EDGE_KEEP);
 845     }
 846 
 847     public void drawTextureRaw(Texture tex,
 848                                float dx1, float dy1, float dx2, float dy2,
 849                                float tx1, float ty1, float tx2, float ty2)
 850     {
 851         if (PrismSettings.debug) {
 852             System.out.println("+ drawTextureRaw");
 853         }
 854 
 855         int w = tex.getContentWidth();
 856         int h = tex.getContentHeight();
 857         tx1 *= w;
 858         ty1 *= h;
 859         tx2 *= w;
 860         ty2 *= h;
 861         drawTexture(tex, dx1, dy1, dx2, dy2, tx1, ty1, tx2, ty2);
 862     }
 863 
 864     public void drawMappedTextureRaw(Texture tex,
 865                                      float dx1, float dy1, float dx2, float dy2,
 866                                      float tx11, float ty11, float tx21, float ty21,
 867                                      float tx12, float ty12, float tx22, float ty22)
 868     {
 869         if (PrismSettings.debug) {
 870             System.out.println("+ drawMappedTextureRaw");
 871         }
 872 
 873         final double _mxx, _myx, _mxy, _myy, _mxt, _myt;
 874         _mxx = tx.getMxx();
 875         _myx = tx.getMyx();
 876         _mxy = tx.getMxy();
 877         _myy = tx.getMyy();
 878         _mxt = tx.getMxt();
 879         _myt = tx.getMyt();
 880 
 881         try {
 882             final float mxx = tx21-tx11;
 883             final float myx = ty21-ty11;
 884             final float mxy = tx12-tx11;
 885             final float myy = ty12-ty11;
 886 
 887             final BaseTransform tmpTx = new Affine2D(mxx, myx, mxy, myy, tx11, ty11);
 888             tmpTx.invert();
 889 
 890             tx.setToIdentity();
 891             tx.deriveWithTranslation(dx1, dy1);
 892             tx.deriveWithConcatenation(dx2 - dx1, 0, 0, dy2 - dy2, 0, 0);
 893             tx.deriveWithConcatenation(tmpTx);
 894             this.drawTexture(tex, 0, 0, 1, 1, 0, 0, tex.getContentWidth(), tex.getContentHeight());
 895         } catch (NoninvertibleTransformException e) { }
 896 
 897         tx.restoreTransform(_mxx, _myx, _mxy, _myy, _mxt, _myt);
 898     }
 899 
 900     public boolean canReadBack() {
 901         return true;
 902     }
 903 
 904     public RTTexture readBack(Rectangle view) {
 905         if (PrismSettings.debug) {
 906             System.out.println("+ readBack, rect: " + view + ", target.dims: " + target.getDimensions());
 907         }
 908 
 909         final int w = Math.max(1, view.width);
 910         final int h = Math.max(1, view.height);
 911         final SWRTTexture rbb = context.validateRBBuffer(w, h);
 912 
 913         if (view.isEmpty()) {
 914             return rbb;
 915         }
 916 
 917         final int pixels[] = rbb.getDataNoClone();
 918         this.target.getSurface().getRGB(pixels, 0, rbb.getPhysicalWidth(), view.x, view.y, w, h);
 919         return rbb;
 920     }
 921 
 922     public void releaseReadBackBuffer(RTTexture view) {
 923     }
 924 
 925     public void setState3D(boolean flag) {
 926     }
 927 
 928     public boolean isState3D() {
 929         return false;
 930     }
 931 
 932     public void setup3DRendering() {
 933     }
 934 
 935     @Override
 936     public void setPixelScaleFactors(float pixelScaleX, float pixelScaleY) {
 937         this.pixelScaleX = pixelScaleX;
 938     }
 939 
 940     @Override
 941     public float getPixelScaleFactorX() {
 942         return pixelScaleX;
 943     }
 944 
 945     @Override
 946     public float getPixelScaleFactorY() {
 947         return pixelScaleY;
 948     }
 949 
 950     @Override
 951     public void setLights(NGLightBase[] lights) {
 952         // Light are not supported by SW pipeline
 953     }
 954 
 955     @Override
 956     public NGLightBase[] getLights() {
 957         // Light are not supported by SW pipeline
 958         return null;
 959     }
 960 
 961     @Override
 962     public void blit(RTTexture srcTex, RTTexture dstTex,
 963                     int srcX0, int srcY0, int srcX1, int srcY1,
 964                     int dstX0, int dstY0, int dstX1, int dstY1) {
 965         Graphics g = dstTex.createGraphics();
 966         g.drawTexture(srcTex,
 967                       dstX0, dstY0, dstX1, dstY1,
 968                       srcX0, srcY0, srcX1, srcY1);
 969     }
 970 }