platform/cc/TextBlob.cc (245 lines of code) (raw):
#include <cstring>
#include <iostream>
#include <jni.h>
#include "SkData.h"
#include "SkSerialProcs.h"
#include "SkTextBlob.h"
#include "interop.hh"
static void unrefTextBlob(SkTextBlob* ptr) {
ptr->unref();
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nGetFinalizer
(JNIEnv* env, jclass jclass) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unrefTextBlob));
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nBounds
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkRect bounds = instance->bounds();
return skija::Rect::fromSkRect(env, instance->bounds());
}
extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_TextBlob__1nGetUniqueId
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
return instance->uniqueID();
}
extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skija_TextBlob__1nGetIntercepts
(JNIEnv* env, jclass jclass, jlong ptr, jfloat lower, jfloat upper, jlong paintPtr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
std::vector<float> bounds {lower, upper};
SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr));
int len = instance->getIntercepts(bounds.data(), nullptr, paint);
std::vector<float> intervals(len);
instance->getIntercepts(bounds.data(), intervals.data(), paint);
return javaFloatArray(env, intervals);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nMakeFromPosH
(JNIEnv* env, jclass jclass, jshortArray glyphsArr, jfloatArray xposArr, jfloat ypos, jlong fontPtr) {
jsize len = env->GetArrayLength(glyphsArr);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
jfloat* xpos = env->GetFloatArrayElements(xposArr, nullptr);
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
SkTextBlob* instance = SkTextBlob::MakeFromPosTextH(glyphs, len * sizeof(jshort), xpos, ypos, *font, SkTextEncoding::kGlyphID).release();
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
env->ReleaseFloatArrayElements(xposArr, xpos, 0);
return reinterpret_cast<jlong>(instance);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nMakeFromPos
(JNIEnv* env, jclass jclass, jshortArray glyphsArr, jfloatArray posArr, jlong fontPtr ) {
jsize len = env->GetArrayLength(glyphsArr);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
jfloat* pos = env->GetFloatArrayElements(posArr, nullptr);
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
SkTextBlob* instance = SkTextBlob::MakeFromPosText(glyphs, len * sizeof(jshort), reinterpret_cast<SkPoint*>(pos), *font, SkTextEncoding::kGlyphID).release();
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
env->ReleaseFloatArrayElements(posArr, pos, 0);
return reinterpret_cast<jlong>(instance);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nMakeFromRSXform
(JNIEnv* env, jclass jclass, jshortArray glyphsArr, jfloatArray xformArr, jlong fontPtr ) {
jsize len = env->GetArrayLength(glyphsArr);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
jfloat* xform = env->GetFloatArrayElements(xformArr, nullptr);
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
SkTextBlob* instance = SkTextBlob::MakeFromRSXform(glyphs, len * sizeof(jshort), reinterpret_cast<SkRSXform*>(xform), *font, SkTextEncoding::kGlyphID).release();
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
env->ReleaseFloatArrayElements(xformArr, xform, 0);
return reinterpret_cast<jlong>(instance);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nSerializeToData
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkData* data = instance->serialize({}).release();
return reinterpret_cast<jlong>(data);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nMakeFromData
(JNIEnv* env, jclass jclass, jlong dataPtr) {
SkData* data = reinterpret_cast<SkData*>(static_cast<uintptr_t>(dataPtr));
SkTextBlob* instance = SkTextBlob::Deserialize(data->data(), data->size(), {}).release();
return reinterpret_cast<jlong>(instance);
}
// Must match SkTextBlobPriv.h
//
// Extended Textblob runs have more data after the Pos[] array:
//
// -------------------------------------------------------------------------
// ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
// -------------------------------------------------------------------------
class RunRecordClone {
public:
SkFont fFont;
uint32_t fCount;
SkPoint fOffset;
uint32_t fFlags;
uint16_t* glyphBuffer() const {
// Glyphs are stored immediately following the record.
return reinterpret_cast<uint16_t*>(const_cast<RunRecordClone*>(this) + 1);
}
SkScalar* posBuffer() const {
// Position scalars follow the (aligned) glyph buffer.
return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
SkAlign4(fCount * sizeof(uint16_t)));
}
uint32_t* textSizePtr() const {
// textSize follows the position buffer.
return (uint32_t*)(&this->posBuffer()[fCount * ScalarsPerGlyph(positioning())]);
}
uint32_t textSize() const {
return isExtended() ? *this->textSizePtr() : 0;
}
uint32_t* clusterBuffer() const {
// clusters follow the textSize.
return isExtended() ? 1 + this->textSizePtr() : nullptr;
}
char* textBuffer() const {
return isExtended()
? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
: nullptr;
}
uint8_t positioning() const {
return fFlags & 0x3; // kPositioning_Mask
}
bool isExtended() const {
return fFlags & 0x8; // kExtended_Flag
}
static unsigned ScalarsPerGlyph(uint8_t pos) {
const uint8_t gScalarsPerPositioning[] = {
0, // kDefault_Positioning
1, // kHorizontal_Positioning
2, // kFull_Positioning
4, // kRSXform_Positioning
};
return gScalarsPerPositioning[pos];
}
};
extern "C" JNIEXPORT jshortArray JNICALL Java_org_jetbrains_skija_TextBlob__1nGetGlyphs
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
std::vector<jshort> glyphs;
size_t stored = 0;
while (iter.next(&run)) {
glyphs.resize(stored + run.fGlyphCount);
memcpy(&glyphs[stored], run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
stored += run.fGlyphCount;
}
return javaShortArray(env, glyphs);
}
extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skija_TextBlob__1nGetPositions
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
std::vector<jfloat> positions;
size_t stored = 0;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
unsigned scalarsPerGlyph = RunRecordClone::ScalarsPerGlyph(runRecord->positioning());
positions.resize(stored + run.fGlyphCount * scalarsPerGlyph);
memcpy(&positions[stored], runRecord->posBuffer(), run.fGlyphCount * scalarsPerGlyph * sizeof(SkScalar));
stored += run.fGlyphCount * scalarsPerGlyph;
}
return javaFloatArray(env, positions);
}
extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skija_TextBlob__1nGetClusters
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
std::vector<jint> clusters;
size_t stored = 0;
// uint32_t cluster8 = 0;
uint32_t runStart16 = 0;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (!runRecord->isExtended())
return nullptr;
skija::UtfIndicesConverter conv(runRecord->textBuffer(), runRecord->textSize());
clusters.resize(stored + run.fGlyphCount);
uint32_t* clusterBuffer = runRecord->clusterBuffer();
for (int i = 0; i < run.fGlyphCount; ++i)
clusters[stored + i] = runStart16 + conv.from8To16(clusterBuffer[i]);
runStart16 += conv.from8To16(runRecord->textSize());
// memcpy(&clusters[stored], runRecord->clusterBuffer(), run.fGlyphCount * sizeof(uint32_t));
stored += run.fGlyphCount;
}
return javaIntArray(env, clusters);
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nGetTightBounds
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
auto bounds = SkRect::MakeEmpty();
SkRect tmpBounds;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;
runRecord->fFont.measureText(runRecord->glyphBuffer(), run.fGlyphCount * sizeof(uint16_t), SkTextEncoding::kGlyphID, &tmpBounds, nullptr);
SkScalar* posBuffer = runRecord->posBuffer();
tmpBounds.offset(posBuffer[0], posBuffer[1]);
bounds.join(tmpBounds);
}
return skija::Rect::fromSkRect(env, bounds);
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nGetBlockBounds
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
auto bounds = SkRect::MakeEmpty();
SkFontMetrics metrics;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;
SkScalar* posBuffer = runRecord->posBuffer();
const SkFont& font = runRecord->fFont;
font.getMetrics(&metrics);
SkScalar lastLeft = posBuffer[(run.fGlyphCount - 1) * 2];
SkScalar lastWidth;
if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft))
lastWidth = 0;
else
font.getWidths(&run.fGlyphIndices[run.fGlyphCount - 1], 1, &lastWidth);
auto runBounds = SkRect::MakeLTRB(posBuffer[0], posBuffer[1] + metrics.fAscent, lastLeft + lastWidth, posBuffer[1] + metrics.fDescent);
bounds.join(runBounds);
}
return skija::Rect::fromSkRect(env, bounds);
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nGetFirstBaseline
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;
return javaFloat(env, runRecord->posBuffer()[1]);
}
return nullptr;
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nGetLastBaseline
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
SkScalar baseline = 0;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;
baseline = std::max(baseline, runRecord->posBuffer()[1]);
}
return javaFloat(env, baseline);
}