summaryrefslogtreecommitdiffstats
path: root/core/jni/android/graphics
diff options
context:
space:
mode:
authorRaph Levien <raph@google.com>2012-10-30 15:55:33 -0700
committerRaph Levien <raph@google.com>2013-01-15 09:58:06 -0800
commitaaedde51b76901ff05f2a2348eb41f0f5323d954 (patch)
treef4642f70a3c0bc1e9a6eff2775e7bc71eb06aa65 /core/jni/android/graphics
parentfef45db2aa3271bc414794ffb9c34d7659dccb1a (diff)
downloadframeworks_base-aaedde51b76901ff05f2a2348eb41f0f5323d954.zip
frameworks_base-aaedde51b76901ff05f2a2348eb41f0f5323d954.tar.gz
frameworks_base-aaedde51b76901ff05f2a2348eb41f0f5323d954.tar.bz2
Update framework text layout to use Harfbuzz NG
These are the frameworks changes to use the new version of Harfbuzz. Change-Id: Idbef325e8fc1c27a9f2296414ddb1f79b778a00e
Diffstat (limited to 'core/jni/android/graphics')
-rw-r--r--core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp177
-rw-r--r--core/jni/android/graphics/HarfBuzzNGFaceSkia.h (renamed from core/jni/android/graphics/HarfbuzzSkia.h)36
-rw-r--r--core/jni/android/graphics/HarfbuzzSkia.cpp214
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp575
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h45
5 files changed, 477 insertions, 570 deletions
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
new file mode 100644
index 0000000..1752e5b
--- /dev/null
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "TextLayoutCache"
+
+#include "HarfBuzzNGFaceSkia.h"
+
+#include <cutils/log.h>
+#include <SkFontHost.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkUtils.h>
+
+#include <hb.h>
+
+namespace android {
+
+// Our implementation of the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+struct HarfBuzzFontData {
+ HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
+ SkPaint* m_paint;
+};
+
+static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
+{
+ ALOG_ASSERT(codepoint <= 0xFFFF);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ SkScalar skWidth;
+ SkRect skBounds;
+ uint16_t glyph = codepoint;
+
+ paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
+ ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
+ if (width)
+ *width = SkScalarToHBFixed(skWidth);
+ if (extents) {
+ // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
+ extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
+ extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
+ extents->width = SkScalarToHBFixed(skBounds.width());
+ extents->height = SkScalarToHBFixed(-skBounds.height());
+ }
+}
+
+static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+ if (unicode > 0x10ffff) {
+ unicode = 0xfffd;
+ }
+ SkPaint* paint = hbFontData->m_paint;
+ // It would be better to use kUTF32_TextEncoding directly
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ uint16_t glyph16;
+ uint16_t unichar[2];
+ size_t size = SkUTF16_FromUnichar(unicode, unichar);
+ paint->textToGlyphs(unichar, size * sizeof(*unichar), &glyph16);
+ *glyph = glyph16;
+ return !!*glyph;
+}
+
+static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+ hb_position_t advance = 0;
+
+ SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
+ return advance;
+}
+
+static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
+{
+ // Just return true, following the way that Harfbuzz-FreeType
+ // implementation does.
+ return true;
+}
+
+static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+ SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
+ return true;
+}
+
+static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
+{
+ static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
+
+ // We don't set callback functions which we can't support.
+ // Harfbuzz will use the fallback implementation if they aren't set.
+ if (!harfbuzzSkiaFontFuncs) {
+ harfbuzzSkiaFontFuncs = hb_font_funcs_create();
+ hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
+ hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
+ hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
+ hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
+ hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
+ }
+ return harfbuzzSkiaFontFuncs;
+}
+
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
+{
+ SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
+ SkFontID uniqueID = typeface->uniqueID();
+
+ const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag);
+ if (!tableSize)
+ return 0;
+
+ char* buffer = reinterpret_cast<char*>(malloc(tableSize));
+ if (!buffer)
+ return 0;
+ size_t actualSize = SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
+ if (tableSize != actualSize) {
+ free(buffer);
+ return 0;
+ }
+
+ return hb_blob_create(const_cast<char*>(buffer), tableSize,
+ HB_MEMORY_MODE_WRITABLE, buffer, free);
+}
+
+static void destroyHarfBuzzFontData(void* data) {
+ delete (HarfBuzzFontData*)data;
+}
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
+ hb_font_t* font = hb_font_create(face);
+
+ // Note: this needs to be reworked when we do subpixels
+ int x_ppem = floor(sizeX + 0.5);
+ int y_ppem = floor(sizeY + 0.5);
+ hb_font_set_ppem(font, x_ppem, y_ppem);
+ hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
+
+ HarfBuzzFontData* data = new HarfBuzzFontData(paint);
+ hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
+
+ return font;
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
index 2772f4d..23ec81a 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
@@ -24,31 +24,35 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef HarfbuzzSkia_h
-#define HarfbuzzSkia_h
+#ifndef HarfBuzzNGFaceSkia_h
+#define HarfBuzzNGFaceSkia_h
-#include "SkScalar.h"
-#include "SkTypeface.h"
-#include "SkPaint.h"
+#include <SkScalar.h>
+#include <SkPaint.h>
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
+#include <hb.h>
namespace android {
-static inline float HBFixedToFloat(HB_Fixed v) {
- // Harfbuzz uses 26.6 fixed point values for pixel offsets
- return v * (1.0f / 64);
+static inline float
+HBFixedToFloat (hb_position_t v)
+{
+ return scalblnf (v, -8);
+}
+
+static inline hb_position_t
+HBFloatToFixed (float v)
+{
+ return scalblnf (v, +8);
}
-static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
- // HB_Fixed is a 26.6 fixed point format.
- return SkScalarToFloat(value) * 64.0f;
+static inline hb_position_t SkScalarToHBFixed(SkScalar value) {
+ return HBFloatToFixed(SkScalarToFloat(value));
}
-HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
-extern const HB_FontClass harfbuzzSkiaClass;
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData);
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY);
} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
deleted file mode 100644
index 7e08379..0000000
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2011, The Android Open Source Project
- * Copyright 2011, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "HarfbuzzSkia"
-
-#include "HarfbuzzSkia.h"
-
-#include "SkFontHost.h"
-
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkPoint.h"
-#include "SkRect.h"
-#include "SkTypeface.h"
-
-#include <utils/Log.h>
-
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
-
-// This file implements the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-namespace android {
-
-static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
- HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
- uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
- int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
-
- // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
- // |glyphs| array needs to be converted.
- for (int i = numGlyphs - 1; i >= 0; --i) {
- glyphs[i] = skiaGlyphs[i];
- }
-
- *glyphsSize = numGlyphs;
- return 1;
-}
-
-static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
- HB_Fixed* advances, int flags)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t* glyphs16 = new uint16_t[numGlyphs];
- if (!glyphs16)
- return;
- for (unsigned i = 0; i < numGlyphs; ++i)
- glyphs16[i] = glyphs[i];
- SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
- paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
-
- // The |advances| values which Skia outputs are SkScalars, which are floats
- // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
- // These two formats are both 32-bits long.
- for (unsigned i = 0; i < numGlyphs; ++i) {
- advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
-#if DEBUG_ADVANCES
- ALOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
-#endif
- }
- delete glyphs16;
-}
-
-static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
- uint16_t* glyphs16 = new uint16_t[length];
- int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
-
- bool result = true;
- for (int i = 0; i < numGlyphs; ++i) {
- if (!glyphs16[i]) {
- result = false;
- break;
- }
- }
- delete glyphs16;
- return result;
-}
-
-static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
- HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
-{
- if (flags & HB_ShaperFlag_UseDesignMetrics)
- // This is requesting pre-hinted positions. We can't support this.
- return HB_Err_Invalid_Argument;
-
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t glyph16 = glyph;
- SkPath path;
- paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
- uint32_t numPoints = path.getPoints(0, 0);
- if (point >= numPoints)
- return HB_Err_Invalid_SubTable;
- SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
- if (!points)
- return HB_Err_Invalid_SubTable;
- // Skia does let us get a single point from the path.
- path.getPoints(points, point + 1);
- *xPos = SkScalarToHBFixed(points[point].fX);
- *yPos = SkScalarToHBFixed(points[point].fY);
- *resultingNumPoints = numPoints;
- delete points;
-
- return HB_Err_Ok;
-}
-
-static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t glyph16 = glyph;
- SkScalar width;
- SkRect bounds;
- paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
-
- metrics->x = SkScalarToHBFixed(bounds.fLeft);
- metrics->y = SkScalarToHBFixed(bounds.fTop);
- metrics->width = SkScalarToHBFixed(bounds.width());
- metrics->height = SkScalarToHBFixed(bounds.height());
-
- metrics->xOffset = SkScalarToHBFixed(width);
- // We can't actually get the |y| correct because Skia doesn't export
- // the vertical advance. However, nor we do ever render vertical text at
- // the moment so it's unimportant.
- metrics->yOffset = 0;
-}
-
-static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-
- SkPaint::FontMetrics skiaMetrics;
- paint->getFontMetrics(&skiaMetrics);
-
- switch (metric) {
- case HB_FontAscent:
- return SkScalarToHBFixed(-skiaMetrics.fAscent);
- // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
- default:
- return 0;
- }
- return 0;
-}
-
-const HB_FontClass harfbuzzSkiaClass = {
- stringToGlyphs,
- glyphsToAdvances,
- canRender,
- getOutlinePoint,
- getGlyphMetrics,
- getFontMetric,
-};
-
-HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
-{
- SkTypeface* typeface = static_cast<SkTypeface*>(font);
-
- if (!typeface) {
- ALOGD("Typeface cannot be null");
- return HB_Err_Invalid_Argument;
- }
- const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag);
- if (!tableSize)
- return HB_Err_Invalid_Argument;
- // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
- if (!buffer) {
- *len = tableSize;
- return HB_Err_Ok;
- }
-
- if (*len < tableSize)
- return HB_Err_Invalid_Argument;
- SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer);
- return HB_Err_Ok;
-}
-
-} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index dce48a3..d7df402 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -22,13 +22,10 @@
#include "TextLayout.h"
#include "SkFontHost.h"
#include "SkTypeface_android.h"
+#include "HarfBuzzNGFaceSkia.h"
#include <unicode/unistr.h>
-#include <unicode/normlzr.h>
#include <unicode/uchar.h>
-
-extern "C" {
- #include "harfbuzz-unicode.h"
-}
+#include <hb-icu.h>
namespace android {
@@ -341,20 +338,10 @@ uint32_t TextLayoutValue::getElapsedTime() {
return mElapsedTime;
}
-TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
+TextLayoutShaper::TextLayoutShaper() {
init();
- mFontRec.klass = &harfbuzzSkiaClass;
- mFontRec.userData = 0;
-
- // 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));
-
- mShaperItem.font = &mFontRec;
- mShaperItem.font->userData = &mShapingPaint;
+ mBuffer = hb_buffer_create();
}
void TextLayoutShaper::init() {
@@ -366,8 +353,9 @@ void TextLayoutShaper::unrefTypefaces() {
}
TextLayoutShaper::~TextLayoutShaper() {
+ hb_buffer_destroy(mBuffer);
+
unrefTypefaces();
- deleteShaperItemGlyphArrays();
}
void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
@@ -394,7 +382,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
bool forceLTR = false;
bool forceRTL = false;
- switch (dirFlags) {
+ switch (dirFlags & kBidi_Mask) {
case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
@@ -478,7 +466,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
i, startRun, lengthRun, isRTL);
#endif
- computeRunValues(paint, chars + startRun, lengthRun, isRTL,
+ computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
outAdvances, outTotalAdvance, outGlyphs, outPos);
}
@@ -502,7 +490,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
ALOGD("Using a SINGLE BiDi Run "
"-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
#endif
- computeRunValues(paint, chars + start, count, isRTL,
+ computeRunValues(paint, chars, start, count, contextCount, isRTL,
outAdvances, outTotalAdvance, outGlyphs, outPos);
}
@@ -512,18 +500,197 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
#endif
}
-static void logGlyphs(HB_ShaperItem shaperItem) {
- ALOGD(" -- glyphs count=%d", shaperItem.num_glyphs);
- for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
- ALOGD(" -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i,
- shaperItem.glyphs[i],
- HBFixedToFloat(shaperItem.offsets[i].x),
- HBFixedToFloat(shaperItem.offsets[i].y));
+#define HB_IsHighSurrogate(ucs) \
+ (((ucs) & 0xfc00) == 0xd800)
+
+#define HB_IsLowSurrogate(ucs) \
+ (((ucs) & 0xfc00) == 0xdc00)
+
+#ifndef HB_SurrogateToUcs4
+#define HB_SurrogateToUcs4_(high, low) \
+ (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
+#endif
+
+#define HB_InvalidCodePoint ~0u
+
+hb_codepoint_t
+utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
+ const uint16_t v = chars[(*iter)++];
+ if (HB_IsHighSurrogate(v)) {
+ // surrogate pair
+ if (size_t(*iter) >= len) {
+ // the surrogate is incomplete.
+ return HB_InvalidCodePoint;
+ }
+ const uint16_t v2 = chars[(*iter)++];
+ if (!HB_IsLowSurrogate(v2)) {
+ // invalidate surrogate pair.
+ (*iter)--;
+ return HB_InvalidCodePoint;
+ }
+
+ return HB_SurrogateToUcs4(v, v2);
+ }
+
+ if (HB_IsLowSurrogate(v)) {
+ // this isn't a valid code point
+ return HB_InvalidCodePoint;
+ }
+
+ return v;
+}
+
+hb_codepoint_t
+utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
+ const uint16_t v = chars[(*iter)--];
+ if (HB_IsLowSurrogate(v)) {
+ // surrogate pair
+ if (*iter < 0) {
+ // the surrogate is incomplete.
+ return HB_InvalidCodePoint;
+ }
+ const uint16_t v2 = chars[(*iter)--];
+ if (!HB_IsHighSurrogate(v2)) {
+ // invalidate surrogate pair.
+ (*iter)++;
+ return HB_InvalidCodePoint;
+ }
+
+ return HB_SurrogateToUcs4(v2, v);
+ }
+
+ if (HB_IsHighSurrogate(v)) {
+ // this isn't a valid code point
+ return HB_InvalidCodePoint;
+ }
+
+ return v;
+}
+
+struct ScriptRun {
+ hb_script_t script;
+ size_t pos;
+ size_t length;
+};
+
+hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
+ static hb_unicode_funcs_t* u;
+ if (!u) {
+ u = hb_icu_get_unicode_funcs();
+ }
+ return hb_unicode_script(u, codepoint);
+}
+
+bool
+hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+ if (size_t(*iter) == len)
+ return false;
+
+ run->pos = *iter;
+ const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
+ const hb_script_t init_script = code_point_to_script(init_cp);
+ hb_script_t current_script = init_script;
+ run->script = init_script;
+
+ for (;;) {
+ if (size_t(*iter) == len)
+ break;
+ const ssize_t prev_iter = *iter;
+ const uint32_t cp = utf16_to_code_point(chars, len, iter);
+ const hb_script_t script = code_point_to_script(cp);
+
+ if (script != current_script) {
+ /* BEGIN android-changed
+ The condition was not correct by doing "a == b == constant"
+ END android-changed */
+ if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+ // If we started off as inherited, we take whatever we can find.
+ run->script = script;
+ current_script = script;
+ continue;
+ } else if (script == HB_SCRIPT_INHERITED) {
+ continue;
+ } else {
+ *iter = prev_iter;
+ break;
+ }
+ }
+ }
+
+ if (run->script == HB_SCRIPT_INHERITED)
+ run->script = HB_SCRIPT_COMMON;
+
+ run->length = *iter - run->pos;
+ return true;
+}
+
+bool
+hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+ if (*iter == -1)
+ return false;
+
+ const size_t ending_index = *iter;
+ const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
+ const hb_script_t init_script = code_point_to_script(init_cp);
+ hb_script_t current_script = init_script;
+ run->script = init_script;
+
+ for (;;) {
+ if (*iter < 0)
+ break;
+ const ssize_t prev_iter = *iter;
+ const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
+ const hb_script_t script = code_point_to_script(cp);
+
+ if (script != current_script) {
+ if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+ // If we started off as inherited, we take whatever we can find.
+ run->script = script;
+ current_script = script;
+ continue;
+ } else if (script == HB_SCRIPT_INHERITED) {
+ /* BEGIN android-changed
+ We apply the same fix for Chrome to Android.
+ Chrome team will talk with upsteam about it.
+ Just assume that whatever follows this combining character is within
+ the same script. This is incorrect if you had language1 + combining
+ char + language 2, but that is rare and this code is suspicious
+ anyway.
+ END android-changed */
+ continue;
+ } else {
+ *iter = prev_iter;
+ break;
+ }
+ }
+ }
+
+ if (run->script == HB_SCRIPT_INHERITED)
+ run->script = HB_SCRIPT_COMMON;
+
+ run->pos = *iter + 1;
+ run->length = ending_index - *iter;
+ return true;
+}
+
+
+static void logGlyphs(hb_buffer_t* buffer) {
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
+ hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
+ ALOGD(" -- glyphs count=%d", numGlyphs);
+ for (size_t i = 0; i < numGlyphs; i++) {
+ ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
+ info[i].codepoint,
+ info[i].cluster,
+ HBFixedToFloat(positions[i].x_advance),
+ HBFixedToFloat(positions[i].x_offset),
+ HBFixedToFloat(positions[i].y_offset));
}
}
-void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars,
- size_t count, bool isRTL,
+void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
+ size_t start, size_t count, size_t contextCount, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
if (!count) {
@@ -535,95 +702,9 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
for (size_t i = 0; i < count; i++) {
outAdvances->add(0);
}
- UErrorCode error = U_ZERO_ERROR;
- bool useNormalizedString = false;
- for (ssize_t i = count - 1; i >= 0; --i) {
- UChar ch1 = chars[i];
- if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
- // So we have found a diacritic, let's get now the main code point which is paired
- // with it. As we can have several diacritics in a row, we need to iterate back again
-#if DEBUG_GLYPHS
- ALOGD("The BiDi run '%s' is containing a Diacritic at position %d",
- String8(chars, count).string(), int(i));
-#endif
- ssize_t j = i - 1;
- for (; j >= 0; --j) {
- UChar ch2 = chars[j];
- if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) {
- break;
- }
- }
-
- // We could not found the main code point, so we will just use the initial chars
- if (j < 0) {
- break;
- }
-
-#if DEBUG_GLYPHS
- ALOGD("Found main code point at index %d", int(j));
-#endif
- // We found the main code point, so we can normalize the "chunk" and fill
- // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able
- // to get one advance per char
- mBuffer.remove();
- Normalizer::normalize(UnicodeString(chars + j, i - j + 1),
- UNORM_NFC, 0 /* no options */, mBuffer, error);
- if (U_SUCCESS(error)) {
- if (!useNormalizedString) {
- useNormalizedString = true;
- mNormalizedString.setTo(false /* not terminated*/, chars, count);
- }
- // Set the normalized chars
- for (ssize_t k = j; k < j + mBuffer.length(); ++k) {
- mNormalizedString.setCharAt(k, mBuffer.charAt(k - j));
- }
- // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results
- // because some fonts are missing those glyphs)
- for (ssize_t k = j + mBuffer.length(); k <= i; ++k) {
- mNormalizedString.setCharAt(k, UNICODE_ZWSP);
- }
- }
- i = j - 1;
- }
- }
-
- // Reverse "BiDi mirrored chars" in RTL mode only
- // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
- // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
- // script-run splitting with Harfbuzz is splitting on parenthesis
- if (isRTL) {
- for (ssize_t i = 0; i < ssize_t(count); i++) {
- UChar32 ch = chars[i];
- if (!u_isMirrored(ch)) continue;
- if (!useNormalizedString) {
- useNormalizedString = true;
- mNormalizedString.setTo(false /* not terminated*/, chars, count);
- }
- UChar result = (UChar) u_charMirror(ch);
- mNormalizedString.setCharAt(i, result);
-#if DEBUG_GLYPHS
- ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
- ch, mNormalizedString[i], int(i));
-#endif
- }
- }
-
-#if DEBUG_GLYPHS
- if (useNormalizedString) {
- ALOGD("Will use normalized string '%s', length = %d",
- String8(mNormalizedString.getTerminatedBuffer(),
- mNormalizedString.length()).string(),
- mNormalizedString.length());
- } else {
- ALOGD("Normalization is not needed or cannot be done, using initial string");
- }
-#endif
-
- assert(mNormalizedString.length() == count);
// Set the string properties
- mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars;
- mShaperItem.stringLength = count;
+ const UChar* chars = contextChars + start;
// Define shaping paint properties
mShapingPaint.setTextSize(paint->getTextSize());
@@ -637,130 +718,66 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
// Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
// into the shaperItem
- ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
- unsigned numCodePoints = 0;
+ ssize_t indexFontRun = isRTL ? count - 1 : 0;
jfloat totalAdvance = *outTotalAdvance;
+ ScriptRun run; // relative to chars
while ((isRTL) ?
- hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
- mShaperItem.stringLength, &indexFontRun):
- hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string,
- mShaperItem.stringLength, &indexFontRun)) {
-
- ssize_t startScriptRun = mShaperItem.item.pos;
- size_t countScriptRun = mShaperItem.item.length;
- ssize_t endScriptRun = startScriptRun + countScriptRun;
+ hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
+ hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
#if DEBUG_GLYPHS
ALOGD("-------- Start of Script Run --------");
ALOGD("Shaping Script Run with");
ALOGD(" -- isRTL = %d", isRTL);
- ALOGD(" -- HB script = %d", mShaperItem.item.script);
- ALOGD(" -- startFontRun = %d", int(startScriptRun));
- ALOGD(" -- endFontRun = %d", int(endScriptRun));
- ALOGD(" -- countFontRun = %d", countScriptRun);
- ALOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
+ ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
+ ALOGD(" -- run.pos = %d", int(run.pos));
+ ALOGD(" -- run.length = %d", int(run.length));
+ ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
ALOGD(" -- string = '%s'", String8(chars, count).string());
#endif
+ hb_buffer_reset(mBuffer);
+ // Note: if we want to set unicode functions, etc., this is the place.
+
+ hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
+ hb_buffer_set_script(mBuffer, run.script);
+ // Should set language here (for bug 7004056)
+ hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
+
// Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
// and shape the Font run
- size_t glyphBaseCount = shapeFontRun(paint, isRTL);
+ size_t glyphBaseCount = shapeFontRun(paint);
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+ hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
#if DEBUG_GLYPHS
ALOGD("Got from Harfbuzz");
ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
- ALOGD(" -- num_glypth = %d", mShaperItem.num_glyphs);
- ALOGD(" -- kerning_applied = %d", mShaperItem.kerning_applied);
+ ALOGD(" -- num_glyph = %d", numGlyphs);
ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
+ ALOGD(" -- initial totalAdvance = %f", totalAdvance);
- logGlyphs(mShaperItem);
-#endif
-
- if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
-#if DEBUG_GLYPHS
- ALOGD("Advances array is empty or num_glypth = 0");
-#endif
- continue;
- }
-
-#if DEBUG_GLYPHS
- ALOGD("Returned logclusters");
- for (size_t i = 0; i < mShaperItem.num_glyphs; i++) {
- ALOGD(" -- lc[%d] = %d, hb-adv[%d] = %0.2f", i, mShaperItem.log_clusters[i],
- i, HBFixedToFloat(mShaperItem.advances[i]));
- }
-#endif
- jfloat totalFontRunAdvance = 0;
- size_t clusterStart = 0;
- for (size_t i = 0; i < countScriptRun; i++) {
- size_t cluster = mShaperItem.log_clusters[i];
- size_t clusterNext = i == countScriptRun - 1 ? mShaperItem.num_glyphs :
- mShaperItem.log_clusters[i + 1];
- if (cluster != clusterNext) {
- jfloat advance = 0;
- // The advance for the cluster is the sum of the advances of all glyphs within
- // the cluster.
- for (size_t j = cluster; j < clusterNext; j++) {
- advance += HBFixedToFloat(mShaperItem.advances[j]);
- }
- totalFontRunAdvance += advance;
- outAdvances->replaceAt(advance, startScriptRun + clusterStart);
- clusterStart = i + 1;
- }
- }
-
-#if DEBUG_ADVANCES
- ALOGD("Returned advances");
- for (size_t i = 0; i < countScriptRun; i++) {
- ALOGD(" -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i,
- (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance);
- }
-#endif
-
- // Get Glyphs and reverse them in place if RTL
- if (outGlyphs) {
- size_t countGlyphs = mShaperItem.num_glyphs;
-#if DEBUG_GLYPHS
- ALOGD("Returned script run glyphs -- count = %d", countGlyphs);
-#endif
- for (size_t i = 0; i < countGlyphs; i++) {
- jchar glyph = glyphBaseCount +
- (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
-#if DEBUG_GLYPHS
- ALOGD(" -- glyph[%d] = %d", i, glyph);
+ logGlyphs(mBuffer);
#endif
- 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);
-#if 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]);
- }
+ for (size_t i = 0; i < numGlyphs; i++) {
+ size_t cluster = info[i].cluster - start;
+ float xAdvance = HBFixedToFloat(positions[i].x_advance);
+ outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
+ outGlyphs->add(info[i].codepoint + glyphBaseCount);
+ float xo = HBFixedToFloat(positions[i].x_offset);
+ float yo = -HBFixedToFloat(positions[i].y_offset);
+ outPos->add(totalAdvance + xo + yo * skewX);
+ outPos->add(yo);
+ totalAdvance += xAdvance;
}
-
- totalAdvance += totalFontRunAdvance;
}
*outTotalAdvance = totalAdvance;
#if DEBUG_GLYPHS
+ ALOGD(" -- final totalAdvance = %f", totalAdvance);
ALOGD("-------- End of Script Run --------");
#endif
}
@@ -774,37 +791,33 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars
* for the default font live in a global cache.
*/
SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
- HB_Script script) {
+ hb_script_t script) {
SkTypeface::Style currentStyle = SkTypeface::kNormal;
if (typeface) {
currentStyle = typeface->style();
}
- typeface = SkCreateTypefaceForScript(script, currentStyle);
+ typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
#if DEBUG_GLYPHS
ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
#endif
return typeface;
}
-bool TextLayoutShaper::isComplexScript(HB_Script script) {
+bool TextLayoutShaper::isComplexScript(hb_script_t script) {
switch (script) {
- case HB_Script_Common:
- case HB_Script_Greek:
- case HB_Script_Cyrillic:
- case HB_Script_Hangul:
- case HB_Script_Inherited:
+ case HB_SCRIPT_COMMON:
+ case HB_SCRIPT_GREEK:
+ case HB_SCRIPT_CYRILLIC:
+ case HB_SCRIPT_HANGUL:
+ case HB_SCRIPT_INHERITED:
return false;
default:
return true;
}
}
-size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
- // Reset kerning
- mShaperItem.kerning_applied = false;
-
+size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
// Update Harfbuzz Shaper
- mShaperItem.item.bidiLevel = isRTL;
SkTypeface* typeface = paint->getTypeface();
@@ -813,19 +826,21 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
// when we are shaping any script that needs to use a fallback Font.
// If we are a "common" script we dont need to shift
size_t baseGlyphCount = 0;
- SkUnichar firstUnichar = 0;
- if (isComplexScript(mShaperItem.item.script)) {
- const uint16_t* text16 = (const uint16_t*) (mShaperItem.string + mShaperItem.item.pos);
- const uint16_t* text16End = text16 + mShaperItem.item.length;
- firstUnichar = SkUTF16_NextUnichar(&text16);
- while (firstUnichar == ' ' && text16 < text16End) {
- firstUnichar = SkUTF16_NextUnichar(&text16);
+ hb_codepoint_t firstUnichar = 0;
+ if (isComplexScript(hb_buffer_get_script(mBuffer))) {
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+ for (size_t i = 0; i < numGlyphs; i++) {
+ firstUnichar = info[i].codepoint;
+ if (firstUnichar != ' ') {
+ break;
+ }
}
baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
}
if (baseGlyphCount != 0) {
- typeface = typefaceForScript(paint, typeface, mShaperItem.item.script);
+ typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
if (!typeface) {
typeface = mDefaultTypeface;
SkSafeRef(typeface);
@@ -844,94 +859,44 @@ size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
}
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;
+ hb_face_t* face = referenceCachedHBFace(typeface);
-#if DEBUG_GLYPHS
- ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
- typeface, typeface->uniqueID(), mShaperItem.face);
-#endif
- SkSafeUnref(typeface);
+ float sizeY = paint->getTextSize();
+ float sizeX = sizeY * paint->getTextScaleX();
+ hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
+ hb_face_destroy(face);
- // Shape
- assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0.
- size_t size = mShaperItem.item.length * 3 / 2;
- while (!doShaping(size)) {
- // We overflowed our glyph arrays. Resize and retry.
- // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
- size = mShaperItem.num_glyphs * 2;
- }
- return baseGlyphCount;
-}
-
-bool TextLayoutShaper::doShaping(size_t size) {
- if (size > mShaperItemGlyphArraySize) {
- deleteShaperItemGlyphArrays();
- createShaperItemGlyphArrays(size);
- }
- mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
- memset(mShaperItem.offsets, 0, mShaperItem.num_glyphs * sizeof(HB_FixedPoint));
- return HB_ShapeItem(&mShaperItem);
-}
-
-void TextLayoutShaper::createShaperItemGlyphArrays(size_t size) {
#if DEBUG_GLYPHS
- ALOGD("Creating Glyph Arrays with size = %d", size);
+ ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
+ typeface, typeface->uniqueID(), face);
#endif
- mShaperItemGlyphArraySize = size;
-
- // These arrays are all indexed by glyph.
- mShaperItem.glyphs = new HB_Glyph[size];
- mShaperItem.attributes = new HB_GlyphAttributes[size];
- mShaperItem.advances = new HB_Fixed[size];
- mShaperItem.offsets = new HB_FixedPoint[size];
+ SkSafeUnref(typeface);
- // Although the log_clusters array is indexed by character, Harfbuzz expects that
- // it is big enough to hold one element per glyph. So we allocate log_clusters along
- // with the other glyph arrays above.
- mShaperItem.log_clusters = new unsigned short[size];
-}
+ hb_shape(font, mBuffer, NULL, 0);
+ hb_font_destroy(font);
-void TextLayoutShaper::deleteShaperItemGlyphArrays() {
- delete[] mShaperItem.glyphs;
- delete[] mShaperItem.attributes;
- delete[] mShaperItem.advances;
- delete[] mShaperItem.offsets;
- delete[] mShaperItem.log_clusters;
+ return baseGlyphCount;
}
-HB_Face TextLayoutShaper::getCachedHBFace(SkTypeface* typeface) {
+hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
SkFontID fontId = typeface->uniqueID();
ssize_t index = mCachedHBFaces.indexOfKey(fontId);
if (index >= 0) {
- return mCachedHBFaces.valueAt(index);
+ return hb_face_reference(mCachedHBFaces.valueAt(index));
}
- HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
- if (face) {
+ // TODO: destroy function
+ hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
#if DEBUG_GLYPHS
- ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
+ ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
#endif
- mCachedHBFaces.add(fontId, face);
- }
- return face;
+ mCachedHBFaces.add(fontId, face);
+ return hb_face_reference(face);
}
void TextLayoutShaper::purgeCaches() {
size_t cacheSize = mCachedHBFaces.size();
for (size_t i = 0; i < cacheSize; i++) {
- HB_FreeFace(mCachedHBFaces.valueAt(i));
+ hb_face_destroy(mCachedHBFaces.valueAt(i));
}
mCachedHBFaces.clear();
unrefTypefaces();
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 22de523..06bb941 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -35,11 +35,9 @@
#include <SkLanguage.h>
#include <unicode/ubidi.h>
-#include <unicode/ushape.h>
#include <unicode/unistr.h>
-#include "HarfbuzzSkia.h"
-#include "harfbuzz-shaper.h"
+#include <hb.h>
#include <android_runtime/AndroidRuntime.h>
@@ -189,14 +187,9 @@ public:
private:
/**
- * Harfbuzz shaper item
+ * Harfbuzz buffer for shaping
*/
- HB_ShaperItem mShaperItem;
-
- /**
- * Harfbuzz font
- */
- HB_FontRec mFontRec;
+ hb_buffer_t* mBuffer;
/**
* Skia Paint used for shaping
@@ -211,30 +204,15 @@ private:
/**
* Cache of Harfbuzz faces
*/
- KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
-
- /**
- * Cache of glyph array size
- */
- size_t mShaperItemGlyphArraySize;
-
- /**
- * Buffer for containing the ICU normalized form of a run
- */
- UnicodeString mNormalizedString;
-
- /**
- * Buffer for normalizing a piece of a run with ICU
- */
- UnicodeString mBuffer;
+ KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
void init();
void unrefTypefaces();
SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
- HB_Script script);
+ hb_script_t script);
- size_t shapeFontRun(const SkPaint* paint, bool isRTL);
+ size_t shapeFontRun(const SkPaint* paint);
void computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
@@ -242,17 +220,14 @@ private:
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
void computeRunValues(const SkPaint* paint, const UChar* chars,
- size_t count, bool isRTL,
+ size_t start, size_t count, size_t contextCount, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
- SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
- HB_Face getCachedHBFace(SkTypeface* typeface);
+ SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style);
+ hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
- bool doShaping(size_t size);
- void createShaperItemGlyphArrays(size_t size);
- void deleteShaperItemGlyphArrays();
- bool isComplexScript(HB_Script script);
+ bool isComplexScript(hb_script_t script);
}; // TextLayoutShaper