81 public PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
82 return tracerCPDetector.init(out);
83 }
84
85 public PathConsumer2D traceFiller(PathConsumer2D out) {
86 return tracerFiller.init(out);
87 }
88
89 public PathConsumer2D traceStroker(PathConsumer2D out) {
90 return tracerStroker.init(out);
91 }
92
93 public PathConsumer2D traceDasher(PathConsumer2D out) {
94 return tracerDasher.init(out);
95 }
96
97 public PathConsumer2D detectClosedPath(PathConsumer2D out) {
98 return cpDetector.init(out);
99 }
100
101 public PathConsumer2D pathClipper(PathConsumer2D out,
102 final float rdrOffX,
103 final float rdrOffY)
104 {
105 return pathClipper.init(out, rdrOffX, rdrOffY);
106 }
107
108 public PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
109 BaseTransform at,
110 final float rdrOffX,
111 final float rdrOffY)
112 {
113 if (at == null) {
114 return out;
115 }
116 final float mxx = (float) at.getMxx();
117 final float mxy = (float) at.getMxy();
118 final float myx = (float) at.getMyx();
119 final float myy = (float) at.getMyy();
120
121 if (mxy == 0.0f && myx == 0.0f) {
122 if (mxx == 1.0f && myy == 1.0f) {
123 return out;
124 } else {
125 // Scale only
126 if (rdrCtx.doClip) {
127 // adjust clip rectangle (ymin, ymax, xmin, xmax):
128 adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
129 adjustClipScale(rdrCtx.clipRect, mxx, myy);
130 }
131 return dt_DeltaScaleFilter.init(out, mxx, myy);
132 }
133 } else {
134 if (rdrCtx.doClip) {
135 // adjust clip rectangle (ymin, ymax, xmin, xmax):
136 adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
137 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
138 }
139 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
140 }
141 }
142
143 private static void adjustClipOffset(final float[] clipRect,
144 final float rdrOffX,
145 final float rdrOffY)
146 {
147 clipRect[0] += rdrOffY;
148 clipRect[1] += rdrOffY;
149 clipRect[2] += rdrOffX;
150 clipRect[3] += rdrOffX;
151 }
152
153 private static void adjustClipScale(final float[] clipRect,
154 final float mxx, final float myy)
155 {
156 // Adjust the clipping rectangle (iv_DeltaScaleFilter):
157 clipRect[0] /= myy;
158 clipRect[1] /= myy;
159 clipRect[2] /= mxx;
160 clipRect[3] /= mxx;
161 }
162
163 private static void adjustClipInverseDelta(final float[] clipRect,
164 final float mxx, final float mxy,
165 final float myx, final float myy)
166 {
167 // Adjust the clipping rectangle (iv_DeltaTransformFilter):
168 final float det = mxx * myy - mxy * myx;
169 final float imxx = myy / det;
170 final float imxy = -mxy / det;
171 final float imyx = -myx / det;
172 final float imyy = mxx / det;
173
174 float xmin, xmax, ymin, ymax;
175 float x, y;
176 // xmin, ymin:
177 x = clipRect[2] * imxx + clipRect[0] * imxy;
178 y = clipRect[2] * imyx + clipRect[0] * imyy;
179
180 xmin = xmax = x;
181 ymin = ymax = y;
182
183 // xmax, ymin:
188 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
189
190 // xmin, ymax:
191 x = clipRect[2] * imxx + clipRect[1] * imxy;
192 y = clipRect[2] * imyx + clipRect[1] * imyy;
193
194 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
195 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
196
197 // xmax, ymax:
198 x = clipRect[3] * imxx + clipRect[1] * imxy;
199 y = clipRect[3] * imyx + clipRect[1] * imyy;
200
201 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
202 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
203
204 clipRect[0] = ymin;
205 clipRect[1] = ymax;
206 clipRect[2] = xmin;
207 clipRect[3] = xmax;
208 }
209
210 public PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
211 BaseTransform at)
212 {
213 if (at == null) {
214 return out;
215 }
216 float mxx = (float) at.getMxx();
217 float mxy = (float) at.getMxy();
218 float myx = (float) at.getMyx();
219 float myy = (float) at.getMyy();
220
221 if (mxy == 0.0f && myx == 0.0f) {
222 if (mxx == 1.0f && myy == 1.0f) {
223 return out;
224 } else {
225 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
226 }
227 } else {
228 final float det = mxx * myy - mxy * myx;
229 return iv_DeltaTransformFilter.init(out,
230 myy / det,
231 -mxy / det,
232 -myx / det,
233 mxx / det);
234 }
235 }
236
237 static final class DeltaScaleFilter implements PathConsumer2D {
238 private PathConsumer2D out;
239 private float sx, sy;
240
241 DeltaScaleFilter() {}
242
243 DeltaScaleFilter init(PathConsumer2D out,
244 float mxx, float myy)
245 {
499 private float cx0, cy0;
500
501 // The current point OUTSIDE
502 private float cox0, coy0;
503
504 private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
505 private final CurveClipSplitter curveSplitter;
506
507 PathClipFilter(final RendererContext rdrCtx) {
508 this.clipRect = rdrCtx.clipRect;
509 this.curveSplitter = rdrCtx.curveClipSplitter;
510
511 this.stack = (rdrCtx.stats != null) ?
512 new IndexStack(rdrCtx,
513 rdrCtx.stats.stat_pcf_idxstack_indices,
514 rdrCtx.stats.hist_pcf_idxstack_indices,
515 rdrCtx.stats.stat_array_pcf_idxstack_indices)
516 : new IndexStack(rdrCtx);
517 }
518
519 PathClipFilter init(final PathConsumer2D out,
520 final double rdrOffX,
521 final double rdrOffY)
522 {
523 this.out = out;
524
525 // add a small rounding error:
526 final float margin = 1e-3f;
527
528 final float[] _clipRect = this.clipRect;
529 // Adjust the clipping rectangle with the renderer offsets
530 _clipRect[0] -= margin - rdrOffY;
531 _clipRect[1] += margin + rdrOffY;
532 _clipRect[2] -= margin - rdrOffX;
533 _clipRect[3] += margin + rdrOffX;
534
535 if (MarlinConst.DO_CLIP_SUBDIVIDER) {
536 // adjust padded clip rectangle:
537 curveSplitter.init();
538 }
539
540 this.init_corners = true;
541 this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
542
543 return this; // fluent API
544 }
545
546 /**
547 * Disposes this instance:
548 * clean up before reusing this instance
549 */
550 void dispose() {
551 stack.dispose();
552 }
553
554 private void finishPath() {
832
833 if (outside) {
834 finish();
835 }
836 // clipping disabled:
837 out.quadTo(x1, y1, xe, ye);
838 this.cx0 = xe;
839 this.cy0 = ye;
840 }
841 }
842
843 static final class CurveClipSplitter {
844
845 static final float LEN_TH = MarlinProperties.getSubdividerMinLength();
846 static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0f);
847
848 private static final boolean TRACE = false;
849
850 private static final int MAX_N_CURVES = 3 * 4;
851
852 // clip rectangle (ymin, ymax, xmin, xmax):
853 final float[] clipRect;
854
855 // clip rectangle (ymin, ymax, xmin, xmax) including padding:
856 final float[] clipRectPad = new float[4];
857 private boolean init_clipRectPad = false;
858
859 // This is where the curve to be processed is put. We give it
860 // enough room to store all curves.
861 final float[] middle = new float[MAX_N_CURVES * 8 + 2];
862 // t values at subdivision points
863 private final float[] subdivTs = new float[MAX_N_CURVES];
864
865 // dirty curve
866 private final Curve curve;
867
868 CurveClipSplitter(final RendererContext rdrCtx) {
869 this.clipRect = rdrCtx.clipRect;
870 this.curve = rdrCtx.curve;
871 }
872
873 void init() {
874 this.init_clipRectPad = true;
875 }
876
877 private void initPaddedClip() {
878 // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
879 // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
880 // add a rounding error (curve subdivision ~ 0.1px):
881 final float[] _clipRect = clipRect;
882 final float[] _clipRectPad = clipRectPad;
883
884 _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
885 _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
886 _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
887 _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
888
889 if (TRACE) {
890 MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
891 + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
892 }
893 }
894
895 boolean splitLine(final float x0, final float y0,
896 final float x1, final float y1,
897 final int outCodeOR,
898 final PathConsumer2D out)
899 {
900 if (TRACE) {
901 MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
902 }
903
904 if (DO_CHECK_LENGTH && Helpers.fastLineLen(x0, y0, x1, y1) <= LEN_TH) {
905 return false;
906 }
907
908 final float[] mid = middle;
909 mid[0] = x0; mid[1] = y0;
910 mid[2] = x1; mid[3] = y1;
911
912 return subdivideAtIntersections(4, outCodeOR, out);
913 }
914
915 boolean splitQuad(final float x0, final float y0,
916 final float x1, final float y1,
917 final float x2, final float y2,
918 final int outCodeOR,
919 final PathConsumer2D out)
920 {
921 if (TRACE) {
922 MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
923 }
924
925 if (DO_CHECK_LENGTH && Helpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= LEN_TH) {
926 return false;
927 }
928
929 final float[] mid = middle;
930 mid[0] = x0; mid[1] = y0;
931 mid[2] = x1; mid[3] = y1;
932 mid[4] = x2; mid[5] = y2;
933
934 return subdivideAtIntersections(6, outCodeOR, out);
935 }
936
937 boolean splitCurve(final float x0, final float y0,
938 final float x1, final float y1,
939 final float x2, final float y2,
940 final float x3, final float y3,
941 final int outCodeOR,
942 final PathConsumer2D out)
943 {
944 if (TRACE) {
945 MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
946 }
947
948 if (DO_CHECK_LENGTH && Helpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= LEN_TH) {
949 return false;
950 }
951
952 final float[] mid = middle;
953 mid[0] = x0; mid[1] = y0;
954 mid[2] = x1; mid[3] = y1;
955 mid[4] = x2; mid[5] = y2;
956 mid[6] = x3; mid[7] = y3;
957
958 return subdivideAtIntersections(8, outCodeOR, out);
959 }
960
961 private boolean subdivideAtIntersections(final int type, final int outCodeOR,
962 final PathConsumer2D out)
963 {
964 final float[] mid = middle;
965 final float[] subTs = subdivTs;
966
967 if (init_clipRectPad) {
968 init_clipRectPad = false;
969 initPaddedClip();
970 }
971
972 final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
973 outCodeOR, clipRectPad);
974
975 if (TRACE) {
976 MarlinUtils.logInfo("nSplits: "+ nSplits);
977 MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
978 }
979 if (nSplits == 0) {
980 // only curve support shortcut
981 return false;
982 }
983 float prevT = 0.0f;
984
985 for (int i = 0, off = 0; i < nSplits; i++, off += type) {
986 final float t = subTs[i];
987
988 Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
989 mid, off, mid, off, type);
990 prevT = t;
991 }
992
993 for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
994 if (TRACE) {
995 MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
996 }
|
81 public PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
82 return tracerCPDetector.init(out);
83 }
84
85 public PathConsumer2D traceFiller(PathConsumer2D out) {
86 return tracerFiller.init(out);
87 }
88
89 public PathConsumer2D traceStroker(PathConsumer2D out) {
90 return tracerStroker.init(out);
91 }
92
93 public PathConsumer2D traceDasher(PathConsumer2D out) {
94 return tracerDasher.init(out);
95 }
96
97 public PathConsumer2D detectClosedPath(PathConsumer2D out) {
98 return cpDetector.init(out);
99 }
100
101 public PathConsumer2D pathClipper(PathConsumer2D out) {
102 return pathClipper.init(out);
103 }
104
105 public PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
106 BaseTransform at)
107 {
108 if (at == null) {
109 return out;
110 }
111 final float mxx = (float) at.getMxx();
112 final float mxy = (float) at.getMxy();
113 final float myx = (float) at.getMyx();
114 final float myy = (float) at.getMyy();
115
116 if (mxy == 0.0f && myx == 0.0f) {
117 if (mxx == 1.0f && myy == 1.0f) {
118 return out;
119 } else {
120 // Scale only
121 if (rdrCtx.doClip) {
122 // adjust clip rectangle (ymin, ymax, xmin, xmax):
123 rdrCtx.clipInvScale = adjustClipScale(rdrCtx.clipRect,
124 mxx, myy);
125 }
126 return dt_DeltaScaleFilter.init(out, mxx, myy);
127 }
128 } else {
129 if (rdrCtx.doClip) {
130 // adjust clip rectangle (ymin, ymax, xmin, xmax):
131 rdrCtx.clipInvScale = adjustClipInverseDelta(rdrCtx.clipRect,
132 mxx, mxy, myx, myy);
133 }
134 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
135 }
136 }
137
138 private static float adjustClipScale(final float[] clipRect,
139 final float mxx, final float myy)
140 {
141 // Adjust the clipping rectangle (iv_DeltaScaleFilter):
142 final float scaleY = 1.0f / myy;
143 clipRect[0] *= scaleY;
144 clipRect[1] *= scaleY;
145
146 if (clipRect[1] < clipRect[0]) {
147 float tmp = clipRect[0];
148 clipRect[0] = clipRect[1];
149 clipRect[1] = tmp;
150 }
151
152 final float scaleX = 1.0f / mxx;
153 clipRect[2] *= scaleX;
154 clipRect[3] *= scaleX;
155
156 if (clipRect[3] < clipRect[2]) {
157 float tmp = clipRect[2];
158 clipRect[2] = clipRect[3];
159 clipRect[3] = tmp;
160 }
161
162 if (MarlinConst.DO_LOG_CLIP) {
163 MarlinUtils.logInfo("clipRect (ClipScale): "
164 + Arrays.toString(clipRect));
165 }
166 return 0.5f * (Math.abs(scaleX) + Math.abs(scaleY));
167 }
168
169 private static float adjustClipInverseDelta(final float[] clipRect,
170 final float mxx, final float mxy,
171 final float myx, final float myy)
172 {
173 // Adjust the clipping rectangle (iv_DeltaTransformFilter):
174 final float det = mxx * myy - mxy * myx;
175 final float imxx = myy / det;
176 final float imxy = -mxy / det;
177 final float imyx = -myx / det;
178 final float imyy = mxx / det;
179
180 float xmin, xmax, ymin, ymax;
181 float x, y;
182 // xmin, ymin:
183 x = clipRect[2] * imxx + clipRect[0] * imxy;
184 y = clipRect[2] * imyx + clipRect[0] * imyy;
185
186 xmin = xmax = x;
187 ymin = ymax = y;
188
189 // xmax, ymin:
194 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
195
196 // xmin, ymax:
197 x = clipRect[2] * imxx + clipRect[1] * imxy;
198 y = clipRect[2] * imyx + clipRect[1] * imyy;
199
200 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
201 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
202
203 // xmax, ymax:
204 x = clipRect[3] * imxx + clipRect[1] * imxy;
205 y = clipRect[3] * imyx + clipRect[1] * imyy;
206
207 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
208 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
209
210 clipRect[0] = ymin;
211 clipRect[1] = ymax;
212 clipRect[2] = xmin;
213 clipRect[3] = xmax;
214
215 if (MarlinConst.DO_LOG_CLIP) {
216 MarlinUtils.logInfo("clipRect (ClipInverseDelta): "
217 + Arrays.toString(clipRect));
218 }
219
220 final float scaleX = (float) Math.sqrt(imxx * imxx + imxy * imxy);
221 final float scaleY = (float) Math.sqrt(imyx * imyx + imyy * imyy);
222
223 return 0.5f * (scaleX + scaleY);
224 }
225
226 public PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
227 BaseTransform at)
228 {
229 if (at == null) {
230 return out;
231 }
232 float mxx = (float) at.getMxx();
233 float mxy = (float) at.getMxy();
234 float myx = (float) at.getMyx();
235 float myy = (float) at.getMyy();
236
237 if (mxy == 0.0f && myx == 0.0f) {
238 if (mxx == 1.0f && myy == 1.0f) {
239 return out;
240 } else {
241 return iv_DeltaScaleFilter.init(out, 1.0f / mxx, 1.0f / myy);
242 }
243 } else {
244 final float det = mxx * myy - mxy * myx;
245 return iv_DeltaTransformFilter.init(out,
246 myy / det,
247 -mxy / det,
248 -myx / det,
249 mxx / det);
250 }
251 }
252
253 static final class DeltaScaleFilter implements PathConsumer2D {
254 private PathConsumer2D out;
255 private float sx, sy;
256
257 DeltaScaleFilter() {}
258
259 DeltaScaleFilter init(PathConsumer2D out,
260 float mxx, float myy)
261 {
515 private float cx0, cy0;
516
517 // The current point OUTSIDE
518 private float cox0, coy0;
519
520 private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
521 private final CurveClipSplitter curveSplitter;
522
523 PathClipFilter(final RendererContext rdrCtx) {
524 this.clipRect = rdrCtx.clipRect;
525 this.curveSplitter = rdrCtx.curveClipSplitter;
526
527 this.stack = (rdrCtx.stats != null) ?
528 new IndexStack(rdrCtx,
529 rdrCtx.stats.stat_pcf_idxstack_indices,
530 rdrCtx.stats.hist_pcf_idxstack_indices,
531 rdrCtx.stats.stat_array_pcf_idxstack_indices)
532 : new IndexStack(rdrCtx);
533 }
534
535 PathClipFilter init(final PathConsumer2D out) {
536 this.out = out;
537
538 if (MarlinConst.DO_CLIP_SUBDIVIDER) {
539 // adjust padded clip rectangle:
540 curveSplitter.init();
541 }
542
543 this.init_corners = true;
544 this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
545
546 return this; // fluent API
547 }
548
549 /**
550 * Disposes this instance:
551 * clean up before reusing this instance
552 */
553 void dispose() {
554 stack.dispose();
555 }
556
557 private void finishPath() {
835
836 if (outside) {
837 finish();
838 }
839 // clipping disabled:
840 out.quadTo(x1, y1, xe, ye);
841 this.cx0 = xe;
842 this.cy0 = ye;
843 }
844 }
845
846 static final class CurveClipSplitter {
847
848 static final float LEN_TH = MarlinProperties.getSubdividerMinLength();
849 static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0f);
850
851 private static final boolean TRACE = false;
852
853 private static final int MAX_N_CURVES = 3 * 4;
854
855 private final RendererContext rdrCtx;
856
857 // scaled length threshold:
858 private float minLength;
859
860 // clip rectangle (ymin, ymax, xmin, xmax):
861 final float[] clipRect;
862
863 // clip rectangle (ymin, ymax, xmin, xmax) including padding:
864 final float[] clipRectPad = new float[4];
865 private boolean init_clipRectPad = false;
866
867 // This is where the curve to be processed is put. We give it
868 // enough room to store all curves.
869 final float[] middle = new float[MAX_N_CURVES * 8 + 2];
870 // t values at subdivision points
871 private final float[] subdivTs = new float[MAX_N_CURVES];
872
873 // dirty curve
874 private final Curve curve;
875
876 CurveClipSplitter(final RendererContext rdrCtx) {
877 this.rdrCtx = rdrCtx;
878 this.clipRect = rdrCtx.clipRect;
879 this.curve = rdrCtx.curve;
880 }
881
882 void init() {
883 this.init_clipRectPad = true;
884
885 if (DO_CHECK_LENGTH) {
886 this.minLength = (this.rdrCtx.clipInvScale == 0.0f) ? LEN_TH
887 : (LEN_TH * this.rdrCtx.clipInvScale);
888
889 if (MarlinConst.DO_LOG_CLIP) {
890 MarlinUtils.logInfo("CurveClipSplitter.minLength = "
891 + minLength);
892 }
893 }
894 }
895
896 private void initPaddedClip() {
897 // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
898 // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
899 // add a rounding error (curve subdivision ~ 0.1px):
900 final float[] _clipRect = clipRect;
901 final float[] _clipRectPad = clipRectPad;
902
903 _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
904 _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
905 _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
906 _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
907
908 if (TRACE) {
909 MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
910 + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
911 }
912 }
913
914 boolean splitLine(final float x0, final float y0,
915 final float x1, final float y1,
916 final int outCodeOR,
917 final PathConsumer2D out)
918 {
919 if (TRACE) {
920 MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
921 }
922
923 if (DO_CHECK_LENGTH && Helpers.fastLineLen(x0, y0, x1, y1) <= minLength) {
924 return false;
925 }
926
927 final float[] mid = middle;
928 mid[0] = x0; mid[1] = y0;
929 mid[2] = x1; mid[3] = y1;
930
931 return subdivideAtIntersections(4, outCodeOR, out);
932 }
933
934 boolean splitQuad(final float x0, final float y0,
935 final float x1, final float y1,
936 final float x2, final float y2,
937 final int outCodeOR,
938 final PathConsumer2D out)
939 {
940 if (TRACE) {
941 MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
942 }
943
944 if (DO_CHECK_LENGTH && Helpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= minLength) {
945 return false;
946 }
947
948 final float[] mid = middle;
949 mid[0] = x0; mid[1] = y0;
950 mid[2] = x1; mid[3] = y1;
951 mid[4] = x2; mid[5] = y2;
952
953 return subdivideAtIntersections(6, outCodeOR, out);
954 }
955
956 boolean splitCurve(final float x0, final float y0,
957 final float x1, final float y1,
958 final float x2, final float y2,
959 final float x3, final float y3,
960 final int outCodeOR,
961 final PathConsumer2D out)
962 {
963 if (TRACE) {
964 MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
965 }
966
967 if (DO_CHECK_LENGTH && Helpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= minLength) {
968 return false;
969 }
970
971 final float[] mid = middle;
972 mid[0] = x0; mid[1] = y0;
973 mid[2] = x1; mid[3] = y1;
974 mid[4] = x2; mid[5] = y2;
975 mid[6] = x3; mid[7] = y3;
976
977 return subdivideAtIntersections(8, outCodeOR, out);
978 }
979
980 private boolean subdivideAtIntersections(final int type, final int outCodeOR,
981 final PathConsumer2D out)
982 {
983 final float[] mid = middle;
984 final float[] subTs = subdivTs;
985
986 if (init_clipRectPad) {
987 init_clipRectPad = false;
988 initPaddedClip();
989 }
990
991 final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
992 outCodeOR, clipRectPad);
993
994 if (TRACE) {
995 MarlinUtils.logInfo("nSplits: " + nSplits);
996 MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
997 }
998 if (nSplits == 0) {
999 // only curve support shortcut
1000 return false;
1001 }
1002 float prevT = 0.0f;
1003
1004 for (int i = 0, off = 0; i < nSplits; i++, off += type) {
1005 final float t = subTs[i];
1006
1007 Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
1008 mid, off, mid, off, type);
1009 prevT = t;
1010 }
1011
1012 for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
1013 if (TRACE) {
1014 MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
1015 }
|