Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
7018932: Drawing very large coordinates with a dashed Stroke can caus…
…e Java to hang

Reviewed-by: serb, prr
  • Loading branch information
Laurent Bourgès committed Jan 12, 2021
1 parent 5f7ccce commit e4df2098a823d857792c32f99f00eb9f1fb21040
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 38 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, 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
@@ -170,16 +170,20 @@ public Shape createStrokedShape(Shape src,
* {@code antialias} boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link DPathConsumer2D} object as it is calculated.
* {@link PathConsumer2D} object as it is calculated.
*
* @param src the source path to be widened
* @param bs the {@code BasicSroke} object specifying the
* @param at the transform to be applied to the shape and the
* stroke attributes
* @param bs the {@code BasicStroke} object specifying the
* decorations to be applied to the widened path
* @param thin true if the transformed stroke attributes are smaller
* than the minimum dropout pen width
* @param normalize indicates whether stroke normalization should
* be applied
* @param antialias indicates whether or not adjustments appropriate
* to antialiased rendering should be applied
* @param consumer the {@code DPathConsumer2D} instance to forward
* @param consumer the {@code PathConsumer2D} instance to forward
* the widened geometry to
* @since 1.7
*/
@@ -192,13 +196,92 @@ public void strokeTo(Shape src,
boolean antialias,
final PathConsumer2D consumer)
{
strokeTo(src, at, null, bs, thin, normalize, antialias, consumer);
}

/**
* Sends the geometry for a widened path as specified by the parameters
* to the specified consumer.
* <p>
* The specified {@code src} {@link Shape} is widened according
* to the parameters specified by the {@link BasicStroke} object.
* Adjustments are made to the path as appropriate for the
* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
* {@code normalize} boolean parameter is true.
* Adjustments are made to the path as appropriate for the
* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
* {@code antialias} boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link PathConsumer2D} object as it is calculated.
*
* @param src the source path to be widened
* @param at the transform to be applied to the shape and the
* stroke attributes
* @param clip the current clip in effect in device coordinates
* @param bs the {@code BasicStroke} object specifying the
* decorations to be applied to the widened path
* @param thin true if the transformed stroke attributes are smaller
* than the minimum dropout pen width
* @param normalize indicates whether stroke normalization should
* be applied
* @param antialias indicates whether or not adjustments appropriate
* to antialiased rendering should be applied
* @param consumer the {@code PathConsumer2D} instance to forward
* the widened geometry to
* @since 17
*/
/* @Override (only for 17+) */
public void strokeTo(Shape src,
AffineTransform at,
Region clip,
BasicStroke bs,
boolean thin,
boolean normalize,
boolean antialias,
final PathConsumer2D consumer)
{
// Test if at is identity:
final AffineTransform _at = (at != null && !at.isIdentity()) ? at
: null;

final NormMode norm = (normalize) ?
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
: NormMode.OFF;

final DRendererContext rdrCtx = getRendererContext();
try {
strokeTo(rdrCtx, src, at, bs, thin, norm, antialias,
if ((clip != null) &&
(DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) {
// Define the initial clip bounds:
final double[] clipRect = rdrCtx.clipRect;

// Adjust the clipping rectangle with the renderer offsets
final double rdrOffX = 0.25d; // LBO: is it correct for AA or non AA cases ?
final double rdrOffY = 0.25d; // see NearestPixelQuarter (depends on normalization ?)

// add a small rounding error:
final double margin = 1e-3d;

clipRect[0] = clip.getLoY()
- margin + rdrOffY;
clipRect[1] = clip.getLoY() + clip.getHeight()
+ margin + rdrOffY;
clipRect[2] = clip.getLoX()
- margin + rdrOffX;
clipRect[3] = clip.getLoX() + clip.getWidth()
+ margin + rdrOffX;

if (MarlinConst.DO_LOG_CLIP) {
MarlinUtils.logInfo("clipRect (clip): "
+ Arrays.toString(rdrCtx.clipRect));
}

// Enable clipping:
rdrCtx.doClip = true;
}

strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias,
rdrCtx.p2dAdapter.init(consumer));
} finally {
// recycle the DRendererContext instance
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, 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
@@ -173,8 +173,12 @@ public Shape createStrokedShape(Shape src,
* {@link PathConsumer2D} object as it is calculated.
*
* @param src the source path to be widened
* @param bs the {@code BasicSroke} object specifying the
* @param at the transform to be applied to the shape and the
* stroke attributes
* @param bs the {@code BasicStroke} object specifying the
* decorations to be applied to the widened path
* @param thin true if the transformed stroke attributes are smaller
* than the minimum dropout pen width
* @param normalize indicates whether stroke normalization should
* be applied
* @param antialias indicates whether or not adjustments appropriate
@@ -192,13 +196,92 @@ public void strokeTo(Shape src,
boolean antialias,
final PathConsumer2D consumer)
{
strokeTo(src, at, null, bs, thin, normalize, antialias, consumer);
}

/**
* Sends the geometry for a widened path as specified by the parameters
* to the specified consumer.
* <p>
* The specified {@code src} {@link Shape} is widened according
* to the parameters specified by the {@link BasicStroke} object.
* Adjustments are made to the path as appropriate for the
* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
* {@code normalize} boolean parameter is true.
* Adjustments are made to the path as appropriate for the
* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
* {@code antialias} boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link PathConsumer2D} object as it is calculated.
*
* @param src the source path to be widened
* @param at the transform to be applied to the shape and the
* stroke attributes
* @param clip the current clip in effect in device coordinates
* @param bs the {@code BasicStroke} object specifying the
* decorations to be applied to the widened path
* @param thin true if the transformed stroke attributes are smaller
* than the minimum dropout pen width
* @param normalize indicates whether stroke normalization should
* be applied
* @param antialias indicates whether or not adjustments appropriate
* to antialiased rendering should be applied
* @param consumer the {@code PathConsumer2D} instance to forward
* the widened geometry to
* @since 17
*/
/* @Override (only for 17+) */
public void strokeTo(Shape src,
AffineTransform at,
Region clip,
BasicStroke bs,
boolean thin,
boolean normalize,
boolean antialias,
final PathConsumer2D consumer)
{
// Test if at is identity:
final AffineTransform _at = (at != null && !at.isIdentity()) ? at
: null;

final NormMode norm = (normalize) ?
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
: NormMode.OFF;

final RendererContext rdrCtx = getRendererContext();
try {
strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer);
if ((clip != null) &&
(DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) {
// Define the initial clip bounds:
final float[] clipRect = rdrCtx.clipRect;

// Adjust the clipping rectangle with the renderer offsets
final float rdrOffX = 0.25f; // LBO: is it correct for AA or non AA cases ?
final float rdrOffY = 0.25f; // see NearestPixelQuarter (depends on normalization ?)

// add a small rounding error:
final float margin = 1e-3f;

clipRect[0] = clip.getLoY()
- margin + rdrOffY;
clipRect[1] = clip.getLoY() + clip.getHeight()
+ margin + rdrOffY;
clipRect[2] = clip.getLoX()
- margin + rdrOffX;
clipRect[3] = clip.getLoX() + clip.getWidth()
+ margin + rdrOffX;

if (MarlinConst.DO_LOG_CLIP) {
MarlinUtils.logInfo("clipRect (clip): "
+ Arrays.toString(rdrCtx.clipRect));
}

// Enable clipping:
rdrCtx.doClip = true;
}

strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias, consumer);
} finally {
// recycle the RendererContext instance
returnRendererContext(rdrCtx);
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, 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
@@ -880,6 +880,7 @@ public long getNativeConsumer() {
}
}

/* note: CurveClipSplitter uses double-precision for higher accuracy */
static final class CurveClipSplitter {

static final float LEN_TH = MarlinProperties.getSubdividerMinLength();
@@ -898,22 +899,22 @@ static final class CurveClipSplitter {
final float[] clipRect;

// clip rectangle (ymin, ymax, xmin, xmax) including padding:
final float[] clipRectPad = new float[4];
final double[] clipRectPad = new double[4];
private boolean init_clipRectPad = false;

// This is where the curve to be processed is put. We give it
// enough room to store all curves.
final float[] middle = new float[MAX_N_CURVES * 8 + 2];
final double[] middle = new double[MAX_N_CURVES * 8 + 2];
// t values at subdivision points
private final float[] subdivTs = new float[MAX_N_CURVES];
private final double[] subdivTs = new double[MAX_N_CURVES];

// dirty curve
private final Curve curve;
private final DCurve curve;

CurveClipSplitter(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
this.clipRect = rdrCtx.clipRect;
this.curve = rdrCtx.curve;
this.curve = /* rdrCtx.curve */ new DCurve(); // double-precision curve
}

void init() {
@@ -935,7 +936,7 @@ private void initPaddedClip() {
// adjust padded clip rectangle (ymin, ymax, xmin, xmax):
// add a rounding error (curve subdivision ~ 0.1px):
final float[] _clipRect = clipRect;
final float[] _clipRectPad = clipRectPad;
final double[] _clipRectPad = clipRectPad;

_clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
_clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
@@ -961,7 +962,7 @@ boolean splitLine(final float x0, final float y0,
return false;
}

final float[] mid = middle;
final double[] mid = middle;
mid[0] = x0; mid[1] = y0;
mid[2] = x1; mid[3] = y1;

@@ -982,7 +983,7 @@ boolean splitQuad(final float x0, final float y0,
return false;
}

final float[] mid = middle;
final double[] mid = middle;
mid[0] = x0; mid[1] = y0;
mid[2] = x1; mid[3] = y1;
mid[4] = x2; mid[5] = y2;
@@ -1005,7 +1006,7 @@ boolean splitCurve(final float x0, final float y0,
return false;
}

final float[] mid = middle;
final double[] mid = middle;
mid[0] = x0; mid[1] = y0;
mid[2] = x1; mid[3] = y1;
mid[4] = x2; mid[5] = y2;
@@ -1017,15 +1018,15 @@ boolean splitCurve(final float x0, final float y0,
private boolean subdivideAtIntersections(final int type, final int outCodeOR,
final PathConsumer2D out)
{
final float[] mid = middle;
final float[] subTs = subdivTs;
final double[] mid = middle;
final double[] subTs = subdivTs;

if (init_clipRectPad) {
init_clipRectPad = false;
initPaddedClip();
}

final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
outCodeOR, clipRectPad);

if (TRACE) {
@@ -1036,12 +1037,12 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
// only curve support shortcut
return false;
}
float prevT = 0.0f;
double prevT = 0.0d;

for (int i = 0, off = 0; i < nSplits; i++, off += type) {
final float t = subTs[i];
final double t = subTs[i];

Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
mid, off, mid, off, type);
prevT = t;
}
@@ -1055,19 +1056,19 @@ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
return true;
}

static void emitCurrent(final int type, final float[] pts,
static void emitCurrent(final int type, final double[] pts,
final int off, final PathConsumer2D out)
{
// if instead of switch (perf + most probable cases first)
if (type == 8) {
out.curveTo(pts[off + 2], pts[off + 3],
pts[off + 4], pts[off + 5],
pts[off + 6], pts[off + 7]);
out.curveTo((float)pts[off + 2], (float)pts[off + 3],
(float)pts[off + 4], (float)pts[off + 5],
(float)pts[off + 6], (float)pts[off + 7]);
} else if (type == 4) {
out.lineTo(pts[off + 2], pts[off + 3]);
out.lineTo((float)pts[off + 2], (float)pts[off + 3]);
} else {
out.quadTo(pts[off + 2], pts[off + 3],
pts[off + 4], pts[off + 5]);
out.quadTo((float)pts[off + 2], (float)pts[off + 3],
(float)pts[off + 4], (float)pts[off + 5]);
}
}
}

0 comments on commit e4df209

Please sign in to comment.