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