67 },
68 OFF{
69 @Override
70 PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
71 final PathIterator src)
72 {
73 // return original path iterator if normalization is disabled:
74 return src;
75 }
76 };
77
78 abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
79 PathIterator src);
80 }
81
82 private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
83
84 static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
85 static final double LOWER_BND = -UPPER_BND;
86
87 /**
88 * Public constructor
89 */
90 public DMarlinRenderingEngine() {
91 super();
92 logSettings(DMarlinRenderingEngine.class.getName());
93 }
94
95 /**
96 * Create a widened path as specified by the parameters.
97 * <p>
98 * The specified {@code src} {@link Shape} is widened according
99 * to the specified attribute parameters as per the
100 * {@link BasicStroke} specification.
101 *
102 * @param src the source path to be widened
103 * @param width the width of the widened path as per {@code BasicStroke}
104 * @param caps the end cap decorations as per {@code BasicStroke}
105 * @param join the segment join decorations as per {@code BasicStroke}
106 * @param miterlimit the miter limit as per {@code BasicStroke}
307 float dashphase,
308 DPathConsumer2D pc2d)
309 {
310 // We use strokerat so that in Stroker and Dasher we can work only
311 // with the pre-transformation coordinates. This will repeat a lot of
312 // computations done in the path iterator, but the alternative is to
313 // work with transformed paths and compute untransformed coordinates
314 // as needed. This would be faster but I do not think the complexity
315 // of working with both untransformed and transformed coordinates in
316 // the same code is worth it.
317 // However, if a path's width is constant after a transformation,
318 // we can skip all this untransforming.
319
320 // As pathTo() will check transformed coordinates for invalid values
321 // (NaN / Infinity) to ignore such points, it is necessary to apply the
322 // transformation before the path processing.
323 AffineTransform strokerat = null;
324
325 int dashLen = -1;
326 boolean recycleDashes = false;
327 double[] dashesD = null;
328
329 // Ensure converting dashes to double precision:
330 if (dashes != null) {
331 recycleDashes = true;
332 dashLen = dashes.length;
333 dashesD = rdrCtx.dasher.copyDashArray(dashes);
334 }
335
336 if (at != null && !at.isIdentity()) {
337 final double a = at.getScaleX();
338 final double b = at.getShearX();
339 final double c = at.getShearY();
340 final double d = at.getScaleY();
341 final double det = a * d - c * b;
342
343 if (Math.abs(det) <= (2.0d * Double.MIN_VALUE)) {
344 // this rendering engine takes one dimensional curves and turns
345 // them into 2D shapes by giving them width.
346 // However, if everything is to be passed through a singular
347 // transformation, these 2D shapes will be squashed down to 1D
348 // again so, nothing can be drawn.
349
350 // Every path needs an initial moveTo and a pathDone. If these
351 // are not there this causes a SIGSEGV in libawt.so (at the time
352 // of writing of this comment (September 16, 2010)). Actually,
353 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
354 // but the pathDone is definitely needed.
355 pc2d.moveTo(0.0d, 0.0d);
356 pc2d.pathDone();
357 return;
358 }
359
360 // If the transform is a constant multiple of an orthogonal transformation
361 // then every length is just multiplied by a constant, so we just
362 // need to transform input paths to stroker and tell stroker
363 // the scaled width. This condition is satisfied if
364 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
365 // leave a bit of room for error.
366 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
367 final double scale = Math.sqrt(a*a + c*c);
368
369 if (dashesD != null) {
370 for (int i = 0; i < dashLen; i++) {
371 dashesD[i] *= scale;
372 }
373 dashphase *= scale;
374 }
375 width *= scale;
376
377 // by now strokerat == null. Input paths to
378 // stroker (and maybe dasher) will have the full transform at
379 // applied to them and nothing will happen to the output paths.
380 } else {
381 strokerat = at;
382
383 // by now strokerat == at. Input paths to
384 // stroker (and maybe dasher) will have the full transform at
385 // applied to them, then they will be normalized, and then
386 // the inverse of *only the non translation part of at* will
387 // be applied to the normalized paths. This won't cause problems
388 // in stroker, because, suppose at = T*A, where T is just the
389 // translation part of at, and A is the rest. T*A has already
390 // been applied to Stroker/Dasher's input. Then Ainv will be
391 // applied. Ainv*T*A is not equal to T, but it is a translation,
392 // which means that none of stroker's assumptions about its
393 // input will be violated. After all this, A will be applied
394 // to stroker's output.
395 }
396 } else {
397 // either at is null or it's the identity. In either case
398 // we don't transform the path.
399 at = null;
400 }
401
402 if (USE_SIMPLIFIER) {
403 // Use simplifier after stroker before Renderer
404 // to remove collinear segments (notably due to cap square)
405 pc2d = rdrCtx.simplifier.init(pc2d);
406 }
407
408 final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
409 pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
410
411 pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
412
413 if (dashesD != null) {
414 pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
415 recycleDashes);
416 }
417 pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
418
419 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
420 src.getPathIterator(at));
421
422 pathTo(rdrCtx, pi, pc2d);
423
424 /*
425 * Pipeline seems to be:
426 * shape.getPathIterator(at)
427 * -> (NormalizingPathIterator)
428 * -> (inverseDeltaTransformConsumer)
429 * -> (Dasher)
430 * -> Stroker
431 * -> (deltaTransformConsumer)
432 *
433 * -> (CollinearSimplifier) to remove redundant segments
434 *
435 * -> pc2d = Renderer (bounding box)
436 */
437 }
438
788 final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
789
790 if (bs == null) {
791 // fill shape:
792 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
793 s.getPathIterator(_at));
794
795 // note: Winding rule may be EvenOdd ONLY for fill operations !
796 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
797 clip.getWidth(), clip.getHeight(),
798 pi.getWindingRule());
799
800 // TODO: subdivide quad/cubic curves into monotonic curves ?
801 pathTo(rdrCtx, pi, r);
802 } else {
803 // draw shape with given stroke:
804 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
805 clip.getWidth(), clip.getHeight(),
806 PathIterator.WIND_NON_ZERO);
807
808 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
809 }
810 if (r.endRendering()) {
811 ptg = rdrCtx.ptg.init();
812 ptg.getBbox(bbox);
813 // note: do not returnRendererContext(rdrCtx)
814 // as it will be called later by MarlinTileGenerator.dispose()
815 r = null;
816 }
817 } finally {
818 if (r != null) {
819 // dispose renderer and recycle the RendererContext instance:
820 r.dispose();
821 }
822 }
823
824 // Return null to cancel AA tile generation (nothing to render)
825 return ptg;
826 }
827
845 x -= (ldx1 + ldx2) / 2.0d;
846 y -= (ldy1 + ldy2) / 2.0d;
847 dx1 += ldx1;
848 dy1 += ldy1;
849 dx2 += ldx2;
850 dy2 += ldy2;
851 if (lw1 > 1.0d && lw2 > 1.0d) {
852 // Inner parallelogram was entirely consumed by stroke...
853 innerpgram = false;
854 }
855 } else {
856 ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
857 }
858
859 MarlinTileGenerator ptg = null;
860 DRenderer r = null;
861
862 final DRendererContext rdrCtx = getRendererContext();
863 try {
864 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
865 clip.getWidth(), clip.getHeight(),
866 DRenderer.WIND_EVEN_ODD);
867
868 r.moveTo( x, y);
869 r.lineTo( (x+dx1), (y+dy1));
870 r.lineTo( (x+dx1+dx2), (y+dy1+dy2));
871 r.lineTo( (x+dx2), (y+dy2));
872 r.closePath();
873
874 if (innerpgram) {
875 x += ldx1 + ldx2;
876 y += ldy1 + ldy2;
877 dx1 -= 2.0d * ldx1;
878 dy1 -= 2.0d * ldy1;
879 dx2 -= 2.0d * ldx2;
880 dy2 -= 2.0d * ldy2;
881 r.moveTo( x, y);
882 r.lineTo( (x+dx1), (y+dy1));
883 r.lineTo( (x+dx1+dx2), (y+dy1+dy2));
884 r.lineTo( (x+dx2), (y+dy2));
885 r.closePath();
886 }
1027 + MarlinConst.TILE_W_LG);
1028 logInfo("sun.java2d.renderer.blockSize_log2 = "
1029 + MarlinConst.BLOCK_SIZE_LG);
1030
1031 // RLE / blockFlags settings
1032
1033 logInfo("sun.java2d.renderer.forceRLE = "
1034 + MarlinProperties.isForceRLE());
1035 logInfo("sun.java2d.renderer.forceNoRLE = "
1036 + MarlinProperties.isForceNoRLE());
1037 logInfo("sun.java2d.renderer.useTileFlags = "
1038 + MarlinProperties.isUseTileFlags());
1039 logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1040 + MarlinProperties.isUseTileFlagsWithHeuristics());
1041 logInfo("sun.java2d.renderer.rleMinWidth = "
1042 + MarlinCache.RLE_MIN_WIDTH);
1043
1044 // optimisation parameters
1045 logInfo("sun.java2d.renderer.useSimplifier = "
1046 + MarlinConst.USE_SIMPLIFIER);
1047
1048 // debugging parameters
1049 logInfo("sun.java2d.renderer.doStats = "
1050 + MarlinConst.DO_STATS);
1051 logInfo("sun.java2d.renderer.doMonitors = "
1052 + MarlinConst.DO_MONITORS);
1053 logInfo("sun.java2d.renderer.doChecks = "
1054 + MarlinConst.DO_CHECKS);
1055
1056 // logging parameters
1057 logInfo("sun.java2d.renderer.useLogger = "
1058 + MarlinConst.USE_LOGGER);
1059 logInfo("sun.java2d.renderer.logCreateContext = "
1060 + MarlinConst.LOG_CREATE_CONTEXT);
1061 logInfo("sun.java2d.renderer.logUnsafeMalloc = "
1062 + MarlinConst.LOG_UNSAFE_MALLOC);
1063
1064 // quality settings
1065 logInfo("sun.java2d.renderer.cubic_dec_d2 = "
1066 + MarlinProperties.getCubicDecD2());
|
67 },
68 OFF{
69 @Override
70 PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
71 final PathIterator src)
72 {
73 // return original path iterator if normalization is disabled:
74 return src;
75 }
76 };
77
78 abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
79 PathIterator src);
80 }
81
82 private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
83
84 static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
85 static final double LOWER_BND = -UPPER_BND;
86
87 static final boolean DO_CLIP = MarlinProperties.isDoClip();
88 static final boolean DO_TRACE = false;
89
90 /**
91 * Public constructor
92 */
93 public DMarlinRenderingEngine() {
94 super();
95 logSettings(DMarlinRenderingEngine.class.getName());
96 }
97
98 /**
99 * Create a widened path as specified by the parameters.
100 * <p>
101 * The specified {@code src} {@link Shape} is widened according
102 * to the specified attribute parameters as per the
103 * {@link BasicStroke} specification.
104 *
105 * @param src the source path to be widened
106 * @param width the width of the widened path as per {@code BasicStroke}
107 * @param caps the end cap decorations as per {@code BasicStroke}
108 * @param join the segment join decorations as per {@code BasicStroke}
109 * @param miterlimit the miter limit as per {@code BasicStroke}
310 float dashphase,
311 DPathConsumer2D pc2d)
312 {
313 // We use strokerat so that in Stroker and Dasher we can work only
314 // with the pre-transformation coordinates. This will repeat a lot of
315 // computations done in the path iterator, but the alternative is to
316 // work with transformed paths and compute untransformed coordinates
317 // as needed. This would be faster but I do not think the complexity
318 // of working with both untransformed and transformed coordinates in
319 // the same code is worth it.
320 // However, if a path's width is constant after a transformation,
321 // we can skip all this untransforming.
322
323 // As pathTo() will check transformed coordinates for invalid values
324 // (NaN / Infinity) to ignore such points, it is necessary to apply the
325 // transformation before the path processing.
326 AffineTransform strokerat = null;
327
328 int dashLen = -1;
329 boolean recycleDashes = false;
330 double scale = 1.0d;
331 double[] dashesD = null;
332
333 // Ensure converting dashes to double precision:
334 if (dashes != null) {
335 recycleDashes = true;
336 dashLen = dashes.length;
337 dashesD = rdrCtx.dasher.copyDashArray(dashes);
338 }
339
340 if (at != null && !at.isIdentity()) {
341 final double a = at.getScaleX();
342 final double b = at.getShearX();
343 final double c = at.getShearY();
344 final double d = at.getScaleY();
345 final double det = a * d - c * b;
346
347 if (Math.abs(det) <= (2.0d * Double.MIN_VALUE)) {
348 // this rendering engine takes one dimensional curves and turns
349 // them into 2D shapes by giving them width.
350 // However, if everything is to be passed through a singular
351 // transformation, these 2D shapes will be squashed down to 1D
352 // again so, nothing can be drawn.
353
354 // Every path needs an initial moveTo and a pathDone. If these
355 // are not there this causes a SIGSEGV in libawt.so (at the time
356 // of writing of this comment (September 16, 2010)). Actually,
357 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
358 // but the pathDone is definitely needed.
359 pc2d.moveTo(0.0d, 0.0d);
360 pc2d.pathDone();
361 return;
362 }
363
364 // If the transform is a constant multiple of an orthogonal transformation
365 // then every length is just multiplied by a constant, so we just
366 // need to transform input paths to stroker and tell stroker
367 // the scaled width. This condition is satisfied if
368 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
369 // leave a bit of room for error.
370 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
371 scale = Math.sqrt(a*a + c*c);
372
373 if (dashesD != null) {
374 for (int i = 0; i < dashLen; i++) {
375 dashesD[i] *= scale;
376 }
377 dashphase *= scale;
378 }
379 width *= scale;
380
381 // by now strokerat == null. Input paths to
382 // stroker (and maybe dasher) will have the full transform at
383 // applied to them and nothing will happen to the output paths.
384 } else {
385 strokerat = at;
386
387 // by now strokerat == at. Input paths to
388 // stroker (and maybe dasher) will have the full transform at
389 // applied to them, then they will be normalized, and then
390 // the inverse of *only the non translation part of at* will
391 // be applied to the normalized paths. This won't cause problems
392 // in stroker, because, suppose at = T*A, where T is just the
393 // translation part of at, and A is the rest. T*A has already
394 // been applied to Stroker/Dasher's input. Then Ainv will be
395 // applied. Ainv*T*A is not equal to T, but it is a translation,
396 // which means that none of stroker's assumptions about its
397 // input will be violated. After all this, A will be applied
398 // to stroker's output.
399 }
400 } else {
401 // either at is null or it's the identity. In either case
402 // we don't transform the path.
403 at = null;
404 }
405
406 final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
407
408 if (DO_TRACE) {
409 // trace Stroker:
410 pc2d = transformerPC2D.traceStroker(pc2d);
411 }
412
413 if (USE_SIMPLIFIER) {
414 // Use simplifier after stroker before Renderer
415 // to remove collinear segments (notably due to cap square)
416 pc2d = rdrCtx.simplifier.init(pc2d);
417 }
418
419 // deltaTransformConsumer may adjust the clip rectangle:
420 pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
421
422 // stroker will adjust the clip rectangle (width / miter limit):
423 pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
424
425 if (dashesD != null) {
426 pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
427 recycleDashes);
428 } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
429 if (DO_TRACE) {
430 pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
431 }
432
433 // If no dash and clip is enabled:
434 // detect closedPaths (polygons) for caps
435 pc2d = transformerPC2D.detectClosedPath(pc2d);
436 }
437 pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
438
439 if (DO_TRACE) {
440 // trace Input:
441 pc2d = transformerPC2D.traceInput(pc2d);
442 }
443
444 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
445 src.getPathIterator(at));
446
447 pathTo(rdrCtx, pi, pc2d);
448
449 /*
450 * Pipeline seems to be:
451 * shape.getPathIterator(at)
452 * -> (NormalizingPathIterator)
453 * -> (inverseDeltaTransformConsumer)
454 * -> (Dasher)
455 * -> Stroker
456 * -> (deltaTransformConsumer)
457 *
458 * -> (CollinearSimplifier) to remove redundant segments
459 *
460 * -> pc2d = Renderer (bounding box)
461 */
462 }
463
813 final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
814
815 if (bs == null) {
816 // fill shape:
817 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
818 s.getPathIterator(_at));
819
820 // note: Winding rule may be EvenOdd ONLY for fill operations !
821 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
822 clip.getWidth(), clip.getHeight(),
823 pi.getWindingRule());
824
825 // TODO: subdivide quad/cubic curves into monotonic curves ?
826 pathTo(rdrCtx, pi, r);
827 } else {
828 // draw shape with given stroke:
829 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
830 clip.getWidth(), clip.getHeight(),
831 PathIterator.WIND_NON_ZERO);
832
833 if (DO_CLIP) {
834 // Define the initial clip bounds:
835 final double[] clipRect = rdrCtx.clipRect;
836 clipRect[0] = clip.getLoY();
837 clipRect[1] = clip.getLoY() + clip.getHeight();
838 clipRect[2] = clip.getLoX();
839 clipRect[3] = clip.getLoX() + clip.getWidth();
840
841 // Enable clipping:
842 rdrCtx.doClip = true;
843 }
844
845 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
846 }
847 if (r.endRendering()) {
848 ptg = rdrCtx.ptg.init();
849 ptg.getBbox(bbox);
850 // note: do not returnRendererContext(rdrCtx)
851 // as it will be called later by MarlinTileGenerator.dispose()
852 r = null;
853 }
854 } finally {
855 if (r != null) {
856 // dispose renderer and recycle the RendererContext instance:
857 r.dispose();
858 }
859 }
860
861 // Return null to cancel AA tile generation (nothing to render)
862 return ptg;
863 }
864
882 x -= (ldx1 + ldx2) / 2.0d;
883 y -= (ldy1 + ldy2) / 2.0d;
884 dx1 += ldx1;
885 dy1 += ldy1;
886 dx2 += ldx2;
887 dy2 += ldy2;
888 if (lw1 > 1.0d && lw2 > 1.0d) {
889 // Inner parallelogram was entirely consumed by stroke...
890 innerpgram = false;
891 }
892 } else {
893 ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
894 }
895
896 MarlinTileGenerator ptg = null;
897 DRenderer r = null;
898
899 final DRendererContext rdrCtx = getRendererContext();
900 try {
901 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
902 clip.getWidth(), clip.getHeight(),
903 DRenderer.WIND_EVEN_ODD);
904
905 r.moveTo( x, y);
906 r.lineTo( (x+dx1), (y+dy1));
907 r.lineTo( (x+dx1+dx2), (y+dy1+dy2));
908 r.lineTo( (x+dx2), (y+dy2));
909 r.closePath();
910
911 if (innerpgram) {
912 x += ldx1 + ldx2;
913 y += ldy1 + ldy2;
914 dx1 -= 2.0d * ldx1;
915 dy1 -= 2.0d * ldy1;
916 dx2 -= 2.0d * ldx2;
917 dy2 -= 2.0d * ldy2;
918 r.moveTo( x, y);
919 r.lineTo( (x+dx1), (y+dy1));
920 r.lineTo( (x+dx1+dx2), (y+dy1+dy2));
921 r.lineTo( (x+dx2), (y+dy2));
922 r.closePath();
923 }
1064 + MarlinConst.TILE_W_LG);
1065 logInfo("sun.java2d.renderer.blockSize_log2 = "
1066 + MarlinConst.BLOCK_SIZE_LG);
1067
1068 // RLE / blockFlags settings
1069
1070 logInfo("sun.java2d.renderer.forceRLE = "
1071 + MarlinProperties.isForceRLE());
1072 logInfo("sun.java2d.renderer.forceNoRLE = "
1073 + MarlinProperties.isForceNoRLE());
1074 logInfo("sun.java2d.renderer.useTileFlags = "
1075 + MarlinProperties.isUseTileFlags());
1076 logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1077 + MarlinProperties.isUseTileFlagsWithHeuristics());
1078 logInfo("sun.java2d.renderer.rleMinWidth = "
1079 + MarlinCache.RLE_MIN_WIDTH);
1080
1081 // optimisation parameters
1082 logInfo("sun.java2d.renderer.useSimplifier = "
1083 + MarlinConst.USE_SIMPLIFIER);
1084 logInfo("sun.java2d.renderer.clip = "
1085 + MarlinProperties.isDoClip());
1086
1087 // debugging parameters
1088 logInfo("sun.java2d.renderer.doStats = "
1089 + MarlinConst.DO_STATS);
1090 logInfo("sun.java2d.renderer.doMonitors = "
1091 + MarlinConst.DO_MONITORS);
1092 logInfo("sun.java2d.renderer.doChecks = "
1093 + MarlinConst.DO_CHECKS);
1094
1095 // logging parameters
1096 logInfo("sun.java2d.renderer.useLogger = "
1097 + MarlinConst.USE_LOGGER);
1098 logInfo("sun.java2d.renderer.logCreateContext = "
1099 + MarlinConst.LOG_CREATE_CONTEXT);
1100 logInfo("sun.java2d.renderer.logUnsafeMalloc = "
1101 + MarlinConst.LOG_UNSAFE_MALLOC);
1102
1103 // quality settings
1104 logInfo("sun.java2d.renderer.cubic_dec_d2 = "
1105 + MarlinProperties.getCubicDecD2());
|