| 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 |