platform/cc/interop.cc (909 lines of code) (raw):

#include <array> #include <cstring> #include "interop.hh" #include <iostream> #include <jni.h> #include <memory> #include "shaper/interop.hh" #include "src/utils/SkUTF.h" #include "paragraph/interop.hh" namespace java { namespace io { namespace OutputStream { jclass cls; jmethodID write; jmethodID flush; void onLoad(JNIEnv* env) { jclass cls = env->FindClass("java/io/OutputStream"); write = env->GetMethodID(cls, "write", "([BII)V"); flush = env->GetMethodID(cls, "flush", "()V"); } } } namespace lang { namespace Float { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("java/lang/Float"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(F)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace RuntimeException { jclass cls; void onLoad(JNIEnv* env) { jclass local = env->FindClass("java/lang/RuntimeException"); cls = static_cast<jclass>(env->NewGlobalRef(local)); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace String { jclass cls; void onLoad(JNIEnv* env) { jclass local = env->FindClass("java/lang/String"); cls = static_cast<jclass>(env->NewGlobalRef(local)); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace Throwable { jmethodID printStackTrace; void onLoad(JNIEnv* env) { jclass cls = env->FindClass("java/lang/Throwable"); printStackTrace = env->GetMethodID(cls, "printStackTrace", "()V"); } bool exceptionThrown(JNIEnv* env) { if (env->ExceptionCheck()) { auto th = skija::AutoLocal<jthrowable>(env, env->ExceptionOccurred()); env->CallVoidMethod(th.get(), printStackTrace); env->ExceptionCheck(); // ignore return true; } else return false; } } } namespace util { namespace Iterator { jclass cls; jmethodID next; jmethodID hasNext; void onLoad(JNIEnv* env) { jclass local = env->FindClass("java/util/Iterator"); cls = static_cast<jclass>(env->NewGlobalRef(local)); next = env->GetMethodID(cls, "next", "()Ljava/lang/Object;"); hasNext = env->GetMethodID(cls, "hasNext", "()Z"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace function { namespace BooleanSupplier { jclass cls; jmethodID apply; void onLoad(JNIEnv* env) { jclass local = env->FindClass("java/util/function/BooleanSupplier"); cls = static_cast<jclass>(env->NewGlobalRef(local)); apply = env->GetMethodID(cls, "getAsBoolean", "()Z"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } } } void onLoad(JNIEnv* env) { io::OutputStream::onLoad(env); lang::Float::onLoad(env); lang::RuntimeException::onLoad(env); lang::String::onLoad(env); lang::Throwable::onLoad(env); util::Iterator::onLoad(env); util::function::BooleanSupplier::onLoad(env); } void onUnload(JNIEnv* env) { util::function::BooleanSupplier::onUnload(env); util::Iterator::onUnload(env); lang::String::onUnload(env); lang::RuntimeException::onUnload(env); lang::Float::onUnload(env); } } namespace skija { namespace AnimationFrameInfo { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/AnimationFrameInfo"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(IIZIZIILorg/jetbrains/skija/IRect;)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject toJava(JNIEnv* env, const SkCodec::FrameInfo& i) { SkBlendMode blend; switch (i.fBlend) { case SkCodecAnimation::Blend::kSrcOver: blend = SkBlendMode::kSrcOver; break; case SkCodecAnimation::Blend::kSrc: blend = SkBlendMode::kSrc; break; } jobject res = env->NewObject(cls, ctor, i.fRequiredFrame, i.fDuration, i.fFullyReceived, static_cast<jint>(i.fAlphaType), i.fHasAlphaWithinBounds, static_cast<jint>(i.fDisposalMethod), static_cast<jint>(blend), IRect::fromSkIRect(env, i.fFrameRect)); return java::lang::Throwable::exceptionThrown(env) ? nullptr : res; } } namespace Color4f { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/Color4f"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(FFFF)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace Drawable { jclass cls; jmethodID onDraw; jmethodID onGetBounds; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/Drawable"); cls = static_cast<jclass>(env->NewGlobalRef(local)); onDraw = env->GetMethodID(cls, "_onDraw", "(J)V"); onGetBounds = env->GetMethodID(cls, "onGetBounds", "()Lorg/jetbrains/skija/Rect;"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace FontFamilyName { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontFamilyName"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace FontFeature { jclass cls; jmethodID ctor; jfieldID tag; jfieldID value; jfieldID start; jfieldID end; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontFeature"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;I)V"); tag = env->GetFieldID(cls, "_tag", "I"); value = env->GetFieldID(cls, "_value", "I"); start = env->GetFieldID(cls, "_start", "J"); end = env->GetFieldID(cls, "_end", "J"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } std::vector<SkShaper::Feature> fromJavaArray(JNIEnv* env, jobjectArray featuresArr) { jsize featuresLen = featuresArr == nullptr ? 0 : env->GetArrayLength(featuresArr); std::vector<SkShaper::Feature> features(featuresLen); for (int i = 0; i < featuresLen; ++i) { skija::AutoLocal<jobject> featureObj(env, env->GetObjectArrayElement(featuresArr, i)); features[i] = {static_cast<SkFourByteTag>(env->GetIntField(featureObj.get(), skija::FontFeature::tag)), static_cast<uint32_t>(env->GetIntField(featureObj.get(), skija::FontFeature::value)), static_cast<size_t>(env->GetLongField(featureObj.get(), skija::FontFeature::start)), static_cast<size_t>(env->GetLongField(featureObj.get(), skija::FontFeature::end))}; } return features; } } namespace FontMetrics { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontMetrics"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(FFFFFFFFFFFLjava/lang/Float;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Float;)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject toJava(JNIEnv* env, const SkFontMetrics& m) { float f1, f2, f3, f4; return env->NewObject(cls, ctor, m.fTop, m.fAscent, m.fDescent, m.fBottom, m.fLeading, m.fAvgCharWidth, m.fMaxCharWidth, m.fXMin, m.fXMax, m.fXHeight, m.fCapHeight, m.hasUnderlineThickness(&f1) ? javaFloat(env, f1) : nullptr, m.hasUnderlinePosition(&f2) ? javaFloat(env, f2) : nullptr, m.hasStrikeoutThickness(&f3) ? javaFloat(env, f3) : nullptr, m.hasStrikeoutPosition(&f4) ? javaFloat(env, f4) : nullptr); } } namespace FontMgr { jclass cls; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontMgr"); cls = static_cast<jclass>(env->NewGlobalRef(local)); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace FontStyle { SkFontStyle fromJava(jint style) { return SkFontStyle(style & 0xFFFF, (style >> 16) & 0xFF, static_cast<SkFontStyle::Slant>((style >> 24) & 0xFF)); } jint toJava(const SkFontStyle& fs) { return (static_cast<int>(fs.slant()) << 24)| (fs.width() << 16) | fs.weight(); } } namespace FontVariation { jclass cls; jmethodID ctor; jfieldID tag; jfieldID value; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontVariation"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(IF)V"); tag = env->GetFieldID(cls, "_tag", "I"); value = env->GetFieldID(cls, "_value", "F"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace FontVariationAxis { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/FontVariationAxis"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(IFFFZ)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace ImageInfo { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/ImageInfo"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(IIIIJ)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject toJava(JNIEnv* env, const SkImageInfo& info) { return env->NewObject(cls, ctor, info.width(), info.height(), static_cast<jint>(info.colorType()), static_cast<jint>(info.alphaType()), reinterpret_cast<jlong>(info.refColorSpace().release())); } } namespace IPoint { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/IPoint"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(II)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject make(JNIEnv* env, jint x, jint y) { return env->NewObject(cls, ctor, x, y); } jobject fromSkIPoint(JNIEnv* env, const SkIPoint& p) { return env->NewObject(cls, ctor, p.fX, p.fY); } } namespace IRect { jclass cls; jmethodID makeLTRB; jfieldID left; jfieldID top; jfieldID right; jfieldID bottom; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/IRect"); cls = static_cast<jclass>(env->NewGlobalRef(local)); makeLTRB = env->GetStaticMethodID(cls, "makeLTRB", "(IIII)Lorg/jetbrains/skija/IRect;"); left = env->GetFieldID(cls, "_left", "I"); top = env->GetFieldID(cls, "_top", "I"); right = env->GetFieldID(cls, "_right", "I"); bottom = env->GetFieldID(cls, "_bottom", "I"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject fromSkIRect(JNIEnv* env, const SkIRect& rect) { jobject res = env->CallStaticObjectMethod(cls, makeLTRB, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); return java::lang::Throwable::exceptionThrown(env) ? nullptr : res; } std::unique_ptr<SkIRect> toSkIRect(JNIEnv* env, jobject obj) { if (obj == nullptr) return std::unique_ptr<SkIRect>(nullptr); else { return std::unique_ptr<SkIRect>(new SkIRect{ env->GetIntField(obj, left), env->GetIntField(obj, top), env->GetIntField(obj, right), env->GetIntField(obj, bottom) }); } } } namespace Path { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/Path"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(J)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace PathSegment { jclass cls; jmethodID ctorDone; jmethodID ctorMoveClose; jmethodID ctorLine; jmethodID ctorQuad; jmethodID ctorConic; jmethodID ctorCubic; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/PathSegment"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctorDone = env->GetMethodID(cls, "<init>", "()V"); ctorMoveClose = env->GetMethodID(cls, "<init>", "(IFFZ)V"); ctorLine = env->GetMethodID(cls, "<init>", "(FFFFZZ)V"); ctorQuad = env->GetMethodID(cls, "<init>", "(FFFFFFZ)V"); ctorConic = env->GetMethodID(cls, "<init>", "(FFFFFFFZ)V"); ctorCubic = env->GetMethodID(cls, "<init>", "(FFFFFFFFZ)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace Point { jclass cls; jmethodID ctor; jfieldID x; jfieldID y; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/Point"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(FF)V"); x = env->GetFieldID(cls, "_x", "F"); y = env->GetFieldID(cls, "_y", "F"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } jobject make(JNIEnv* env, float x, float y) { return env->NewObject(cls, ctor, x, y); } jobject fromSkPoint(JNIEnv* env, const SkPoint& p) { return env->NewObject(cls, ctor, p.fX, p.fY); } jobjectArray fromSkPoints(JNIEnv* env, const std::vector<SkPoint>& ps) { jobjectArray res = env->NewObjectArray((jsize) ps.size(), cls, nullptr); for (int i = 0; i < ps.size(); ++i) { skija::AutoLocal<jobject> pointObj(env, fromSkPoint(env, ps[i])); env->SetObjectArrayElement(res, i, pointObj.get()); } return res; } } namespace PaintFilterCanvas { JavaVM* _vm; jmethodID onFilterId; void onLoad(JNIEnv* env) { env->GetJavaVM(&_vm); jclass local = env->FindClass("org/jetbrains/skija/PaintFilterCanvas"); onFilterId = env->GetMethodID(local, "onFilter", "(J)Z"); } void onUnload(JNIEnv* env) { } bool onFilter(jobject obj, SkPaint& paint) { JNIEnv *env; _vm->AttachCurrentThread((void **) &env, NULL); jboolean result = env->CallBooleanMethod(obj, onFilterId, reinterpret_cast<jlong>(&paint)); _vm->DetachCurrentThread(); return result; } jobject attach(JNIEnv* env, jobject obj) { return env->NewGlobalRef(obj); } void detach(jobject obj) { JNIEnv *env; _vm->AttachCurrentThread((void **) &env, NULL); env->DeleteGlobalRef(obj); _vm->DetachCurrentThread(); } } namespace Rect { jclass cls; jmethodID makeLTRB; jfieldID left; jfieldID top; jfieldID right; jfieldID bottom; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/Rect"); cls = static_cast<jclass>(env->NewGlobalRef(local)); makeLTRB = env->GetStaticMethodID(cls, "makeLTRB", "(FFFF)Lorg/jetbrains/skija/Rect;"); left = env->GetFieldID(cls, "_left", "F"); top = env->GetFieldID(cls, "_top", "F"); right = env->GetFieldID(cls, "_right", "F"); bottom = env->GetFieldID(cls, "_bottom", "F"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } std::unique_ptr<SkRect> toSkRect(JNIEnv* env, jobject rectObj) { if (rectObj == nullptr) return std::unique_ptr<SkRect>(nullptr); else { SkRect* rect = new SkRect(); rect->setLTRB(env->GetFloatField(rectObj, left), env->GetFloatField(rectObj, top), env->GetFloatField(rectObj, right), env->GetFloatField(rectObj, bottom)); if (java::lang::Throwable::exceptionThrown(env)) return std::unique_ptr<SkRect>(nullptr); return std::unique_ptr<SkRect>(rect); } } jobject fromLTRB(JNIEnv* env, float left, float top, float right, float bottom) { jobject res = env->CallStaticObjectMethod(cls, makeLTRB, left, top, right, bottom); return java::lang::Throwable::exceptionThrown(env) ? nullptr : res; } jobject fromSkRect(JNIEnv* env, const SkRect& rect) { return fromLTRB(env, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } } namespace RRect { jclass cls; jmethodID makeLTRB1; jmethodID makeLTRB2; jmethodID makeLTRB4; jmethodID makeNinePatchLTRB; jmethodID makeComplexLTRB; jfieldID left; jfieldID top; jfieldID right; jfieldID bottom; jfieldID radii; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/RRect"); cls = static_cast<jclass>(env->NewGlobalRef(local)); makeLTRB1 = env->GetStaticMethodID(cls, "makeLTRB", "(FFFFF)Lorg/jetbrains/skija/RRect;"); makeLTRB2 = env->GetStaticMethodID(cls, "makeLTRB", "(FFFFFF)Lorg/jetbrains/skija/RRect;"); makeLTRB4 = env->GetStaticMethodID(cls, "makeLTRB", "(FFFFFFFF)Lorg/jetbrains/skija/RRect;"); makeNinePatchLTRB = env->GetStaticMethodID(cls, "makeNinePatchLTRB", "(FFFFFFFF)Lorg/jetbrains/skija/RRect;"); makeComplexLTRB = env->GetStaticMethodID(cls, "makeComplexLTRB", "(FFFF[F)Lorg/jetbrains/skija/RRect;"); left = env->GetFieldID(cls, "_left", "F"); top = env->GetFieldID(cls, "_top", "F"); right = env->GetFieldID(cls, "_right", "F"); bottom = env->GetFieldID(cls, "_bottom", "F"); radii = env->GetFieldID(cls, "_radii", "[F"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } SkRRect toSkRRect(JNIEnv* env, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloatArray jradii) { SkRect rect {left, top, right, bottom}; SkRRect rrect = SkRRect::MakeEmpty(); jfloat* radii = env->GetFloatArrayElements(jradii, 0); switch (env->GetArrayLength(jradii)) { case 1: rrect.setRectXY(rect, radii[0], radii[0]); break; case 2: rrect.setRectXY(rect, radii[0], radii[1]); break; case 4: { SkVector vradii[4] = {{radii[0], radii[0]}, {radii[1], radii[1]}, {radii[2], radii[2]}, {radii[3], radii[3]}}; rrect.setRectRadii(rect, vradii); break; } case 8: { SkVector vradii[4] = {{radii[0], radii[1]}, {radii[2], radii[3]}, {radii[4], radii[5]}, {radii[6], radii[7]}}; rrect.setRectRadii(rect, vradii); break; } } env->ReleaseFloatArrayElements(jradii, radii, 0); return rrect; } jobject fromSkRRect(JNIEnv* env, const SkRRect& rr) { const SkRect& r = rr.rect(); switch (rr.getType()) { case SkRRect::Type::kEmpty_Type: case SkRRect::Type::kRect_Type: return env->CallStaticObjectMethod(cls, makeLTRB1, r.fLeft, r.fTop, r.fRight, r.fBottom, 0); case SkRRect::Type::kOval_Type: case SkRRect::Type::kSimple_Type: { float rx = rr.getSimpleRadii().fX; float ry = rr.getSimpleRadii().fY; if (SkScalarNearlyEqual(rx, ry)) return env->CallStaticObjectMethod(cls, makeLTRB1, r.fLeft, r.fTop, r.fRight, r.fBottom, rx); else return env->CallStaticObjectMethod(cls, makeLTRB2, r.fLeft, r.fTop, r.fRight, r.fBottom, rx, ry); } case SkRRect::Type::kNinePatch_Type: return env->CallStaticObjectMethod(cls, makeNinePatchLTRB, r.fLeft, r.fTop, r.fRight, r.fBottom, rr.radii(SkRRect::Corner::kUpperLeft_Corner).fX, rr.radii(SkRRect::Corner::kUpperLeft_Corner).fY, rr.radii(SkRRect::Corner::kLowerRight_Corner).fX, rr.radii(SkRRect::Corner::kLowerRight_Corner).fY); case SkRRect::Type::kComplex_Type: std::vector<float> radii = { rr.radii(SkRRect::Corner::kUpperLeft_Corner).fX, rr.radii(SkRRect::Corner::kUpperLeft_Corner).fY, rr.radii(SkRRect::Corner::kUpperRight_Corner).fX, rr.radii(SkRRect::Corner::kUpperRight_Corner).fY, rr.radii(SkRRect::Corner::kLowerRight_Corner).fX, rr.radii(SkRRect::Corner::kLowerRight_Corner).fY, rr.radii(SkRRect::Corner::kLowerLeft_Corner).fX, rr.radii(SkRRect::Corner::kLowerLeft_Corner).fY }; return env->CallStaticObjectMethod(cls, makeComplexLTRB, r.fLeft, r.fTop, r.fRight, r.fBottom, javaFloatArray(env, radii)); } return nullptr; } } namespace RSXform { jclass cls; jmethodID ctor; void onLoad(JNIEnv* env) { jclass local = env->FindClass("org/jetbrains/skija/RSXform"); cls = static_cast<jclass>(env->NewGlobalRef(local)); ctor = env->GetMethodID(cls, "<init>", "(FFFF)V"); } void onUnload(JNIEnv* env) { env->DeleteGlobalRef(cls); } } namespace SamplingMode { SkSamplingOptions unpack(jlong val) { if (0x8000000000000000 & val) { val = val & 0x7FFFFFFFFFFFFFFF; float* ptr = reinterpret_cast<float*>(&val); return SkSamplingOptions(SkCubicResampler {ptr[1], ptr[0]}); } else { int32_t filter = (int32_t) ((val >> 32) & 0xFFFFFFFF); int32_t mipmap = (int32_t) (val & 0xFFFFFFFF); return SkSamplingOptions(static_cast<SkFilterMode>(filter), static_cast<SkMipmapMode>(mipmap)); } } } namespace SurfaceProps { jmethodID _getFlags; jmethodID _getPixelGeometryOrdinal; void onLoad(JNIEnv* env) { jclass cls = env->FindClass("org/jetbrains/skija/SurfaceProps"); _getFlags = env->GetMethodID(cls, "_getFlags", "()I"); _getPixelGeometryOrdinal = env->GetMethodID(cls, "_getPixelGeometryOrdinal", "()I"); } std::unique_ptr<SkSurfaceProps> toSkSurfaceProps(JNIEnv* env, jobject surfacePropsObj) { if (surfacePropsObj == nullptr) return std::unique_ptr<SkSurfaceProps>(nullptr); uint32_t flags = static_cast<uint32_t>(env->CallIntMethod(surfacePropsObj, _getFlags)); if (java::lang::Throwable::exceptionThrown(env)) std::unique_ptr<SkSurfaceProps>(nullptr); SkPixelGeometry geom = static_cast<SkPixelGeometry>(env->CallIntMethod(surfacePropsObj, _getPixelGeometryOrdinal)); if (java::lang::Throwable::exceptionThrown(env)) std::unique_ptr<SkSurfaceProps>(nullptr); return std::make_unique<SkSurfaceProps>(flags, geom); } } namespace impl { namespace Native { jfieldID _ptr; void onLoad(JNIEnv* env) { jclass cls = env->FindClass("org/jetbrains/skija/impl/Native"); _ptr = env->GetFieldID(cls, "_ptr", "J"); } void* fromJava(JNIEnv* env, jobject obj, jclass cls) { if (env->IsInstanceOf(obj, cls)) { jlong ptr = env->GetLongField(obj, skija::impl::Native::_ptr); return reinterpret_cast<void*>(static_cast<uintptr_t>(ptr)); } return nullptr; } } } void onLoad(JNIEnv* env) { AnimationFrameInfo::onLoad(env); Color4f::onLoad(env); Drawable::onLoad(env); FontFamilyName::onLoad(env); FontFeature::onLoad(env); FontMetrics::onLoad(env); FontVariation::onLoad(env); FontVariationAxis::onLoad(env); ImageInfo::onLoad(env); IPoint::onLoad(env); IRect::onLoad(env); Path::onLoad(env); PathSegment::onLoad(env); Point::onLoad(env); PaintFilterCanvas::onLoad(env); Rect::onLoad(env); RRect::onLoad(env); RSXform::onLoad(env); SurfaceProps::onLoad(env); impl::Native::onLoad(env); } void onUnload(JNIEnv* env) { RSXform::onUnload(env); RRect::onUnload(env); Rect::onUnload(env); PaintFilterCanvas::onUnload(env); Point::onUnload(env); PathSegment::onUnload(env); Path::onUnload(env); IRect::onUnload(env); IPoint::onUnload(env); ImageInfo::onUnload(env); FontVariationAxis::onUnload(env); FontVariation::onUnload(env); FontMetrics::onUnload(env); FontFeature::onUnload(env); FontFamilyName::onUnload(env); Drawable::onUnload(env); Color4f::onUnload(env); AnimationFrameInfo::onUnload(env); } } std::unique_ptr<SkMatrix> skMatrix(JNIEnv* env, jfloatArray matrixArray) { if (matrixArray == nullptr) return std::unique_ptr<SkMatrix>(nullptr); else { jfloat* m = env->GetFloatArrayElements(matrixArray, 0); SkMatrix* ptr = new SkMatrix(); ptr->setAll(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); env->ReleaseFloatArrayElements(matrixArray, m, 0); return std::unique_ptr<SkMatrix>(ptr); } } std::unique_ptr<SkM44> skM44(JNIEnv* env, jfloatArray matrixArray) { if (matrixArray == nullptr) return std::unique_ptr<SkM44>(nullptr); else { jfloat* m = env->GetFloatArrayElements(matrixArray, 0); SkM44* ptr = new SkM44(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]); env->ReleaseFloatArrayElements(matrixArray, m, 0); return std::unique_ptr<SkM44>(ptr); } } // bytes range bits byte 1 byte 2 byte 3 byte 4 byte 5 byte 6 // // Normal UTF-8: // // 1 U+ 0.. 7F 7 0xxxxxxx // 2 U+ 80.. 7FF 11 110xxxxx 10xxxxxx // 3 U+ 800.. FFFF 16 1110xxxx 10xxxxxx 10xxxxxx // 4 U+ 10000..10FFFF 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // // Modified UTF-8 (Java): // // 1 U+ 1.. 7F 7 0xxxxxxx // 2 U+ 0 11000000 10000000 // 2 U+ 80.. 7FF 11 110xxxxx 10xxxxxx // 3 U+ 800.. FFFF 16 1110xxxx 10xxxxxx 10xxxxxx // 6 U+ 10000..10FFFF 20 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx (+ 0x10000) size_t utfToUtf8(unsigned char *data, size_t len) { size_t read_offset = 0; size_t write_offset = 0; while (read_offset < len) { unsigned char byte1 = data[read_offset]; // single-byte U+0001..007F if ((byte1 & 0b10000000) == 0) { data[write_offset] = byte1; read_offset += 1; write_offset += 1; continue; } SkASSERT(read_offset + 1 < len); unsigned char byte2 = data[read_offset + 1]; // two-byte U+0000 if (byte1 == 0b11000000 && byte2 == 0b10000000) { data[write_offset] = 0; read_offset += 2; write_offset += 1; continue; } // two-byte U+0080..07FF if ((byte1 & 0b11100000) == 0b11000000) { SkASSERT((byte2 & 0b11000000) == 0b10000000); data[write_offset] = byte1; data[write_offset + 1] = byte2; read_offset += 2; write_offset += 2; continue; } SkASSERT(read_offset + 2 < len); unsigned char byte3 = data[read_offset + 2]; // Six-byte modified UTF-8 // 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx if (byte1 == 0b11101101 && (byte2 & 0b11110000) == 0b10100000) { SkASSERT(read_offset + 5 < len); unsigned char byte4 = data[read_offset + 3]; unsigned char byte5 = data[read_offset + 4]; unsigned char byte6 = data[read_offset + 5]; SkASSERT((byte3 & 0b11000000) == 0b10000000); SkASSERT(byte4 == 0b11101101); SkASSERT((byte5 & 0b11110000) == 0b10110000); SkASSERT((byte6 & 0b11000000) == 0b10000000); uint32_t codepoint = (((byte2 & 0b00001111) << 16) | ((byte3 & 0b00111111) << 10) | ((byte5 & 0b00001111) << 6) | (byte6 & 0b00111111)) + 0x10000; // Four-byte UTF-8 // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx data[write_offset] = 0b11110000 | ((codepoint >> 18) & 0b00000111); data[write_offset + 1] = 0b10000000 | ((codepoint >> 12) & 0b00111111); data[write_offset + 2] = 0b10000000 | ((codepoint >> 6) & 0b00111111); data[write_offset + 3] = 0b10000000 | (codepoint & 0b00111111); read_offset += 6; write_offset += 4; continue; } // three-byte U+0800..FFFF if ((byte1 & 0b11110000) == 0b11100000) { SkASSERT((byte2 & 0b11000000) == 0b10000000); SkASSERT((byte3 & 0b11000000) == 0b10000000); data[write_offset] = byte1; data[write_offset + 1] = byte2; data[write_offset + 2] = byte3; read_offset += 3; write_offset += 3; continue; } } return write_offset; } SkString skString(JNIEnv* env, jstring s) { if (s == nullptr) { return SkString(); } else { jsize utfUnits = env->GetStringUTFLength(s); jsize utf16Units = env->GetStringLength(s); SkString res(utfUnits); env->GetStringUTFRegion(s, 0, utf16Units, res.writable_str()); size_t utf8Units = utfToUtf8((unsigned char *) res.writable_str(), utfUnits); res.resize(utf8Units); return res; } } jstring javaString(JNIEnv* env, const SkString& str) { return javaString(env, str.c_str(), str.size()); } jstring javaString(JNIEnv* env, const char* chars, size_t len) { if (!chars || !len) return nullptr; int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, chars, len); auto utf16 = std::unique_ptr<uint16_t[]>(new uint16_t[utf16Units]); SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, chars, len); return env->NewString(utf16.get(), utf16Units); } jstring javaString(JNIEnv* env, const char* chars) { return chars ? javaString(env, chars, strlen(chars)) : nullptr; } jobject javaFloat(JNIEnv* env, SkScalar val) { return env->NewObject(java::lang::Float::cls, java::lang::Float::ctor, val); } jlong packTwoInts(int32_t a, int32_t b) { return (uint64_t (a) << 32) | b; } jlong packIPoint(SkIPoint p) { return packTwoInts(p.fX, p.fY); } jlong packISize(SkISize p) { return packTwoInts(p.fWidth, p.fHeight); } jbyteArray javaByteArray(JNIEnv* env, const std::vector<jbyte>& bytes) { jbyteArray res = env->NewByteArray((jsize) bytes.size()); env->SetByteArrayRegion(res, 0, (jsize) bytes.size(), bytes.data()); return res; } jshortArray javaShortArray(JNIEnv* env, const std::vector<jshort>& shorts) { jshortArray res = env->NewShortArray((jsize) shorts.size()); env->SetShortArrayRegion(res, 0, (jsize) shorts.size(), shorts.data()); return res; } jintArray javaIntArray(JNIEnv* env, const std::vector<jint>& ints) { jintArray res = env->NewIntArray((jsize) ints.size()); env->SetIntArrayRegion( res, 0, (jsize) ints.size(), ints.data()); return res; } jlongArray javaLongArray(JNIEnv* env, const std::vector<jlong>& longs) { jlongArray res = env->NewLongArray((jsize) longs.size()); env->SetLongArrayRegion(res, 0, (jsize) longs.size(), longs.data()); return res; } jfloatArray javaFloatArray(JNIEnv* env, const std::vector<jfloat>& floats) { jfloatArray res = env->NewFloatArray((jsize) floats.size()); env->SetFloatArrayRegion(res, 0, (jsize) floats.size(), floats.data()); return res; } std::vector<SkString> skStringVector(JNIEnv* env, jobjectArray arr) { if (arr == nullptr) { return std::vector<SkString>(0); } else { jsize len = env->GetArrayLength(arr); std::vector<SkString> res(len); for (jint i = 0; i < len; ++i) { jstring str = static_cast<jstring>(env->GetObjectArrayElement(arr, i)); res[i] = skString(env, str); env->DeleteLocalRef(str); } return res; } } jobjectArray javaStringArray(JNIEnv* env, const std::vector<SkString>& strings) { jobjectArray res = env->NewObjectArray((jsize) strings.size(), java::lang::String::cls, nullptr); for (jint i = 0; i < (jsize) strings.size(); ++i) { skija::AutoLocal<jstring> str(env, javaString(env, strings[i])); env->SetObjectArrayElement(res, i, str.get()); } return res; } void deleteJBytes(void* addr, void*) { delete[] (jbyte*) addr; } skija::UtfIndicesConverter::UtfIndicesConverter(const char* chars8, size_t len8): fStart8(chars8), fPtr8(chars8), fEnd8(chars8 + len8), fPos16(0) {} skija::UtfIndicesConverter::UtfIndicesConverter(const SkString& str): skija::UtfIndicesConverter::UtfIndicesConverter(str.c_str(), str.size()) {} size_t skija::UtfIndicesConverter::from16To8(uint32_t i16) { if (i16 >= fPos16) { // if new i16 >= last fPos16, continue from where we started } else { fPtr8 = fStart8; fPos16 = 0; } while (fPtr8 < fEnd8 && fPos16 < i16) { SkUnichar u = SkUTF::NextUTF8(&fPtr8, fEnd8); fPos16 += (uint32_t) SkUTF::ToUTF16(u); } return fPtr8 - fStart8; } uint32_t skija::UtfIndicesConverter::from8To16(size_t i8) { if (i8 >= (size_t) (fPtr8 - fStart8)) { // if new i8 >= last fPtr8, continue from where we started } else { fPtr8 = fStart8; fPos16 = 0; } while (fPtr8 < fEnd8 && (size_t) (fPtr8 - fStart8) < i8) { SkUnichar u = SkUTF::NextUTF8(&fPtr8, fEnd8); fPos16 += (uint32_t) SkUTF::ToUTF16(u); } return fPos16; }