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