Skip to content

Commit 57bdd3d

Browse files
raphlinusAndroid (Google) Code Review
authored andcommitted
Merge "Software-only implementation of glyph positioning (bug 5443796)"
2 parents db8d60f + 2301d32 commit 57bdd3d

File tree

3 files changed

+111
-23
lines changed

3 files changed

+111
-23
lines changed

core/jni/android/graphics/Canvas.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,15 +766,64 @@ class SkCanvasGlue {
766766
if (value == NULL) {
767767
return;
768768
}
769-
doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint);
769+
SkPaint::Align align = paint->getTextAlign();
770+
if (align == SkPaint::kCenter_Align) {
771+
x -= 0.5 * value->getTotalAdvance();
772+
} else if (align == SkPaint::kRight_Align) {
773+
x -= value->getTotalAdvance();
774+
}
775+
paint->setTextAlign(SkPaint::kLeft_Align);
776+
doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint);
777+
doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint);
778+
paint->setTextAlign(align);
779+
}
780+
781+
// Same values used by Skia
782+
#define kStdStrikeThru_Offset (-6.0f / 21.0f)
783+
#define kStdUnderline_Offset (1.0f / 9.0f)
784+
#define kStdUnderline_Thickness (1.0f / 18.0f)
785+
786+
static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) {
787+
uint32_t flags = paint->getFlags();
788+
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
789+
SkScalar left = SkFloatToScalar(x);
790+
SkScalar right = SkFloatToScalar(x + length);
791+
float textSize = paint->getTextSize();
792+
float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
793+
if (flags & SkPaint::kUnderlineText_Flag) {
794+
SkScalar top = SkFloatToScalar(y + textSize * kStdUnderline_Offset
795+
- 0.5f * strokeWidth);
796+
SkScalar bottom = SkFloatToScalar(y + textSize * kStdUnderline_Offset
797+
+ 0.5f * strokeWidth);
798+
canvas->drawRectCoords(left, top, right, bottom, *paint);
799+
}
800+
if (flags & SkPaint::kStrikeThruText_Flag) {
801+
SkScalar top = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
802+
- 0.5f * strokeWidth);
803+
SkScalar bottom = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
804+
+ 0.5f * strokeWidth);
805+
canvas->drawRectCoords(left, top, right, bottom, *paint);
806+
}
770807
}
808+
}
771809

772810
static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
773811
jfloat x, jfloat y, int flags, SkPaint* paint) {
774812
// Beware: this needs Glyph encoding (already done on the Paint constructor)
775813
canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
776814
}
777815

816+
static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
817+
int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
818+
SkPoint* posPtr = new SkPoint[count];
819+
for (int indx = 0; indx < count; indx++) {
820+
posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]);
821+
posPtr[indx].fY = SkFloatToScalar(y + posArray[indx * 2 + 1]);
822+
}
823+
canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
824+
delete[] posPtr;
825+
}
826+
778827
static void drawTextRun___CIIIIFFIPaint(
779828
JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
780829
int count, int contextIndex, int contextCount,

core/jni/android/graphics/TextLayoutCache.cpp

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,12 @@ TextLayoutValue::TextLayoutValue(size_t contextCount) :
308308
// Give a hint for advances and glyphs vectors size
309309
mAdvances.setCapacity(contextCount);
310310
mGlyphs.setCapacity(contextCount);
311+
mPos.setCapacity(contextCount * 2);
311312
}
312313

313314
size_t TextLayoutValue::getSize() const {
314315
return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
315-
sizeof(jchar) * mGlyphs.capacity();
316+
sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
316317
}
317318

318319
void TextLayoutValue::setElapsedTime(uint32_t time) {
@@ -329,13 +330,9 @@ TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
329330
mFontRec.klass = &harfbuzzSkiaClass;
330331
mFontRec.userData = 0;
331332

332-
// The values which harfbuzzSkiaClass returns are already scaled to
333-
// pixel units, so we just set all these to one to disable further
334-
// scaling.
335-
mFontRec.x_ppem = 1;
336-
mFontRec.y_ppem = 1;
337-
mFontRec.x_scale = 1;
338-
mFontRec.y_scale = 1;
333+
// Note that the scaling values (x_ and y_ppem, x_ and y_scale) will be set
334+
// below, when the paint transform and em unit of the actual shaping font
335+
// are known.
339336

340337
memset(&mShaperItem, 0, sizeof(mShaperItem));
341338

@@ -360,7 +357,7 @@ void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* pain
360357
size_t start, size_t count, size_t contextCount, int dirFlags) {
361358

362359
computeValues(paint, chars, start, count, contextCount, dirFlags,
363-
&value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
360+
&value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
364361
#if DEBUG_ADVANCES
365362
ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
366363
contextCount, value->mTotalAdvance);
@@ -370,9 +367,9 @@ void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* pain
370367
void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
371368
size_t start, size_t count, size_t contextCount, int dirFlags,
372369
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
373-
Vector<jchar>* const outGlyphs) {
370+
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
371+
*outTotalAdvance = 0;
374372
if (!count) {
375-
*outTotalAdvance = 0;
376373
return;
377374
}
378375

@@ -437,6 +434,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
437434
ALOGW("Visual run is not valid");
438435
outGlyphs->clear();
439436
outAdvances->clear();
437+
outPos->clear();
440438
*outTotalAdvance = 0;
441439
isRTL = (paraDir == 1);
442440
useSingleRun = true;
@@ -459,15 +457,13 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
459457

460458
lengthRun = endRun - startRun;
461459
isRTL = (runDir == UBIDI_RTL);
462-
jfloat runTotalAdvance = 0;
463460
#if DEBUG_GLYPHS
464461
ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
465462
i, startRun, lengthRun, isRTL);
466463
#endif
467464
computeRunValues(paint, chars + startRun, lengthRun, isRTL,
468-
outAdvances, &runTotalAdvance, outGlyphs);
465+
outAdvances, outTotalAdvance, outGlyphs, outPos);
469466

470-
*outTotalAdvance += runTotalAdvance;
471467
}
472468
}
473469
} else {
@@ -490,7 +486,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
490486
"-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
491487
#endif
492488
computeRunValues(paint, chars + start, count, isRTL,
493-
outAdvances, outTotalAdvance, outGlyphs);
489+
outAdvances, outTotalAdvance, outGlyphs, outPos);
494490
}
495491

496492
#if DEBUG_GLYPHS
@@ -512,10 +508,9 @@ static void logGlyphs(HB_ShaperItem shaperItem) {
512508
void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars,
513509
size_t count, bool isRTL,
514510
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
515-
Vector<jchar>* const outGlyphs) {
511+
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
516512
if (!count) {
517513
// We cannot shape an empty run.
518-
*outTotalAdvance = 0;
519514
return;
520515
}
521516

@@ -615,7 +610,8 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
615610

616611
// Define shaping paint properties
617612
mShapingPaint.setTextSize(paint->getTextSize());
618-
mShapingPaint.setTextSkewX(paint->getTextSkewX());
613+
float skewX = paint->getTextSkewX();
614+
mShapingPaint.setTextSkewX(skewX);
619615
mShapingPaint.setTextScaleX(paint->getTextScaleX());
620616
mShapingPaint.setFlags(paint->getFlags());
621617
mShapingPaint.setHinting(paint->getHinting());
@@ -624,7 +620,7 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
624620
// into the shaperItem
625621
ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
626622
unsigned numCodePoints = 0;
627-
jfloat totalAdvance = 0;
623+
jfloat totalAdvance = *outTotalAdvance;
628624
while ((isRTL) ?
629625
hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
630626
mShaperItem.stringLength, &indexFontRun):
@@ -692,7 +688,6 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
692688
currentAdvance = HBFixedToFloat(mShaperItem.advances[i]);
693689
totalFontRunAdvance += currentAdvance;
694690
}
695-
totalAdvance += totalFontRunAdvance;
696691

697692
#if DEBUG_ADVANCES
698693
ALOGD("Returned advances");
@@ -717,6 +712,30 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
717712
outGlyphs->add(glyph);
718713
}
719714
}
715+
716+
// Get glyph positions (and reverse them in place if RTL)
717+
if (outPos) {
718+
size_t countGlyphs = mShaperItem.num_glyphs;
719+
jfloat x = totalAdvance;
720+
for (size_t i = 0; i < countGlyphs; i++) {
721+
size_t index = (!isRTL) ? i : countGlyphs - 1 - i;
722+
float xo = HBFixedToFloat(mShaperItem.offsets[index].x);
723+
float yo = HBFixedToFloat(mShaperItem.offsets[index].y);
724+
// Apply skewX component of transform to position offsets. Note
725+
// that scale has already been applied through x_ and y_scale
726+
// set in the mFontRec.
727+
outPos->add(x + xo + yo * skewX);
728+
outPos->add(yo);
729+
#ifdef DEBUG_GLYPHS
730+
ALOGD(" -- hb adv[%d] = %f, log_cluster[%d] = %d",
731+
index, HBFixedToFloat(mShaperItem.advances[index]),
732+
index, mShaperItem.log_clusters[index]);
733+
#endif
734+
x += HBFixedToFloat(mShaperItem.advances[index]);
735+
}
736+
}
737+
738+
totalAdvance += totalFontRunAdvance;
720739
}
721740

722741
*outTotalAdvance = totalAdvance;
@@ -807,6 +826,19 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
807826
mShapingPaint.setTypeface(typeface);
808827
mShaperItem.face = getCachedHBFace(typeface);
809828

829+
int textSize = paint->getTextSize();
830+
float scaleX = paint->getTextScaleX();
831+
mFontRec.x_ppem = floor(scaleX * textSize + 0.5);
832+
mFontRec.y_ppem = textSize;
833+
uint32_t unitsPerEm = SkFontHost::GetUnitsPerEm(typeface->uniqueID());
834+
// x_ and y_scale are the conversion factors from font design space
835+
// (unitsPerEm) to 1/64th of device pixels in 16.16 format.
836+
const int kDevicePixelFraction = 64;
837+
const int kMultiplyFor16Dot16 = 1 << 16;
838+
float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)unitsPerEm;
839+
mFontRec.x_scale = emScale * scaleX * textSize;
840+
mFontRec.y_scale = emScale * textSize;
841+
810842
#if DEBUG_GLYPHS
811843
ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
812844
typeface, typeface->uniqueID(), mShaperItem.face);

core/jni/android/graphics/TextLayoutCache.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ class TextLayoutValue : public RefBase {
130130
inline jfloat getTotalAdvance() const { return mTotalAdvance; }
131131
inline const jchar* getGlyphs() const { return mGlyphs.array(); }
132132
inline size_t getGlyphsCount() const { return mGlyphs.size(); }
133+
inline const jfloat* getPos() const { return mPos.array(); }
134+
inline size_t getPosCount() const { return mPos.size(); }
133135

134136
/**
135137
* Advances vector
@@ -146,6 +148,11 @@ class TextLayoutValue : public RefBase {
146148
*/
147149
Vector<jchar> mGlyphs;
148150

151+
/**
152+
* Pos vector (2 * i is x pos, 2 * i + 1 is y pos, same as drawPosText)
153+
*/
154+
Vector<jfloat> mPos;
155+
149156
/**
150157
* Get the size of the Cache entry
151158
*/
@@ -224,12 +231,12 @@ class TextLayoutShaper {
224231
void computeValues(const SkPaint* paint, const UChar* chars,
225232
size_t start, size_t count, size_t contextCount, int dirFlags,
226233
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
227-
Vector<jchar>* const outGlyphs);
234+
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
228235

229236
void computeRunValues(const SkPaint* paint, const UChar* chars,
230237
size_t count, bool isRTL,
231238
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
232-
Vector<jchar>* const outGlyphs);
239+
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
233240

234241
SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
235242
HB_Face getCachedHBFace(SkTypeface* typeface);

0 commit comments

Comments
 (0)