OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
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 | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 #include "config.h" | |
32 #include "platform/fonts/Font.h" | |
33 | |
34 #include "platform/RuntimeEnabledFeatures.h" | |
35 #include "platform/fonts/SimpleFontData.h" | |
36 #include "platform/fonts/harfbuzz/HarfBuzzShaper.h" | |
37 #include "platform/fonts/GlyphBuffer.h" | |
38 #include "platform/geometry/FloatRect.h" | |
39 #include "platform/graphics/GraphicsContext.h" | |
40 | |
41 #include "SkPaint.h" | |
42 #include "SkTemplates.h" | |
43 | |
44 #include "wtf/unicode/Unicode.h" | |
45 | |
46 #include <algorithm> | |
47 | |
48 namespace blink { | |
49 | |
50 static SkPaint textFillPaint(GraphicsContext* gc, const SimpleFontData* font) | |
51 { | |
52 SkPaint paint = gc->fillPaint(); | |
53 font->platformData().setupPaint(&paint, gc); | |
54 gc->adjustTextRenderMode(&paint); | |
55 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
56 return paint; | |
57 } | |
58 | |
59 static SkPaint textStrokePaint(GraphicsContext* gc, const SimpleFontData* font,
bool isFilling) | |
60 { | |
61 SkPaint paint = gc->strokePaint(); | |
62 font->platformData().setupPaint(&paint, gc); | |
63 gc->adjustTextRenderMode(&paint); | |
64 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
65 if (isFilling) { | |
66 // If there is a shadow and we filled above, there will already be | |
67 // a shadow. We don't want to draw it again or it will be too dark | |
68 // and it will go on top of the fill. | |
69 // | |
70 // Note that this isn't strictly correct, since the stroke could be | |
71 // very thick and the shadow wouldn't account for this. The "right" | |
72 // thing would be to draw to a new layer and then draw that layer | |
73 // with a shadow. But this is a lot of extra work for something | |
74 // that isn't normally an issue. | |
75 paint.setLooper(0); | |
76 } | |
77 return paint; | |
78 } | |
79 | |
80 static void paintGlyphs(GraphicsContext* gc, const SimpleFontData* font, | |
81 const Glyph glyphs[], unsigned numGlyphs, | |
82 const SkPoint pos[], const FloatRect& textRect) | |
83 { | |
84 TextDrawingModeFlags textMode = gc->textDrawingMode(); | |
85 | |
86 // We draw text up to two times (once for fill, once for stroke). | |
87 if (textMode & TextModeFill) { | |
88 SkPaint paint = textFillPaint(gc, font); | |
89 gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint)
; | |
90 } | |
91 | |
92 if ((textMode & TextModeStroke) && gc->hasStroke()) { | |
93 SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); | |
94 gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint)
; | |
95 } | |
96 } | |
97 | |
98 static void paintGlyphsHorizontal(GraphicsContext* gc, const SimpleFontData* fon
t, | |
99 const Glyph glyphs[], unsigned numGlyphs, | |
100 const SkScalar xpos[], SkScalar constY, const FloatRect& textRect) | |
101 { | |
102 TextDrawingModeFlags textMode = gc->textDrawingMode(); | |
103 | |
104 if (textMode & TextModeFill) { | |
105 SkPaint paint = textFillPaint(gc, font); | |
106 gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRe
ct, paint); | |
107 } | |
108 | |
109 if ((textMode & TextModeStroke) && gc->hasStroke()) { | |
110 SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); | |
111 gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRe
ct, paint); | |
112 } | |
113 } | |
114 | |
115 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, | |
116 const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, | |
117 const FloatPoint& point, const FloatRect& textRect) const | |
118 { | |
119 SkScalar x = SkFloatToScalar(point.x()); | |
120 SkScalar y = SkFloatToScalar(point.y()); | |
121 | |
122 const OpenTypeVerticalData* verticalData = font->verticalData(); | |
123 if (font->platformData().orientation() == Vertical && verticalData) { | |
124 SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); | |
125 SkPoint* pos = storage.get(); | |
126 | |
127 AffineTransform savedMatrix = gc->getCTM(); | |
128 gc->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); | |
129 gc->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); | |
130 | |
131 const unsigned kMaxBufferLength = 256; | |
132 Vector<FloatPoint, kMaxBufferLength> translations; | |
133 | |
134 const FontMetrics& metrics = font->fontMetrics(); | |
135 SkScalar verticalOriginX = SkFloatToScalar(point.x() + metrics.floatAsce
nt() - metrics.floatAscent(IdeographicBaseline)); | |
136 float horizontalOffset = point.x(); | |
137 | |
138 unsigned glyphIndex = 0; | |
139 while (glyphIndex < numGlyphs) { | |
140 unsigned chunkLength = std::min(kMaxBufferLength, numGlyphs - glyphI
ndex); | |
141 | |
142 const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); | |
143 translations.resize(chunkLength); | |
144 verticalData->getVerticalTranslationsForGlyphs(font, &glyphs[0], chu
nkLength, reinterpret_cast<float*>(&translations[0])); | |
145 | |
146 x = verticalOriginX; | |
147 y = SkFloatToScalar(point.y() + horizontalOffset - point.x()); | |
148 | |
149 float currentWidth = 0; | |
150 for (unsigned i = 0; i < chunkLength; ++i, ++glyphIndex) { | |
151 pos[i].set( | |
152 x + SkIntToScalar(lroundf(translations[i].x())), | |
153 y + -SkIntToScalar(-lroundf(currentWidth - translations[i].y
()))); | |
154 currentWidth += glyphBuffer.advanceAt(from + glyphIndex); | |
155 } | |
156 horizontalOffset += currentWidth; | |
157 paintGlyphs(gc, font, glyphs, chunkLength, pos, textRect); | |
158 } | |
159 | |
160 gc->setCTM(savedMatrix); | |
161 return; | |
162 } | |
163 | |
164 if (!glyphBuffer.hasOffsets()) { | |
165 SkAutoSTMalloc<64, SkScalar> storage(numGlyphs); | |
166 SkScalar* xpos = storage.get(); | |
167 const float* adv = glyphBuffer.advances(from); | |
168 for (unsigned i = 0; i < numGlyphs; i++) { | |
169 xpos[i] = x; | |
170 x += SkFloatToScalar(adv[i]); | |
171 } | |
172 const Glyph* glyphs = glyphBuffer.glyphs(from); | |
173 paintGlyphsHorizontal(gc, font, glyphs, numGlyphs, xpos, SkFloatToScalar
(y), textRect); | |
174 return; | |
175 } | |
176 | |
177 ASSERT(glyphBuffer.hasOffsets()); | |
178 const GlyphBufferWithOffsets& glyphBufferWithOffsets = | |
179 static_cast<const GlyphBufferWithOffsets&>(glyphBuffer); | |
180 SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); | |
181 SkPoint* pos = storage.get(); | |
182 const FloatSize* offsets = glyphBufferWithOffsets.offsets(from); | |
183 const float* advances = glyphBufferWithOffsets.advances(from); | |
184 SkScalar advanceSoFar = SkFloatToScalar(0); | |
185 for (unsigned i = 0; i < numGlyphs; i++) { | |
186 pos[i].set( | |
187 x + SkFloatToScalar(offsets[i].width()) + advanceSoFar, | |
188 y + SkFloatToScalar(offsets[i].height())); | |
189 advanceSoFar += SkFloatToScalar(advances[i]); | |
190 } | |
191 | |
192 const Glyph* glyphs = glyphBufferWithOffsets.glyphs(from); | |
193 paintGlyphs(gc, font, glyphs, numGlyphs, pos, textRect); | |
194 } | |
195 | |
196 void Font::drawTextBlob(GraphicsContext* gc, const SkTextBlob* blob, const SkPoi
nt& origin) const | |
197 { | |
198 ASSERT(RuntimeEnabledFeatures::textBlobEnabled()); | |
199 | |
200 // FIXME: It would be good to move this to Font.cpp, if we're sure that none | |
201 // of the things in FontMac's setupPaint need to apply here. | |
202 // See also paintGlyphs. | |
203 TextDrawingModeFlags textMode = gc->textDrawingMode(); | |
204 | |
205 if (textMode & TextModeFill) | |
206 gc->drawTextBlob(blob, origin, gc->fillPaint()); | |
207 | |
208 if ((textMode & TextModeStroke) && gc->hasStroke()) { | |
209 SkPaint paint = gc->strokePaint(); | |
210 if (textMode & TextModeFill) | |
211 paint.setLooper(0); | |
212 gc->drawTextBlob(blob, origin, paint); | |
213 } | |
214 } | |
215 | |
216 float Font::drawComplexText(GraphicsContext* gc, const TextRunPaintInfo& runInfo
, const FloatPoint& point) const | |
217 { | |
218 if (!runInfo.run.length()) | |
219 return 0; | |
220 | |
221 TextDrawingModeFlags textMode = gc->textDrawingMode(); | |
222 bool fill = textMode & TextModeFill; | |
223 bool stroke = (textMode & TextModeStroke) && gc->hasStroke(); | |
224 | |
225 if (!fill && !stroke) | |
226 return 0; | |
227 | |
228 GlyphBufferWithOffsets glyphBuffer; | |
229 HarfBuzzShaper shaper(this, runInfo.run); | |
230 shaper.setDrawRange(runInfo.from, runInfo.to); | |
231 if (!shaper.shape(&glyphBuffer) || glyphBuffer.isEmpty()) | |
232 return 0; | |
233 return drawGlyphBuffer(gc, runInfo, glyphBuffer, point); | |
234 } | |
235 | |
236 void Font::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextR
unPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const | |
237 { | |
238 GlyphBuffer glyphBuffer; | |
239 | |
240 float initialAdvance = getGlyphsAndAdvancesForComplexText(runInfo, glyphBuff
er, ForTextEmphasis); | |
241 | |
242 if (glyphBuffer.isEmpty()) | |
243 return; | |
244 | |
245 drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x()
+ initialAdvance, point.y())); | |
246 } | |
247 | |
248 float Font::getGlyphsAndAdvancesForComplexText(const TextRunPaintInfo& runInfo,
GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const | |
249 { | |
250 HarfBuzzShaper shaper(this, runInfo.run, HarfBuzzShaper::ForTextEmphasis); | |
251 shaper.setDrawRange(runInfo.from, runInfo.to); | |
252 shaper.shape(&glyphBuffer); | |
253 return 0; | |
254 } | |
255 | |
256 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon
tData*>* fallbackFonts, IntRectExtent* glyphBounds) const | |
257 { | |
258 HarfBuzzShaper shaper(this, run, HarfBuzzShaper::NotForTextEmphasis, fallbac
kFonts); | |
259 if (!shaper.shape()) | |
260 return 0; | |
261 | |
262 glyphBounds->setTop(floorf(-shaper.glyphBoundingBox().top())); | |
263 glyphBounds->setBottom(ceilf(shaper.glyphBoundingBox().bottom())); | |
264 glyphBounds->setLeft(std::max<int>(0, floorf(-shaper.glyphBoundingBox().left
()))); | |
265 glyphBounds->setRight(std::max<int>(0, ceilf(shaper.glyphBoundingBox().right
() - shaper.totalWidth()))); | |
266 | |
267 return shaper.totalWidth(); | |
268 } | |
269 | |
270 // Return the code point index for the given |x| offset into the text run. | |
271 int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, | |
272 bool includePartialGlyphs) const | |
273 { | |
274 HarfBuzzShaper shaper(this, run); | |
275 if (!shaper.shape()) | |
276 return 0; | |
277 return shaper.offsetForPosition(xFloat); | |
278 } | |
279 | |
280 // Return the rectangle for selecting the given range of code-points in the Text
Run. | |
281 FloatRect Font::selectionRectForComplexText(const TextRun& run, | |
282 const FloatPoint& point, int height, | |
283 int from, int to) const | |
284 { | |
285 HarfBuzzShaper shaper(this, run); | |
286 if (!shaper.shape()) | |
287 return FloatRect(); | |
288 return shaper.selectionRect(point, height, from, to); | |
289 } | |
290 | |
291 PassTextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, float initia
lAdvance, | |
292 const FloatRect& bounds, float& advance, bool couldUseLCD) const | |
293 { | |
294 ASSERT(RuntimeEnabledFeatures::textBlobEnabled()); | |
295 | |
296 // FIXME: Except for setupPaint, this is not specific to FontHarfBuzz. | |
297 // FIXME: Also implement the more general full-positioning path. | |
298 ASSERT(!glyphBuffer.hasOffsets()); | |
299 | |
300 SkTextBlobBuilder builder; | |
301 SkScalar x = SkFloatToScalar(initialAdvance); | |
302 SkRect skBounds = bounds; | |
303 | |
304 unsigned i = 0; | |
305 while (i < glyphBuffer.size()) { | |
306 const SimpleFontData* fontData = glyphBuffer.fontDataAt(i); | |
307 | |
308 // FIXME: Handle vertical text. | |
309 if (fontData->platformData().orientation() == Vertical) | |
310 return nullptr; | |
311 | |
312 // FIXME: Handle SVG fonts. | |
313 if (fontData->isSVGFont()) | |
314 return nullptr; | |
315 | |
316 // FIXME: FontPlatformData makes some decisions on the device scale | |
317 // factor, which is found via the GraphicsContext. This should be fixed | |
318 // to avoid correctness problems here. | |
319 SkPaint paint; | |
320 fontData->platformData().setupPaint(&paint); | |
321 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
322 | |
323 // FIXME: this should go away after the big LCD cleanup. | |
324 paint.setLCDRenderText(paint.isLCDRenderText() && couldUseLCD); | |
325 | |
326 unsigned start = i++; | |
327 while (i < glyphBuffer.size() && glyphBuffer.fontDataAt(i) == fontData) | |
328 i++; | |
329 unsigned count = i - start; | |
330 | |
331 const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPosH(paint,
count, 0, &skBounds); | |
332 | |
333 const uint16_t* glyphs = glyphBuffer.glyphs(start); | |
334 std::copy(glyphs, glyphs + count, buffer.glyphs); | |
335 | |
336 const float* advances = glyphBuffer.advances(start); | |
337 for (unsigned j = 0; j < count; j++) { | |
338 buffer.pos[j] = x; | |
339 x += SkFloatToScalar(advances[j]); | |
340 } | |
341 } | |
342 | |
343 advance = x; | |
344 return adoptRef(builder.build()); | |
345 } | |
346 | |
347 } // namespace blink | |
OLD | NEW |