1 /*
   2  * Copyright (c) 2010, 2014, 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.RectBounds;
  29 import com.sun.javafx.geom.Path2D;
  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.openpisces.AlphaConsumer;
  34 import com.sun.openpisces.Renderer;
  35 import com.sun.prism.BasicStroke;
  36 import com.sun.prism.impl.PrismSettings;
  37 import java.nio.ByteBuffer;
  38 
  39 public class OpenPiscesRasterizer implements ShapeRasterizer {
  40     private static MaskData emptyData = MaskData.create(new byte[1], 0, 0, 1, 1);
  41 
  42     private static Consumer savedConsumer;
  43 
  44     @Override
  45     public MaskData getMaskData(Shape shape,
  46                                 BasicStroke stroke,
  47                                 RectBounds xformBounds,
  48                                 BaseTransform xform,
  49                                 boolean close, boolean antialiasedShape)
  50     {
  51         if (stroke != null && stroke.getType() != BasicStroke.TYPE_CENTERED) {
  52             // RT-27427
  53             // TODO: Optimize the combinatorial strokes for simple
  54             // shapes and/or teach the rasterizer to be able to
  55             // do a "differential fill" between two shapes.
  56             // Note that most simple shapes will use a more optimized path
  57             // than this method for the INNER/OUTER strokes anyway.
  58             shape = stroke.createStrokedShape(shape);
  59             stroke = null;
  60         }
  61         if (xformBounds == null) {
  62             if (stroke != null) {
  63                 // Note that all places that pass null for xformbounds also
  64                 // pass null for stroke so that the following is not typically
  65                 // executed, but just here as a safety net.
  66                 shape = stroke.createStrokedShape(shape);
  67                 stroke = null;
  68             }
  69 
  70             xformBounds = new RectBounds();
  71             //TODO: Need to verify that this is a safe cast ... (RT-27427)
  72             xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds);
  73         }
  74         Rectangle rclip = new Rectangle(xformBounds);
  75         if (rclip.isEmpty()) {
  76             return emptyData;
  77         }
  78         Renderer renderer = null;
  79         if (shape instanceof Path2D) {
  80             renderer = OpenPiscesPrismUtils.setupRenderer((Path2D) shape, stroke, xform, rclip,
  81                     antialiasedShape);
  82         }
  83         if (renderer == null) {
  84             renderer = OpenPiscesPrismUtils.setupRenderer(shape, stroke, xform, rclip,
  85                     antialiasedShape);
  86         }
  87         int outpix_xmin = renderer.getOutpixMinX();
  88         int outpix_ymin = renderer.getOutpixMinY();
  89         int outpix_xmax = renderer.getOutpixMaxX();
  90         int outpix_ymax = renderer.getOutpixMaxY();
  91         int w = outpix_xmax - outpix_xmin;
  92         int h = outpix_ymax - outpix_ymin;
  93         if (w <= 0 || h <= 0) {
  94             return emptyData;
  95         }
  96 
  97         Consumer consumer = savedConsumer;
  98         if (consumer == null || w * h > consumer.getAlphaLength()) {
  99             int csize = (w * h + 0xfff) & (~0xfff);
 100             savedConsumer = consumer = new Consumer(csize);
 101             if (PrismSettings.verbose) {
 102                 System.out.println("new alphas");
 103             }
 104         }
 105         consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h);
 106         renderer.produceAlphas(consumer);
 107         return consumer.getMaskData();
 108     }
 109 
 110     private static class Consumer implements AlphaConsumer {
 111         static byte savedAlphaMap[];
 112         int x, y, width, height;
 113         byte alphas[];
 114         byte alphaMap[];
 115         ByteBuffer alphabuffer;
 116         MaskData maskdata = new MaskData();
 117 
 118         public Consumer(int alphalen) {
 119             this.alphas = new byte[alphalen];
 120             alphabuffer = ByteBuffer.wrap(alphas);
 121         }
 122 
 123         public void setBoundsNoClone(int x, int y, int w, int h) {
 124             this.x = x;
 125             this.y = y;
 126             this.width = w;
 127             this.height = h;
 128             maskdata.update(alphabuffer, x, y, w, h);
 129         }
 130 
 131         @Override
 132         public int getOriginX() {
 133             return x;
 134         }
 135 
 136         @Override
 137         public int getOriginY() {
 138             return y;
 139         }
 140 
 141         @Override
 142         public int getWidth() {
 143             return width;
 144         }
 145 
 146         @Override
 147         public int getHeight() {
 148             return height;
 149         }
 150 
 151         public byte[] getAlphasNoClone() {
 152             return alphas;
 153         }
 154 
 155         public int getAlphaLength() {
 156             return alphas.length;
 157         }
 158 
 159         public MaskData getMaskData() {
 160             return maskdata;
 161         }
 162 
 163         @Override
 164         public void setMaxAlpha(int maxalpha) {
 165             byte map[] = savedAlphaMap;
 166             if (map == null || map.length != maxalpha+1) {
 167                 map = new byte[maxalpha+1];
 168                 for (int i = 0; i <= maxalpha; i++) {
 169                     map[i] = (byte) ((i*255 + maxalpha/2)/maxalpha);
 170                 }
 171                 savedAlphaMap = map;
 172             }
 173             this.alphaMap = map;
 174         }
 175 
 176         @Override
 177         public void setAndClearRelativeAlphas(int[] alphaRow, int pix_y,
 178                                               int pix_from, int pix_to)
 179         {
 180 //            System.out.println("setting row "+(pix_y - y)+
 181 //                               " out of "+width+" x "+height);
 182             int w = width;
 183             int off = (pix_y - y) * w;
 184             byte out[] = this.alphas;
 185             byte map[] = this.alphaMap;
 186             int a = 0;
 187             for (int i = 0; i < w; i++) {
 188                 a += alphaRow[i];
 189                 alphaRow[i] = 0;
 190                 out[off+i] = map[a];
 191             }
 192         }
 193 
 194         public void setAndClearRelativeAlphas2(int[] alphaDeltas, int pix_y,
 195                                                int pix_from, int pix_to)
 196         {
 197             if (pix_to >= pix_from) {
 198                 byte out[] = this.alphas;
 199                 byte map[] = this.alphaMap;
 200                 int from = pix_from - x;
 201                 int to = pix_to - x;
 202                 int w = width;
 203                 int off = (pix_y - y) * w;
 204 
 205                 int i = 0;
 206                 while (i < from) {
 207                     out[off+i] = 0;
 208                     i++;
 209                 }
 210                 int curAlpha = 0;
 211                 while (i <= to) {
 212                     curAlpha += alphaDeltas[i];
 213                     alphaDeltas[i] = 0;
 214                     byte a = map[curAlpha];
 215                     out[off+i] = a;
 216                     i++;
 217                 }
 218                 alphaDeltas[i] = 0;
 219                 while (i < w) {
 220                     out[off+i] = 0;
 221                     i++;
 222                 }
 223             } else {
 224                 java.util.Arrays.fill(alphaDeltas, 0);
 225             }
 226         }
 227     }
 228 }