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