Merge "Software-only implementation of glyph positioning (bug 5443796)"
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 6b74705..5c27602 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -766,15 +766,64 @@
         if (value == NULL) {
             return;
         }
-        doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint);
+        SkPaint::Align align = paint->getTextAlign();
+        if (align == SkPaint::kCenter_Align) {
+            x -= 0.5 * value->getTotalAdvance();
+        } else if (align == SkPaint::kRight_Align) {
+            x -= value->getTotalAdvance();
+        }
+        paint->setTextAlign(SkPaint::kLeft_Align);
+        doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint);
+        doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint);
+        paint->setTextAlign(align);
     }
 
+// Same values used by Skia
+#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
+#define kStdUnderline_Offset    (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) {
+    uint32_t flags = paint->getFlags();
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        SkScalar left = SkFloatToScalar(x);
+        SkScalar right = SkFloatToScalar(x + length);
+        float textSize = paint->getTextSize();
+        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar top = SkFloatToScalar(y + textSize * kStdUnderline_Offset
+                    - 0.5f * strokeWidth);
+            SkScalar bottom = SkFloatToScalar(y + textSize * kStdUnderline_Offset
+                    + 0.5f * strokeWidth);
+            canvas->drawRectCoords(left, top, right, bottom, *paint);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar top = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
+                    - 0.5f * strokeWidth);
+            SkScalar bottom = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
+                    + 0.5f * strokeWidth);
+            canvas->drawRectCoords(left, top, right, bottom, *paint);
+        }
+    }
+}
+
     static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
             jfloat x, jfloat y, int flags, SkPaint* paint) {
         // Beware: this needs Glyph encoding (already done on the Paint constructor)
         canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
     }
 
+    static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
+            int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
+        SkPoint* posPtr = new SkPoint[count];
+        for (int indx = 0; indx < count; indx++) {
+            posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]);
+            posPtr[indx].fY = SkFloatToScalar(y + posArray[indx * 2 + 1]);
+        }
+        canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
+        delete[] posPtr;
+    }
+
     static void drawTextRun___CIIIIFFIPaint(
         JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
         int count, int contextIndex, int contextCount,
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 2bdba87..37747bf 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -308,11 +308,12 @@
     // Give a hint for advances and glyphs vectors size
     mAdvances.setCapacity(contextCount);
     mGlyphs.setCapacity(contextCount);
+    mPos.setCapacity(contextCount * 2);
 }
 
 size_t TextLayoutValue::getSize() const {
     return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
-            sizeof(jchar) * mGlyphs.capacity();
+            sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
 }
 
 void TextLayoutValue::setElapsedTime(uint32_t time) {
@@ -329,13 +330,9 @@
     mFontRec.klass = &harfbuzzSkiaClass;
     mFontRec.userData = 0;
 
-    // The values which harfbuzzSkiaClass returns are already scaled to
-    // pixel units, so we just set all these to one to disable further
-    // scaling.
-    mFontRec.x_ppem = 1;
-    mFontRec.y_ppem = 1;
-    mFontRec.x_scale = 1;
-    mFontRec.y_scale = 1;
+    // Note that the scaling values (x_ and y_ppem, x_ and y_scale) will be set
+    // below, when the paint transform and em unit of the actual shaping font
+    // are known.
 
     memset(&mShaperItem, 0, sizeof(mShaperItem));
 
@@ -360,7 +357,7 @@
         size_t start, size_t count, size_t contextCount, int dirFlags) {
 
     computeValues(paint, chars, start, count, contextCount, dirFlags,
-            &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
+            &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
 #if DEBUG_ADVANCES
     ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
             contextCount, value->mTotalAdvance);
@@ -370,9 +367,9 @@
 void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
         size_t start, size_t count, size_t contextCount, int dirFlags,
         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-        Vector<jchar>* const outGlyphs) {
+        Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
+        *outTotalAdvance = 0;
         if (!count) {
-            *outTotalAdvance = 0;
             return;
         }
 
@@ -437,6 +434,7 @@
                                 ALOGW("Visual run is not valid");
                                 outGlyphs->clear();
                                 outAdvances->clear();
+                                outPos->clear();
                                 *outTotalAdvance = 0;
                                 isRTL = (paraDir == 1);
                                 useSingleRun = true;
@@ -459,15 +457,13 @@
 
                             lengthRun = endRun - startRun;
                             isRTL = (runDir == UBIDI_RTL);
-                            jfloat runTotalAdvance = 0;
 #if DEBUG_GLYPHS
                             ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
                                     i, startRun, lengthRun, isRTL);
 #endif
                             computeRunValues(paint, chars + startRun, lengthRun, isRTL,
-                                    outAdvances, &runTotalAdvance, outGlyphs);
+                                    outAdvances, outTotalAdvance, outGlyphs, outPos);
 
-                            *outTotalAdvance += runTotalAdvance;
                         }
                     }
                 } else {
@@ -490,7 +486,7 @@
                     "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
 #endif
             computeRunValues(paint, chars + start, count, isRTL,
-                    outAdvances, outTotalAdvance, outGlyphs);
+                    outAdvances, outTotalAdvance, outGlyphs, outPos);
         }
 
 #if DEBUG_GLYPHS
@@ -512,10 +508,9 @@
 void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars,
         size_t count, bool isRTL,
         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-        Vector<jchar>* const outGlyphs) {
+        Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
     if (!count) {
         // We cannot shape an empty run.
-        *outTotalAdvance = 0;
         return;
     }
 
@@ -615,7 +610,8 @@
 
     // Define shaping paint properties
     mShapingPaint.setTextSize(paint->getTextSize());
-    mShapingPaint.setTextSkewX(paint->getTextSkewX());
+    float skewX = paint->getTextSkewX();
+    mShapingPaint.setTextSkewX(skewX);
     mShapingPaint.setTextScaleX(paint->getTextScaleX());
     mShapingPaint.setFlags(paint->getFlags());
     mShapingPaint.setHinting(paint->getHinting());
@@ -624,7 +620,7 @@
     // into the shaperItem
     ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
     unsigned numCodePoints = 0;
-    jfloat totalAdvance = 0;
+    jfloat totalAdvance = *outTotalAdvance;
     while ((isRTL) ?
             hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
                     mShaperItem.stringLength, &indexFontRun):
@@ -692,7 +688,6 @@
             currentAdvance = HBFixedToFloat(mShaperItem.advances[i]);
             totalFontRunAdvance += currentAdvance;
         }
-        totalAdvance += totalFontRunAdvance;
 
 #if DEBUG_ADVANCES
         ALOGD("Returned advances");
@@ -717,6 +712,30 @@
                 outGlyphs->add(glyph);
             }
         }
+
+        // Get glyph positions (and reverse them in place if RTL)
+        if (outPos) {
+            size_t countGlyphs = mShaperItem.num_glyphs;
+            jfloat x = totalAdvance;
+            for (size_t i = 0; i < countGlyphs; i++) {
+                size_t index = (!isRTL) ? i : countGlyphs - 1 - i;
+                float xo = HBFixedToFloat(mShaperItem.offsets[index].x);
+                float yo = HBFixedToFloat(mShaperItem.offsets[index].y);
+                // Apply skewX component of transform to position offsets. Note
+                // that scale has already been applied through x_ and y_scale
+                // set in the mFontRec.
+                outPos->add(x + xo + yo * skewX);
+                outPos->add(yo);
+#ifdef DEBUG_GLYPHS
+                ALOGD("         -- hb adv[%d] = %f, log_cluster[%d] = %d",
+                        index, HBFixedToFloat(mShaperItem.advances[index]),
+                        index, mShaperItem.log_clusters[index]);
+#endif
+                x += HBFixedToFloat(mShaperItem.advances[index]);
+            }
+        }
+
+        totalAdvance += totalFontRunAdvance;
     }
 
     *outTotalAdvance = totalAdvance;
@@ -807,6 +826,19 @@
     mShapingPaint.setTypeface(typeface);
     mShaperItem.face = getCachedHBFace(typeface);
 
+    int textSize = paint->getTextSize();
+    float scaleX = paint->getTextScaleX();
+    mFontRec.x_ppem = floor(scaleX * textSize + 0.5);
+    mFontRec.y_ppem = textSize;
+    uint32_t unitsPerEm = SkFontHost::GetUnitsPerEm(typeface->uniqueID());
+    // x_ and y_scale are the conversion factors from font design space
+    // (unitsPerEm) to 1/64th of device pixels in 16.16 format.
+    const int kDevicePixelFraction = 64;
+    const int kMultiplyFor16Dot16 = 1 << 16;
+    float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)unitsPerEm;
+    mFontRec.x_scale = emScale * scaleX * textSize;
+    mFontRec.y_scale = emScale * textSize;
+
 #if DEBUG_GLYPHS
     ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
             typeface, typeface->uniqueID(), mShaperItem.face);
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 0b25e93..96ba34a 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -130,6 +130,8 @@
     inline jfloat getTotalAdvance() const { return mTotalAdvance; }
     inline const jchar* getGlyphs() const { return mGlyphs.array(); }
     inline size_t getGlyphsCount() const { return mGlyphs.size(); }
+    inline const jfloat* getPos() const { return mPos.array(); }
+    inline size_t getPosCount() const { return mPos.size(); }
 
     /**
      * Advances vector
@@ -147,6 +149,11 @@
     Vector<jchar> mGlyphs;
 
     /**
+     * Pos vector (2 * i is x pos, 2 * i + 1 is y pos, same as drawPosText)
+     */
+    Vector<jfloat> mPos;
+
+    /**
      * Get the size of the Cache entry
      */
     size_t getSize() const;
@@ -224,12 +231,12 @@
     void computeValues(const SkPaint* paint, const UChar* chars,
             size_t start, size_t count, size_t contextCount, int dirFlags,
             Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-            Vector<jchar>* const outGlyphs);
+            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
 
     void computeRunValues(const SkPaint* paint, const UChar* chars,
             size_t count, bool isRTL,
             Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-            Vector<jchar>* const outGlyphs);
+            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
 
     SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
     HB_Face getCachedHBFace(SkTypeface* typeface);