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