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