summaryrefslogtreecommitdiff
path: root/libs/hwui/jni/Paint.cpp
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2020-02-21 11:43:02 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-02-21 11:43:02 +0000
commit76e7430baead21c67fd4c81a36774a6461d2fb99 (patch)
tree239a62b59c57b4b95d8e82943bf9a48e84439819 /libs/hwui/jni/Paint.cpp
parent1ab8e44c06ca05806a81c6737cb3f6e316d8d25a (diff)
parent2173ea286afff6766043227de0bc2d82d9595f77 (diff)
Merge changes from topic "HWUI_JNI"
* changes: Export symbols for the newly exposed APEX/internal headers Remove dependence on libandroid_runtime from Bitmap.cpp Update Region.cpp to use AParcel NDK APIs Cleanup header and build targets for libhwui clients. Remove dependencies on headers outside UI module Cleanup LOG_TAG when bundled in HWUI Move android.graphics JNI & APEX files into HWUI
Diffstat (limited to 'libs/hwui/jni/Paint.cpp')
-rw-r--r--libs/hwui/jni/Paint.cpp1159
1 files changed, 1159 insertions, 0 deletions
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
new file mode 100644
index 000000000000..df8635a8fe5a
--- /dev/null
+++ b/libs/hwui/jni/Paint.cpp
@@ -0,0 +1,1159 @@
+/* libs/android_runtime/android/graphics/Paint.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.
+*/
+
+#undef LOG_TAG
+#define LOG_TAG "Paint"
+
+#include <utils/Log.h>
+
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "SkBlurDrawLooper.h"
+#include "SkColorFilter.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
+#include "SkFontTypes.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkShader.h"
+#include "SkBlendMode.h"
+#include "unicode/uloc.h"
+#include "unicode/ushape.h"
+#include "utils/Blur.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
+#include <minikin/GraphemeBreak.h>
+#include <minikin/LocaleList.h>
+#include <minikin/Measurement.h>
+#include <minikin/MinikinPaint.h>
+#include <unicode/utf16.h>
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace android {
+
+struct JMetricsID {
+ jfieldID top;
+ jfieldID ascent;
+ jfieldID descent;
+ jfieldID bottom;
+ jfieldID leading;
+};
+
+static jclass gFontMetrics_class;
+static JMetricsID gFontMetrics_fieldID;
+
+static jclass gFontMetricsInt_class;
+static JMetricsID gFontMetricsInt_fieldID;
+
+static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
+ const SkPoint pos[], SkPath* dst) {
+ dst->reset();
+ struct Rec {
+ SkPath* fDst;
+ const SkPoint* fPos;
+ } rec = { dst, pos };
+ font.getPaths(glyphs, count, [](const SkPath* src, const SkMatrix& mx, void* ctx) {
+ Rec* rec = (Rec*)ctx;
+ if (src) {
+ SkMatrix tmp(mx);
+ tmp.postTranslate(rec->fPos->fX, rec->fPos->fY);
+ rec->fDst->addPath(*src, tmp);
+ }
+ rec->fPos += 1;
+ }, &rec);
+}
+
+namespace PaintGlue {
+ enum MoveOpt {
+ AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
+ };
+
+ static void deletePaint(Paint* paint) {
+ delete paint;
+ }
+
+ static jlong getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint));
+ }
+
+ static jlong init(JNIEnv* env, jobject) {
+ return reinterpret_cast<jlong>(new Paint);
+ }
+
+ static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Paint* obj = new Paint(*paint);
+ return reinterpret_cast<jlong>(obj);
+ }
+
+ static int breakText(JNIEnv* env, const Paint& paint, const Typeface* typeface,
+ const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
+ const bool forwardScan) {
+ size_t measuredCount = 0;
+ float measured = 0;
+
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text,
+ 0, count, count, advancesArray.get());
+
+ for (int i = 0; i < count; i++) {
+ // traverse in the given direction
+ int index = forwardScan ? i : (count - i - 1);
+ float width = advancesArray[index];
+ if (measured + width > maxWidth) {
+ break;
+ }
+ // properly handle clusters when scanning backwards
+ if (forwardScan || width != 0.0f) {
+ measuredCount = i + 1;
+ }
+ measured += width;
+ }
+
+ if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
+ AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
+ jfloat* array = autoMeasured.ptr();
+ array[0] = measured;
+ }
+ return measuredCount;
+ }
+
+ static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray jtext,
+ jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+ NPE_CHECK_RETURN_ZERO(env, jtext);
+
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+
+ bool forwardTextDirection;
+ if (count < 0) {
+ forwardTextDirection = false;
+ count = -count;
+ }
+ else {
+ forwardTextDirection = true;
+ }
+
+ if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
+ doThrowAIOOBE(env);
+ return 0;
+ }
+
+ const jchar* text = env->GetCharArrayElements(jtext, nullptr);
+ count = breakText(env, *paint, typeface, text + index, count, maxWidth,
+ bidiFlags, jmeasuredWidth, forwardTextDirection);
+ env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
+ JNI_ABORT);
+ return count;
+ }
+
+ static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jstring jtext,
+ jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+ NPE_CHECK_RETURN_ZERO(env, jtext);
+
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+
+ int count = env->GetStringLength(jtext);
+ const jchar* text = env->GetStringChars(jtext, nullptr);
+ count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
+ env->ReleaseStringChars(jtext, text);
+ return count;
+ }
+
+ static jfloat doTextAdvances(JNIEnv *env, Paint *paint, const Typeface* typeface,
+ const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
+ jfloatArray advances, jint advancesIndex) {
+ NPE_CHECK_RETURN_ZERO(env, text);
+
+ if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
+ doThrowAIOOBE(env);
+ return 0;
+ }
+ if (count == 0) {
+ return 0;
+ }
+ if (advances) {
+ size_t advancesLength = env->GetArrayLength(advances);
+ if ((size_t)(count + advancesIndex) > advancesLength) {
+ doThrowAIOOBE(env);
+ return 0;
+ }
+ }
+ std::unique_ptr<jfloat[]> advancesArray;
+ if (advances) {
+ advancesArray.reset(new jfloat[count]);
+ }
+ const float advance = MinikinUtils::measureText(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount,
+ advancesArray.get());
+ if (advances) {
+ env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
+ }
+ return advance;
+ }
+
+ static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
+ jint bidiFlags, jfloatArray advances, jint advancesIndex) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ jchar* textArray = env->GetCharArrayElements(text, nullptr);
+ jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
+ index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ return result;
+ }
+
+ static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
+ jfloatArray advances, jint advancesIndex) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const jchar* textArray = env->GetStringChars(text, nullptr);
+ jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
+ advances, advancesIndex);
+ env->ReleaseStringChars(text, textArray);
+ return result;
+ }
+
+ static jint doTextRunCursor(JNIEnv *env, Paint* paint, const Typeface* typeface,
+ const jchar *text, jint start, jint count, jint dir, jint offset, jint opt) {
+ minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt);
+ minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
+ advancesArray.get());
+ size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
+ start, count, offset, moveOpt);
+ return static_cast<jint>(result);
+ }
+
+ static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
+ jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ jchar* textArray = env->GetCharArrayElements(text, nullptr);
+ jint result = doTextRunCursor(env, paint, typeface, textArray,
+ contextStart, contextCount, dir, offset, cursorOpt);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ return result;
+ }
+
+ static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jstring text, jint contextStart, jint contextEnd, jint dir, jint offset,
+ jint cursorOpt) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const jchar* textArray = env->GetStringChars(text, nullptr);
+ jint result = doTextRunCursor(env, paint, typeface, textArray,
+ contextStart, contextEnd - contextStart, dir, offset, cursorOpt);
+ env->ReleaseStringChars(text, textArray);
+ return result;
+ }
+
+ class GetTextFunctor {
+ public:
+ GetTextFunctor(const minikin::Layout& layout, SkPath* path, jfloat x, jfloat y,
+ Paint* paint, uint16_t* glyphs, SkPoint* pos)
+ : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
+ }
+
+ void operator()(size_t start, size_t end) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[i].fX = x + layout.getX(i);
+ pos[i].fY = y + layout.getY(i);
+ }
+ const SkFont& font = paint->getSkFont();
+ if (start == 0) {
+ getPosTextPath(font, glyphs, end, pos, path);
+ } else {
+ getPosTextPath(font, glyphs + start, end - start, pos + start, &tmpPath);
+ path->addPath(tmpPath);
+ }
+ }
+ private:
+ const minikin::Layout& layout;
+ SkPath* path;
+ jfloat x;
+ jfloat y;
+ Paint* paint;
+ uint16_t* glyphs;
+ SkPoint* pos;
+ SkPath tmpPath;
+ };
+
+ static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text,
+ jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
+ minikin::Layout layout = MinikinUtils::doLayout(
+ paint, static_cast<minikin::Bidi>(bidiFlags), typeface,
+ text, count, // text buffer
+ 0, count, // draw range
+ 0, count, // context range
+ nullptr);
+ size_t nGlyphs = layout.nGlyphs();
+ uint16_t* glyphs = new uint16_t[nGlyphs];
+ SkPoint* pos = new SkPoint[nGlyphs];
+
+ x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+ Paint::Align align = paint->getTextAlign();
+ paint->setTextAlign(Paint::kLeft_Align);
+ GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
+ MinikinUtils::forFontRun(layout, paint, f);
+ paint->setTextAlign(align);
+ delete[] glyphs;
+ delete[] pos;
+ }
+
+ static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+ jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ const jchar* textArray = env->GetCharArrayElements(text, nullptr);
+ getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
+ env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
+ }
+
+ static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+ jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ const jchar* textArray = env->GetStringChars(text, nullptr);
+ getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
+ env->ReleaseStringChars(text, textArray);
+ }
+
+ static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
+ const Paint& paint, const Typeface* typeface, jint bidiFlags) {
+ SkRect r;
+ SkIRect ir;
+
+ minikin::Layout layout = MinikinUtils::doLayout(&paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface,
+ text, count, // text buffer
+ 0, count, // draw range
+ 0, count, // context range
+ nullptr);
+ minikin::MinikinRect rect;
+ layout.getBounds(&rect);
+ r.fLeft = rect.mLeft;
+ r.fTop = rect.mTop;
+ r.fRight = rect.mRight;
+ r.fBottom = rect.mBottom;
+ r.roundOut(&ir);
+ GraphicsJNI::irect_to_jrect(ir, env, bounds);
+ }
+
+ static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jstring text, jint start,
+ jint end, jint bidiFlags, jobject bounds) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const jchar* textArray = env->GetStringChars(text, nullptr);
+ doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
+ env->ReleaseStringChars(text, textArray);
+ }
+
+ static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jcharArray text,
+ jint index, jint count, jint bidiFlags, jobject bounds) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const jchar* textArray = env->GetCharArrayElements(text, nullptr);
+ doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
+ env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
+ JNI_ABORT);
+ }
+
+ // Returns true if the given string is exact one pair of regional indicators.
+ static bool isFlag(const jchar* str, size_t length) {
+ const jchar RI_LEAD_SURROGATE = 0xD83C;
+ const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+ const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+ if (length != 4) {
+ return false;
+ }
+ if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+ return false;
+ }
+ return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+ RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+ }
+
+ static jboolean layoutContainsNotdef(const minikin::Layout& layout) {
+ for (size_t i = 0; i < layout.nGlyphs(); i++) {
+ if (layout.getGlyphId(i) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Don't count glyphs that are the recommended "space" glyph and are zero width.
+ // This logic makes assumptions about HarfBuzz layout, but does correctly handle
+ // cases where ligatures form and zero width space glyphs are left in as
+ // placeholders.
+ static size_t countNonSpaceGlyphs(const minikin::Layout& layout) {
+ size_t count = 0;
+ static unsigned int kSpaceGlyphId = 3;
+ for (size_t i = 0; i < layout.nGlyphs(); i++) {
+ if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jint bidiFlags,
+ jstring string) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ ScopedStringChars str(env, string);
+
+ /* Start by rejecting unsupported base code point and variation selector pairs. */
+ size_t nChars = 0;
+ const uint32_t kStartOfString = 0xFFFFFFFF;
+ uint32_t prevCp = kStartOfString;
+ for (size_t i = 0; i < str.size(); i++) {
+ jchar cu = str[i];
+ uint32_t cp = cu;
+ if (U16_IS_TRAIL(cu)) {
+ // invalid UTF-16, unpaired trailing surrogate
+ return false;
+ } else if (U16_IS_LEAD(cu)) {
+ if (i + 1 == str.size()) {
+ // invalid UTF-16, unpaired leading surrogate at end of string
+ return false;
+ }
+ i++;
+ jchar cu2 = str[i];
+ if (!U16_IS_TRAIL(cu2)) {
+ // invalid UTF-16, unpaired leading surrogate
+ return false;
+ }
+ cp = U16_GET_SUPPLEMENTARY(cu, cu2);
+ }
+
+ if (prevCp != kStartOfString &&
+ ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) {
+ bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp);
+ if (!hasVS) {
+ // No font has a glyph for the code point and variation selector pair.
+ return false;
+ } else if (nChars == 1 && i + 1 == str.size()) {
+ // The string is just a codepoint and a VS, we have an authoritative answer
+ return true;
+ }
+ }
+ nChars++;
+ prevCp = cp;
+ }
+ minikin::Layout layout = MinikinUtils::doLayout(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface,
+ str.get(), str.size(), // text buffer
+ 0, str.size(), // draw range
+ 0, str.size(), // context range
+ nullptr);
+ size_t nGlyphs = countNonSpaceGlyphs(layout);
+ if (nGlyphs != 1 && nChars > 1) {
+ // multiple-character input, and was not a ligature
+ // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
+ // in joining scripts, such as Arabic and Mongolian.
+ return false;
+ }
+
+ if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
+ return false; // The collection doesn't have a glyph.
+ }
+
+ if (nChars == 2 && isFlag(str.get(), str.size())) {
+ // Some font may have a special glyph for unsupported regional indicator pairs.
+ // To return false for this case, need to compare the glyph id with the one of ZZ
+ // since ZZ is reserved for unknown or invalid territory.
+ // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
+ static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
+ minikin::Layout zzLayout = MinikinUtils::doLayout(paint,
+ static_cast<minikin::Bidi>(bidiFlags), typeface,
+ ZZ_FLAG_STR, 4, // text buffer
+ 0, 4, // draw range
+ 0, 4, // context range
+ nullptr);
+ if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
+ // The font collection doesn't have a glyph for unknown flag. Just return true.
+ return true;
+ }
+ return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
+ }
+ return true;
+ }
+
+ static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
+ jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
+ minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ if (offset == start + count) {
+ return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
+ bufSize, nullptr);
+ }
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
+ advancesArray.get());
+ return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
+ }
+
+ static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text,
+ jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ ScopedCharArrayRO textArray(env, text);
+ jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart, isRtl,
+ offset - contextStart);
+ return result;
+ }
+
+ static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
+ jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
+ minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
+ advancesArray.get());
+ return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
+ }
+
+ static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle,
+ jcharArray text, jint start, jint end, jint contextStart, jint contextEnd,
+ jboolean isRtl, jfloat advance) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ ScopedCharArrayRO textArray(env, text);
+ jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart,
+ start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
+ result += contextStart;
+ return result;
+ }
+
+ // ------------------ @FastNative ---------------------------
+
+ static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
+ obj->setMinikinLocaleListId(minikinLocaleListId);
+ return minikinLocaleListId;
+ }
+
+ static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (!settings) {
+ paint->setFontFeatureSettings(std::string());
+ } else {
+ ScopedUtfChars settingsChars(env, settings);
+ paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ }
+ }
+
+ static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
+ const int kElegantTop = 2500;
+ const int kElegantBottom = -1000;
+ const int kElegantAscent = 1900;
+ const int kElegantDescent = -500;
+ const int kElegantLeading = 0;
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ SkFont* font = &paint->getSkFont();
+ const Typeface* typeface = paint->getAndroidTypeface();
+ typeface = Typeface::resolveDefault(typeface);
+ minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
+ float saveSkewX = font->getSkewX();
+ bool savefakeBold = font->isEmbolden();
+ MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery);
+ SkScalar spacing = font->getMetrics(metrics);
+ // The populateSkPaint call may have changed fake bold / text skew
+ // because we want to measure with those effects applied, so now
+ // restore the original settings.
+ font->setSkewX(saveSkewX);
+ font->setEmbolden(savefakeBold);
+ if (paint->getFamilyVariant() == minikin::FamilyVariant::ELEGANT) {
+ SkScalar size = font->getSize();
+ metrics->fTop = -size * kElegantTop / 2048;
+ metrics->fBottom = -size * kElegantBottom / 2048;
+ metrics->fAscent = -size * kElegantAscent / 2048;
+ metrics->fDescent = -size * kElegantDescent / 2048;
+ metrics->fLeading = size * kElegantLeading / 2048;
+ spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+ }
+ return spacing;
+ }
+
+ static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+ SkFontMetrics metrics;
+ SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
+
+ if (metricsObj) {
+ SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+ }
+ return SkScalarToFloat(spacing);
+ }
+
+ static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+ SkFontMetrics metrics;
+
+ getMetricsInternal(paintHandle, &metrics);
+ int ascent = SkScalarRoundToInt(metrics.fAscent);
+ int descent = SkScalarRoundToInt(metrics.fDescent);
+ int leading = SkScalarRoundToInt(metrics.fLeading);
+
+ if (metricsObj) {
+ SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
+ }
+ return descent - ascent + leading;
+ }
+
+
+ // ------------------ @CriticalNative ---------------------------
+
+ static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+ reinterpret_cast<Paint*>(objHandle)->reset();
+ }
+
+ static void assign(CRITICAL_JNI_PARAMS_COMMA jlong dstPaintHandle, jlong srcPaintHandle) {
+ Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
+ const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
+ *dst = *src;
+ }
+
+ static jint getFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ uint32_t flags = reinterpret_cast<Paint*>(paintHandle)->getJavaFlags();
+ return static_cast<jint>(flags);
+ }
+
+ static void setFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint flags) {
+ reinterpret_cast<Paint*>(paintHandle)->setJavaFlags(flags);
+ }
+
+ static jint getHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getSkFont().getHinting()
+ == SkFontHinting::kNone ? 0 : 1;
+ }
+
+ static void setHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint mode) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setHinting(
+ mode == 0 ? SkFontHinting::kNone : SkFontHinting::kNormal);
+ }
+
+ static void setAntiAlias(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) {
+ reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
+ }
+
+ static void setLinearText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean linearText) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setLinearMetrics(linearText);
+ }
+
+ static void setSubpixelText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean subpixelText) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSubpixel(subpixelText);
+ }
+
+ static void setUnderlineText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean underlineText) {
+ reinterpret_cast<Paint*>(paintHandle)->setUnderline(underlineText);
+ }
+
+ static void setStrikeThruText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean strikeThruText) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrikeThru(strikeThruText);
+ }
+
+ static void setFakeBoldText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean fakeBoldText) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setEmbolden(fakeBoldText);
+ }
+
+ static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
+ reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
+ filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+ }
+
+ static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
+ reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
+ }
+
+ static jint getStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStyle());
+ }
+
+ static void setStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint styleHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Style style = static_cast<Paint::Style>(styleHandle);
+ obj->setStyle(style);
+ }
+
+ static void setColorLong(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jlong colorSpaceHandle,
+ jlong colorLong) {
+ SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+ }
+
+ static void setColor(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint color) {
+ reinterpret_cast<Paint*>(paintHandle)->setColor(color);
+ }
+
+ static void setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint a) {
+ reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
+ }
+
+ static jfloat getStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
+ }
+
+ static void setStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat width) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
+ }
+
+ static jfloat getStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
+ }
+
+ static void setStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat miter) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
+ }
+
+ static jint getStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStrokeCap());
+ }
+
+ static void setStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint capHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
+ obj->setStrokeCap(cap);
+ }
+
+ static jint getStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStrokeJoin());
+ }
+
+ static void setStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint joinHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Join join = (Paint::Join) joinHandle;
+ obj->setStrokeJoin(join);
+ }
+
+ static jboolean getFillPath(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong srcHandle, jlong dstHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+ SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+ return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
+ obj->setShader(sk_ref_sp(shader));
+ return reinterpret_cast<jlong>(obj->getShader());
+ }
+
+ static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) {
+ Paint* obj = reinterpret_cast<Paint *>(objHandle);
+ SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
+ obj->setColorFilter(sk_ref_sp(filter));
+ return reinterpret_cast<jlong>(obj->getColorFilter());
+ }
+
+ static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) {
+ // validate that the Java enum values match our expectations
+ static_assert(0 == static_cast<int>(SkBlendMode::kClear), "xfermode_mismatch");
+ static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "xfermode_mismatch");
+ static_assert(2 == static_cast<int>(SkBlendMode::kDst), "xfermode_mismatch");
+ static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "xfermode_mismatch");
+ static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "xfermode_mismatch");
+ static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "xfermode_mismatch");
+ static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "xfermode_mismatch");
+ static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "xfermode_mismatch");
+ static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "xfermode_mismatch");
+ static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
+ static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
+ static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
+ static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
+ static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
+ static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
+ static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
+ static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
+ static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+ static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
+ static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
+ static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
+ static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
+ static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
+ static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
+ static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
+ static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
+ static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
+ static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
+ static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
+
+ SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setBlendMode(mode);
+ }
+
+ static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
+ obj->setPathEffect(sk_ref_sp(effect));
+ return reinterpret_cast<jlong>(obj->getPathEffect());
+ }
+
+ static jlong setMaskFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong maskfilterHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
+ obj->setMaskFilter(sk_ref_sp(maskfilter));
+ return reinterpret_cast<jlong>(obj->getMaskFilter());
+ }
+
+ static void setTypeface(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong typefaceHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(objHandle);
+ paint->setAndroidTypeface(reinterpret_cast<Typeface*>(typefaceHandle));
+ }
+
+ static jint getTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getTextAlign());
+ }
+
+ static void setTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint alignHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Align align = static_cast<Paint::Align>(alignHandle);
+ obj->setTextAlign(align);
+ }
+
+ static void setTextLocalesByMinikinLocaleListId(CRITICAL_JNI_PARAMS_COMMA jlong objHandle,
+ jint minikinLocaleListId) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ obj->setMinikinLocaleListId(minikinLocaleListId);
+ }
+
+ static jboolean isElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+ return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT;
+ }
+
+ static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+ obj->setFamilyVariant(
+ aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT);
+ }
+
+ static jfloat getTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize());
+ }
+
+ static void setTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat textSize) {
+ if (textSize >= 0) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSize(textSize);
+ }
+ }
+
+ static jfloat getTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getScaleX());
+ }
+
+ static void setTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat scaleX) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setScaleX(scaleX);
+ }
+
+ static jfloat getTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSkewX());
+ }
+
+ static void setTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat skewX) {
+ reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSkewX(skewX);
+ }
+
+ static jfloat getLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getLetterSpacing();
+ }
+
+ static void setLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat letterSpacing) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setLetterSpacing(letterSpacing);
+ }
+
+ static jfloat getWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getWordSpacing();
+ }
+
+ static void setWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat wordSpacing) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setWordSpacing(wordSpacing);
+ }
+
+ static jint getStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return static_cast<jint>(paint->getStartHyphenEdit());
+ }
+
+ static jint getEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return static_cast<jint>(paint->getEndHyphenEdit());
+ }
+
+ static void setStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setStartHyphenEdit((uint32_t)hyphen);
+ }
+
+ static void setEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setEndHyphenEdit((uint32_t)hyphen);
+ }
+
+ static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+ return SkScalarToFloat(metrics.fAscent);
+ }
+
+ static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+ return SkScalarToFloat(metrics.fDescent);
+ }
+
+ static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+ SkScalar position;
+ if (metrics.hasUnderlinePosition(&position)) {
+ return SkScalarToFloat(position);
+ } else {
+ const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+ return SkScalarToFloat(Paint::kStdUnderline_Top * textSize);
+ }
+ }
+
+ static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+ SkScalar thickness;
+ if (metrics.hasUnderlineThickness(&thickness)) {
+ return SkScalarToFloat(thickness);
+ } else {
+ const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+ return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize);
+ }
+ }
+
+ static jfloat getStrikeThruPosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+ return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize);
+ }
+
+ static jfloat getStrikeThruThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+ return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
+ }
+
+ static void setShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat radius,
+ jfloat dx, jfloat dy, jlong colorSpaceHandle,
+ jlong colorLong) {
+ SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (radius <= 0) {
+ paint->setLooper(nullptr);
+ }
+ else {
+ SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+ paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+ }
+ }
+
+ static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
+ }
+
+ static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) {
+ if (lPaint == rPaint) {
+ return true;
+ }
+ Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
+ Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
+
+ const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
+ const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
+ minikin::MinikinPaint leftMinikinPaint
+ = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
+ minikin::MinikinPaint rightMinikinPaint
+ = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
+
+ return leftMinikinPaint == rightMinikinPaint;
+ }
+
+}; // namespace PaintGlue
+
+static const JNINativeMethod methods[] = {
+ {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
+ {"nInit","()J", (void*) PaintGlue::init},
+ {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+ {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+ {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+ {"nGetTextAdvances","(J[CIIIII[FI)F",
+ (void*) PaintGlue::getTextAdvances___CIIIII_FI},
+ {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F",
+ (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
+
+ {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+ {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
+ (void*) PaintGlue::getTextRunCursor__String},
+ {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+ {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
+ {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
+ (void*) PaintGlue::getStringBounds },
+ {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
+ (void*) PaintGlue::getCharArrayBounds },
+ {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
+ {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
+ {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
+ (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+
+ // --------------- @FastNative ----------------------
+
+ {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
+ {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
+ (void*) PaintGlue::setFontFeatureSettings},
+ {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
+ (void*)PaintGlue::getFontMetrics},
+ {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
+ (void*)PaintGlue::getFontMetricsInt},
+
+ // --------------- @CriticalNative ------------------
+
+ {"nReset","(J)V", (void*) PaintGlue::reset},
+ {"nSet","(JJ)V", (void*) PaintGlue::assign},
+ {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
+ {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
+ {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
+ {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
+ {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
+ {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
+ {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
+ {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
+ {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
+ {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
+ {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
+ {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
+ {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
+ {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+ {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
+ {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong},
+ {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
+ {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
+ {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
+ {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
+ {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
+ {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
+ {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
+ {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
+ {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
+ {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
+ {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
+ {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
+ {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
+ {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
+ {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
+ {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface},
+ {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
+ {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
+ {"nSetTextLocalesByMinikinLocaleListId","(JI)V",
+ (void*) PaintGlue::setTextLocalesByMinikinLocaleListId},
+ {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
+ {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+ {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
+ {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
+ {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
+ {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
+ {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
+ {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
+ {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
+ {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
+ {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing},
+ {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing},
+ {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit},
+ {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit},
+ {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit},
+ {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit},
+ {"nAscent","(J)F", (void*) PaintGlue::ascent},
+ {"nDescent","(J)F", (void*) PaintGlue::descent},
+ {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition},
+ {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
+ {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
+ {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
+ {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
+ {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
+ {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+};
+
+int register_android_graphics_Paint(JNIEnv* env) {
+ gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
+ gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
+
+ gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
+ gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
+ gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
+ gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
+ gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
+
+ gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
+ gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
+
+ gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
+ gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
+ gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
+ gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
+ gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+
+ return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
+}
+
+}