Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7cd0859a authored by Raph Levien's avatar Raph Levien Committed by Android (Google) Code Review
Browse files

Merge "Update framework text layout to use Harfbuzz NG"

parents 77e843a3 aaedde51
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ LOCAL_SRC_FILES:= \
	android/graphics/DrawFilter.cpp \
	android/graphics/CreateJavaOutputStreamAdaptor.cpp \
	android/graphics/Graphics.cpp \
	android/graphics/HarfbuzzSkia.cpp \
	android/graphics/HarfBuzzNGFaceSkia.cpp \
	android/graphics/Interpolator.cpp \
	android/graphics/LayerRasterizer.cpp \
	android/graphics/MaskFilter.cpp \
@@ -170,8 +170,7 @@ LOCAL_C_INCLUDES += \
	external/icu4c/i18n \
	external/icu4c/common \
	external/jpeg \
	external/harfbuzz/contrib \
	external/harfbuzz/src \
	external/harfbuzz_ng/src \
	external/zlib \
	frameworks/opt/emoji \
	libcore/include
@@ -206,7 +205,7 @@ LOCAL_SHARED_LIBRARIES := \
	libwpa_client \
	libjpeg \
	libusbhost \
	libharfbuzz \
	libharfbuzz_ng \
	libz

ifeq ($(USE_OPENGL_RENDERER),true)
+177 −0
Original line number Diff line number Diff line
/*
 * 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
+20 −16
Original line number Diff line number Diff line
@@ -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

+0 −214
Original line number Diff line number Diff line
/*
 * 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
+270 −305

File changed.

Preview size limit exceeded, changes collapsed.

Loading