Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright 2011, The Android Open Source Project
      3  * Copyright 2011, Google Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "HarfbuzzSkia.h"
     28 
     29 #include "SkFontHost.h"
     30 
     31 #include "SkPaint.h"
     32 #include "SkPath.h"
     33 #include "SkPoint.h"
     34 #include "SkRect.h"
     35 #include "SkTypeface.h"
     36 
     37 #include <utils/Log.h>
     38 
     39 extern "C" {
     40 #include "harfbuzz-shaper.h"
     41 }
     42 
     43 // This file implements the callbacks which Harfbuzz requires by using Skia
     44 // calls. See the Harfbuzz source for references about what these callbacks do.
     45 
     46 namespace android {
     47 
     48 static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
     49     paint->setTypeface(data->typeFace);
     50     paint->setTextSize(data->textSize);
     51     paint->setTextSkewX(data->textSkewX);
     52     paint->setTextScaleX(data->textScaleX);
     53     paint->setFlags(data->flags);
     54     paint->setHinting(data->hinting);
     55 }
     56 
     57 static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
     58         HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
     59 {
     60     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
     61     SkPaint paint;
     62     setupPaintWithFontData(&paint, data);
     63 
     64     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
     65     uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
     66     int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
     67 
     68     // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
     69     // |glyphs| array needs to be converted.
     70     for (int i = numGlyphs - 1; i >= 0; --i) {
     71         glyphs[i] = skiaGlyphs[i];
     72     }
     73 
     74     *glyphsSize = numGlyphs;
     75     return 1;
     76 }
     77 
     78 static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
     79         HB_Fixed* advances, int flags)
     80 {
     81     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
     82     SkPaint paint;
     83     setupPaintWithFontData(&paint, data);
     84 
     85     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
     86 
     87     uint16_t* glyphs16 = new uint16_t[numGlyphs];
     88     if (!glyphs16)
     89         return;
     90     for (unsigned i = 0; i < numGlyphs; ++i)
     91         glyphs16[i] = glyphs[i];
     92     SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
     93     paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
     94 
     95     // The |advances| values which Skia outputs are SkScalars, which are floats
     96     // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
     97     // These two formats are both 32-bits long.
     98     for (unsigned i = 0; i < numGlyphs; ++i) {
     99         advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
    100 #if DEBUG_ADVANCES
    101         LOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
    102 #endif
    103     }
    104     delete glyphs16;
    105 }
    106 
    107 static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
    108 {
    109     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    110     SkPaint paint;
    111     setupPaintWithFontData(&paint, data);
    112 
    113     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    114 
    115     uint16_t* glyphs16 = new uint16_t[length];
    116     int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
    117 
    118     bool result = true;
    119     for (int i = 0; i < numGlyphs; ++i) {
    120         if (!glyphs16[i]) {
    121             result = false;
    122             break;
    123         }
    124     }
    125     delete glyphs16;
    126     return result;
    127 }
    128 
    129 static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
    130         HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
    131 {
    132     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    133     SkPaint paint;
    134     setupPaintWithFontData(&paint, data);
    135 
    136     if (flags & HB_ShaperFlag_UseDesignMetrics)
    137         // This is requesting pre-hinted positions. We can't support this.
    138         return HB_Err_Invalid_Argument;
    139 
    140     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    141     uint16_t glyph16 = glyph;
    142     SkPath path;
    143     paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
    144     uint32_t numPoints = path.getPoints(0, 0);
    145     if (point >= numPoints)
    146         return HB_Err_Invalid_SubTable;
    147     SkPoint* points = reinterpret_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
    148     if (!points)
    149         return HB_Err_Invalid_SubTable;
    150     // Skia does let us get a single point from the path.
    151     path.getPoints(points, point + 1);
    152     *xPos = SkScalarToHBFixed(points[point].fX);
    153     *yPos = SkScalarToHBFixed(points[point].fY);
    154     *resultingNumPoints = numPoints;
    155     delete points;
    156 
    157     return HB_Err_Ok;
    158 }
    159 
    160 static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
    161 {
    162     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    163     SkPaint paint;
    164     setupPaintWithFontData(&paint, data);
    165 
    166     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    167     uint16_t glyph16 = glyph;
    168     SkScalar width;
    169     SkRect bounds;
    170     paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
    171 
    172     metrics->x = SkScalarToHBFixed(bounds.fLeft);
    173     metrics->y = SkScalarToHBFixed(bounds.fTop);
    174     metrics->width = SkScalarToHBFixed(bounds.width());
    175     metrics->height = SkScalarToHBFixed(bounds.height());
    176 
    177     metrics->xOffset = SkScalarToHBFixed(width);
    178     // We can't actually get the |y| correct because Skia doesn't export
    179     // the vertical advance. However, nor we do ever render vertical text at
    180     // the moment so it's unimportant.
    181     metrics->yOffset = 0;
    182 }
    183 
    184 static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
    185 {
    186     FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
    187     SkPaint paint;
    188     setupPaintWithFontData(&paint, data);
    189 
    190     SkPaint::FontMetrics skiaMetrics;
    191     paint.getFontMetrics(&skiaMetrics);
    192 
    193     switch (metric) {
    194     case HB_FontAscent:
    195         return SkScalarToHBFixed(-skiaMetrics.fAscent);
    196     // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
    197     default:
    198         return 0;
    199     }
    200     return 0;
    201 }
    202 
    203 const HB_FontClass harfbuzzSkiaClass = {
    204     stringToGlyphs,
    205     glyphsToAdvances,
    206     canRender,
    207     getOutlinePoint,
    208     getGlyphMetrics,
    209     getFontMetric,
    210 };
    211 
    212 HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
    213 {
    214     FontData* data = reinterpret_cast<FontData*>(voidface);
    215     SkTypeface* typeface = data->typeFace;
    216 
    217     const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag);
    218     if (!tableSize)
    219         return HB_Err_Invalid_Argument;
    220     // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
    221     if (!buffer) {
    222         *len = tableSize;
    223         return HB_Err_Ok;
    224     }
    225 
    226     if (*len < tableSize)
    227         return HB_Err_Invalid_Argument;
    228     SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer);
    229     return HB_Err_Ok;
    230 }
    231 
    232 }  // namespace android
    233