1 /* libs/android_runtime/android/graphics/Path.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 // This file was generated from the C++ include file: SkPath.h
19 // Any changes made to this file will be discarded by the build.
20 // To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
21 // or one of the auxilary file specifications in device/tools/gluemaker.
22 
23 #include "jni.h"
24 #include "GraphicsJNI.h"
25 #include "core_jni_helpers.h"
26 
27 #include "SkPath.h"
28 #include "SkPathOps.h"
29 
30 #include <Caches.h>
31 #include <vector>
32 #include <map>
33 
34 namespace android {
35 
36 class SkPathGlue {
37 public:
38 
finalizer(JNIEnv * env,jobject clazz,jlong objHandle)39     static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
40         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
41         // Purge entries from the HWUI path cache if this path's data is unique
42         if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
43             android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
44         }
45         delete obj;
46     }
47 
init1(JNIEnv * env,jobject clazz)48     static jlong init1(JNIEnv* env, jobject clazz) {
49         return reinterpret_cast<jlong>(new SkPath());
50     }
51 
init2(JNIEnv * env,jobject clazz,jlong valHandle)52     static jlong init2(JNIEnv* env, jobject clazz, jlong valHandle) {
53         SkPath* val = reinterpret_cast<SkPath*>(valHandle);
54         return reinterpret_cast<jlong>(new SkPath(*val));
55     }
56 
reset(JNIEnv * env,jobject clazz,jlong objHandle)57     static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
58         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
59         obj->reset();
60     }
61 
rewind(JNIEnv * env,jobject clazz,jlong objHandle)62     static void rewind(JNIEnv* env, jobject clazz, jlong objHandle) {
63         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
64         obj->rewind();
65     }
66 
assign(JNIEnv * env,jobject clazz,jlong dstHandle,jlong srcHandle)67     static void assign(JNIEnv* env, jobject clazz, jlong dstHandle, jlong srcHandle) {
68         SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
69         const SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
70         *dst = *src;
71     }
72 
isConvex(JNIEnv * env,jobject clazz,jlong objHandle)73     static jboolean isConvex(JNIEnv* env, jobject clazz, jlong objHandle) {
74         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
75         return obj->isConvex();
76     }
77 
getFillType(JNIEnv * env,jobject clazz,jlong objHandle)78     static jint getFillType(JNIEnv* env, jobject clazz, jlong objHandle) {
79         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
80         return obj->getFillType();
81     }
82 
setFillType(JNIEnv * env,jobject clazz,jlong pathHandle,jint ftHandle)83     static void setFillType(JNIEnv* env, jobject clazz, jlong pathHandle, jint ftHandle) {
84         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
85         SkPath::FillType ft = static_cast<SkPath::FillType>(ftHandle);
86         path->setFillType(ft);
87     }
88 
isEmpty(JNIEnv * env,jobject clazz,jlong objHandle)89     static jboolean isEmpty(JNIEnv* env, jobject clazz, jlong objHandle) {
90         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
91         return obj->isEmpty();
92     }
93 
isRect(JNIEnv * env,jobject clazz,jlong objHandle,jobject jrect)94     static jboolean isRect(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect) {
95         SkRect rect;
96         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
97         jboolean result = obj->isRect(&rect);
98         GraphicsJNI::rect_to_jrectf(rect, env, jrect);
99         return result;
100     }
101 
computeBounds(JNIEnv * env,jobject clazz,jlong objHandle,jobject jbounds)102     static void computeBounds(JNIEnv* env, jobject clazz, jlong objHandle, jobject jbounds) {
103         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
104         const SkRect& bounds = obj->getBounds();
105         GraphicsJNI::rect_to_jrectf(bounds, env, jbounds);
106     }
107 
incReserve(JNIEnv * env,jobject clazz,jlong objHandle,jint extraPtCount)108     static void incReserve(JNIEnv* env, jobject clazz, jlong objHandle, jint extraPtCount) {
109         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
110         obj->incReserve(extraPtCount);
111     }
112 
moveTo__FF(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x,jfloat y)113     static void moveTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
114         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
115         obj->moveTo(x, y);
116     }
117 
rMoveTo(JNIEnv * env,jobject clazz,jlong objHandle,jfloat dx,jfloat dy)118     static void rMoveTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
119         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
120         obj->rMoveTo(dx, dy);
121     }
122 
lineTo__FF(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x,jfloat y)123     static void lineTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) {
124         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
125         obj->lineTo(x, y);
126     }
127 
rLineTo(JNIEnv * env,jobject clazz,jlong objHandle,jfloat dx,jfloat dy)128     static void rLineTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
129         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
130         obj->rLineTo(dx, dy);
131     }
132 
quadTo__FFFF(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x1,jfloat y1,jfloat x2,jfloat y2)133     static void quadTo__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
134         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
135         obj->quadTo(x1, y1, x2, y2);
136     }
137 
rQuadTo(JNIEnv * env,jobject clazz,jlong objHandle,jfloat dx1,jfloat dy1,jfloat dx2,jfloat dy2)138     static void rQuadTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) {
139         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
140         obj->rQuadTo(dx1, dy1, dx2, dy2);
141     }
142 
cubicTo__FFFFFF(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x1,jfloat y1,jfloat x2,jfloat y2,jfloat x3,jfloat y3)143     static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
144         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
145         obj->cubicTo(x1, y1, x2, y2, x3, y3);
146     }
147 
rCubicTo(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x1,jfloat y1,jfloat x2,jfloat y2,jfloat x3,jfloat y3)148     static void rCubicTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
149         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
150         obj->rCubicTo(x1, y1, x2, y2, x3, y3);
151     }
152 
arcTo(JNIEnv * env,jobject clazz,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat startAngle,jfloat sweepAngle,jboolean forceMoveTo)153     static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
154             jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
155             jboolean forceMoveTo) {
156         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
157         SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
158         obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
159     }
160 
close(JNIEnv * env,jobject clazz,jlong objHandle)161     static void close(JNIEnv* env, jobject clazz, jlong objHandle) {
162         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
163         obj->close();
164     }
165 
addRect(JNIEnv * env,jobject clazz,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jint dirHandle)166     static void addRect(JNIEnv* env, jobject clazz, jlong objHandle,
167             jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
168         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
169         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
170         obj->addRect(left, top, right, bottom, dir);
171     }
172 
addOval(JNIEnv * env,jobject clazz,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jint dirHandle)173     static void addOval(JNIEnv* env, jobject clazz, jlong objHandle,
174             jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
175         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
176         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
177         SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
178         obj->addOval(oval, dir);
179     }
180 
addCircle(JNIEnv * env,jobject clazz,jlong objHandle,jfloat x,jfloat y,jfloat radius,jint dirHandle)181     static void addCircle(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y, jfloat radius, jint dirHandle) {
182         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
183         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
184         obj->addCircle(x, y, radius, dir);
185     }
186 
addArc(JNIEnv * env,jobject clazz,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat startAngle,jfloat sweepAngle)187     static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
188             jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) {
189         SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
190         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
191         obj->addArc(oval, startAngle, sweepAngle);
192     }
193 
addRoundRectXY(JNIEnv * env,jobject clazz,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat rx,jfloat ry,jint dirHandle)194     static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,
195             jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {
196         SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
197         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
198         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
199         obj->addRoundRect(rect, rx, ry, dir);
200     }
201 
addRoundRect8(JNIEnv * env,jobject,jlong objHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloatArray array,jint dirHandle)202     static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top,
203             jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
204         SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
205         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
206         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
207         AutoJavaFloatArray  afa(env, array, 8);
208 #ifdef SK_SCALAR_IS_FLOAT
209         const float* src = afa.ptr();
210 #else
211         #error Need to convert float array to SkScalar array before calling the following function.
212 #endif
213         obj->addRoundRect(rect, src, dir);
214     }
215 
addPath__PathFF(JNIEnv * env,jobject clazz,jlong objHandle,jlong srcHandle,jfloat dx,jfloat dy)216     static void addPath__PathFF(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jfloat dx, jfloat dy) {
217         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
218         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
219         obj->addPath(*src, dx, dy);
220     }
221 
addPath__Path(JNIEnv * env,jobject clazz,jlong objHandle,jlong srcHandle)222     static void addPath__Path(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle) {
223         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
224         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
225         obj->addPath(*src);
226     }
227 
addPath__PathMatrix(JNIEnv * env,jobject clazz,jlong objHandle,jlong srcHandle,jlong matrixHandle)228     static void addPath__PathMatrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong matrixHandle) {
229         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
230         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
231         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
232         obj->addPath(*src, *matrix);
233     }
234 
offset__FF(JNIEnv * env,jobject clazz,jlong objHandle,jfloat dx,jfloat dy)235     static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
236         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
237         obj->offset(dx, dy);
238     }
239 
setLastPoint(JNIEnv * env,jobject clazz,jlong objHandle,jfloat dx,jfloat dy)240     static void setLastPoint(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
241         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
242         obj->setLastPt(dx, dy);
243     }
244 
transform__MatrixPath(JNIEnv * env,jobject clazz,jlong objHandle,jlong matrixHandle,jlong dstHandle)245     static void transform__MatrixPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle, jlong dstHandle) {
246         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
247         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
248         SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
249         obj->transform(*matrix, dst);
250     }
251 
transform__Matrix(JNIEnv * env,jobject clazz,jlong objHandle,jlong matrixHandle)252     static void transform__Matrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle) {
253         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
254         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
255         obj->transform(*matrix);
256     }
257 
op(JNIEnv * env,jobject clazz,jlong p1Handle,jlong p2Handle,jint opHandle,jlong rHandle)258     static jboolean op(JNIEnv* env, jobject clazz, jlong p1Handle, jlong p2Handle, jint opHandle, jlong rHandle) {
259         SkPath* p1  = reinterpret_cast<SkPath*>(p1Handle);
260         SkPath* p2  = reinterpret_cast<SkPath*>(p2Handle);
261         SkPathOp op = static_cast<SkPathOp>(opHandle);
262         SkPath* r   = reinterpret_cast<SkPath*>(rHandle);
263         return Op(*p1, *p2, op, r);
264      }
265 
266     typedef SkPoint (*bezierCalculation)(float t, const SkPoint* points);
267 
addMove(std::vector<SkPoint> & segmentPoints,std::vector<float> & lengths,const SkPoint & point)268     static void addMove(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths,
269             const SkPoint& point) {
270         float length = 0;
271         if (!lengths.empty()) {
272             length = lengths.back();
273         }
274         segmentPoints.push_back(point);
275         lengths.push_back(length);
276     }
277 
addLine(std::vector<SkPoint> & segmentPoints,std::vector<float> & lengths,const SkPoint & toPoint)278     static void addLine(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths,
279             const SkPoint& toPoint) {
280         if (segmentPoints.empty()) {
281             segmentPoints.push_back(SkPoint::Make(0, 0));
282             lengths.push_back(0);
283         } else if (segmentPoints.back() == toPoint) {
284             return; // Empty line
285         }
286         float length = lengths.back() + SkPoint::Distance(segmentPoints.back(), toPoint);
287         segmentPoints.push_back(toPoint);
288         lengths.push_back(length);
289     }
290 
cubicCoordinateCalculation(float t,float p0,float p1,float p2,float p3)291     static float cubicCoordinateCalculation(float t, float p0, float p1, float p2, float p3) {
292         float oneMinusT = 1 - t;
293         float oneMinusTSquared = oneMinusT * oneMinusT;
294         float oneMinusTCubed = oneMinusTSquared * oneMinusT;
295         float tSquared = t * t;
296         float tCubed = tSquared * t;
297         return (oneMinusTCubed * p0) + (3 * oneMinusTSquared * t * p1)
298                 + (3 * oneMinusT * tSquared * p2) + (tCubed * p3);
299     }
300 
cubicBezierCalculation(float t,const SkPoint * points)301     static SkPoint cubicBezierCalculation(float t, const SkPoint* points) {
302         float x = cubicCoordinateCalculation(t, points[0].x(), points[1].x(),
303             points[2].x(), points[3].x());
304         float y = cubicCoordinateCalculation(t, points[0].y(), points[1].y(),
305             points[2].y(), points[3].y());
306         return SkPoint::Make(x, y);
307     }
308 
quadraticCoordinateCalculation(float t,float p0,float p1,float p2)309     static float quadraticCoordinateCalculation(float t, float p0, float p1, float p2) {
310         float oneMinusT = 1 - t;
311         return oneMinusT * ((oneMinusT * p0) + (t * p1)) + t * ((oneMinusT * p1) + (t * p2));
312     }
313 
quadraticBezierCalculation(float t,const SkPoint * points)314     static SkPoint quadraticBezierCalculation(float t, const SkPoint* points) {
315         float x = quadraticCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x());
316         float y = quadraticCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y());
317         return SkPoint::Make(x, y);
318     }
319 
320     // Subdivide a section of the Bezier curve, set the mid-point and the mid-t value.
321     // Returns true if further subdivision is necessary as defined by errorSquared.
subdividePoints(const SkPoint * points,bezierCalculation bezierFunction,float t0,const SkPoint & p0,float t1,const SkPoint & p1,float & midT,SkPoint & midPoint,float errorSquared)322     static bool subdividePoints(const SkPoint* points, bezierCalculation bezierFunction,
323             float t0, const SkPoint &p0, float t1, const SkPoint &p1,
324             float& midT, SkPoint &midPoint, float errorSquared) {
325         midT = (t1 + t0) / 2;
326         float midX = (p1.x() + p0.x()) / 2;
327         float midY = (p1.y() + p0.y()) / 2;
328 
329         midPoint = (*bezierFunction)(midT, points);
330         float xError = midPoint.x() - midX;
331         float yError = midPoint.y() - midY;
332         float midErrorSquared = (xError * xError) + (yError * yError);
333         return midErrorSquared > errorSquared;
334     }
335 
336     // Divides Bezier curves until linear interpolation is very close to accurate, using
337     // errorSquared as a metric. Cubic Bezier curves can have an inflection point that improperly
338     // short-circuit subdivision. If you imagine an S shape, the top and bottom points being the
339     // starting and end points, linear interpolation would mark the center where the curve places
340     // the point. It is clearly not the case that we can linearly interpolate at that point.
341     // doubleCheckDivision forces a second examination between subdivisions to ensure that linear
342     // interpolation works.
addBezier(const SkPoint * points,bezierCalculation bezierFunction,std::vector<SkPoint> & segmentPoints,std::vector<float> & lengths,float errorSquared,bool doubleCheckDivision)343     static void addBezier(const SkPoint* points,
344             bezierCalculation bezierFunction, std::vector<SkPoint>& segmentPoints,
345             std::vector<float>& lengths, float errorSquared, bool doubleCheckDivision) {
346         typedef std::map<float, SkPoint> PointMap;
347         PointMap tToPoint;
348 
349         tToPoint[0] = (*bezierFunction)(0, points);
350         tToPoint[1] = (*bezierFunction)(1, points);
351 
352         PointMap::iterator iter = tToPoint.begin();
353         PointMap::iterator next = iter;
354         ++next;
355         while (next != tToPoint.end()) {
356             bool needsSubdivision = true;
357             SkPoint midPoint;
358             do {
359                 float midT;
360                 needsSubdivision = subdividePoints(points, bezierFunction, iter->first,
361                     iter->second, next->first, next->second, midT, midPoint, errorSquared);
362                 if (!needsSubdivision && doubleCheckDivision) {
363                     SkPoint quarterPoint;
364                     float quarterT;
365                     needsSubdivision = subdividePoints(points, bezierFunction, iter->first,
366                         iter->second, midT, midPoint, quarterT, quarterPoint, errorSquared);
367                     if (needsSubdivision) {
368                         // Found an inflection point. No need to double-check.
369                         doubleCheckDivision = false;
370                     }
371                 }
372                 if (needsSubdivision) {
373                     next = tToPoint.insert(iter, PointMap::value_type(midT, midPoint));
374                 }
375             } while (needsSubdivision);
376             iter = next;
377             next++;
378         }
379 
380         // Now that each division can use linear interpolation with less than the allowed error
381         for (iter = tToPoint.begin(); iter != tToPoint.end(); ++iter) {
382             addLine(segmentPoints, lengths, iter->second);
383         }
384     }
385 
createVerbSegments(SkPath::Verb verb,const SkPoint * points,std::vector<SkPoint> & segmentPoints,std::vector<float> & lengths,float errorSquared)386     static void createVerbSegments(SkPath::Verb verb, const SkPoint* points,
387         std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths, float errorSquared) {
388         switch (verb) {
389             case SkPath::kMove_Verb:
390                 addMove(segmentPoints, lengths, points[0]);
391                 break;
392             case SkPath::kClose_Verb:
393                 addLine(segmentPoints, lengths, points[0]);
394                 break;
395             case SkPath::kLine_Verb:
396                 addLine(segmentPoints, lengths, points[1]);
397                 break;
398             case SkPath::kQuad_Verb:
399                 addBezier(points, quadraticBezierCalculation, segmentPoints, lengths,
400                     errorSquared, false);
401                 break;
402             case SkPath::kCubic_Verb:
403                 addBezier(points, cubicBezierCalculation, segmentPoints, lengths,
404                     errorSquared, true);
405                 break;
406             default:
407                 // Leave element as NULL, Conic sections are not supported.
408                 break;
409         }
410     }
411 
412     // Returns a float[] with each point along the path represented by 3 floats
413     // * fractional length along the path that the point resides
414     // * x coordinate
415     // * y coordinate
416     // Note that more than one point may have the same length along the path in
417     // the case of a move.
418     // NULL can be returned if the Path is empty.
approximate(JNIEnv * env,jclass,jlong pathHandle,float acceptableError)419     static jfloatArray approximate(JNIEnv* env, jclass, jlong pathHandle, float acceptableError)
420     {
421         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
422         SkASSERT(path);
423         SkPath::Iter pathIter(*path, false);
424         SkPath::Verb verb;
425         SkPoint points[4];
426         std::vector<SkPoint> segmentPoints;
427         std::vector<float> lengths;
428         float errorSquared = acceptableError * acceptableError;
429 
430         while ((verb = pathIter.next(points, false)) != SkPath::kDone_Verb) {
431             createVerbSegments(verb, points, segmentPoints, lengths, errorSquared);
432         }
433 
434         if (segmentPoints.empty()) {
435             int numVerbs = path->countVerbs();
436             if (numVerbs == 1) {
437                 addMove(segmentPoints, lengths, path->getPoint(0));
438             } else {
439                 // Invalid or empty path. Fall back to point(0,0)
440                 addMove(segmentPoints, lengths, SkPoint());
441             }
442         }
443 
444         float totalLength = lengths.back();
445         if (totalLength == 0) {
446             // Lone Move instructions should still be able to animate at the same value.
447             segmentPoints.push_back(segmentPoints.back());
448             lengths.push_back(1);
449             totalLength = 1;
450         }
451 
452         size_t numPoints = segmentPoints.size();
453         size_t approximationArraySize = numPoints * 3;
454 
455         float* approximation = new float[approximationArraySize];
456 
457         int approximationIndex = 0;
458         for (size_t i = 0; i < numPoints; i++) {
459             const SkPoint& point = segmentPoints[i];
460             approximation[approximationIndex++] = lengths[i] / totalLength;
461             approximation[approximationIndex++] = point.x();
462             approximation[approximationIndex++] = point.y();
463         }
464 
465         jfloatArray result = env->NewFloatArray(approximationArraySize);
466         env->SetFloatArrayRegion(result, 0, approximationArraySize, approximation);
467         delete[] approximation;
468         return result;
469     }
470 };
471 
472 static const JNINativeMethod methods[] = {
473     {"finalizer", "(J)V", (void*) SkPathGlue::finalizer},
474     {"init1","()J", (void*) SkPathGlue::init1},
475     {"init2","(J)J", (void*) SkPathGlue::init2},
476     {"native_reset","(J)V", (void*) SkPathGlue::reset},
477     {"native_rewind","(J)V", (void*) SkPathGlue::rewind},
478     {"native_set","(JJ)V", (void*) SkPathGlue::assign},
479     {"native_isConvex","(J)Z", (void*) SkPathGlue::isConvex},
480     {"native_getFillType","(J)I", (void*) SkPathGlue::getFillType},
481     {"native_setFillType","(JI)V", (void*) SkPathGlue::setFillType},
482     {"native_isEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
483     {"native_isRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
484     {"native_computeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
485     {"native_incReserve","(JI)V", (void*) SkPathGlue::incReserve},
486     {"native_moveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
487     {"native_rMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
488     {"native_lineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
489     {"native_rLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
490     {"native_quadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
491     {"native_rQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
492     {"native_cubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
493     {"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
494     {"native_arcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
495     {"native_close","(J)V", (void*) SkPathGlue::close},
496     {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
497     {"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
498     {"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
499     {"native_addArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
500     {"native_addRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
501     {"native_addRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
502     {"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
503     {"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
504     {"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
505     {"native_offset","(JFF)V", (void*) SkPathGlue::offset__FF},
506     {"native_setLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
507     {"native_transform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
508     {"native_transform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
509     {"native_op","(JJIJ)Z", (void*) SkPathGlue::op},
510     {"native_approximate", "(JF)[F", (void*) SkPathGlue::approximate},
511 };
512 
register_android_graphics_Path(JNIEnv * env)513 int register_android_graphics_Path(JNIEnv* env) {
514     return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods));
515 
516     static_assert(0  == SkPath::kCW_Direction,  "direction_mismatch");
517     static_assert(1  == SkPath::kCCW_Direction, "direction_mismatch");
518 }
519 
520 }
521