< prev index next >
openjfx9/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinRasterizer.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -23,25 +23,35 @@
* questions.
*/
package com.sun.prism.impl.shape;
-import com.sun.javafx.geom.RectBounds;
import com.sun.javafx.geom.Path2D;
+import com.sun.javafx.geom.RectBounds;
import com.sun.javafx.geom.Rectangle;
import com.sun.javafx.geom.Shape;
import com.sun.javafx.geom.transform.BaseTransform;
-import com.sun.openpisces.AlphaConsumer;
-import com.sun.openpisces.Renderer;
+import com.sun.marlin.FloatMath;
+import com.sun.marlin.IntArrayCache;
+import com.sun.marlin.MarlinAlphaConsumer;
+import com.sun.marlin.MarlinConst;
+import static com.sun.marlin.MarlinConst.BLOCK_SIZE_LG;
+import com.sun.marlin.MarlinRenderer;
+import com.sun.marlin.MarlinRenderingEngine;
+import com.sun.marlin.OffHeapArray;
+import com.sun.marlin.RendererContext;
import com.sun.prism.BasicStroke;
import com.sun.prism.impl.PrismSettings;
import java.nio.ByteBuffer;
+import java.util.Arrays;
+import jdk.internal.misc.Unsafe;
-public class OpenPiscesRasterizer implements ShapeRasterizer {
- private static MaskData emptyData = MaskData.create(new byte[1], 0, 0, 1, 1);
-
- private static Consumer savedConsumer;
+/**
+ * Thread-safe Marlin rasterizer (TL or CLQ storage)
+ */
+public final class MarlinRasterizer implements ShapeRasterizer {
+ private static final MaskData EMPTY_MASK = MaskData.create(new byte[1], 0, 0, 1, 1);
@Override
public MaskData getMaskData(Shape shape,
BasicStroke stroke,
RectBounds xformBounds,
@@ -69,53 +79,66 @@
xformBounds = new RectBounds();
//TODO: Need to verify that this is a safe cast ... (RT-27427)
xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds);
}
- Rectangle rclip = new Rectangle(xformBounds);
- if (rclip.isEmpty()) {
- return emptyData;
- }
- Renderer renderer = null;
- if (shape instanceof Path2D) {
- renderer = OpenPiscesPrismUtils.setupRenderer((Path2D) shape, stroke, xform, rclip,
- antialiasedShape);
- }
- if (renderer == null) {
- renderer = OpenPiscesPrismUtils.setupRenderer(shape, stroke, xform, rclip,
- antialiasedShape);
- }
- int outpix_xmin = renderer.getOutpixMinX();
- int outpix_ymin = renderer.getOutpixMinY();
- int outpix_xmax = renderer.getOutpixMaxX();
- int outpix_ymax = renderer.getOutpixMaxY();
- int w = outpix_xmax - outpix_xmin;
- int h = outpix_ymax - outpix_ymin;
- if (w <= 0 || h <= 0) {
- return emptyData;
- }
-
- Consumer consumer = savedConsumer;
- if (consumer == null || w * h > consumer.getAlphaLength()) {
- int csize = (w * h + 0xfff) & (~0xfff);
- savedConsumer = consumer = new Consumer(csize);
- if (PrismSettings.verbose) {
- System.out.println("new alphas");
- }
- }
- consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h);
- renderer.produceAlphas(consumer);
- return consumer.getMaskData();
+ if (xformBounds.isEmpty()) {
+ return EMPTY_MASK;
+ }
+
+ final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext();
+ MarlinRenderer renderer = null;
+ try {
+ final Rectangle rclip = rdrCtx.clip;
+ rclip.setBounds(xformBounds);
+
+ if (shape instanceof Path2D) {
+ renderer = MarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, xform, rclip,
+ antialiasedShape);
+ }
+ if (renderer == null) {
+ renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip,
+ antialiasedShape);
+ }
+ final int outpix_xmin = renderer.getOutpixMinX();
+ final int outpix_xmax = renderer.getOutpixMaxX();
+ final int outpix_ymin = renderer.getOutpixMinY();
+ final int outpix_ymax = renderer.getOutpixMaxY();
+ final int w = outpix_xmax - outpix_xmin;
+ final int h = outpix_ymax - outpix_ymin;
+ if ((w <= 0) || (h <= 0)) {
+ return EMPTY_MASK;
+ }
+
+ Consumer consumer = (Consumer)rdrCtx.consumer;
+ if (consumer == null || (w * h) > consumer.getAlphaLength()) {
+ final int csize = (w * h + 0xfff) & (~0xfff);
+ rdrCtx.consumer = consumer = new Consumer(csize);
+ if (PrismSettings.verbose) {
+ System.out.println("new alphas with length = " + csize);
+ }
+ }
+ consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h);
+ renderer.produceAlphas(consumer);
+ return consumer.getMaskData();
+ } finally {
+ if (renderer != null) {
+ renderer.dispose();
+ }
+ // recycle the RendererContext instance
+ MarlinRenderingEngine.returnRendererContext(rdrCtx);
+ }
}
- private static class Consumer implements AlphaConsumer {
- static byte savedAlphaMap[];
+ private static final class Consumer implements MarlinAlphaConsumer {
int x, y, width, height;
- byte alphas[];
- byte alphaMap[];
- ByteBuffer alphabuffer;
- MaskData maskdata = new MaskData();
+ final byte alphas[];
+ final ByteBuffer alphabuffer;
+ final MaskData maskdata = new MaskData();
+
+ boolean useFastFill;
+ int fastFillThreshold;
public Consumer(int alphalen) {
this.alphas = new byte[alphalen];
alphabuffer = ByteBuffer.wrap(alphas);
}
@@ -124,10 +147,15 @@
this.x = x;
this.y = y;
this.width = w;
this.height = h;
maskdata.update(alphabuffer, x, y, w, h);
+
+ useFastFill = (w >= 32);
+ if (useFastFill) {
+ fastFillThreshold = (w >= 128) ? (w >> 1) : (w >> 2);
+ }
}
@Override
public int getOriginX() {
return x;
@@ -158,71 +186,299 @@
public MaskData getMaskData() {
return maskdata;
}
+ OffHeapArray ALPHA_MAP_USED = null;
+
@Override
public void setMaxAlpha(int maxalpha) {
- byte map[] = savedAlphaMap;
- if (map == null || map.length != maxalpha+1) {
- map = new byte[maxalpha+1];
- for (int i = 0; i <= maxalpha; i++) {
- map[i] = (byte) ((i*255 + maxalpha/2)/maxalpha);
- }
- savedAlphaMap = map;
+ ALPHA_MAP_USED = (maxalpha == 1) ? ALPHA_MAP_UNSAFE_NO_AA : ALPHA_MAP_UNSAFE;
+ }
+
+ // The alpha map used by this object (taken out of our map cache) to convert
+ // pixel coverage counts (which are in the range [0, maxalpha])
+ // into alpha values, which are in [0,255]).
+ static final byte[] ALPHA_MAP;
+ static final OffHeapArray ALPHA_MAP_UNSAFE;
+
+ static final byte[] ALPHA_MAP_NO_AA;
+ static final OffHeapArray ALPHA_MAP_UNSAFE_NO_AA;
+
+ static {
+ final Unsafe _unsafe = OffHeapArray.UNSAFE;
+
+ // AA:
+ byte[] _ALPHA_MAP = buildAlphaMap(MarlinConst.MAX_AA_ALPHA);
+ ALPHA_MAP = _ALPHA_MAP; // Keep alive the OffHeapArray
+ ALPHA_MAP_UNSAFE = new OffHeapArray(ALPHA_MAP, ALPHA_MAP.length); // 1K
+
+ long addr = ALPHA_MAP_UNSAFE.address;
+
+ for (int i = 0; i < _ALPHA_MAP.length; i++) {
+ _unsafe.putByte(addr + i, _ALPHA_MAP[i]);
+ }
+
+ // NoAA:
+ byte[] _ALPHA_MAP_NO_AA = buildAlphaMap(1);
+ ALPHA_MAP_NO_AA = _ALPHA_MAP_NO_AA; // Keep alive the OffHeapArray
+ ALPHA_MAP_UNSAFE_NO_AA = new OffHeapArray(ALPHA_MAP_NO_AA, ALPHA_MAP_NO_AA.length);
+
+ addr = ALPHA_MAP_UNSAFE_NO_AA.address;
+
+ for (int i = 0; i < _ALPHA_MAP_NO_AA.length; i++) {
+ _unsafe.putByte(addr + i, _ALPHA_MAP_NO_AA[i]);
+ }
+ }
+
+ private static byte[] buildAlphaMap(final int maxalpha) {
+ final byte[] alMap = new byte[maxalpha << 1];
+ final int halfmaxalpha = maxalpha >> 2;
+ for (int i = 0; i <= maxalpha; i++) {
+ alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
+// System.out.println("alphaMap[" + i + "] = "
+// + Byte.toUnsignedInt(alMap[i]));
}
- this.alphaMap = map;
+ return alMap;
}
@Override
- public void setAndClearRelativeAlphas(int[] alphaRow, int pix_y,
- int pix_from, int pix_to)
+ public boolean supportBlockFlags() {
+ return true;
+ }
+
+ @Override
+ public void clearAlphas(final int pix_y) {
+ final int w = width;
+ final int off = (pix_y - y) * w;
+
+ // Clear complete row:
+ Arrays.fill(this.alphas, off, off + w, (byte)0);
+ }
+
+ @Override
+ public void setAndClearRelativeAlphas(final int[] alphaDeltas, final int pix_y,
+ final int pix_from, final int pix_to)
{
// System.out.println("setting row "+(pix_y - y)+
// " out of "+width+" x "+height);
- int w = width;
- int off = (pix_y - y) * w;
- byte out[] = this.alphas;
- byte map[] = this.alphaMap;
- int a = 0;
- for (int i = 0; i < w; i++) {
- a += alphaRow[i];
- alphaRow[i] = 0;
- out[off+i] = map[a];
- }
- }
- public void setAndClearRelativeAlphas2(int[] alphaDeltas, int pix_y,
- int pix_from, int pix_to)
- {
- if (pix_to >= pix_from) {
- byte out[] = this.alphas;
- byte map[] = this.alphaMap;
- int from = pix_from - x;
- int to = pix_to - x;
- int w = width;
- int off = (pix_y - y) * w;
+ final byte out[] = this.alphas;
+ final int w = width;
+ final int off = (pix_y - y) * w;
+
+ final Unsafe _unsafe = OffHeapArray.UNSAFE;
+ final long addr_alpha = ALPHA_MAP_USED.address;
+
+ final int from = pix_from - x;
+
+ // skip useless pixels above boundary
+ final int to = pix_to - x;
+ final int ato = Math.min(to, width);
+ // fast fill ?
+ final boolean fast = useFastFill && ((ato - from) < fastFillThreshold);
+
+ if (fast) {
+ // Zero-fill complete row:
+ Arrays.fill(out, off, off + w, (byte) 0);
+
+ int i = from;
+ int curAlpha = 0;
+
+ while (i < ato) {
+ curAlpha += alphaDeltas[i];
+
+ out[off + i] = _unsafe.getByte(addr_alpha + curAlpha); // [0..255]
+ i++;
+ }
+
+ } else {
int i = 0;
+
while (i < from) {
- out[off+i] = 0;
+ out[off + i] = 0;
i++;
}
+
int curAlpha = 0;
- while (i <= to) {
+
+ while (i < ato) {
curAlpha += alphaDeltas[i];
- alphaDeltas[i] = 0;
- byte a = map[curAlpha];
- out[off+i] = a;
+
+ out[off + i] = _unsafe.getByte(addr_alpha + curAlpha); // [0..255]
i++;
}
- alphaDeltas[i] = 0;
+
while (i < w) {
- out[off+i] = 0;
+ out[off + i] = 0;
i++;
}
+ }
+
+ // Clear alpha row for reuse:
+ IntArrayCache.fill(alphaDeltas, from, to + 1, 0);
+ }
+
+ @Override
+ public void setAndClearRelativeAlphas(final int[] blkFlags, final int[] alphaDeltas, final int pix_y,
+ final int pix_from, final int pix_to)
+ {
+// System.out.println("setting row "+(pix_y - y)+
+// " out of "+width+" x "+height);
+
+ final byte out[] = this.alphas;
+ final int w = width;
+ final int off = (pix_y - y) * w;
+
+ final Unsafe _unsafe = OffHeapArray.UNSAFE;
+ final long addr_alpha = ALPHA_MAP_USED.address;
+
+ final int from = pix_from - x;
+
+ // skip useless pixels above boundary
+ final int to = pix_to - x;
+ final int ato = Math.min(to, width);
+
+ // fast fill ?
+ final boolean fast = useFastFill && ((ato - from) < fastFillThreshold);
+
+ final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
+
+ // traverse flagged blocks:
+ final int blkW = (from >> _BLK_SIZE_LG);
+ final int blkE = (ato >> _BLK_SIZE_LG) + 1;
+
+ // Perform run-length encoding and store results in the piscesCache
+ int curAlpha = 0;
+
+ final int _MAX_VALUE = Integer.MAX_VALUE;
+ int last_t0 = _MAX_VALUE;
+ byte val;
+
+ if (fast) {
+ int i = from;
+
+ // Zero-fill complete row:
+ Arrays.fill(out, off, off + w, (byte) 0);
+
+ for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
+ if (blkFlags[t] != 0) {
+ blkFlags[t] = 0;
+
+ if (last_t0 == _MAX_VALUE) {
+ last_t0 = t;
+ }
+ continue;
+ }
+ if (last_t0 != _MAX_VALUE) {
+ // emit blocks:
+ blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
+ last_t0 = _MAX_VALUE;
+
+ // (last block pixel+1) inclusive => +1
+ blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, ato);
+
+ for (cx = blk_x0; cx < blk_x1; cx++) {
+ if ((delta = alphaDeltas[cx]) != 0) {
+ alphaDeltas[cx] = 0;
+
+ // fill span:
+ if (cx != i) {
+ // skip alpha = 0
+ if (curAlpha == 0) {
+ i = cx;
+ } else {
+ val = _unsafe.getByte(addr_alpha + curAlpha);
+
+ do {
+ out[off + i] = val;
+ i++;
+ } while (i < cx);
+ }
+ }
+
+ // alpha value = running sum of coverage delta:
+ curAlpha += delta;
+ }
+ }
+ }
+ }
+
+ // Process remaining span:
+ val = _unsafe.getByte(addr_alpha + curAlpha);
+
+ do {
+ out[off + i] = val;
+ i++;
+ } while (i < ato);
+
} else {
- java.util.Arrays.fill(alphaDeltas, 0);
+ int i = 0;
+
+ while (i < from) {
+ out[off + i] = 0;
+ i++;
+ }
+
+ for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
+ if (blkFlags[t] != 0) {
+ blkFlags[t] = 0;
+
+ if (last_t0 == _MAX_VALUE) {
+ last_t0 = t;
+ }
+ continue;
+ }
+ if (last_t0 != _MAX_VALUE) {
+ // emit blocks:
+ blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
+ last_t0 = _MAX_VALUE;
+
+ // (last block pixel+1) inclusive => +1
+ blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, ato);
+
+ for (cx = blk_x0; cx < blk_x1; cx++) {
+ if ((delta = alphaDeltas[cx]) != 0) {
+ alphaDeltas[cx] = 0;
+
+ // fill span:
+ if (cx != i) {
+ val = _unsafe.getByte(addr_alpha + curAlpha);
+
+ do {
+ out[off + i] = val;
+ i++;
+ } while (i < cx);
+ }
+
+ // alpha value = running sum of coverage delta:
+ curAlpha += delta;
+ }
+ }
+ }
+ }
+
+ // Process remaining span:
+ val = _unsafe.getByte(addr_alpha + curAlpha);
+
+ do {
+ out[off + i] = val;
+ i++;
+ } while (i < ato);
+
+ while (i < w) {
+ out[off + i] = 0;
+ i++;
+ }
+ }
+
+ // Clear alpha row for reuse:
+ alphaDeltas[ato] = 0;
+
+ if (MarlinConst.DO_CHECKS) {
+ IntArrayCache.check(blkFlags, blkW, blkE, 0);
+ IntArrayCache.check(alphaDeltas, from, to + 1, 0);
}
}
}
}
< prev index next >