1 /*
   2  * Copyright (c) 2011, 2016, 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.javafx.geom.Path2D;
  29 import com.sun.javafx.geom.Rectangle;
  30 import com.sun.javafx.geom.Shape;
  31 import com.sun.javafx.geom.transform.BaseTransform;
  32 import com.sun.marlin.DMarlinRenderer;
  33 import com.sun.marlin.DMarlinRenderingEngine;
  34 import com.sun.marlin.DRendererContext;
  35 import com.sun.marlin.IntArrayCache;
  36 import com.sun.marlin.MarlinAlphaConsumer;
  37 import com.sun.marlin.MarlinConst;
  38 import com.sun.marlin.MarlinRenderer;
  39 import com.sun.marlin.MarlinRenderingEngine;
  40 import com.sun.marlin.RendererContext;
  41 import com.sun.openpisces.Renderer;
  42 import com.sun.pisces.PiscesRenderer;
  43 import com.sun.prism.BasicStroke;
  44 import com.sun.prism.PixelFormat;
  45 import com.sun.prism.ResourceFactory;
  46 import com.sun.prism.Texture;
  47 import com.sun.prism.impl.PrismSettings;
  48 import com.sun.prism.impl.shape.DMarlinPrismUtils;
  49 import com.sun.prism.impl.shape.DMarlinRasterizer;
  50 import com.sun.prism.impl.shape.MarlinPrismUtils;
  51 import com.sun.prism.impl.shape.MaskData;
  52 import com.sun.prism.impl.shape.OpenPiscesPrismUtils;
  53 import com.sun.prism.impl.shape.ShapeUtil;
  54 
  55 import java.lang.ref.SoftReference;
  56 
  57 final class SWContext {
  58 
  59     private final ResourceFactory factory;
  60     private final ShapeRenderer shapeRenderer;
  61     private SoftReference<SWRTTexture> readBackBufferRef;
  62     private SoftReference<SWArgbPreTexture> imagePaintTextureRef;
  63 
  64     interface ShapeRenderer {
  65         void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape);
  66         void dispose();
  67     }
  68 
  69     class NativeShapeRenderer implements ShapeRenderer {
  70         private SoftReference<SWMaskTexture> maskTextureRef;
  71 
  72         public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) {
  73             final MaskData mask = ShapeUtil.rasterizeShape(shape, stroke, clip.toRectBounds(), tr, true, antialiasedShape);
  74             final SWMaskTexture tex = this.validateMaskTexture(mask.getWidth(), mask.getHeight());
  75             mask.uploadToTexture(tex, 0, 0, false);
  76             pr.fillAlphaMask(tex.getDataNoClone(), mask.getOriginX(), mask.getOriginY(),
  77                              mask.getWidth(), mask.getHeight(), 0, tex.getPhysicalWidth());
  78         }
  79 
  80         private SWMaskTexture initMaskTexture(int width, int height) {
  81             final SWMaskTexture tex = (SWMaskTexture)factory.createMaskTexture(width, height, Texture.WrapMode.CLAMP_NOT_NEEDED);
  82             maskTextureRef = new SoftReference<SWMaskTexture>(tex);
  83             return tex;
  84         }
  85 
  86         private void disposeMaskTexture() {
  87             if (maskTextureRef != null){
  88                 maskTextureRef.clear();
  89                 maskTextureRef = null;
  90             }
  91         }
  92 
  93         private SWMaskTexture validateMaskTexture(int width, int height) {
  94             SWMaskTexture tex;
  95             if (maskTextureRef == null) {
  96                 tex = this.initMaskTexture(width, height);
  97             } else {
  98                 tex = maskTextureRef.get();
  99                 if (tex == null ||
 100                     tex.getPhysicalWidth() < width ||
 101                     tex.getPhysicalHeight() < height)
 102                 {
 103                     this.disposeMaskTexture();
 104                     tex = this.initMaskTexture(width, height);
 105                 }
 106             }
 107             return tex;
 108         }
 109 
 110         public void dispose() {
 111             this.disposeMaskTexture();
 112         }
 113     }
 114 
 115     class JavaShapeRenderer implements ShapeRenderer {
 116         private final DirectRTPiscesAlphaConsumer alphaConsumer = new DirectRTPiscesAlphaConsumer();
 117 
 118         public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) {
 119             if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) {
 120                 // RT-27427
 121                 // TODO: Optimize the combinatorial strokes for simple
 122                 // shapes and/or teach the rasterizer to be able to
 123                 // do a "differential fill" between two shapes.
 124                 // Note that most simple shapes will use a more optimized path
 125                 // than this method for the INNER/OUTER strokes anyway.
 126                 shape = stroke.createStrokedShape(shape);
 127                 stroke = null;
 128             }
 129             final Renderer r = OpenPiscesPrismUtils.setupRenderer(shape, stroke, tr, clip, antialiasedShape);
 130             alphaConsumer.initConsumer(r, pr);
 131             r.produceAlphas(alphaConsumer);
 132         }
 133 
 134         public void dispose() { }
 135     }
 136 
 137     static final class MarlinShapeRenderer implements ShapeRenderer {
 138         private final DirectRTMarlinAlphaConsumer alphaConsumer = new DirectRTMarlinAlphaConsumer();
 139 
 140         @Override
 141         public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) {
 142             if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) {
 143                 // RT-27427
 144                 // TODO: Optimize the combinatorial strokes for simple
 145                 // shapes and/or teach the rasterizer to be able to
 146                 // do a "differential fill" between two shapes.
 147                 // Note that most simple shapes will use a more optimized path
 148                 // than this method for the INNER/OUTER strokes anyway.
 149                 shape = stroke.createStrokedShape(shape);
 150                 stroke = null;
 151             }
 152             final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext();
 153             MarlinRenderer renderer = null;
 154             try {
 155                 if (shape instanceof Path2D) {
 156                     renderer = MarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, tr, clip,
 157                             antialiasedShape);
 158                 }
 159                 if (renderer == null) {
 160                     renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, tr, clip,
 161                             antialiasedShape);
 162                 }
 163                 final int outpix_xmin = renderer.getOutpixMinX();
 164                 final int outpix_xmax = renderer.getOutpixMaxX();
 165                 final int outpix_ymin = renderer.getOutpixMinY();
 166                 final int outpix_ymax = renderer.getOutpixMaxY();
 167                 final int w = outpix_xmax - outpix_xmin;
 168                 final int h = outpix_ymax - outpix_ymin;
 169                 if ((w <= 0) || (h <= 0)) {
 170                     return;
 171                 }
 172                 alphaConsumer.initConsumer(outpix_xmin, outpix_ymin, w, h, pr);
 173                 renderer.produceAlphas(alphaConsumer);
 174             } finally {
 175                 if (renderer != null) {
 176                     renderer.dispose();
 177                 }
 178                 // recycle the RendererContext instance
 179                 MarlinRenderingEngine.returnRendererContext(rdrCtx);
 180             }
 181         }
 182 
 183         @Override
 184         public void dispose() { }
 185     }
 186 
 187     static final class DirectRTMarlinAlphaConsumer implements MarlinAlphaConsumer {
 188         private byte alpha_map[];
 189         private int x;
 190         private int y;
 191         private int w;
 192         private int h;
 193         private int rowNum;
 194 
 195         private PiscesRenderer pr;
 196 
 197         public void initConsumer(int x, int y, int w, int h, PiscesRenderer pr) {
 198             this.x = x;
 199             this.y = y;
 200             this.w = w;
 201             this.h = h;
 202             rowNum = 0;
 203             this.pr = pr;
 204         }
 205 
 206         @Override
 207         public int getOriginX() {
 208             return x;
 209         }
 210 
 211         @Override
 212         public int getOriginY() {
 213             return y;
 214         }
 215 
 216         @Override
 217         public int getWidth() {
 218             return w;
 219         }
 220 
 221         @Override
 222         public int getHeight() {
 223             return h;
 224         }
 225 
 226         @Override
 227         public void setMaxAlpha(int maxalpha) {
 228             if ((alpha_map == null) || (alpha_map.length != maxalpha+1)) {
 229                 alpha_map = new byte[maxalpha+1];
 230                 for (int i = 0; i <= maxalpha; i++) {
 231                     alpha_map[i] = (byte) ((i*255 + maxalpha/2)/maxalpha);
 232                 }
 233             }
 234         }
 235 
 236         @Override
 237         public boolean supportBlockFlags() {
 238             return false;
 239         }
 240 
 241         @Override
 242         public void clearAlphas(final int pix_y) {
 243             // noop
 244         }
 245 
 246         @Override
 247         public void setAndClearRelativeAlphas(final int[] alphaDeltas, final int pix_y,
 248                                               final int pix_from, final int pix_to)
 249         {
 250             // use x instead of pix_from as it cause artefacts:
 251             // note: it would be more efficient to skip all those empty pixels [x to pix_from[
 252             // but the native implementation must be fixed too.
 253 //                pr.emitAndClearAlphaRow(alpha_map, alphaDeltas, pix_y, pix_from, pix_to, rowNum);
 254             pr.emitAndClearAlphaRow(alpha_map, alphaDeltas, pix_y, x, pix_to, rowNum);
 255             rowNum++;
 256 
 257             // clear properly the end of the alphaDeltas:
 258             final int to = pix_to - x;
 259             if (to <= w) {
 260                 alphaDeltas[to] = 0;
 261             } else {
 262                 alphaDeltas[w]  = 0;
 263             }
 264 
 265             if (MarlinConst.DO_CHECKS) {
 266                 IntArrayCache.check(alphaDeltas, pix_from - x, to + 1, 0);
 267             }
 268         }
 269 
 270         @Override
 271         public void setAndClearRelativeAlphas(final int[] blkFlags, final int[] alphaDeltas, final int pix_y,
 272                                               final int pix_from, final int pix_to)
 273         {
 274             throw new UnsupportedOperationException();
 275         }
 276     }
 277 
 278     static final class DMarlinShapeRenderer implements ShapeRenderer {
 279         private final DirectRTMarlinAlphaConsumer alphaConsumer = new DirectRTMarlinAlphaConsumer();
 280 
 281         @Override
 282         public void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) {
 283             if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) {
 284                 // RT-27427
 285                 // TODO: Optimize the combinatorial strokes for simple
 286                 // shapes and/or teach the rasterizer to be able to
 287                 // do a "differential fill" between two shapes.
 288                 // Note that most simple shapes will use a more optimized path
 289                 // than this method for the INNER/OUTER strokes anyway.
 290                 shape = stroke.createStrokedShape(shape);
 291                 stroke = null;
 292             }
 293             final DRendererContext rdrCtx = DMarlinRenderingEngine.getRendererContext();
 294             DMarlinRenderer renderer = null;
 295             try {
 296                 if (shape instanceof Path2D) {
 297                     renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, tr, clip,
 298                             antialiasedShape);
 299                 }
 300                 if (renderer == null) {
 301                     renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, tr, clip,
 302                             antialiasedShape);
 303                 }
 304                 final int outpix_xmin = renderer.getOutpixMinX();
 305                 final int outpix_xmax = renderer.getOutpixMaxX();
 306                 final int outpix_ymin = renderer.getOutpixMinY();
 307                 final int outpix_ymax = renderer.getOutpixMaxY();
 308                 final int w = outpix_xmax - outpix_xmin;
 309                 final int h = outpix_ymax - outpix_ymin;
 310                 if ((w <= 0) || (h <= 0)) {
 311                     return;
 312                 }
 313                 alphaConsumer.initConsumer(outpix_xmin, outpix_ymin, w, h, pr);
 314                 renderer.produceAlphas(alphaConsumer);
 315             } finally {
 316                 if (renderer != null) {
 317                     renderer.dispose();
 318                 }
 319                 // recycle the RendererContext instance
 320                 DMarlinRenderingEngine.returnRendererContext(rdrCtx);
 321             }
 322         }
 323 
 324         @Override
 325         public void dispose() { }
 326     }
 327 
 328     SWContext(ResourceFactory factory) {
 329         this.factory = factory;
 330         switch (PrismSettings.rasterizerSpec) {
 331             case JavaPisces:
 332                 this.shapeRenderer = new JavaShapeRenderer();
 333                 break;
 334             case NativePisces:
 335                 this.shapeRenderer = new NativeShapeRenderer();
 336                 break;
 337             case FloatMarlin:
 338                 this.shapeRenderer = new MarlinShapeRenderer();
 339                 break;
 340             default:
 341             case DoubleMarlin:
 342                 this.shapeRenderer = new DMarlinShapeRenderer();
 343                 break;
 344         }
 345     }
 346 
 347     void renderShape(PiscesRenderer pr, Shape shape, BasicStroke stroke, BaseTransform tr, Rectangle clip, boolean antialiasedShape) {
 348         this.shapeRenderer.renderShape(pr, shape, stroke, tr, clip, antialiasedShape);
 349     }
 350 
 351     private SWRTTexture initRBBuffer(int width, int height) {
 352         final SWRTTexture tex = (SWRTTexture)factory.createRTTexture(width, height, Texture.WrapMode.CLAMP_NOT_NEEDED);
 353         readBackBufferRef = new SoftReference<SWRTTexture>(tex);
 354         return tex;
 355     }
 356 
 357     private void disposeRBBuffer() {
 358         if (readBackBufferRef != null) {
 359             readBackBufferRef.clear();
 360             readBackBufferRef = null;
 361         }
 362     }
 363 
 364     SWRTTexture validateRBBuffer(int width, int height) {
 365         SWRTTexture tex;
 366         if (readBackBufferRef == null) {
 367             tex = this.initRBBuffer(width, height);
 368         } else {
 369             tex = readBackBufferRef.get();
 370             if (tex == null ||
 371                 tex.getPhysicalWidth() < width ||
 372                 tex.getPhysicalHeight() < height)
 373             {
 374                 this.disposeRBBuffer();
 375                 tex = this.initRBBuffer(width, height);
 376             }
 377             tex.setContentWidth(width);
 378             tex.setContentHeight(height);
 379         }
 380         return tex;
 381     }
 382 
 383     private SWArgbPreTexture initImagePaintTexture(int width, int height) {
 384         final SWArgbPreTexture tex = (SWArgbPreTexture)factory.createTexture(PixelFormat.INT_ARGB_PRE,
 385                 Texture.Usage.DEFAULT, Texture.WrapMode.REPEAT, width, height);
 386         imagePaintTextureRef = new SoftReference<SWArgbPreTexture>(tex);
 387         return tex;
 388     }
 389 
 390     private void disposeImagePaintTexture() {
 391         if (imagePaintTextureRef != null) {
 392             imagePaintTextureRef.clear();
 393             imagePaintTextureRef = null;
 394         }
 395     }
 396 
 397     SWArgbPreTexture validateImagePaintTexture(int width, int height) {
 398         SWArgbPreTexture tex;
 399         if (imagePaintTextureRef == null) {
 400             tex = this.initImagePaintTexture(width, height);
 401         } else {
 402             tex = imagePaintTextureRef.get();
 403             if (tex == null ||
 404                 tex.getPhysicalWidth() < width ||
 405                 tex.getPhysicalHeight() < height)
 406             {
 407                 this.disposeImagePaintTexture();
 408                 tex = this.initImagePaintTexture(width, height);
 409             }
 410             tex.setContentWidth(width);
 411             tex.setContentHeight(height);
 412         }
 413         return tex;
 414     }
 415 
 416 
 417     void dispose() {
 418         this.disposeRBBuffer();
 419         this.disposeImagePaintTexture();
 420         this.shapeRenderer.dispose();
 421     }
 422 }