• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 extern "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 
48 namespace android {
49 
stringToGlyphs(HB_Font hbFont,const HB_UChar16 * characters,hb_uint32 length,HB_Glyph * glyphs,hb_uint32 * glyphsSize,HB_Bool isRTL)50 static 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 
glyphsToAdvances(HB_Font hbFont,const HB_Glyph * glyphs,hb_uint32 numGlyphs,HB_Fixed * advances,int flags)69 static 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 
canRender(HB_Font hbFont,const HB_UChar16 * characters,hb_uint32 length)95 static 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 
getOutlinePoint(HB_Font hbFont,HB_Glyph glyph,int flags,hb_uint32 point,HB_Fixed * xPos,HB_Fixed * yPos,hb_uint32 * resultingNumPoints)114 static 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 
getGlyphMetrics(HB_Font hbFont,HB_Glyph glyph,HB_GlyphMetrics * metrics)143 static 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 
getFontMetric(HB_Font hbFont,HB_FontMetric metric)165 static 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 
182 const HB_FontClass harfbuzzSkiaClass = {
183     stringToGlyphs,
184     glyphsToAdvances,
185     canRender,
186     getOutlinePoint,
187     getGlyphMetrics,
188     getFontMetric,
189 };
190 
harfbuzzSkiaGetTable(void * font,const HB_Tag tag,HB_Byte * buffer,HB_UInt * len)191 HB_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