[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-2d-dev
Subject:    Re: [OpenJDK 2D-Dev] RFR JDK-8184429: Path clipper added in Marlin2D & MarlinFX 0.8.0
From:       Laurent_Bourgès <bourges.laurent () gmail ! com>
Date:       2017-09-07 6:52:18
Message-ID: CAKjRUT5Ced1DM61rd6jgOpZeDwQJ+oTmOP8qsd8ms=3ULyJaiQ () mail ! gmail ! com
[Download RAW message or body]

Hi Kevin,

Ok I propose to withdraw or postpone this review after JavaOne where we
will be able to discuss in a face to face meeting about Marlin & MarlinFX
changes for JDK10.

I hope the 2d / jfx groups have other Graphics Guru to help, as good as Jim
Graham.

Cheers,
Laurent

Le 6 sept. 2017 16:23, "Kevin Rushforth" <kevin.rushforth@oracle.com> a
écrit :

> Hi Laurent,
>
> Some combination of Phil, Sergey, and I will take a look at this when we
> can. Perhaps there might be others on these two lists who could lend a
> helping hand?
>
> -- Kevin
>
>
> Laurent Bourgès wrote:
>
>> Hi all,
>>
>> As Jim is no more available to review & answer my questions on java2d /
>> computer graphics, I need another reviewer on this webrev implementing
>> path
>> clipping in Marlin (huge potential gains).
>>
>> Do you know someone else who can help me in the 2d / prism fields to
>> improve the Marlin renderer even more ?
>>
>> Thanks,
>> Laurent
>>
>>
>> Le 5 sept. 2017 22:41, "Laurent Bourgès" <bourges.laurent@gmail.com> a
>> écrit :
>>
>>
>>
>>> Hi Jim,
>>>
>>> I made good progress on my PathClipFilter that works now perfectly with
>>> many test maps for the NZ rule (EO has artefacts if I enable the filter
>>> in
>>> that case).
>>>
>>> Here is the updated code below to illustrate the approach:
>>> - use a new IndexStack in (D)Helpers to store only corner indexes (0/1
>>> for
>>> Top/Bottom Left, 2/3 for Top/Bottom Right)
>>> - when the segment is out, I now check (L/R case) if the segment ends
>>> have
>>> different outcodes to insert needed corners that can be removed later if
>>> a
>>> segment does the same in the reverse order (same repeated corner is
>>> cancelled out): see IndexStack.push(int)
>>>
>>> - PathClipFilter:
>>>
>>>     static final class PathClipFilter implements DPathConsumer2D {
>>>
>>>         private DPathConsumer2D out;
>>>
>>>         // Bounds of the drawing region, at pixel precision.
>>>         private final double[] clipRect;
>>>
>>>         private final double[] corners = new double[8];
>>>         private boolean init_corners = false;
>>>
>>>         private final IndexStack stack;
>>>
>>>         // the outcode of the starting point
>>>         private int sOutCode = 0;
>>>
>>>         // the current outcode of the current sub path
>>>         private int cOutCode = 0;
>>>
>>>         private boolean outside = false;
>>>         private double cx0, cy0;
>>>
>>>         PathClipFilter(final DRendererContext rdrCtx) {
>>>             this.clipRect = rdrCtx.clipRect;
>>>             this.stack = (rdrCtx.stats != null) ?
>>>                 new IndexStack(rdrCtx,
>>>                         rdrCtx.stats.stat_pcf_idxstack_indices,
>>>                         rdrCtx.stats.hist_pcf_idxstack_indices,
>>>                         rdrCtx.stats.stat_array_pcf_idxstack_indices)
>>>                 : new IndexStack(rdrCtx);
>>>         }
>>>
>>>         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;
>>>
>>>             final double[] _clipRect = this.clipRect;
>>>             _clipRect[0] -= margin - rdrOffY;
>>>             _clipRect[1] += margin + rdrOffY;
>>>             _clipRect[2] -= margin - rdrOffX;
>>>             _clipRect[3] += margin + rdrOffX;
>>>
>>>             init_corners = true;
>>>
>>>             return this; // fluent API
>>>         }
>>>
>>>         /**
>>>          * Disposes this instance:
>>>          * clean up before reusing this instance
>>>          */
>>>         void dispose() {
>>>             stack.dispose();
>>>         }
>>>
>>>         @Override
>>>         public void pathDone() {
>>>             out.pathDone();
>>>
>>>             // TODO: fix possible leak if exception happened
>>>             // Dispose this instance:
>>>             dispose();
>>>         }
>>>
>>>         @Override
>>>         public void closePath() {
>>>             if (outside) {
>>>                 this.outside = false;
>>>
>>>                 if (sOutCode == 0) {
>>>                     finish();
>>>                 } else {
>>>                     stack.reset();
>>>                 }
>>>             }
>>>             out.closePath();
>>>             this.cOutCode = sOutCode;
>>>         }
>>>
>>>         private void finish() {
>>>             if (!stack.isEmpty()) {
>>>                 if (init_corners) {
>>>                     init_corners = false;
>>>                     // Top Left (0):
>>>                     corners[0] = clipRect[2];
>>>                     corners[1] = clipRect[0];
>>>                     // Bottom Left (1):
>>>                     corners[2] = clipRect[2];
>>>                     corners[3] = clipRect[1];
>>>                     // Top right (2):
>>>                     corners[4] = clipRect[3];
>>>                     corners[5] = clipRect[0];
>>>                     // Bottom Right (3):
>>>                     corners[6] = clipRect[3];
>>>                     corners[7] = clipRect[1];
>>>                 }
>>>                 stack.pullAll(corners, out);
>>>             }
>>>             out.lineTo(cx0, cy0);
>>>         }
>>>
>>>         @Override
>>>         public void moveTo(final double x0, final double y0) {
>>>             final int outcode = DHelpers.outcode(x0, y0, clipRect);
>>>             this.sOutCode = outcode;
>>>             this.cOutCode = outcode;
>>>             this.outside = false;
>>>             out.moveTo(x0, y0);
>>>         }
>>>
>>>         @Override
>>>         public void lineTo(final double xe, final double ye) {
>>>             final int outcode0 = this.cOutCode;
>>>             final int outcode1 = DHelpers.outcode(xe, ye, clipRect);
>>>             this.cOutCode = outcode1;
>>>
>>>             final int sideCode = (outcode0 & outcode1);
>>>
>>>             // basic rejection criteria:
>>>             if (sideCode != 0) {
>>>                 // keep last point coordinate before entering the clip
>>> again:
>>>                 this.outside = true;
>>>                 this.cx0 = xe;
>>>                 this.cy0 = ye;
>>>
>>>                 clip(sideCode, outcode0, outcode1);
>>>                 return;
>>>             }
>>>             if (outside) {
>>>                 this.outside = false;
>>>                 finish();
>>>             }
>>>             // clipping disabled:
>>>             out.lineTo(xe, ye);
>>>         }
>>>
>>>         private void clip(final int sideCode,
>>>                           final int outcode0,
>>>                           final int outcode1)
>>>         {
>>>             // corner or cross-boundary on left or right side:
>>>             if ((outcode0 != outcode1)
>>>                     && ((sideCode & DHelpers.OUTCODE_MASK_T_B) != 0))
>>>             {
>>>                 // combine outcodes:
>>>                 final int mergeCode = (outcode0 | outcode1);
>>>                 final int tbCode = mergeCode & DHelpers.OUTCODE_MASK_T_B;
>>>                 final int lrCode = mergeCode & DHelpers.OUTCODE_MASK_L_R;
>>>                 // add corners to outside stack:
>>>                 final int off = (lrCode == DHelpers.OUTCODE_LEFT) ? 0 :
>>> 2;
>>>
>>>                 switch (tbCode) {
>>>                     case DHelpers.OUTCODE_TOP:
>>>                         stack.push(off); // top
>>>                         return;
>>>                     case DHelpers.OUTCODE_BOTTOM:
>>>                         stack.push(off + 1); // bottom
>>>                         return;
>>>                     default:
>>>                         // both TOP / BOTTOM:
>>>                         if ((outcode0 & DHelpers.OUTCODE_TOP) != 0) {
>>>                             // top to bottom
>>>                             stack.push(off); // top
>>>                             stack.push(off + 1); // bottom
>>>                         } else {
>>>                             // bottom to top
>>>                             stack.push(off + 1); // bottom
>>>                             stack.push(off); // top
>>>                         }
>>>                 }
>>>             }
>>>         }
>>>
>>>         @Override
>>>         public void curveTo(final double x1, final double y1,
>>>                             final double x2, final double y2,
>>>                             final double xe, final double ye)
>>>         {
>>>             final int outcode0 = this.cOutCode;
>>>             final int outcode3 = DHelpers.outcode(xe, ye, clipRect);
>>>             this.cOutCode = outcode3;
>>>
>>>             int sideCode = outcode0 & outcode3;
>>>
>>>             if (sideCode != 0) {
>>>                 sideCode &= DHelpers.outcode(x1, y1, clipRect);
>>>                 sideCode &= DHelpers.outcode(x2, y2, clipRect);
>>>
>>>                 // basic rejection criteria:
>>>                 if (sideCode != 0) {
>>>                     // keep last point coordinate before entering the
>>> clip
>>> again:
>>>                     this.outside = true;
>>>                     this.cx0 = xe;
>>>                     this.cy0 = ye;
>>>
>>>                     clip(sideCode, outcode0, outcode3);
>>>                     return;
>>>                 }
>>>             }
>>>             if (outside) {
>>>                 this.outside = false;
>>>                 finish();
>>>             }
>>>             // clipping disabled:
>>>             out.curveTo(x1, y1, x2, y2, xe, ye);
>>>         }
>>>
>>>         @Override
>>>         public void quadTo(final double x1, final double y1,
>>>                            final double xe, final double ye)
>>>         {
>>>             final int outcode0 = this.cOutCode;
>>>             final int outcode2 = DHelpers.outcode(xe, ye, clipRect);
>>>             this.cOutCode = outcode2;
>>>
>>>             int sideCode = outcode0 & outcode2;
>>>
>>>             if (outcode2 != 0) {
>>>                 sideCode &= DHelpers.outcode(x1, y1, clipRect);
>>>
>>>                 // basic rejection criteria:
>>>                 if (sideCode != 0) {
>>>                     // keep last point coordinate before entering the
>>> clip
>>> again:
>>>                     this.outside = true;
>>>                     this.cx0 = xe;
>>>                     this.cy0 = ye;
>>>
>>>                     clip(sideCode, outcode0, outcode2);
>>>                     return;
>>>                 }
>>>             }
>>>             if (outside) {
>>>                 this.outside = false;
>>>                 finish();
>>>             }
>>>             // clipping disabled:
>>>             out.quadTo(x1, y1, xe, ye);
>>>         }
>>>
>>>         @Override
>>>         public long getNativeConsumer() {
>>>             throw new InternalError("Not using a native peer");
>>>         }
>>>     }
>>>
>>> - DHelpers.IndexStack:
>>>     // a stack of integer indices
>>>     static final class IndexStack {
>>>
>>>         // integer capacity = edges count / 4 ~ 1024
>>>         private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >>
>>> 2;
>>>
>>>         private int end;
>>>         private int[] indices;
>>>
>>>         // indices ref (dirty)
>>>         private final IntArrayCache.Reference indices_ref;
>>>
>>>         // used marks (stats only)
>>>         private int indicesUseMark;
>>>
>>>         private final StatLong stat_idxstack_indices;
>>>         private final Histogram hist_idxstack_indices;
>>>         private final StatLong stat_array_idxstack_indices;
>>>
>>>         IndexStack(final DRendererContext rdrCtx) {
>>>             this(rdrCtx, null, null, null);
>>>         }
>>>
>>>         IndexStack(final DRendererContext rdrCtx,
>>>                    final StatLong stat_idxstack_indices,
>>>                    final Histogram hist_idxstack_indices,
>>>                    final StatLong stat_array_idxstack_indices)
>>>         {
>>>             indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); //
>>> 4K
>>>             indices     = indices_ref.initial;
>>>             end = 0;
>>>
>>>             if (DO_STATS) {
>>>                 indicesUseMark = 0;
>>>             }
>>>             this.stat_idxstack_indices = stat_idxstack_indices;
>>>             this.hist_idxstack_indices = hist_idxstack_indices;
>>>             this.stat_array_idxstack_indices =
>>> stat_array_idxstack_indices;
>>>         }
>>>
>>>         /**
>>>          * Disposes this PolyStack:
>>>          * clean up before reusing this instance
>>>          */
>>>         void dispose() {
>>>             end = 0;
>>>
>>>             if (DO_STATS) {
>>>                 stat_idxstack_indices.add(indicesUseMark);
>>>                 hist_idxstack_indices.add(indicesUseMark);
>>>
>>>                 // reset marks
>>>                 indicesUseMark = 0;
>>>             }
>>>
>>>             // Return arrays:
>>>             // values is kept dirty
>>>             indices = indices_ref.putArray(indices);
>>>         }
>>>
>>>         boolean isEmpty() {
>>>             return (end == 0);
>>>         }
>>>
>>>         void reset() {
>>>             end = 0;
>>>         }
>>>
>>>         void push(final int v) {
>>>             // remove redundant values (reverse order):
>>>             int[] _values = indices;
>>>             final int nc = end;
>>>             if (nc != 0) {
>>>                 if (_values[nc - 1] == v) {
>>>                     // remove both duplicated values:
>>>                     end--;
>>>                     return;
>>>                 }
>>>             }
>>>             if (_values.length <= nc) {
>>>                 if (DO_STATS) {
>>>                     stat_array_idxstack_indices.add(nc + 1);
>>>                 }
>>>                 indices = _values = indices_ref.widenArray(_values, nc,
>>> nc + 1);
>>>             }
>>>             _values[end++] = v;
>>>
>>>             if (DO_STATS) {
>>>                 // update used marks:
>>>                 if (end > indicesUseMark) {
>>>                     indicesUseMark = end;
>>>                 }
>>>             }
>>>         }
>>>
>>>         void pullAll(final double[] points, final DPathConsumer2D io) {
>>>             final int nc = end;
>>>             if (nc == 0) {
>>>                 return;
>>>             }
>>>             final int[] _values = indices;
>>>
>>>             for (int i = 0, j; i < nc; i++) {
>>>                 j = _values[i] << 1;
>>>                 io.lineTo(points[j], points[j + 1]);
>>>             }
>>>             end = 0;
>>>         }
>>>     }
>>>
>>>
>>> Here is a screenshot illustrating the remaining paths in Renderer after
>>>
>>>
>>>> clipping a 4000x4000 spiral converted as stroked shape:
>>>> http://cr.openjdk.java.net/~lbourges/png/SpiralTest-dash-false.ser.png
>>>>
>>>>
>>>>
>>> Now all useless rounds are totally discarded from the path sent to the
>>> Renderer (removing lots of edges on the left/right sides)
>>>
>>>
>>>
>>>
>>>> clip off: ~ 145ms
>>>> clip on: ~ 106ms
>>>>
>>>>
>>>>
>>> clip on: ~ 68ms for this huge filled spiral ~ 50% faster
>>>
>>>
>>> Could you answer my previous email on EO questions ?
>>> How to deal with self intersections or is it possible to skip left
>>> segments in the EO case or not ?
>>> (I am a bit lost)
>>>
>>> I need a simple path to test clipping with the EO rule (redudant
>>> segments); any idea ?
>>>
>>> Cheers,
>>> Laurent
>>>
>>>
>>>
>>

[Attachment #3 (text/html)]

<div dir="auto">Hi Kevin,<div dir="auto"><br></div><div dir="auto">Ok I propose to \
withdraw or postpone this review after JavaOne where we will be able to discuss in a \
face to face meeting about Marlin &amp; MarlinFX changes for JDK10.</div><div \
dir="auto"><br></div><div dir="auto">I hope the 2d / jfx groups have other Graphics \
Guru to help, as good as Jim Graham.</div><div dir="auto"><br></div><div \
dir="auto">Cheers,</div><div dir="auto">Laurent</div></div><div \
class="gmail_extra"><br><div class="gmail_quote">Le 6 sept. 2017 16:23, &quot;Kevin \
Rushforth&quot; &lt;<a \
href="mailto:kevin.rushforth@oracle.com">kevin.rushforth@oracle.com</a>&gt; a écrit \
:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 \
.8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Laurent,<br> <br>
Some combination of Phil, Sergey, and I will take a look at this when we can. Perhaps \
there might be others on these two lists who could lend a helping hand?<br> <br>
-- Kevin<br>
<br>
<br>
Laurent Bourgès wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> Hi all,<br>
<br>
As Jim is no more available to review &amp; answer my questions on java2d /<br>
computer graphics, I need another reviewer on this webrev implementing path<br>
clipping in Marlin (huge potential gains).<br>
<br>
Do you know someone else who can help me in the 2d / prism fields to<br>
improve the Marlin renderer even more ?<br>
<br>
Thanks,<br>
Laurent<br>
<br>
<br>
Le 5 sept. 2017 22:41, &quot;Laurent Bourgès&quot; &lt;<a \
href="mailto:bourges.laurent@gmail.com" \
target="_blank">bourges.laurent@gmail.com</a>&gt; a<br> écrit :<br>
<br>
  <br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> Hi Jim,<br>
<br>
I made good progress on my PathClipFilter that works now perfectly with<br>
many test maps for the NZ rule (EO has artefacts if I enable the filter in<br>
that case).<br>
<br>
Here is the updated code below to illustrate the approach:<br>
- use a new IndexStack in (D)Helpers to store only corner indexes (0/1 for<br>
Top/Bottom Left, 2/3 for Top/Bottom Right)<br>
- when the segment is out, I now check (L/R case) if the segment ends have<br>
different outcodes to insert needed corners that can be removed later if a<br>
segment does the same in the reverse order (same repeated corner is<br>
cancelled out): see IndexStack.push(int)<br>
<br>
- PathClipFilter:<br>
<br>
    static final class PathClipFilter implements DPathConsumer2D {<br>
<br>
        private DPathConsumer2D out;<br>
<br>
        // Bounds of the drawing region, at pixel precision.<br>
        private final double[] clipRect;<br>
<br>
        private final double[] corners = new double[8];<br>
        private boolean init_corners = false;<br>
<br>
        private final IndexStack stack;<br>
<br>
        // the outcode of the starting point<br>
        private int sOutCode = 0;<br>
<br>
        // the current outcode of the current sub path<br>
        private int cOutCode = 0;<br>
<br>
        private boolean outside = false;<br>
        private double cx0, cy0;<br>
<br>
        PathClipFilter(final DRendererContext rdrCtx) {<br>
            this.clipRect = rdrCtx.clipRect;<br>
            this.stack = (rdrCtx.stats != null) ?<br>
                new IndexStack(rdrCtx,<br>
                        rdrCtx.stats.stat_pcf_idxstack<wbr>_indices,<br>
                        rdrCtx.stats.hist_pcf_idxstack<wbr>_indices,<br>
                        rdrCtx.stats.stat_array_pcf_id<wbr>xstack_indices)<br>
                : new IndexStack(rdrCtx);<br>
        }<br>
<br>
        PathClipFilter init(final DPathConsumer2D out) {<br>
            this.out = out;<br>
<br>
            // Adjust the clipping rectangle with the renderer offsets<br>
            final double rdrOffX = DRenderer.RDR_OFFSET_X;<br>
            final double rdrOffY = DRenderer.RDR_OFFSET_Y;<br>
<br>
            // add a small rounding error:<br>
            final double margin = 1e-3d;<br>
<br>
            final double[] _clipRect = this.clipRect;<br>
            _clipRect[0] -= margin - rdrOffY;<br>
            _clipRect[1] += margin + rdrOffY;<br>
            _clipRect[2] -= margin - rdrOffX;<br>
            _clipRect[3] += margin + rdrOffX;<br>
<br>
            init_corners = true;<br>
<br>
            return this; // fluent API<br>
        }<br>
<br>
        /**<br>
         * Disposes this instance:<br>
         * clean up before reusing this instance<br>
         */<br>
        void dispose() {<br>
            stack.dispose();<br>
        }<br>
<br>
        @Override<br>
        public void pathDone() {<br>
            out.pathDone();<br>
<br>
            // TODO: fix possible leak if exception happened<br>
            // Dispose this instance:<br>
            dispose();<br>
        }<br>
<br>
        @Override<br>
        public void closePath() {<br>
            if (outside) {<br>
                this.outside = false;<br>
<br>
                if (sOutCode == 0) {<br>
                    finish();<br>
                } else {<br>
                    stack.reset();<br>
                }<br>
            }<br>
            out.closePath();<br>
            this.cOutCode = sOutCode;<br>
        }<br>
<br>
        private void finish() {<br>
            if (!stack.isEmpty()) {<br>
                if (init_corners) {<br>
                    init_corners = false;<br>
                    // Top Left (0):<br>
                    corners[0] = clipRect[2];<br>
                    corners[1] = clipRect[0];<br>
                    // Bottom Left (1):<br>
                    corners[2] = clipRect[2];<br>
                    corners[3] = clipRect[1];<br>
                    // Top right (2):<br>
                    corners[4] = clipRect[3];<br>
                    corners[5] = clipRect[0];<br>
                    // Bottom Right (3):<br>
                    corners[6] = clipRect[3];<br>
                    corners[7] = clipRect[1];<br>
                }<br>
                stack.pullAll(corners, out);<br>
            }<br>
            out.lineTo(cx0, cy0);<br>
        }<br>
<br>
        @Override<br>
        public void moveTo(final double x0, final double y0) {<br>
            final int outcode = DHelpers.outcode(x0, y0, clipRect);<br>
            this.sOutCode = outcode;<br>
            this.cOutCode = outcode;<br>
            this.outside = false;<br>
            out.moveTo(x0, y0);<br>
        }<br>
<br>
        @Override<br>
        public void lineTo(final double xe, final double ye) {<br>
            final int outcode0 = this.cOutCode;<br>
            final int outcode1 = DHelpers.outcode(xe, ye, clipRect);<br>
            this.cOutCode = outcode1;<br>
<br>
            final int sideCode = (outcode0 &amp; outcode1);<br>
<br>
            // basic rejection criteria:<br>
            if (sideCode != 0) {<br>
                // keep last point coordinate before entering the clip<br>
again:<br>
                this.outside = true;<br>
                this.cx0 = xe;<br>
                this.cy0 = ye;<br>
<br>
                clip(sideCode, outcode0, outcode1);<br>
                return;<br>
            }<br>
            if (outside) {<br>
                this.outside = false;<br>
                finish();<br>
            }<br>
            // clipping disabled:<br>
            out.lineTo(xe, ye);<br>
        }<br>
<br>
        private void clip(final int sideCode,<br>
                          final int outcode0,<br>
                          final int outcode1)<br>
        {<br>
            // corner or cross-boundary on left or right side:<br>
            if ((outcode0 != outcode1)<br>
                    &amp;&amp; ((sideCode &amp; DHelpers.OUTCODE_MASK_T_B) != 0))<br>
            {<br>
                // combine outcodes:<br>
                final int mergeCode = (outcode0 | outcode1);<br>
                final int tbCode = mergeCode &amp; DHelpers.OUTCODE_MASK_T_B;<br>
                final int lrCode = mergeCode &amp; DHelpers.OUTCODE_MASK_L_R;<br>
                // add corners to outside stack:<br>
                final int off = (lrCode == DHelpers.OUTCODE_LEFT) ? 0 : 2;<br>
<br>
                switch (tbCode) {<br>
                    case DHelpers.OUTCODE_TOP:<br>
                        stack.push(off); // top<br>
                        return;<br>
                    case DHelpers.OUTCODE_BOTTOM:<br>
                        stack.push(off + 1); // bottom<br>
                        return;<br>
                    default:<br>
                        // both TOP / BOTTOM:<br>
                        if ((outcode0 &amp; DHelpers.OUTCODE_TOP) != 0) {<br>
                            // top to bottom<br>
                            stack.push(off); // top<br>
                            stack.push(off + 1); // bottom<br>
                        } else {<br>
                            // bottom to top<br>
                            stack.push(off + 1); // bottom<br>
                            stack.push(off); // top<br>
                        }<br>
                }<br>
            }<br>
        }<br>
<br>
        @Override<br>
        public void curveTo(final double x1, final double y1,<br>
                            final double x2, final double y2,<br>
                            final double xe, final double ye)<br>
        {<br>
            final int outcode0 = this.cOutCode;<br>
            final int outcode3 = DHelpers.outcode(xe, ye, clipRect);<br>
            this.cOutCode = outcode3;<br>
<br>
            int sideCode = outcode0 &amp; outcode3;<br>
<br>
            if (sideCode != 0) {<br>
                sideCode &amp;= DHelpers.outcode(x1, y1, clipRect);<br>
                sideCode &amp;= DHelpers.outcode(x2, y2, clipRect);<br>
<br>
                // basic rejection criteria:<br>
                if (sideCode != 0) {<br>
                    // keep last point coordinate before entering the clip<br>
again:<br>
                    this.outside = true;<br>
                    this.cx0 = xe;<br>
                    this.cy0 = ye;<br>
<br>
                    clip(sideCode, outcode0, outcode3);<br>
                    return;<br>
                }<br>
            }<br>
            if (outside) {<br>
                this.outside = false;<br>
                finish();<br>
            }<br>
            // clipping disabled:<br>
            out.curveTo(x1, y1, x2, y2, xe, ye);<br>
        }<br>
<br>
        @Override<br>
        public void quadTo(final double x1, final double y1,<br>
                           final double xe, final double ye)<br>
        {<br>
            final int outcode0 = this.cOutCode;<br>
            final int outcode2 = DHelpers.outcode(xe, ye, clipRect);<br>
            this.cOutCode = outcode2;<br>
<br>
            int sideCode = outcode0 &amp; outcode2;<br>
<br>
            if (outcode2 != 0) {<br>
                sideCode &amp;= DHelpers.outcode(x1, y1, clipRect);<br>
<br>
                // basic rejection criteria:<br>
                if (sideCode != 0) {<br>
                    // keep last point coordinate before entering the clip<br>
again:<br>
                    this.outside = true;<br>
                    this.cx0 = xe;<br>
                    this.cy0 = ye;<br>
<br>
                    clip(sideCode, outcode0, outcode2);<br>
                    return;<br>
                }<br>
            }<br>
            if (outside) {<br>
                this.outside = false;<br>
                finish();<br>
            }<br>
            // clipping disabled:<br>
            out.quadTo(x1, y1, xe, ye);<br>
        }<br>
<br>
        @Override<br>
        public long getNativeConsumer() {<br>
            throw new InternalError(&quot;Not using a native peer&quot;);<br>
        }<br>
    }<br>
<br>
- DHelpers.IndexStack:<br>
    // a stack of integer indices<br>
    static final class IndexStack {<br>
<br>
        // integer capacity = edges count / 4 ~ 1024<br>
        private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT &gt;&gt; 2;<br>
<br>
        private int end;<br>
        private int[] indices;<br>
<br>
        // indices ref (dirty)<br>
        private final IntArrayCache.Reference indices_ref;<br>
<br>
        // used marks (stats only)<br>
        private int indicesUseMark;<br>
<br>
        private final StatLong stat_idxstack_indices;<br>
        private final Histogram hist_idxstack_indices;<br>
        private final StatLong stat_array_idxstack_indices;<br>
<br>
        IndexStack(final DRendererContext rdrCtx) {<br>
            this(rdrCtx, null, null, null);<br>
        }<br>
<br>
        IndexStack(final DRendererContext rdrCtx,<br>
                   final StatLong stat_idxstack_indices,<br>
                   final Histogram hist_idxstack_indices,<br>
                   final StatLong stat_array_idxstack_indices)<br>
        {<br>
            indices_ref = rdrCtx.newDirtyIntArrayRef(INI<wbr>TIAL_COUNT); // 4K<br>
            indices     = indices_ref.initial;<br>
            end = 0;<br>
<br>
            if (DO_STATS) {<br>
                indicesUseMark = 0;<br>
            }<br>
            this.stat_idxstack_indices = stat_idxstack_indices;<br>
            this.hist_idxstack_indices = hist_idxstack_indices;<br>
            this.stat_array_idxstack_indic<wbr>es =<br>
stat_array_idxstack_indices;<br>
        }<br>
<br>
        /**<br>
         * Disposes this PolyStack:<br>
         * clean up before reusing this instance<br>
         */<br>
        void dispose() {<br>
            end = 0;<br>
<br>
            if (DO_STATS) {<br>
                stat_idxstack_indices.add(indi<wbr>cesUseMark);<br>
                hist_idxstack_indices.add(indi<wbr>cesUseMark);<br>
<br>
                // reset marks<br>
                indicesUseMark = 0;<br>
            }<br>
<br>
            // Return arrays:<br>
            // values is kept dirty<br>
            indices = indices_ref.putArray(indices);<br>
        }<br>
<br>
        boolean isEmpty() {<br>
            return (end == 0);<br>
        }<br>
<br>
        void reset() {<br>
            end = 0;<br>
        }<br>
<br>
        void push(final int v) {<br>
            // remove redundant values (reverse order):<br>
            int[] _values = indices;<br>
            final int nc = end;<br>
            if (nc != 0) {<br>
                if (_values[nc - 1] == v) {<br>
                    // remove both duplicated values:<br>
                    end--;<br>
                    return;<br>
                }<br>
            }<br>
            if (_values.length &lt;= nc) {<br>
                if (DO_STATS) {<br>
                    <a \
href="http://stat_array_idxstack_indices.ad">stat_array_idxstack_indices.ad</a><wbr>d(nc \
+ 1);<br>  }<br>
                indices = _values = indices_ref.widenArray(_values<wbr>, nc,<br>
nc + 1);<br>
            }<br>
            _values[end++] = v;<br>
<br>
            if (DO_STATS) {<br>
                // update used marks:<br>
                if (end &gt; indicesUseMark) {<br>
                    indicesUseMark = end;<br>
                }<br>
            }<br>
        }<br>
<br>
        void pullAll(final double[] points, final DPathConsumer2D io) {<br>
            final int nc = end;<br>
            if (nc == 0) {<br>
                return;<br>
            }<br>
            final int[] _values = indices;<br>
<br>
            for (int i = 0, j; i &lt; nc; i++) {<br>
                j = _values[i] &lt;&lt; 1;<br>
                io.lineTo(points[j], points[j + 1]);<br>
            }<br>
            end = 0;<br>
        }<br>
    }<br>
<br>
<br>
Here is a screenshot illustrating the remaining paths in Renderer after<br>
    <br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> clipping a 4000x4000 spiral converted as stroked shape:<br>
<a href="http://cr.openjdk.java.net/~lbourges/png/SpiralTest-dash-false.ser.png" \
rel="noreferrer" target="_blank">http://cr.openjdk.java.net/~lb<wbr>ourges/png/SpiralTest-dash-fal<wbr>se.ser.png</a><br>
 <br>
      <br>
</blockquote>
Now all useless rounds are totally discarded from the path sent to the<br>
Renderer (removing lots of edges on the left/right sides)<br>
<br>
<br>
    <br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> clip off: ~ 145ms<br>
clip on: ~ 106ms<br>
<br>
      <br>
</blockquote>
clip on: ~ 68ms for this huge filled spiral ~ 50% faster<br>
<br>
<br>
Could you answer my previous email on EO questions ?<br>
How to deal with self intersections or is it possible to skip left<br>
segments in the EO case or not ?<br>
(I am a bit lost)<br>
<br>
I need a simple path to test clipping with the EO rule (redudant<br>
segments); any idea ?<br>
<br>
Cheers,<br>
Laurent<br>
<br>
    <br>
</blockquote></blockquote>
</blockquote></div></div>



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic