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