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

 133         int x, y, width, height;
 134         final byte alphas[];
 135         final ByteBuffer alphabuffer;
 136         final MaskData maskdata = new MaskData();
 137 
 138         boolean useFastFill;
 139         int fastFillThreshold;
 140 
 141         public Consumer(int alphalen) {
 142             this.alphas = new byte[alphalen];
 143             alphabuffer = ByteBuffer.wrap(alphas);
 144         }
 145 
 146         public void setBoundsNoClone(int x, int y, int w, int h) {
 147             this.x = x;
 148             this.y = y;
 149             this.width = w;
 150             this.height = h;
 151             maskdata.update(alphabuffer, x, y, w, h);
 152 
 153             useFastFill = (w >= 32);
 154             if (useFastFill) {
 155                 fastFillThreshold = (w >= 128) ? (w >> 1) : (w >> 2);
 156             }
 157         }
 158 
 159         @Override
 160         public int getOriginX() {
 161             return x;
 162         }
 163 
 164         @Override
 165         public int getOriginY() {
 166             return y;
 167         }
 168 
 169         @Override
 170         public int getWidth() {
 171             return width;
 172         }
 173 
 174         @Override
 175         public int getHeight() {
 176             return height;
 177         }
 178 
 179         public byte[] getAlphasNoClone() {
 180             return alphas;
 181         }
 182 
 183         public int getAlphaLength() {
 184             return alphas.length;
 185         }
 186 
 187         public MaskData getMaskData() {
 188             return maskdata;
 189         }
 190 
 191         OffHeapArray ALPHA_MAP_USED = null;
 192 
 193         @Override
 194         public void setMaxAlpha(int maxalpha) {
 195             ALPHA_MAP_USED = (maxalpha == 1) ? ALPHA_MAP_UNSAFE_NO_AA : ALPHA_MAP_UNSAFE;
 196         }
 197 
 198         // The alpha map used by this object (taken out of our map cache) to convert
 199         // pixel coverage counts (which are in the range [0, maxalpha])
 200         // into alpha values, which are in [0,255]).
 201         static final byte[] ALPHA_MAP;
 202         static final OffHeapArray ALPHA_MAP_UNSAFE;
 203 
 204         static final byte[] ALPHA_MAP_NO_AA;
 205         static final OffHeapArray ALPHA_MAP_UNSAFE_NO_AA;
 206 
 207         static {
 208             final Unsafe _unsafe = OffHeapArray.UNSAFE;
 209 
 210             // AA:
 211             byte[] _ALPHA_MAP = buildAlphaMap(MarlinConst.MAX_AA_ALPHA);
 212             ALPHA_MAP = _ALPHA_MAP; // Keep alive the OffHeapArray
 213             ALPHA_MAP_UNSAFE = new OffHeapArray(ALPHA_MAP, ALPHA_MAP.length); // 1K
 214 
 215             long addr = ALPHA_MAP_UNSAFE.address;
 216 
 217             for (int i = 0; i < _ALPHA_MAP.length; i++) {
 218                 _unsafe.putByte(addr + i, _ALPHA_MAP[i]);
 219             }
 220 
 221             // NoAA:
 222             byte[] _ALPHA_MAP_NO_AA = buildAlphaMap(1);
 223             ALPHA_MAP_NO_AA = _ALPHA_MAP_NO_AA; // Keep alive the OffHeapArray
 224             ALPHA_MAP_UNSAFE_NO_AA = new OffHeapArray(ALPHA_MAP_NO_AA, ALPHA_MAP_NO_AA.length);
 225 
 226             addr = ALPHA_MAP_UNSAFE_NO_AA.address;
 227 
 228             for (int i = 0; i < _ALPHA_MAP_NO_AA.length; i++) {
 229                 _unsafe.putByte(addr + i, _ALPHA_MAP_NO_AA[i]);
 230             }
 231         }
 232 
 233         private static byte[] buildAlphaMap(final int maxalpha) {
 234             final byte[] alMap = new byte[maxalpha << 1];
 235             final int halfmaxalpha = maxalpha >> 2;
 236             for (int i = 0; i <= maxalpha; i++) {
 237                 alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
 238 //            System.out.println("alphaMap[" + i + "] = "
 239 //                               + Byte.toUnsignedInt(alMap[i]));
 240             }
 241             return alMap;
 242         }
 243 
 244         @Override
 245         public boolean supportBlockFlags() {
 246             return true;
 247         }
 248 
 249         @Override
 250         public void clearAlphas(final int pix_y) {
 251             final int w = width;
 252             final int off = (pix_y - y) * w;
 253 
 254             // Clear complete row:
 255            Arrays.fill(this.alphas, off, off + w, (byte)0);
 256         }
 257 
 258         @Override
 259         public void setAndClearRelativeAlphas(final int[] alphaDeltas, final int pix_y,
 260                                               final int pix_from, final int pix_to)
 261         {
 262 //            System.out.println("setting row "+(pix_y - y)+
 263 //                               " out of "+width+" x "+height);











 264 
 265             final byte out[] = this.alphas;
 266             final int w = width;
 267             final int off = (pix_y - y) * w;
 268 
 269             final Unsafe _unsafe = OffHeapArray.UNSAFE;
 270             final long addr_alpha = ALPHA_MAP_USED.address;
 271 
 272             final int from = pix_from - x;
 273 
 274             // skip useless pixels above boundary
 275             final int to = pix_to - x;
 276             final int ato = Math.min(to, width);
 277 
 278             // fast fill ?
 279             final boolean fast = useFastFill && ((ato - from) < fastFillThreshold);
 280 
 281             if (fast) {
 282                 // Zero-fill complete row:
 283                 Arrays.fill(out, off, off + w, (byte) 0);
 284 
 285                 int i = from;
 286                 int curAlpha = 0;
 287 
 288                 while (i < ato) {
 289                     curAlpha += alphaDeltas[i];
 290 
 291                     out[off + i] = _unsafe.getByte(addr_alpha + curAlpha); // [0..255]
 292                     i++;
 293                 }
 294 
 295             } else {
 296                 int i = 0;
 297 
 298                 while (i < from) {
 299                     out[off + i] = 0;
 300                     i++;
 301                 }
 302 
 303                 int curAlpha = 0;
 304 
 305                 while (i < ato) {
 306                     curAlpha += alphaDeltas[i];
 307 
 308                     out[off + i] = _unsafe.getByte(addr_alpha + curAlpha); // [0..255]

 309                     i++;
 310                 }
 311 
 312                 while (i < w) {
 313                     out[off + i] = 0;
 314                     i++;
 315                 }
 316             }
 317 
 318             // Clear alpha row for reuse:
 319             IntArrayCache.fill(alphaDeltas, from, to + 1, 0);
 320         }
 321 
 322         @Override
 323         public void setAndClearRelativeAlphas(final int[] blkFlags, final int[] alphaDeltas, final int pix_y,
 324                                               final int pix_from, final int pix_to)
 325         {
 326 //            System.out.println("setting row "+(pix_y - y)+
 327 //                               " out of "+width+" x "+height);
 328 
 329             final byte out[] = this.alphas;
 330             final int w = width;
 331             final int off = (pix_y - y) * w;
 332 
 333             final Unsafe _unsafe = OffHeapArray.UNSAFE;
 334             final long addr_alpha = ALPHA_MAP_USED.address;
 335 
 336             final int from = pix_from - x;
 337 
 338             // skip useless pixels above boundary
 339             final int to = pix_to - x;
 340             final int ato = Math.min(to, width);
 341 
 342             // fast fill ?
 343             final boolean fast = useFastFill && ((ato - from) < fastFillThreshold);
 344 
 345             final int _BLK_SIZE_LG  = BLOCK_SIZE_LG;
 346 
 347             // traverse flagged blocks:
 348             final int blkW = (from >> _BLK_SIZE_LG);
 349             final int blkE = (ato   >> _BLK_SIZE_LG) + 1;
 350 
 351             // Perform run-length encoding and store results in the piscesCache
 352             int curAlpha = 0;
 353 
 354             final int _MAX_VALUE = Integer.MAX_VALUE;
 355             int last_t0 = _MAX_VALUE;
 356             byte val;
 357 
 358             if (fast) {
 359                 int i = from;
 360 
 361                 // Zero-fill complete row:
 362                 Arrays.fill(out, off, off + w, (byte) 0);
 363 
 364                 for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
 365                     if (blkFlags[t] != 0) {
 366                         blkFlags[t] = 0;
 367 
 368                         if (last_t0 == _MAX_VALUE) {
 369                             last_t0 = t;
 370                         }
 371                         continue;
 372                     }
 373                     if (last_t0 != _MAX_VALUE) {
 374                         // emit blocks:
 375                         blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
 376                         last_t0 = _MAX_VALUE;
 377 
 378                         // (last block pixel+1) inclusive => +1
 379                         blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, ato);
 380 
 381                         for (cx = blk_x0; cx < blk_x1; cx++) {
 382                             if ((delta = alphaDeltas[cx]) != 0) {
 383                                 alphaDeltas[cx] = 0;
 384 
 385                                 // fill span:
 386                                 if (cx != i) {
 387                                     // skip alpha = 0
 388                                     if (curAlpha == 0) {
 389                                         i = cx;
 390                                     } else {
 391                                         val = _unsafe.getByte(addr_alpha + curAlpha);
 392 
 393                                         do {
 394                                             out[off + i] = val;
 395                                             i++;
 396                                         } while (i < cx);
 397                                     }
 398                                 }
 399 
 400                                 // alpha value = running sum of coverage delta:
 401                                 curAlpha += delta;
 402                             }
 403                         }
 404                     }
 405                 }
 406 
 407                 // Process remaining span:
 408                 val = _unsafe.getByte(addr_alpha + curAlpha);
 409 
 410                 do {
 411                     out[off + i] = val;
 412                     i++;
 413                 } while (i < ato);
 414 
 415             } else {
 416                 int i = 0;
 417 
 418                 while (i < from) {
 419                     out[off + i] = 0;
 420                     i++;
 421                 }
 422 
 423                 for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
 424                     if (blkFlags[t] != 0) {
 425                         blkFlags[t] = 0;
 426 
 427                         if (last_t0 == _MAX_VALUE) {
 428                             last_t0 = t;
 429                         }
 430                         continue;
 431                     }
 432                     if (last_t0 != _MAX_VALUE) {
 433                         // emit blocks:
 434                         blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
 435                         last_t0 = _MAX_VALUE;
 436 
 437                         // (last block pixel+1) inclusive => +1
 438                         blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, ato);
 439 
 440                         for (cx = blk_x0; cx < blk_x1; cx++) {
 441                             if ((delta = alphaDeltas[cx]) != 0) {
 442                                 alphaDeltas[cx] = 0;
 443 
 444                                 // fill span:
 445                                 if (cx != i) {
 446                                     val = _unsafe.getByte(addr_alpha + curAlpha);
 447 
 448                                     do {
 449                                         out[off + i] = val;
 450                                         i++;
 451                                     } while (i < cx);
 452                                 }
 453 
 454                                 // alpha value = running sum of coverage delta:
 455                                 curAlpha += delta;
 456                             }
 457                         }
 458                     }
 459                 }
 460 
 461                 // Process remaining span:
 462                 val = _unsafe.getByte(addr_alpha + curAlpha);
 463 
 464                 do {
 465                     out[off + i] = val;
 466                     i++;
 467                 } while (i < ato);
 468 
 469                 while (i < w) {
 470                     out[off + i] = 0;
 471                     i++;
 472                 }
 473             }
 474 
 475             // Clear alpha row for reuse:
 476             alphaDeltas[ato] = 0;
 477 
 478             if (MarlinConst.DO_CHECKS) {
 479                 IntArrayCache.check(blkFlags, blkW, blkE, 0);
 480                 IntArrayCache.check(alphaDeltas, from, to + 1, 0);
 481             }
 482         }
 483     }
 484 }
--- EOF ---