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