diff --git a/pom.xml b/pom.xml index 2d81c4d..4958be7 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.marlin marlin jar - 0.9.2-Unsafe + 0.9.3-Unsafe Marlin software rasterizer https://github.com/bourgesl/marlin-renderer @@ -40,7 +40,7 @@ UTF-8 - + org.apache.maven.plugins maven-compiler-plugin @@ -52,7 +52,7 @@ UTF-8 - + org.apache.maven.plugins maven-jar-plugin @@ -99,7 +99,7 @@ - + org.apache.maven.plugins maven-source-plugin @@ -133,7 +133,7 @@ - + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/org/marlin/pisces/DMarlinRenderingEngine.java b/src/main/java/org/marlin/pisces/DMarlinRenderingEngine.java index 11f0c86..3024001 100644 --- a/src/main/java/org/marlin/pisces/DMarlinRenderingEngine.java +++ b/src/main/java/org/marlin/pisces/DMarlinRenderingEngine.java @@ -30,6 +30,7 @@ import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.security.AccessController; +import java.util.Arrays; import org.marlin.geom.Path2D; import static org.marlin.pisces.MarlinUtils.logInfo; import org.marlin.ReentrantContextProvider; @@ -334,7 +335,6 @@ void strokeTo(final DRendererContext rdrCtx, int dashLen = -1; boolean recycleDashes = false; - double scale = 1.0d; double[] dashesD = null; // Ensure converting dashes to double precision: @@ -375,7 +375,7 @@ void strokeTo(final DRendererContext rdrCtx, // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we // leave a bit of room for error. if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) { - scale = Math.sqrt(a*a + c*c); + final double scale = Math.sqrt(a*a + c*c); if (dashesD != null) { for (int i = 0; i < dashLen; i++) { @@ -427,7 +427,7 @@ void strokeTo(final DRendererContext rdrCtx, pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat); // stroker will adjust the clip rectangle (width / miter limit): - pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale, + pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, (dashesD == null)); // Curve Monotizer: @@ -839,6 +839,11 @@ public AATileGenerator getAATileGenerator(Shape s, clipRect[2] = clip.getLoX(); clipRect[3] = clip.getLoX() + clip.getWidth(); + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (clip): " + + Arrays.toString(rdrCtx.clipRect)); + } + // Enable clipping: rdrCtx.doClip = true; } diff --git a/src/main/java/org/marlin/pisces/DStroker.java b/src/main/java/org/marlin/pisces/DStroker.java index a811487..77dc645 100644 --- a/src/main/java/org/marlin/pisces/DStroker.java +++ b/src/main/java/org/marlin/pisces/DStroker.java @@ -139,7 +139,6 @@ final class DStroker implements DPathConsumer2D, MarlinConst { * JOIN_MITER, JOIN_ROUND or * JOIN_BEVEL. * @param miterLimit the desired miter limit - * @param scale scaling factor applied to clip boundaries * @param subdivideCurves true to indicate to subdivide curves, false if dasher does * @return this instance */ @@ -148,7 +147,6 @@ DStroker init(final DPathConsumer2D pc2d, final int capStyle, final int joinStyle, final double miterLimit, - final double scale, final boolean subdivideCurves) { this.out = pc2d; @@ -169,7 +167,6 @@ DStroker init(final DPathConsumer2D pc2d, if (rdrCtx.doClip) { // Adjust the clipping rectangle with the stroker margin (miter limit, width) - double rdrOffX = 0.0d, rdrOffY = 0.0d; double margin = lineWidth2; if (capStyle == CAP_SQUARE) { @@ -178,23 +175,21 @@ DStroker init(final DPathConsumer2D pc2d, if ((joinStyle == JOIN_MITER) && (margin < limit)) { margin = limit; } - if (scale != 1.0d) { - margin *= scale; - rdrOffX = scale * DRenderer.RDR_OFFSET_X; - rdrOffY = scale * DRenderer.RDR_OFFSET_Y; - } - // add a small rounding error: - margin += 1e-3d; // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY // adjust clip rectangle (ymin, ymax, xmin, xmax): final double[] _clipRect = rdrCtx.clipRect; - _clipRect[0] -= margin - rdrOffY; - _clipRect[1] += margin + rdrOffY; - _clipRect[2] -= margin - rdrOffX; - _clipRect[3] += margin + rdrOffX; + _clipRect[0] -= margin; + _clipRect[1] += margin; + _clipRect[2] -= margin; + _clipRect[3] += margin; this.clipRect = _clipRect; + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (stroker): " + + Arrays.toString(rdrCtx.clipRect)); + } + // initialize curve splitter here for stroker & dasher: if (DO_CLIP_SUBDIVIDER) { subdivide = subdivideCurves; diff --git a/src/main/java/org/marlin/pisces/DTransformingPathConsumer2D.java b/src/main/java/org/marlin/pisces/DTransformingPathConsumer2D.java index accac77..5a138c0 100644 --- a/src/main/java/org/marlin/pisces/DTransformingPathConsumer2D.java +++ b/src/main/java/org/marlin/pisces/DTransformingPathConsumer2D.java @@ -104,6 +104,10 @@ DPathConsumer2D pathClipper(DPathConsumer2D out) { DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, AffineTransform at) { + if (rdrCtx.doClip) { + adjustClipOffset(rdrCtx.clipRect); + } + if (at == null) { return out; } @@ -133,30 +137,51 @@ DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, } private static void adjustClipOffset(final double[] clipRect) { - clipRect[0] += Renderer.RDR_OFFSET_Y; - clipRect[1] += Renderer.RDR_OFFSET_Y; - clipRect[2] += Renderer.RDR_OFFSET_X; - clipRect[3] += Renderer.RDR_OFFSET_X; + // Adjust the clipping rectangle with the renderer offsets + final double rdrOffX = DRenderer.RDR_OFFSET_X; + final double rdrOffY = DRenderer.RDR_OFFSET_Y; + + // add a small rounding error: + final double margin = 1e-3d; + + clipRect[0] -= margin - rdrOffY; + clipRect[1] += margin + rdrOffY; + clipRect[2] -= margin - rdrOffX; + clipRect[3] += margin + rdrOffX; } private static void adjustClipScale(final double[] clipRect, final double mxx, final double myy) { - adjustClipOffset(clipRect); - // Adjust the clipping rectangle (iv_DeltaScaleFilter): clipRect[0] /= myy; clipRect[1] /= myy; + + if (clipRect[1] < clipRect[0]) { + double tmp = clipRect[0]; + clipRect[0] = clipRect[1]; + clipRect[1] = tmp; + } + clipRect[2] /= mxx; clipRect[3] /= mxx; + + if (clipRect[3] < clipRect[2]) { + double tmp = clipRect[2]; + clipRect[2] = clipRect[3]; + clipRect[3] = tmp; + } + + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (ClipScale): " + + Arrays.toString(clipRect)); + } } private static void adjustClipInverseDelta(final double[] clipRect, final double mxx, final double mxy, final double myx, final double myy) { - adjustClipOffset(clipRect); - // Adjust the clipping rectangle (iv_DeltaTransformFilter): final double det = mxx * myy - mxy * myx; final double imxx = myy / det; @@ -198,6 +223,11 @@ private static void adjustClipInverseDelta(final double[] clipRect, clipRect[1] = ymax; clipRect[2] = xmin; clipRect[3] = xmax; + + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (ClipInverseDelta): " + + Arrays.toString(clipRect)); + } } DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out, @@ -532,18 +562,12 @@ static final class PathClipFilter implements DPathConsumer2D { PathClipFilter init(final DPathConsumer2D out) { this.out = out; - // Adjust the clipping rectangle with the renderer offsets - final double rdrOffX = DRenderer.RDR_OFFSET_X; - final double rdrOffY = DRenderer.RDR_OFFSET_Y; - - // add a small rounding error: - final double margin = 1e-3d; + adjustClipOffset(this.clipRect); - final double[] _clipRect = this.clipRect; - _clipRect[0] -= margin - rdrOffY; - _clipRect[1] += margin + rdrOffY; - _clipRect[2] -= margin - rdrOffX; - _clipRect[3] += margin + rdrOffX; + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (PathClipFilter): " + + Arrays.toString(clipRect)); + } if (MarlinConst.DO_CLIP_SUBDIVIDER) { // adjust padded clip rectangle: @@ -890,6 +914,8 @@ static final class CurveClipSplitter { void init() { this.init_clipRectPad = true; + + // TODO: adjust LEN_TH by rough scale ? } private void initPaddedClip() { @@ -906,7 +932,7 @@ private void initPaddedClip() { if (TRACE) { MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] " - + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]"); + + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]"); } } @@ -991,7 +1017,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR, outCodeOR, clipRectPad); if (TRACE) { - MarlinUtils.logInfo("nSplits: "+ nSplits); + MarlinUtils.logInfo("nSplits: " + nSplits); MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits))); } if (nSplits == 0) { diff --git a/src/main/java/org/marlin/pisces/MarlinConst.java b/src/main/java/org/marlin/pisces/MarlinConst.java index 8fc7f97..64ec3d3 100644 --- a/src/main/java/org/marlin/pisces/MarlinConst.java +++ b/src/main/java/org/marlin/pisces/MarlinConst.java @@ -82,9 +82,12 @@ interface MarlinConst { static final boolean DO_CLIP_SUBDIVIDER = MarlinProperties.isDoClipSubdivider(); - // flag to enable logs related bounds checks + // flag to enable logs related to bounds checks static final boolean DO_LOG_BOUNDS = ENABLE_LOGS && false; + // flag to enable logs related to clip rect + static final boolean DO_LOG_CLIP = ENABLE_LOGS && false; + // Initial Array sizing (initial context capacity) ~ 450K // 4096 pixels (width) for initial capacity diff --git a/src/main/java/org/marlin/pisces/MarlinRenderingEngine.java b/src/main/java/org/marlin/pisces/MarlinRenderingEngine.java index 858ee4a..ccc5cb5 100644 --- a/src/main/java/org/marlin/pisces/MarlinRenderingEngine.java +++ b/src/main/java/org/marlin/pisces/MarlinRenderingEngine.java @@ -30,6 +30,7 @@ import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.security.AccessController; +import java.util.Arrays; import org.marlin.geom.Path2D; import static org.marlin.pisces.MarlinUtils.logInfo; import sun.awt.geom.PathConsumer2D; @@ -333,7 +334,6 @@ void strokeTo(final RendererContext rdrCtx, int dashLen = -1; boolean recycleDashes = false; - float scale = 1.0f; if (at != null && !at.isIdentity()) { final double a = at.getScaleX(); @@ -366,7 +366,7 @@ void strokeTo(final RendererContext rdrCtx, // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we // leave a bit of room for error. if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) { - scale = (float) Math.sqrt(a*a + c*c); + final float scale = (float) Math.sqrt(a*a + c*c); if (dashes != null) { recycleDashes = true; @@ -421,7 +421,7 @@ void strokeTo(final RendererContext rdrCtx, pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat); // stroker will adjust the clip rectangle (width / miter limit): - pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale, + pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, (dashes == null)); // Curve Monotizer: @@ -836,6 +836,11 @@ public AATileGenerator getAATileGenerator(Shape s, clipRect[2] = clip.getLoX(); clipRect[3] = clip.getLoX() + clip.getWidth(); + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (clip): " + + Arrays.toString(rdrCtx.clipRect)); + } + // Enable clipping: rdrCtx.doClip = true; } diff --git a/src/main/java/org/marlin/pisces/Stroker.java b/src/main/java/org/marlin/pisces/Stroker.java index f56d9b1..0acde45 100644 --- a/src/main/java/org/marlin/pisces/Stroker.java +++ b/src/main/java/org/marlin/pisces/Stroker.java @@ -141,7 +141,6 @@ final class Stroker implements PathConsumer2D, MarlinConst { * JOIN_MITER, JOIN_ROUND or * JOIN_BEVEL. * @param miterLimit the desired miter limit - * @param scale scaling factor applied to clip boundaries * @param subdivideCurves true to indicate to subdivide curves, false if dasher does * @return this instance */ @@ -150,7 +149,6 @@ Stroker init(final PathConsumer2D pc2d, final int capStyle, final int joinStyle, final float miterLimit, - final float scale, final boolean subdivideCurves) { this.out = pc2d; @@ -171,7 +169,6 @@ Stroker init(final PathConsumer2D pc2d, if (rdrCtx.doClip) { // Adjust the clipping rectangle with the stroker margin (miter limit, width) - float rdrOffX = 0.0f, rdrOffY = 0.0f; float margin = lineWidth2; if (capStyle == CAP_SQUARE) { @@ -180,23 +177,21 @@ Stroker init(final PathConsumer2D pc2d, if ((joinStyle == JOIN_MITER) && (margin < limit)) { margin = limit; } - if (scale != 1.0f) { - margin *= scale; - rdrOffX = scale * Renderer.RDR_OFFSET_X; - rdrOffY = scale * Renderer.RDR_OFFSET_Y; - } - // add a small rounding error: - margin += 1e-3f; // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY // adjust clip rectangle (ymin, ymax, xmin, xmax): final float[] _clipRect = rdrCtx.clipRect; - _clipRect[0] -= margin - rdrOffY; - _clipRect[1] += margin + rdrOffY; - _clipRect[2] -= margin - rdrOffX; - _clipRect[3] += margin + rdrOffX; + _clipRect[0] -= margin; + _clipRect[1] += margin; + _clipRect[2] -= margin; + _clipRect[3] += margin; this.clipRect = _clipRect; + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (stroker): " + + Arrays.toString(rdrCtx.clipRect)); + } + // initialize curve splitter here for stroker & dasher: if (DO_CLIP_SUBDIVIDER) { subdivide = subdivideCurves; diff --git a/src/main/java/org/marlin/pisces/TransformingPathConsumer2D.java b/src/main/java/org/marlin/pisces/TransformingPathConsumer2D.java index 1fdf12c..ec6b541 100644 --- a/src/main/java/org/marlin/pisces/TransformingPathConsumer2D.java +++ b/src/main/java/org/marlin/pisces/TransformingPathConsumer2D.java @@ -105,6 +105,10 @@ PathConsumer2D pathClipper(PathConsumer2D out) { PathConsumer2D deltaTransformConsumer(PathConsumer2D out, AffineTransform at) { + if (rdrCtx.doClip) { + adjustClipOffset(rdrCtx.clipRect); + } + if (at == null) { return out; } @@ -134,30 +138,51 @@ PathConsumer2D deltaTransformConsumer(PathConsumer2D out, } private static void adjustClipOffset(final float[] clipRect) { - clipRect[0] += Renderer.RDR_OFFSET_Y; - clipRect[1] += Renderer.RDR_OFFSET_Y; - clipRect[2] += Renderer.RDR_OFFSET_X; - clipRect[3] += Renderer.RDR_OFFSET_X; + // Adjust the clipping rectangle with the renderer offsets + final float rdrOffX = Renderer.RDR_OFFSET_X; + final float rdrOffY = Renderer.RDR_OFFSET_Y; + + // add a small rounding error: + final float margin = 1e-3f; + + clipRect[0] -= margin - rdrOffY; + clipRect[1] += margin + rdrOffY; + clipRect[2] -= margin - rdrOffX; + clipRect[3] += margin + rdrOffX; } private static void adjustClipScale(final float[] clipRect, final float mxx, final float myy) { - adjustClipOffset(clipRect); - // Adjust the clipping rectangle (iv_DeltaScaleFilter): clipRect[0] /= myy; clipRect[1] /= myy; + + if (clipRect[1] < clipRect[0]) { + float tmp = clipRect[0]; + clipRect[0] = clipRect[1]; + clipRect[1] = tmp; + } + clipRect[2] /= mxx; clipRect[3] /= mxx; + + if (clipRect[3] < clipRect[2]) { + float tmp = clipRect[2]; + clipRect[2] = clipRect[3]; + clipRect[3] = tmp; + } + + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (ClipScale): " + + Arrays.toString(clipRect)); + } } private static void adjustClipInverseDelta(final float[] clipRect, final float mxx, final float mxy, final float myx, final float myy) { - adjustClipOffset(clipRect); - // Adjust the clipping rectangle (iv_DeltaTransformFilter): final float det = mxx * myy - mxy * myx; final float imxx = myy / det; @@ -199,6 +224,11 @@ private static void adjustClipInverseDelta(final float[] clipRect, clipRect[1] = ymax; clipRect[2] = xmin; clipRect[3] = xmax; + + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (ClipInverseDelta): " + + Arrays.toString(clipRect)); + } } PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out, @@ -533,18 +563,12 @@ static final class PathClipFilter implements PathConsumer2D { PathClipFilter init(final PathConsumer2D out) { this.out = out; - // Adjust the clipping rectangle with the renderer offsets - final float rdrOffX = Renderer.RDR_OFFSET_X; - final float rdrOffY = Renderer.RDR_OFFSET_Y; - - // add a small rounding error: - final float margin = 1e-3f; + adjustClipOffset(this.clipRect); - final float[] _clipRect = this.clipRect; - _clipRect[0] -= margin - rdrOffY; - _clipRect[1] += margin + rdrOffY; - _clipRect[2] -= margin - rdrOffX; - _clipRect[3] += margin + rdrOffX; + if (MarlinConst.DO_LOG_CLIP) { + MarlinUtils.logInfo("clipRect (PathClipFilter): " + + Arrays.toString(clipRect)); + } if (MarlinConst.DO_CLIP_SUBDIVIDER) { // adjust padded clip rectangle: @@ -891,6 +915,8 @@ static final class CurveClipSplitter { void init() { this.init_clipRectPad = true; + + // TODO: adjust LEN_TH by rough scale ? } private void initPaddedClip() { @@ -907,7 +933,7 @@ private void initPaddedClip() { if (TRACE) { MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] " - + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]"); + + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]"); } } @@ -992,7 +1018,7 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR, outCodeOR, clipRectPad); if (TRACE) { - MarlinUtils.logInfo("nSplits: "+ nSplits); + MarlinUtils.logInfo("nSplits: " + nSplits); MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits))); } if (nSplits == 0) {