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