< prev index next >

openjfx9/modules/javafx.graphics/src/main/java/com/sun/marlin/Dasher.java

Print this page




   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.marlin;
  27 
  28 import java.util.Arrays;
  29 import sun.awt.geom.PathConsumer2D;
  30 
  31 /**
  32  * The <code>Dasher</code> class takes a series of linear commands
  33  * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
  34  * <code>end</code>) and breaks them into smaller segments according to a
  35  * dash pattern array and a starting dash phase.
  36  *
  37  * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
  38  * short dash, whereas Pisces does not draw anything.  The PostScript
  39  * semantics are unclear.
  40  *
  41  */
  42 final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
  43 
  44     static final int REC_LIMIT = 4;
  45     static final float ERR = 0.01f;
  46     static final float MIN_T_INC = 1f / (1 << REC_LIMIT);
  47 





  48     private PathConsumer2D out;
  49     private float[] dash;
  50     private int dashLen;
  51     private float startPhase;
  52     private boolean startDashOn;
  53     private int startIdx;
  54 
  55     private boolean starting;
  56     private boolean needsMoveTo;
  57 
  58     private int idx;
  59     private boolean dashOn;
  60     private float phase;
  61 
  62     private float sx, sy;
  63     private float x0, y0;
  64 
  65     // temporary storage for the current curve
  66     private final float[] curCurvepts;
  67 


  86         dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
  87 
  88         firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
  89         firstSegmentsBuffer     = firstSegmentsBuffer_ref.initial;
  90 
  91         // we need curCurvepts to be able to contain 2 curves because when
  92         // dashing curves, we need to subdivide it
  93         curCurvepts = new float[8 * 2];
  94     }
  95 
  96     /**
  97      * Initialize the <code>Dasher</code>.
  98      *
  99      * @param out an output <code>PathConsumer2D</code>.
 100      * @param dash an array of <code>float</code>s containing the dash pattern
 101      * @param dashLen length of the given dash array
 102      * @param phase a <code>float</code> containing the dash phase
 103      * @param recycleDashes true to indicate to recycle the given dash array
 104      * @return this instance
 105      */
 106     Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
 107                 float phase, boolean recycleDashes)
 108     {
 109         if (phase < 0f) {
 110             throw new IllegalArgumentException("phase < 0 !");
 111         }
 112         this.out = out;
 113 
 114         // Normalize so 0 <= phase < dash[0]
 115         int idx = 0;
 116         dashOn = true;
 117         float d;
 118         while (phase >= (d = dash[idx])) {
 119             phase -= d;
 120             idx = (idx + 1) % dashLen;
 121             dashOn = !dashOn;

































 122         }
 123 
 124         this.dash = dash;
 125         this.dashLen = dashLen;
 126         this.startPhase = this.phase = phase;
 127         this.startDashOn = dashOn;
 128         this.startIdx = idx;
 129         this.starting = true;
 130         needsMoveTo = false;
 131         firstSegidx = 0;
 132 
 133         this.recycleDashes = recycleDashes;
 134 
 135         return this; // fluent API
 136     }
 137 
 138     /**
 139      * Disposes this dasher:
 140      * clean up before reusing this instance
 141      */
 142     void dispose() {
 143         if (DO_CLEAN_DIRTY) {
 144             // Force zero-fill dirty arrays:
 145             Arrays.fill(curCurvepts, 0f);
 146         }
 147         // Return arrays:
 148         if (recycleDashes) {
 149             dash = dashes_ref.putArray(dash);
 150         }
 151         firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
 152     }
 153 















 154     @Override
 155     public void moveTo(float x0, float y0) {
 156         if (firstSegidx > 0) {
 157             out.moveTo(sx, sy);
 158             emitFirstSegments();
 159         }
 160         needsMoveTo = true;
 161         this.idx = startIdx;
 162         this.dashOn = this.startDashOn;
 163         this.phase = this.startPhase;
 164         this.sx = this.x0 = x0;
 165         this.sy = this.y0 = y0;
 166         this.starting = true;
 167     }
 168 
 169     private void emitSeg(float[] buf, int off, int type) {
 170         switch (type) {
 171         case 8:
 172             out.curveTo(buf[off+0], buf[off+1],
 173                         buf[off+2], buf[off+3],


 191             int type = (int)fSegBuf[i];
 192             emitSeg(fSegBuf, i + 1, type);
 193             i += (type - 1);
 194         }
 195         firstSegidx = 0;
 196     }
 197     // We don't emit the first dash right away. If we did, caps would be
 198     // drawn on it, but we need joins to be drawn if there's a closePath()
 199     // So, we store the path elements that make up the first dash in the
 200     // buffer below.
 201     private float[] firstSegmentsBuffer; // dynamic array
 202     private int firstSegidx;
 203 
 204     // precondition: pts must be in relative coordinates (relative to x0,y0)
 205     // fullCurve is true iff the curve in pts has not been split.
 206     private void goTo(float[] pts, int off, final int type) {
 207         float x = pts[off + type - 4];
 208         float y = pts[off + type - 3];
 209         if (dashOn) {
 210             if (starting) {
 211                 int len = type - 2 + 1;
 212                 int segIdx = firstSegidx;
 213                 float[] buf = firstSegmentsBuffer;
 214                 if (segIdx + len  > buf.length) {
 215                     if (DO_STATS) {
 216                         rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
 217                             .add(segIdx + len);
 218                     }
 219                     firstSegmentsBuffer = buf
 220                         = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
 221                                                              segIdx + len);
 222                 }
 223                 buf[segIdx++] = type;
 224                 len--;
 225                 // small arraycopy (2, 4 or 6) but with offset:
 226                 System.arraycopy(pts, off, buf, segIdx, len);
 227                 segIdx += len;
 228                 firstSegidx = segIdx;
 229             } else {
 230                 if (needsMoveTo) {
 231                     out.moveTo(x0, y0);


 671         lineTo(sx, sy);
 672         if (firstSegidx > 0) {
 673             if (!dashOn || needsMoveTo) {
 674                 out.moveTo(sx, sy);
 675             }
 676             emitFirstSegments();
 677         }
 678         moveTo(sx, sy);
 679     }
 680 
 681     @Override
 682     public void pathDone() {
 683         if (firstSegidx > 0) {
 684             out.moveTo(sx, sy);
 685             emitFirstSegments();
 686         }
 687         out.pathDone();
 688 
 689         // Dispose this instance:
 690         dispose();
 691     }
 692 
 693     @Override
 694     public long getNativeConsumer() {
 695         throw new InternalError("Dasher does not use a native consumer");
 696     }
 697 }
 698 


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.marlin;
  27 
  28 import java.util.Arrays;
  29 import com.sun.javafx.geom.PathConsumer2D;
  30 
  31 /**
  32  * The <code>Dasher</code> class takes a series of linear commands
  33  * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
  34  * <code>end</code>) and breaks them into smaller segments according to a
  35  * dash pattern array and a starting dash phase.
  36  *
  37  * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
  38  * short dash, whereas Pisces does not draw anything.  The PostScript
  39  * semantics are unclear.
  40  *
  41  */
  42 public final class Dasher implements PathConsumer2D, MarlinConst {
  43 
  44     static final int REC_LIMIT = 4;
  45     static final float ERR = 0.01f;
  46     static final float MIN_T_INC = 1f / (1 << REC_LIMIT);
  47 
  48     // More than 24 bits of mantissa means we can no longer accurately
  49     // measure the number of times cycled through the dash array so we
  50     // punt and override the phase to just be 0 past that point.
  51     static final float MAX_CYCLES = 16000000f;
  52 
  53     private PathConsumer2D out;
  54     private float[] dash;
  55     private int dashLen;
  56     private float startPhase;
  57     private boolean startDashOn;
  58     private int startIdx;
  59 
  60     private boolean starting;
  61     private boolean needsMoveTo;
  62 
  63     private int idx;
  64     private boolean dashOn;
  65     private float phase;
  66 
  67     private float sx, sy;
  68     private float x0, y0;
  69 
  70     // temporary storage for the current curve
  71     private final float[] curCurvepts;
  72 


  91         dashes_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
  92 
  93         firstSegmentsBuffer_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_ARRAY); // 1K
  94         firstSegmentsBuffer     = firstSegmentsBuffer_ref.initial;
  95 
  96         // we need curCurvepts to be able to contain 2 curves because when
  97         // dashing curves, we need to subdivide it
  98         curCurvepts = new float[8 * 2];
  99     }
 100 
 101     /**
 102      * Initialize the <code>Dasher</code>.
 103      *
 104      * @param out an output <code>PathConsumer2D</code>.
 105      * @param dash an array of <code>float</code>s containing the dash pattern
 106      * @param dashLen length of the given dash array
 107      * @param phase a <code>float</code> containing the dash phase
 108      * @param recycleDashes true to indicate to recycle the given dash array
 109      * @return this instance
 110      */
 111     public Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
 112                 float phase, boolean recycleDashes)
 113     {



 114         this.out = out;
 115 
 116         // Normalize so 0 <= phase < dash[0]
 117         int sidx = 0;
 118         dashOn = true;
 119         float sum = 0f;
 120         for (float d : dash) {
 121             sum += d;
 122         }
 123         float cycles = phase / sum;
 124         if (phase < 0f) {
 125             if (-cycles >= MAX_CYCLES) {
 126                 phase = 0f;
 127             } else {
 128                 int fullcycles = FloatMath.floor_int(-cycles);
 129                 if ((fullcycles & dash.length & 1) != 0) {
 130                     dashOn = !dashOn;
 131                 }
 132                 phase += fullcycles * sum;
 133                 while (phase < 0f) {
 134                     if (--sidx < 0) {
 135                         sidx = dash.length - 1;
 136                     }
 137                     phase += dash[sidx];
 138                     dashOn = !dashOn;
 139                 }
 140             }
 141         } else if (phase > 0) {
 142             if (cycles >= MAX_CYCLES) {
 143                 phase = 0f;
 144             } else {
 145                 int fullcycles = FloatMath.floor_int(cycles);
 146                 if ((fullcycles & dash.length & 1) != 0) {
 147                     dashOn = !dashOn;
 148                 }
 149                 phase -= fullcycles * sum;
 150                 float d;
 151                 while (phase >= (d = dash[sidx])) {
 152                     phase -= d;
 153                     sidx = (sidx + 1) % dash.length;
 154                     dashOn = !dashOn;
 155                 }
 156             }
 157         }
 158 
 159         this.dash = dash;
 160         this.dashLen = dashLen;
 161         this.startPhase = this.phase = phase;
 162         this.startDashOn = dashOn;
 163         this.startIdx = sidx;
 164         this.starting = true;
 165         needsMoveTo = false;
 166         firstSegidx = 0;
 167 
 168         this.recycleDashes = recycleDashes;
 169 
 170         return this; // fluent API
 171     }
 172 
 173     /**
 174      * Disposes this dasher:
 175      * clean up before reusing this instance
 176      */
 177     void dispose() {
 178         if (DO_CLEAN_DIRTY) {
 179             // Force zero-fill dirty arrays:
 180             Arrays.fill(curCurvepts, 0f);
 181         }
 182         // Return arrays:
 183         if (recycleDashes) {
 184             dash = dashes_ref.putArray(dash);
 185         }
 186         firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
 187     }
 188 
 189     public float[] copyDashArray(final float[] dashes) {
 190         final int len = dashes.length;
 191         final float[] newDashes;
 192         if (len <= MarlinConst.INITIAL_ARRAY) {
 193             newDashes = rdrCtx.dasher.dashes_ref.initial;
 194         } else {
 195             if (DO_STATS) {
 196                 rdrCtx.stats.stat_array_dasher_dasher.add(len);
 197             }
 198             newDashes = rdrCtx.dasher.dashes_ref.getArray(len);
 199         }
 200         System.arraycopy(dashes, 0, newDashes, 0, len);
 201         return newDashes;
 202     }
 203 
 204     @Override
 205     public void moveTo(float x0, float y0) {
 206         if (firstSegidx > 0) {
 207             out.moveTo(sx, sy);
 208             emitFirstSegments();
 209         }
 210         needsMoveTo = true;
 211         this.idx = startIdx;
 212         this.dashOn = this.startDashOn;
 213         this.phase = this.startPhase;
 214         this.sx = this.x0 = x0;
 215         this.sy = this.y0 = y0;
 216         this.starting = true;
 217     }
 218 
 219     private void emitSeg(float[] buf, int off, int type) {
 220         switch (type) {
 221         case 8:
 222             out.curveTo(buf[off+0], buf[off+1],
 223                         buf[off+2], buf[off+3],


 241             int type = (int)fSegBuf[i];
 242             emitSeg(fSegBuf, i + 1, type);
 243             i += (type - 1);
 244         }
 245         firstSegidx = 0;
 246     }
 247     // We don't emit the first dash right away. If we did, caps would be
 248     // drawn on it, but we need joins to be drawn if there's a closePath()
 249     // So, we store the path elements that make up the first dash in the
 250     // buffer below.
 251     private float[] firstSegmentsBuffer; // dynamic array
 252     private int firstSegidx;
 253 
 254     // precondition: pts must be in relative coordinates (relative to x0,y0)
 255     // fullCurve is true iff the curve in pts has not been split.
 256     private void goTo(float[] pts, int off, final int type) {
 257         float x = pts[off + type - 4];
 258         float y = pts[off + type - 3];
 259         if (dashOn) {
 260             if (starting) {
 261                 int len = type - 1; // - 2 + 1
 262                 int segIdx = firstSegidx;
 263                 float[] buf = firstSegmentsBuffer;
 264                 if (segIdx + len  > buf.length) {
 265                     if (DO_STATS) {
 266                         rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
 267                             .add(segIdx + len);
 268                     }
 269                     firstSegmentsBuffer = buf
 270                         = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
 271                                                              segIdx + len);
 272                 }
 273                 buf[segIdx++] = type;
 274                 len--;
 275                 // small arraycopy (2, 4 or 6) but with offset:
 276                 System.arraycopy(pts, off, buf, segIdx, len);
 277                 segIdx += len;
 278                 firstSegidx = segIdx;
 279             } else {
 280                 if (needsMoveTo) {
 281                     out.moveTo(x0, y0);


 721         lineTo(sx, sy);
 722         if (firstSegidx > 0) {
 723             if (!dashOn || needsMoveTo) {
 724                 out.moveTo(sx, sy);
 725             }
 726             emitFirstSegments();
 727         }
 728         moveTo(sx, sy);
 729     }
 730 
 731     @Override
 732     public void pathDone() {
 733         if (firstSegidx > 0) {
 734             out.moveTo(sx, sy);
 735             emitFirstSegments();
 736         }
 737         out.pathDone();
 738 
 739         // Dispose this instance:
 740         dispose();





 741     }
 742 }
 743 
< prev index next >