/* libs/android_runtime/android/graphics/Path.cpp ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ // This file was generated from the C++ include file: SkPath.h // Any changes made to this file will be discarded by the build. // To change this file, either edit the include, or device/tools/gluemaker/main.cpp, // or one of the auxilary file specifications in device/tools/gluemaker. #include "jni.h" #include "GraphicsJNI.h" #include "core_jni_helpers.h" #include "SkPath.h" #include "SkPathOps.h" #include #include #include namespace android { class SkPathGlue { public: static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); // Purge entries from the HWUI path cache if this path's data is unique if (obj->unique() && android::uirenderer::Caches::hasInstance()) { android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj); } delete obj; } static jlong init1(JNIEnv* env, jobject clazz) { return reinterpret_cast(new SkPath()); } static jlong init2(JNIEnv* env, jobject clazz, jlong valHandle) { SkPath* val = reinterpret_cast(valHandle); return reinterpret_cast(new SkPath(*val)); } static void reset(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); obj->reset(); } static void rewind(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); obj->rewind(); } static void assign(JNIEnv* env, jobject clazz, jlong dstHandle, jlong srcHandle) { SkPath* dst = reinterpret_cast(dstHandle); const SkPath* src = reinterpret_cast(srcHandle); *dst = *src; } static jboolean isConvex(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); return obj->isConvex(); } static jint getFillType(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); return obj->getFillType(); } static void setFillType(JNIEnv* env, jobject clazz, jlong pathHandle, jint ftHandle) { SkPath* path = reinterpret_cast(pathHandle); SkPath::FillType ft = static_cast(ftHandle); path->setFillType(ft); } static jboolean isEmpty(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); return obj->isEmpty(); } static jboolean isRect(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect) { SkRect rect; SkPath* obj = reinterpret_cast(objHandle); jboolean result = obj->isRect(&rect); GraphicsJNI::rect_to_jrectf(rect, env, jrect); return result; } static void computeBounds(JNIEnv* env, jobject clazz, jlong objHandle, jobject jbounds) { SkPath* obj = reinterpret_cast(objHandle); const SkRect& bounds = obj->getBounds(); GraphicsJNI::rect_to_jrectf(bounds, env, jbounds); } static void incReserve(JNIEnv* env, jobject clazz, jlong objHandle, jint extraPtCount) { SkPath* obj = reinterpret_cast(objHandle); obj->incReserve(extraPtCount); } static void moveTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) { SkPath* obj = reinterpret_cast(objHandle); obj->moveTo(x, y); } static void rMoveTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkPath* obj = reinterpret_cast(objHandle); obj->rMoveTo(dx, dy); } static void lineTo__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y) { SkPath* obj = reinterpret_cast(objHandle); obj->lineTo(x, y); } static void rLineTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkPath* obj = reinterpret_cast(objHandle); obj->rLineTo(dx, dy); } static void quadTo__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2) { SkPath* obj = reinterpret_cast(objHandle); obj->quadTo(x1, y1, x2, y2); } static void rQuadTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2) { SkPath* obj = reinterpret_cast(objHandle); obj->rQuadTo(dx1, dy1, dx2, dy2); } static void cubicTo__FFFFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { SkPath* obj = reinterpret_cast(objHandle); obj->cubicTo(x1, y1, x2, y2, x3, y3); } static void rCubicTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { SkPath* obj = reinterpret_cast(objHandle); obj->rCubicTo(x1, y1, x2, y2, x3, y3); } static void arcTo(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean forceMoveTo) { SkPath* obj = reinterpret_cast(objHandle); SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo); } static void close(JNIEnv* env, jobject clazz, jlong objHandle) { SkPath* obj = reinterpret_cast(objHandle); obj->close(); } static void addRect(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath::Direction dir = static_cast(dirHandle); obj->addRect(left, top, right, bottom, dir); } static void addOval(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath::Direction dir = static_cast(dirHandle); SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); obj->addOval(oval, dir); } static void addCircle(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y, jfloat radius, jint dirHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath::Direction dir = static_cast(dirHandle); obj->addCircle(x, y, radius, dir); } static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) { SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); SkPath* obj = reinterpret_cast(objHandle); obj->addArc(oval, startAngle, sweepAngle); } static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) { SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); SkPath* obj = reinterpret_cast(objHandle); SkPath::Direction dir = static_cast(dirHandle); obj->addRoundRect(rect, rx, ry, dir); } static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) { SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); SkPath* obj = reinterpret_cast(objHandle); SkPath::Direction dir = static_cast(dirHandle); AutoJavaFloatArray afa(env, array, 8); #ifdef SK_SCALAR_IS_FLOAT const float* src = afa.ptr(); #else #error Need to convert float array to SkScalar array before calling the following function. #endif obj->addRoundRect(rect, src, dir); } static void addPath__PathFF(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jfloat dx, jfloat dy) { SkPath* obj = reinterpret_cast(objHandle); SkPath* src = reinterpret_cast(srcHandle); obj->addPath(*src, dx, dy); } static void addPath__Path(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath* src = reinterpret_cast(srcHandle); obj->addPath(*src); } static void addPath__PathMatrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong matrixHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath* src = reinterpret_cast(srcHandle); SkMatrix* matrix = reinterpret_cast(matrixHandle); obj->addPath(*src, *matrix); } static void offset__FFPath(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy, jlong dstHandle) { SkPath* obj = reinterpret_cast(objHandle); SkPath* dst = reinterpret_cast(dstHandle); obj->offset(dx, dy, dst); } static void offset__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkPath* obj = reinterpret_cast(objHandle); obj->offset(dx, dy); } static void setLastPoint(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkPath* obj = reinterpret_cast(objHandle); obj->setLastPt(dx, dy); } static void transform__MatrixPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle, jlong dstHandle) { SkPath* obj = reinterpret_cast(objHandle); SkMatrix* matrix = reinterpret_cast(matrixHandle); SkPath* dst = reinterpret_cast(dstHandle); obj->transform(*matrix, dst); } static void transform__Matrix(JNIEnv* env, jobject clazz, jlong objHandle, jlong matrixHandle) { SkPath* obj = reinterpret_cast(objHandle); SkMatrix* matrix = reinterpret_cast(matrixHandle); obj->transform(*matrix); } static jboolean op(JNIEnv* env, jobject clazz, jlong p1Handle, jlong p2Handle, jint opHandle, jlong rHandle) { SkPath* p1 = reinterpret_cast(p1Handle); SkPath* p2 = reinterpret_cast(p2Handle); SkPathOp op = static_cast(opHandle); SkPath* r = reinterpret_cast(rHandle); return Op(*p1, *p2, op, r); } typedef SkPoint (*bezierCalculation)(float t, const SkPoint* points); static void addMove(std::vector& segmentPoints, std::vector& lengths, const SkPoint& point) { float length = 0; if (!lengths.empty()) { length = lengths.back(); } segmentPoints.push_back(point); lengths.push_back(length); } static void addLine(std::vector& segmentPoints, std::vector& lengths, const SkPoint& toPoint) { if (segmentPoints.empty()) { segmentPoints.push_back(SkPoint::Make(0, 0)); lengths.push_back(0); } else if (segmentPoints.back() == toPoint) { return; // Empty line } float length = lengths.back() + SkPoint::Distance(segmentPoints.back(), toPoint); segmentPoints.push_back(toPoint); lengths.push_back(length); } static float cubicCoordinateCalculation(float t, float p0, float p1, float p2, float p3) { float oneMinusT = 1 - t; float oneMinusTSquared = oneMinusT * oneMinusT; float oneMinusTCubed = oneMinusTSquared * oneMinusT; float tSquared = t * t; float tCubed = tSquared * t; return (oneMinusTCubed * p0) + (3 * oneMinusTSquared * t * p1) + (3 * oneMinusT * tSquared * p2) + (tCubed * p3); } static SkPoint cubicBezierCalculation(float t, const SkPoint* points) { float x = cubicCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x(), points[3].x()); float y = cubicCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y(), points[3].y()); return SkPoint::Make(x, y); } static float quadraticCoordinateCalculation(float t, float p0, float p1, float p2) { float oneMinusT = 1 - t; return oneMinusT * ((oneMinusT * p0) + (t * p1)) + t * ((oneMinusT * p1) + (t * p2)); } static SkPoint quadraticBezierCalculation(float t, const SkPoint* points) { float x = quadraticCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x()); float y = quadraticCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y()); return SkPoint::Make(x, y); } // Subdivide a section of the Bezier curve, set the mid-point and the mid-t value. // Returns true if further subdivision is necessary as defined by errorSquared. static bool subdividePoints(const SkPoint* points, bezierCalculation bezierFunction, float t0, const SkPoint &p0, float t1, const SkPoint &p1, float& midT, SkPoint &midPoint, float errorSquared) { midT = (t1 + t0) / 2; float midX = (p1.x() + p0.x()) / 2; float midY = (p1.y() + p0.y()) / 2; midPoint = (*bezierFunction)(midT, points); float xError = midPoint.x() - midX; float yError = midPoint.y() - midY; float midErrorSquared = (xError * xError) + (yError * yError); return midErrorSquared > errorSquared; } // Divides Bezier curves until linear interpolation is very close to accurate, using // errorSquared as a metric. Cubic Bezier curves can have an inflection point that improperly // short-circuit subdivision. If you imagine an S shape, the top and bottom points being the // starting and end points, linear interpolation would mark the center where the curve places // the point. It is clearly not the case that we can linearly interpolate at that point. // doubleCheckDivision forces a second examination between subdivisions to ensure that linear // interpolation works. static void addBezier(const SkPoint* points, bezierCalculation bezierFunction, std::vector& segmentPoints, std::vector& lengths, float errorSquared, bool doubleCheckDivision) { typedef std::map PointMap; PointMap tToPoint; tToPoint[0] = (*bezierFunction)(0, points); tToPoint[1] = (*bezierFunction)(1, points); PointMap::iterator iter = tToPoint.begin(); PointMap::iterator next = iter; ++next; while (next != tToPoint.end()) { bool needsSubdivision = true; SkPoint midPoint; do { float midT; needsSubdivision = subdividePoints(points, bezierFunction, iter->first, iter->second, next->first, next->second, midT, midPoint, errorSquared); if (!needsSubdivision && doubleCheckDivision) { SkPoint quarterPoint; float quarterT; needsSubdivision = subdividePoints(points, bezierFunction, iter->first, iter->second, midT, midPoint, quarterT, quarterPoint, errorSquared); if (needsSubdivision) { // Found an inflection point. No need to double-check. doubleCheckDivision = false; } } if (needsSubdivision) { next = tToPoint.insert(iter, PointMap::value_type(midT, midPoint)); } } while (needsSubdivision); iter = next; next++; } // Now that each division can use linear interpolation with less than the allowed error for (iter = tToPoint.begin(); iter != tToPoint.end(); ++iter) { addLine(segmentPoints, lengths, iter->second); } } static void createVerbSegments(SkPath::Verb verb, const SkPoint* points, std::vector& segmentPoints, std::vector& lengths, float errorSquared) { switch (verb) { case SkPath::kMove_Verb: addMove(segmentPoints, lengths, points[0]); break; case SkPath::kClose_Verb: addLine(segmentPoints, lengths, points[0]); break; case SkPath::kLine_Verb: addLine(segmentPoints, lengths, points[1]); break; case SkPath::kQuad_Verb: addBezier(points, quadraticBezierCalculation, segmentPoints, lengths, errorSquared, false); break; case SkPath::kCubic_Verb: addBezier(points, cubicBezierCalculation, segmentPoints, lengths, errorSquared, true); break; default: // Leave element as NULL, Conic sections are not supported. break; } } // Returns a float[] with each point along the path represented by 3 floats // * fractional length along the path that the point resides // * x coordinate // * y coordinate // Note that more than one point may have the same length along the path in // the case of a move. // NULL can be returned if the Path is empty. static jfloatArray approximate(JNIEnv* env, jclass, jlong pathHandle, float acceptableError) { SkPath* path = reinterpret_cast(pathHandle); SkASSERT(path); SkPath::Iter pathIter(*path, false); SkPath::Verb verb; SkPoint points[4]; std::vector segmentPoints; std::vector lengths; float errorSquared = acceptableError * acceptableError; while ((verb = pathIter.next(points, false)) != SkPath::kDone_Verb) { createVerbSegments(verb, points, segmentPoints, lengths, errorSquared); } if (segmentPoints.empty()) { int numVerbs = path->countVerbs(); if (numVerbs == 1) { addMove(segmentPoints, lengths, path->getPoint(0)); } else { // Invalid or empty path. Fall back to point(0,0) addMove(segmentPoints, lengths, SkPoint()); } } float totalLength = lengths.back(); if (totalLength == 0) { // Lone Move instructions should still be able to animate at the same value. segmentPoints.push_back(segmentPoints.back()); lengths.push_back(1); totalLength = 1; } size_t numPoints = segmentPoints.size(); size_t approximationArraySize = numPoints * 3; float* approximation = new float[approximationArraySize]; int approximationIndex = 0; for (size_t i = 0; i < numPoints; i++) { const SkPoint& point = segmentPoints[i]; approximation[approximationIndex++] = lengths[i] / totalLength; approximation[approximationIndex++] = point.x(); approximation[approximationIndex++] = point.y(); } jfloatArray result = env->NewFloatArray(approximationArraySize); env->SetFloatArrayRegion(result, 0, approximationArraySize, approximation); delete[] approximation; return result; } }; static JNINativeMethod methods[] = { {"finalizer", "(J)V", (void*) SkPathGlue::finalizer}, {"init1","()J", (void*) SkPathGlue::init1}, {"init2","(J)J", (void*) SkPathGlue::init2}, {"native_reset","(J)V", (void*) SkPathGlue::reset}, {"native_rewind","(J)V", (void*) SkPathGlue::rewind}, {"native_set","(JJ)V", (void*) SkPathGlue::assign}, {"native_isConvex","(J)Z", (void*) SkPathGlue::isConvex}, {"native_getFillType","(J)I", (void*) SkPathGlue::getFillType}, {"native_setFillType","(JI)V", (void*) SkPathGlue::setFillType}, {"native_isEmpty","(J)Z", (void*) SkPathGlue::isEmpty}, {"native_isRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect}, {"native_computeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds}, {"native_incReserve","(JI)V", (void*) SkPathGlue::incReserve}, {"native_moveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF}, {"native_rMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo}, {"native_lineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF}, {"native_rLineTo","(JFF)V", (void*) SkPathGlue::rLineTo}, {"native_quadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF}, {"native_rQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo}, {"native_cubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF}, {"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo}, {"native_arcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo}, {"native_close","(J)V", (void*) SkPathGlue::close}, {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect}, {"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval}, {"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle}, {"native_addArc","(JFFFFFF)V", (void*) SkPathGlue::addArc}, {"native_addRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY}, {"native_addRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8}, {"native_addPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF}, {"native_addPath","(JJ)V", (void*) SkPathGlue::addPath__Path}, {"native_addPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix}, {"native_offset","(JFFJ)V", (void*) SkPathGlue::offset__FFPath}, {"native_offset","(JFF)V", (void*) SkPathGlue::offset__FF}, {"native_setLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint}, {"native_transform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath}, {"native_transform","(JJ)V", (void*) SkPathGlue::transform__Matrix}, {"native_op","(JJIJ)Z", (void*) SkPathGlue::op}, {"native_approximate", "(JF)[F", (void*) SkPathGlue::approximate}, }; int register_android_graphics_Path(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods)); } }