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