1 /*
   2  * Copyright (c) 2010, 2017, 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.impl.shape;
  27 
  28 import com.sun.javafx.geom.Path2D;
  29 import com.sun.javafx.geom.RectBounds;
  30 import com.sun.javafx.geom.Rectangle;
  31 import com.sun.javafx.geom.Shape;
  32 import com.sun.javafx.geom.transform.BaseTransform;
  33 import com.sun.marlin.MarlinRenderer;
  34 import com.sun.marlin.MarlinRenderingEngine;
  35 import com.sun.marlin.MaskMarlinAlphaConsumer;
  36 import com.sun.marlin.RendererContext;
  37 import com.sun.prism.BasicStroke;
  38 import com.sun.prism.impl.PrismSettings;
  39 
  40 /**
  41  * Thread-safe Marlin rasterizer (TL or CLQ storage)
  42  */
  43 public final class MarlinRasterizer implements ShapeRasterizer {
  44     private static final MaskData EMPTY_MASK = MaskData.create(new byte[1], 0, 0, 1, 1);
  45 
  46     @Override
  47     public MaskData getMaskData(Shape shape,
  48                                 BasicStroke stroke,
  49                                 RectBounds xformBounds,
  50                                 BaseTransform xform,
  51                                 boolean close, boolean antialiasedShape)
  52     {
  53         if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) {
  54             // RT-27427
  55             // TODO: Optimize the combinatorial strokes for simple
  56             // shapes and/or teach the rasterizer to be able to
  57             // do a "differential fill" between two shapes.
  58             // Note that most simple shapes will use a more optimized path
  59             // than this method for the INNER/OUTER strokes anyway.
  60             shape = stroke.createStrokedShape(shape);
  61             stroke = null;
  62         }
  63         if (xformBounds == null) {
  64             if (stroke != null) {
  65                 // Note that all places that pass null for xformbounds also
  66                 // pass null for stroke so that the following is not typically
  67                 // executed, but just here as a safety net.
  68                 shape = stroke.createStrokedShape(shape);
  69                 stroke = null;
  70             }
  71 
  72             xformBounds = new RectBounds();
  73             //TODO: Need to verify that this is a safe cast ... (RT-27427)
  74             xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds);
  75         }
  76         if (xformBounds.isEmpty()) {
  77             return EMPTY_MASK;
  78         }
  79 
  80         final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext();
  81         MarlinRenderer renderer = null;
  82         try {
  83             final Rectangle rclip = rdrCtx.clip;
  84             rclip.setBounds(xformBounds);
  85 
  86             renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip,
  87                     antialiasedShape);
  88 
  89             final int outpix_xmin = renderer.getOutpixMinX();
  90             final int outpix_xmax = renderer.getOutpixMaxX();
  91             final int outpix_ymin = renderer.getOutpixMinY();
  92             final int outpix_ymax = renderer.getOutpixMaxY();
  93             final int w = outpix_xmax - outpix_xmin;
  94             final int h = outpix_ymax - outpix_ymin;
  95             if ((w <= 0) || (h <= 0)) {
  96                 return EMPTY_MASK;
  97             }
  98 
  99             MaskMarlinAlphaConsumer consumer = rdrCtx.consumer;
 100             if (consumer == null || (w * h) > consumer.getAlphaLength()) {
 101                 final int csize = (w * h + 0xfff) & (~0xfff);
 102                 rdrCtx.consumer = consumer = new MaskMarlinAlphaConsumer(csize);
 103                 if (PrismSettings.verbose) {
 104                     System.out.println("new alphas with length = " + csize);
 105                 }
 106             }
 107             consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h);
 108             renderer.produceAlphas(consumer);
 109 
 110             return consumer.getMaskData();
 111         } finally {
 112             if (renderer != null) {
 113                 renderer.dispose();
 114             }
 115             // recycle the RendererContext instance
 116             MarlinRenderingEngine.returnRendererContext(rdrCtx);
 117         }
 118     }
 119 
 120     static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke)
 121     {
 122         final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ?
 123                              stroke.getLineWidth() : stroke.getLineWidth() * 2.0f;
 124 
 125         final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext();
 126         try {
 127             // initialize a large copyable Path2D to avoid a lot of array growing:
 128             final Path2D p2d = rdrCtx.getPath2D();
 129 
 130             MarlinPrismUtils.strokeTo(rdrCtx, s, stroke, lw,
 131                      rdrCtx.transformerPC2D.wrapPath2d(p2d)
 132             );
 133 
 134             // Use Path2D copy constructor (trim)
 135             return new Path2D(p2d);
 136 
 137         } finally {
 138             // recycle the RendererContext instance
 139             MarlinRenderingEngine.returnRendererContext(rdrCtx);
 140         }
 141     }
 142 }