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