1/* 2 * Copyright 2010, The Android Open Source Project 3 * Copyright 2010, 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 "config.h" 28 29#include "FontPlatformData.h" 30 31#include <SkFontHost.h> 32#include <SkPaint.h> 33#include <SkPath.h> 34#include <SkPoint.h> 35#include <SkRect.h> 36#include <wtf/OwnArrayPtr.h> 37#include <wtf/PassOwnArrayPtr.h> 38 39extern "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 46namespace WebCore { 47 48static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) 49{ 50 // HB_Fixed is a 26.6 fixed point format. 51 return value * 64; 52} 53 54static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) 55{ 56 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 57 SkPaint paint; 58 59 font->setupPaint(&paint); 60 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 61 int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); 62 63 // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our 64 // |glyphs| array needs to be converted. 65 for (int i = numGlyphs - 1; i >= 0; --i) { 66 uint16_t value; 67 // We use a memcpy to avoid breaking strict aliasing rules. 68 memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(value)); 69 glyphs[i] = value; 70 } 71 72 *glyphsSize = numGlyphs; 73 return 1; 74} 75 76static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags) 77{ 78 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 79 SkPaint paint; 80 81 font->setupPaint(&paint); 82 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 83 84 OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[numGlyphs]); 85 if (!glyphs16.get()) 86 return; 87 for (unsigned i = 0; i < numGlyphs; ++i) 88 glyphs16[i] = glyphs[i]; 89 paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); 90 91 // The |advances| values which Skia outputs are SkScalars, which are floats 92 // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. 93 // These two formats are both 32-bits long. 94 for (unsigned i = 0; i < numGlyphs; ++i) { 95 float value; 96 // We use a memcpy to avoid breaking strict aliasing rules. 97 memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(value)); 98 advances[i] = SkiaScalarToHarfbuzzFixed(value); 99 } 100} 101 102static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) 103{ 104 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 105 SkPaint paint; 106 107 font->setupPaint(&paint); 108 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 109 110 OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[length]); 111 glyphs16.get(); 112 int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); 113 114 for (int i = 0; i < numGlyphs; ++i) { 115 if (!glyphs16[i]) 116 return false; 117 } 118 119 return true; 120} 121 122static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) 123{ 124 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 125 SkPaint paint; 126 127 if (flags & HB_ShaperFlag_UseDesignMetrics) 128 return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this. 129 130 font->setupPaint(&paint); 131 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 132 uint16_t glyph16 = glyph; 133 SkPath path; 134 paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); 135 uint32_t numPoints = path.getPoints(0, 0); 136 if (point >= numPoints) 137 return HB_Err_Invalid_SubTable; 138 SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1))); 139 if (!points) 140 return HB_Err_Invalid_SubTable; 141 // Skia does let us get a single point from the path. 142 path.getPoints(points, point + 1); 143 *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); 144 *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); 145 *resultingNumPoints = numPoints; 146 fastFree(points); 147 148 return HB_Err_Ok; 149} 150 151static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) 152{ 153 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 154 SkPaint paint; 155 156 font->setupPaint(&paint); 157 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 158 uint16_t glyph16 = glyph; 159 SkScalar width; 160 SkRect bounds; 161 paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); 162 163 metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft); 164 metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop); 165 metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); 166 metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); 167 168 metrics->xOffset = SkiaScalarToHarfbuzzFixed(width); 169 // We can't actually get the |y| correct because Skia doesn't export 170 // the vertical advance. However, nor we do ever render vertical text at 171 // the moment so it's unimportant. 172 metrics->yOffset = 0; 173} 174 175static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) 176{ 177 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 178 SkPaint paint; 179 180 font->setupPaint(&paint); 181 SkPaint::FontMetrics skiaMetrics; 182 paint.getFontMetrics(&skiaMetrics); 183 184 switch (metric) { 185 case HB_FontAscent: 186 return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); 187 // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. 188 default: 189 return 0; 190 } 191} 192 193HB_FontClass harfbuzzSkiaClass = { 194 stringToGlyphs, 195 glyphsToAdvances, 196 canRender, 197 getOutlinePoint, 198 getGlyphMetrics, 199 getFontMetric, 200}; 201 202HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) 203{ 204 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface); 205 206 const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag); 207 if (!tableSize) 208 return HB_Err_Invalid_Argument; 209 // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. 210 if (!buffer) { 211 *len = tableSize; 212 return HB_Err_Ok; 213 } 214 215 if (*len < tableSize) 216 return HB_Err_Invalid_Argument; 217 SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer); 218 return HB_Err_Ok; 219} 220 221} // namespace WebCore 222