< prev index next >
openjfx9/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinRasterizer.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2010, 2014, 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
--- 1,7 ----
/*
! * 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,47 ****
* questions.
*/
package com.sun.prism.impl.shape;
- import com.sun.javafx.geom.RectBounds;
import com.sun.javafx.geom.Path2D;
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.prism.BasicStroke;
import com.sun.prism.impl.PrismSettings;
import java.nio.ByteBuffer;
! public class OpenPiscesRasterizer implements ShapeRasterizer {
! private static MaskData emptyData = MaskData.create(new byte[1], 0, 0, 1, 1);
!
! private static Consumer savedConsumer;
@Override
public MaskData getMaskData(Shape shape,
BasicStroke stroke,
RectBounds xformBounds,
--- 23,57 ----
* questions.
*/
package com.sun.prism.impl.shape;
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.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;
! /**
! * 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,121 ****
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();
}
! private static class Consumer implements AlphaConsumer {
! static byte savedAlphaMap[];
int x, y, width, height;
! byte alphas[];
! byte alphaMap[];
! ByteBuffer alphabuffer;
! MaskData maskdata = new MaskData();
public Consumer(int alphalen) {
this.alphas = new byte[alphalen];
alphabuffer = ByteBuffer.wrap(alphas);
}
--- 79,144 ----
xformBounds = new RectBounds();
//TODO: Need to verify that this is a safe cast ... (RT-27427)
xformBounds = (RectBounds) xform.transform(shape.getBounds(), xformBounds);
}
! 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 final class Consumer implements MarlinAlphaConsumer {
int x, y, width, height;
! 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,133 ****
--- 147,161 ----
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,228 ****
public MaskData getMaskData() {
return maskdata;
}
@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;
}
! this.alphaMap = map;
}
@Override
! public void setAndClearRelativeAlphas(int[] alphaRow, int pix_y,
! int pix_from, 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;
int i = 0;
while (i < from) {
! out[off+i] = 0;
i++;
}
int curAlpha = 0;
! while (i <= to) {
curAlpha += alphaDeltas[i];
! alphaDeltas[i] = 0;
! byte a = map[curAlpha];
! out[off+i] = a;
i++;
}
! alphaDeltas[i] = 0;
while (i < w) {
! out[off+i] = 0;
i++;
}
} else {
! java.util.Arrays.fill(alphaDeltas, 0);
}
}
}
}
--- 186,484 ----
public MaskData getMaskData() {
return maskdata;
}
+ OffHeapArray ALPHA_MAP_USED = null;
+
@Override
public void setMaxAlpha(int maxalpha) {
! 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]));
}
! return alMap;
}
@Override
! 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);
! 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;
i++;
}
+
int curAlpha = 0;
!
! while (i < ato) {
curAlpha += alphaDeltas[i];
!
! out[off + i] = _unsafe.getByte(addr_alpha + curAlpha); // [0..255]
i++;
}
!
while (i < w) {
! 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 {
! 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 >