Skip to content

Commit e4df209

Browse files
author
Laurent Bourgès
committed
7018932: Drawing very large coordinates with a dashed Stroke can cause Java to hang
Reviewed-by: serb, prr
1 parent 5f7ccce commit e4df209

File tree

7 files changed

+344
-38
lines changed

7 files changed

+344
-38
lines changed

src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -170,16 +170,20 @@ public Shape createStrokedShape(Shape src,
170170
* {@code antialias} boolean parameter is true.
171171
* <p>
172172
* The geometry of the widened path is forwarded to the indicated
173-
* {@link DPathConsumer2D} object as it is calculated.
173+
* {@link PathConsumer2D} object as it is calculated.
174174
*
175175
* @param src the source path to be widened
176-
* @param bs the {@code BasicSroke} object specifying the
176+
* @param at the transform to be applied to the shape and the
177+
* stroke attributes
178+
* @param bs the {@code BasicStroke} object specifying the
177179
* decorations to be applied to the widened path
180+
* @param thin true if the transformed stroke attributes are smaller
181+
* than the minimum dropout pen width
178182
* @param normalize indicates whether stroke normalization should
179183
* be applied
180184
* @param antialias indicates whether or not adjustments appropriate
181185
* to antialiased rendering should be applied
182-
* @param consumer the {@code DPathConsumer2D} instance to forward
186+
* @param consumer the {@code PathConsumer2D} instance to forward
183187
* the widened geometry to
184188
* @since 1.7
185189
*/
@@ -192,13 +196,92 @@ public void strokeTo(Shape src,
192196
boolean antialias,
193197
final PathConsumer2D consumer)
194198
{
199+
strokeTo(src, at, null, bs, thin, normalize, antialias, consumer);
200+
}
201+
202+
/**
203+
* Sends the geometry for a widened path as specified by the parameters
204+
* to the specified consumer.
205+
* <p>
206+
* The specified {@code src} {@link Shape} is widened according
207+
* to the parameters specified by the {@link BasicStroke} object.
208+
* Adjustments are made to the path as appropriate for the
209+
* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
210+
* {@code normalize} boolean parameter is true.
211+
* Adjustments are made to the path as appropriate for the
212+
* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
213+
* {@code antialias} boolean parameter is true.
214+
* <p>
215+
* The geometry of the widened path is forwarded to the indicated
216+
* {@link PathConsumer2D} object as it is calculated.
217+
*
218+
* @param src the source path to be widened
219+
* @param at the transform to be applied to the shape and the
220+
* stroke attributes
221+
* @param clip the current clip in effect in device coordinates
222+
* @param bs the {@code BasicStroke} object specifying the
223+
* decorations to be applied to the widened path
224+
* @param thin true if the transformed stroke attributes are smaller
225+
* than the minimum dropout pen width
226+
* @param normalize indicates whether stroke normalization should
227+
* be applied
228+
* @param antialias indicates whether or not adjustments appropriate
229+
* to antialiased rendering should be applied
230+
* @param consumer the {@code PathConsumer2D} instance to forward
231+
* the widened geometry to
232+
* @since 17
233+
*/
234+
/* @Override (only for 17+) */
235+
public void strokeTo(Shape src,
236+
AffineTransform at,
237+
Region clip,
238+
BasicStroke bs,
239+
boolean thin,
240+
boolean normalize,
241+
boolean antialias,
242+
final PathConsumer2D consumer)
243+
{
244+
// Test if at is identity:
245+
final AffineTransform _at = (at != null && !at.isIdentity()) ? at
246+
: null;
247+
195248
final NormMode norm = (normalize) ?
196249
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
197250
: NormMode.OFF;
198251

199252
final DRendererContext rdrCtx = getRendererContext();
200253
try {
201-
strokeTo(rdrCtx, src, at, bs, thin, norm, antialias,
254+
if ((clip != null) &&
255+
(DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) {
256+
// Define the initial clip bounds:
257+
final double[] clipRect = rdrCtx.clipRect;
258+
259+
// Adjust the clipping rectangle with the renderer offsets
260+
final double rdrOffX = 0.25d; // LBO: is it correct for AA or non AA cases ?
261+
final double rdrOffY = 0.25d; // see NearestPixelQuarter (depends on normalization ?)
262+
263+
// add a small rounding error:
264+
final double margin = 1e-3d;
265+
266+
clipRect[0] = clip.getLoY()
267+
- margin + rdrOffY;
268+
clipRect[1] = clip.getLoY() + clip.getHeight()
269+
+ margin + rdrOffY;
270+
clipRect[2] = clip.getLoX()
271+
- margin + rdrOffX;
272+
clipRect[3] = clip.getLoX() + clip.getWidth()
273+
+ margin + rdrOffX;
274+
275+
if (MarlinConst.DO_LOG_CLIP) {
276+
MarlinUtils.logInfo("clipRect (clip): "
277+
+ Arrays.toString(rdrCtx.clipRect));
278+
}
279+
280+
// Enable clipping:
281+
rdrCtx.doClip = true;
282+
}
283+
284+
strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias,
202285
rdrCtx.p2dAdapter.init(consumer));
203286
} finally {
204287
// recycle the DRendererContext instance

src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -173,8 +173,12 @@ public Shape createStrokedShape(Shape src,
173173
* {@link PathConsumer2D} object as it is calculated.
174174
*
175175
* @param src the source path to be widened
176-
* @param bs the {@code BasicSroke} object specifying the
176+
* @param at the transform to be applied to the shape and the
177+
* stroke attributes
178+
* @param bs the {@code BasicStroke} object specifying the
177179
* decorations to be applied to the widened path
180+
* @param thin true if the transformed stroke attributes are smaller
181+
* than the minimum dropout pen width
178182
* @param normalize indicates whether stroke normalization should
179183
* be applied
180184
* @param antialias indicates whether or not adjustments appropriate
@@ -192,13 +196,92 @@ public void strokeTo(Shape src,
192196
boolean antialias,
193197
final PathConsumer2D consumer)
194198
{
199+
strokeTo(src, at, null, bs, thin, normalize, antialias, consumer);
200+
}
201+
202+
/**
203+
* Sends the geometry for a widened path as specified by the parameters
204+
* to the specified consumer.
205+
* <p>
206+
* The specified {@code src} {@link Shape} is widened according
207+
* to the parameters specified by the {@link BasicStroke} object.
208+
* Adjustments are made to the path as appropriate for the
209+
* {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
210+
* {@code normalize} boolean parameter is true.
211+
* Adjustments are made to the path as appropriate for the
212+
* {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the
213+
* {@code antialias} boolean parameter is true.
214+
* <p>
215+
* The geometry of the widened path is forwarded to the indicated
216+
* {@link PathConsumer2D} object as it is calculated.
217+
*
218+
* @param src the source path to be widened
219+
* @param at the transform to be applied to the shape and the
220+
* stroke attributes
221+
* @param clip the current clip in effect in device coordinates
222+
* @param bs the {@code BasicStroke} object specifying the
223+
* decorations to be applied to the widened path
224+
* @param thin true if the transformed stroke attributes are smaller
225+
* than the minimum dropout pen width
226+
* @param normalize indicates whether stroke normalization should
227+
* be applied
228+
* @param antialias indicates whether or not adjustments appropriate
229+
* to antialiased rendering should be applied
230+
* @param consumer the {@code PathConsumer2D} instance to forward
231+
* the widened geometry to
232+
* @since 17
233+
*/
234+
/* @Override (only for 17+) */
235+
public void strokeTo(Shape src,
236+
AffineTransform at,
237+
Region clip,
238+
BasicStroke bs,
239+
boolean thin,
240+
boolean normalize,
241+
boolean antialias,
242+
final PathConsumer2D consumer)
243+
{
244+
// Test if at is identity:
245+
final AffineTransform _at = (at != null && !at.isIdentity()) ? at
246+
: null;
247+
195248
final NormMode norm = (normalize) ?
196249
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
197250
: NormMode.OFF;
198251

199252
final RendererContext rdrCtx = getRendererContext();
200253
try {
201-
strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer);
254+
if ((clip != null) &&
255+
(DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime()))) {
256+
// Define the initial clip bounds:
257+
final float[] clipRect = rdrCtx.clipRect;
258+
259+
// Adjust the clipping rectangle with the renderer offsets
260+
final float rdrOffX = 0.25f; // LBO: is it correct for AA or non AA cases ?
261+
final float rdrOffY = 0.25f; // see NearestPixelQuarter (depends on normalization ?)
262+
263+
// add a small rounding error:
264+
final float margin = 1e-3f;
265+
266+
clipRect[0] = clip.getLoY()
267+
- margin + rdrOffY;
268+
clipRect[1] = clip.getLoY() + clip.getHeight()
269+
+ margin + rdrOffY;
270+
clipRect[2] = clip.getLoX()
271+
- margin + rdrOffX;
272+
clipRect[3] = clip.getLoX() + clip.getWidth()
273+
+ margin + rdrOffX;
274+
275+
if (MarlinConst.DO_LOG_CLIP) {
276+
MarlinUtils.logInfo("clipRect (clip): "
277+
+ Arrays.toString(rdrCtx.clipRect));
278+
}
279+
280+
// Enable clipping:
281+
rdrCtx.doClip = true;
282+
}
283+
284+
strokeTo(rdrCtx, src, _at, bs, thin, norm, antialias, consumer);
202285
} finally {
203286
// recycle the RendererContext instance
204287
returnRendererContext(rdrCtx);

src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -880,6 +880,7 @@ public long getNativeConsumer() {
880880
}
881881
}
882882

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

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

900901
// clip rectangle (ymin, ymax, xmin, xmax) including padding:
901-
final float[] clipRectPad = new float[4];
902+
final double[] clipRectPad = new double[4];
902903
private boolean init_clipRectPad = false;
903904

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

910911
// dirty curve
911-
private final Curve curve;
912+
private final DCurve curve;
912913

913914
CurveClipSplitter(final RendererContext rdrCtx) {
914915
this.rdrCtx = rdrCtx;
915916
this.clipRect = rdrCtx.clipRect;
916-
this.curve = rdrCtx.curve;
917+
this.curve = /* rdrCtx.curve */ new DCurve(); // double-precision curve
917918
}
918919

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

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

964-
final float[] mid = middle;
965+
final double[] mid = middle;
965966
mid[0] = x0; mid[1] = y0;
966967
mid[2] = x1; mid[3] = y1;
967968

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

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

1008-
final float[] mid = middle;
1009+
final double[] mid = middle;
10091010
mid[0] = x0; mid[1] = y0;
10101011
mid[2] = x1; mid[3] = y1;
10111012
mid[4] = x2; mid[5] = y2;
@@ -1017,15 +1018,15 @@ boolean splitCurve(final float x0, final float y0,
10171018
private boolean subdivideAtIntersections(final int type, final int outCodeOR,
10181019
final PathConsumer2D out)
10191020
{
1020-
final float[] mid = middle;
1021-
final float[] subTs = subdivTs;
1021+
final double[] mid = middle;
1022+
final double[] subTs = subdivTs;
10221023

10231024
if (init_clipRectPad) {
10241025
init_clipRectPad = false;
10251026
initPaddedClip();
10261027
}
10271028

1028-
final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
1029+
final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
10291030
outCodeOR, clipRectPad);
10301031

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

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

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

1058-
static void emitCurrent(final int type, final float[] pts,
1059+
static void emitCurrent(final int type, final double[] pts,
10591060
final int off, final PathConsumer2D out)
10601061
{
10611062
// if instead of switch (perf + most probable cases first)
10621063
if (type == 8) {
1063-
out.curveTo(pts[off + 2], pts[off + 3],
1064-
pts[off + 4], pts[off + 5],
1065-
pts[off + 6], pts[off + 7]);
1064+
out.curveTo((float)pts[off + 2], (float)pts[off + 3],
1065+
(float)pts[off + 4], (float)pts[off + 5],
1066+
(float)pts[off + 6], (float)pts[off + 7]);
10661067
} else if (type == 4) {
1067-
out.lineTo(pts[off + 2], pts[off + 3]);
1068+
out.lineTo((float)pts[off + 2], (float)pts[off + 3]);
10681069
} else {
1069-
out.quadTo(pts[off + 2], pts[off + 3],
1070-
pts[off + 4], pts[off + 5]);
1070+
out.quadTo((float)pts[off + 2], (float)pts[off + 3],
1071+
(float)pts[off + 4], (float)pts[off + 5]);
10711072
}
10721073
}
10731074
}

0 commit comments

Comments
 (0)