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