1 /* 2 * Copyright (C) 2006, 2007 Apple Computer, Inc. 3 * Copyright (c) 2006, 2007, 2008, 2009, 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 are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "config.h" 33 #include "Font.h" 34 35 #include "FontFallbackList.h" 36 #include "GlyphBuffer.h" 37 #include "NotImplemented.h" 38 #include "PlatformBridge.h" 39 #include "PlatformContextSkia.h" 40 #include "SimpleFontData.h" 41 #include "SkiaFontWin.h" 42 #include "SkiaUtils.h" 43 #include "TransparencyWin.h" 44 #include "UniscribeHelperTextRun.h" 45 46 #include "skia/ext/platform_canvas.h" 47 #include "skia/ext/skia_utils_win.h" // FIXME: remove this dependency. 48 49 #include <windows.h> 50 51 namespace WebCore { 52 53 namespace { 54 55 bool canvasHasMultipleLayers(const SkCanvas* canvas) 56 { 57 SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); 58 iter.next(); // There is always at least one layer. 59 return !iter.done(); // There is > 1 layer if the the iterator can stil advance. 60 } 61 62 class TransparencyAwareFontPainter { 63 public: 64 TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&); 65 ~TransparencyAwareFontPainter(); 66 67 protected: 68 // Called by our subclass' constructor to initialize GDI if necessary. This 69 // is a separate function so it can be called after the subclass finishes 70 // construction (it calls virtual functions). 71 void init(); 72 73 virtual IntRect estimateTextBounds() = 0; 74 75 // Use the context from the transparency helper when drawing with GDI. It 76 // may point to a temporary one. 77 GraphicsContext* m_graphicsContext; 78 PlatformGraphicsContext* m_platformContext; 79 80 FloatPoint m_point; 81 82 // Set when Windows can handle the type of drawing we're doing. 83 bool m_useGDI; 84 85 // These members are valid only when m_useGDI is set. 86 HDC m_hdc; 87 TransparencyWin m_transparency; 88 89 private: 90 // Call when we're using GDI mode to initialize the TransparencyWin to help 91 // us draw GDI text. 92 void initializeForGDI(); 93 94 bool m_createdTransparencyLayer; // We created a layer to give the font some alpha. 95 }; 96 97 TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context, 98 const FloatPoint& point) 99 : m_graphicsContext(context) 100 , m_platformContext(context->platformContext()) 101 , m_point(point) 102 , m_useGDI(windowsCanHandleTextDrawing(context)) 103 , m_hdc(0) 104 , m_createdTransparencyLayer(false) 105 { 106 } 107 108 void TransparencyAwareFontPainter::init() 109 { 110 if (m_useGDI) 111 initializeForGDI(); 112 } 113 114 void TransparencyAwareFontPainter::initializeForGDI() 115 { 116 m_graphicsContext->save(); 117 SkColor color = m_platformContext->effectiveFillColor(); 118 // Used only when m_createdTransparencyLayer is true. 119 float layerAlpha = 0.0f; 120 if (SkColorGetA(color) != 0xFF) { 121 // When the font has some transparency, apply it by creating a new 122 // transparency layer with that opacity applied. We'll actually create 123 // a new transparency layer after we calculate the bounding box. 124 m_createdTransparencyLayer = true; 125 layerAlpha = SkColorGetA(color) / 255.0f; 126 // The color should be opaque now. 127 color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); 128 } 129 130 TransparencyWin::LayerMode layerMode; 131 IntRect layerRect; 132 if (m_platformContext->isDrawingToImageBuffer()) { 133 // Assume if we're drawing to an image buffer that the background 134 // is not opaque and we have to undo ClearType. We may want to 135 // enhance this to actually check, since it will often be opaque 136 // and we could do ClearType in that case. 137 layerMode = TransparencyWin::TextComposite; 138 layerRect = estimateTextBounds(); 139 m_graphicsContext->clip(layerRect); 140 if (m_createdTransparencyLayer) 141 m_graphicsContext->beginTransparencyLayer(layerAlpha); 142 143 // The transparency helper requires that we draw text in black in 144 // this mode and it will apply the color. 145 m_transparency.setTextCompositeColor(color); 146 color = SkColorSetRGB(0, 0, 0); 147 } else if (m_createdTransparencyLayer || canvasHasMultipleLayers(m_platformContext->canvas())) { 148 // When we're drawing a web page, we know the background is opaque, 149 // but if we're drawing to a layer, we still need extra work. 150 layerMode = TransparencyWin::OpaqueCompositeLayer; 151 layerRect = estimateTextBounds(); 152 m_graphicsContext->clip(layerRect); 153 if (m_createdTransparencyLayer) 154 m_graphicsContext->beginTransparencyLayer(layerAlpha); 155 } else { 156 // Common case of drawing onto the bottom layer of a web page: we 157 // know everything is opaque so don't need to do anything special. 158 layerMode = TransparencyWin::NoLayer; 159 } 160 161 // Bug 26088 - init() might fail if layerRect is invalid. Given this, we 162 // need to be careful to check for null pointers everywhere after this call 163 m_transparency.init(m_graphicsContext, layerMode, 164 TransparencyWin::KeepTransform, layerRect); 165 166 // Set up the DC, using the one from the transparency helper. 167 if (m_transparency.platformContext()) { 168 m_hdc = skia::BeginPlatformPaint(m_transparency.platformContext()->canvas()); 169 SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); 170 SetBkMode(m_hdc, TRANSPARENT); 171 } 172 } 173 174 TransparencyAwareFontPainter::~TransparencyAwareFontPainter() 175 { 176 if (!m_useGDI || !m_graphicsContext || !m_platformContext) 177 return; // Nothing to do. 178 m_transparency.composite(); 179 if (m_createdTransparencyLayer) 180 m_graphicsContext->endTransparencyLayer(); 181 m_graphicsContext->restore(); 182 skia::EndPlatformPaint(m_platformContext->canvas()); 183 } 184 185 // Specialization for simple GlyphBuffer painting. 186 class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter { 187 public: 188 TransparencyAwareGlyphPainter(GraphicsContext*, 189 const SimpleFontData*, 190 const GlyphBuffer&, 191 int from, int numGlyphs, 192 const FloatPoint&); 193 ~TransparencyAwareGlyphPainter(); 194 195 // Draws the partial string of glyphs, starting at |startAdvance| to the 196 // left of m_point. We express it this way so that if we're using the Skia 197 // drawing path we can use floating-point positioning, even though we have 198 // to use integer positioning in the GDI path. 199 bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, float startAdvance) const; 200 201 private: 202 virtual IntRect estimateTextBounds(); 203 204 const SimpleFontData* m_font; 205 const GlyphBuffer& m_glyphBuffer; 206 int m_from; 207 int m_numGlyphs; 208 209 // When m_useGdi is set, this stores the previous HFONT selected into the 210 // m_hdc so we can restore it. 211 HGDIOBJ m_oldFont; // For restoring the DC to its original state. 212 }; 213 214 TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( 215 GraphicsContext* context, 216 const SimpleFontData* font, 217 const GlyphBuffer& glyphBuffer, 218 int from, int numGlyphs, 219 const FloatPoint& point) 220 : TransparencyAwareFontPainter(context, point) 221 , m_font(font) 222 , m_glyphBuffer(glyphBuffer) 223 , m_from(from) 224 , m_numGlyphs(numGlyphs) 225 , m_oldFont(0) 226 { 227 init(); 228 229 if (m_hdc) 230 m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); 231 } 232 233 TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() 234 { 235 if (m_useGDI && m_hdc) 236 ::SelectObject(m_hdc, m_oldFont); 237 } 238 239 240 // Estimates the bounding box of the given text. This is copied from 241 // FontCGWin.cpp, it is possible, but a lot more work, to get the precide 242 // bounds. 243 IntRect TransparencyAwareGlyphPainter::estimateTextBounds() 244 { 245 int totalWidth = 0; 246 for (int i = 0; i < m_numGlyphs; i++) 247 totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i)); 248 249 const FontMetrics& fontMetrics = m_font->fontMetrics(); 250 return IntRect(m_point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, 251 m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(), 252 totalWidth + fontMetrics.ascent() + fontMetrics.descent(), 253 fontMetrics.lineSpacing()); 254 } 255 256 bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, 257 const WORD* glyphs, 258 const int* advances, 259 float startAdvance) const 260 { 261 if (!m_useGDI) { 262 SkPoint origin = m_point; 263 origin.fX += SkFloatToScalar(startAdvance); 264 return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(), 265 numGlyphs, glyphs, advances, 0, &origin); 266 } 267 268 if (!m_graphicsContext || !m_hdc) 269 return true; 270 271 // Windows' origin is the top-left of the bounding box, so we have 272 // to subtract off the font ascent to get it. 273 int x = lroundf(m_point.x() + startAdvance); 274 int y = lroundf(m_point.y() - m_font->fontMetrics().ascent()); 275 276 // If there is a non-blur shadow and both the fill color and shadow color 277 // are opaque, handle without skia. 278 FloatSize shadowOffset; 279 float shadowBlur; 280 Color shadowColor; 281 ColorSpace shadowColorSpace; 282 if (m_graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)) { 283 // If there is a shadow and this code is reached, windowsCanHandleDrawTextShadow() 284 // will have already returned true during the ctor initiatization of m_useGDI 285 ASSERT(shadowColor.alpha() == 255); 286 ASSERT(m_graphicsContext->fillColor().alpha() == 255); 287 ASSERT(shadowBlur == 0); 288 COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); 289 COLORREF savedTextColor = GetTextColor(m_hdc); 290 SetTextColor(m_hdc, textColor); 291 ExtTextOut(m_hdc, x + shadowOffset.width(), y + shadowOffset.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); 292 SetTextColor(m_hdc, savedTextColor); 293 } 294 295 return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); 296 } 297 298 class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter { 299 public: 300 TransparencyAwareUniscribePainter(GraphicsContext*, 301 const Font*, 302 const TextRun&, 303 int from, int to, 304 const FloatPoint&); 305 ~TransparencyAwareUniscribePainter(); 306 307 // Uniscibe will draw directly into our buffer, so we need to expose our DC. 308 HDC hdc() const { return m_hdc; } 309 310 private: 311 virtual IntRect estimateTextBounds(); 312 313 const Font* m_font; 314 const TextRun& m_run; 315 int m_from; 316 int m_to; 317 }; 318 319 TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter( 320 GraphicsContext* context, 321 const Font* font, 322 const TextRun& run, 323 int from, int to, 324 const FloatPoint& point) 325 : TransparencyAwareFontPainter(context, point) 326 , m_font(font) 327 , m_run(run) 328 , m_from(from) 329 , m_to(to) 330 { 331 init(); 332 } 333 334 TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter() 335 { 336 } 337 338 IntRect TransparencyAwareUniscribePainter::estimateTextBounds() 339 { 340 // This case really really sucks. There is no convenient way to estimate 341 // the bounding box. So we run Uniscribe twice. If we find this happens a 342 // lot, the way to fix it is to make the extra layer after the 343 // UniscribeHelper has measured the text. 344 IntPoint intPoint(lroundf(m_point.x()), 345 lroundf(m_point.y())); 346 347 UniscribeHelperTextRun state(m_run, *m_font); 348 int left = lroundf(m_point.x()) + state.characterToX(m_from); 349 int right = lroundf(m_point.x()) + state.characterToX(m_to); 350 351 // Adjust for RTL script since we just want to know the text bounds. 352 if (left > right) 353 std::swap(left, right); 354 355 // This algorithm for estimating how much extra space we need (the text may 356 // go outside the selection rect) is based roughly on 357 // TransparencyAwareGlyphPainter::estimateTextBounds above. 358 const FontMetrics& fontMetrics = m_font->fontMetrics(); 359 return IntRect(left - (fontMetrics.ascent() + fontMetrics.descent()) / 2, 360 m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(), 361 (right - left) + fontMetrics.ascent() + fontMetrics.descent(), 362 fontMetrics.lineSpacing()); 363 } 364 365 } // namespace 366 367 bool Font::canReturnFallbackFontsForComplexText() 368 { 369 return false; 370 } 371 372 bool Font::canExpandAroundIdeographsInComplexText() 373 { 374 return false; 375 } 376 377 static void drawGlyphsWin(GraphicsContext* graphicsContext, 378 const SimpleFontData* font, 379 const GlyphBuffer& glyphBuffer, 380 int from, 381 int numGlyphs, 382 const FloatPoint& point) { 383 graphicsContext->platformContext()->prepareForSoftwareDraw(); 384 385 TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); 386 387 // We draw the glyphs in chunks to avoid having to do a heap allocation for 388 // the arrays of characters and advances. Since ExtTextOut is the 389 // lowest-level text output function on Windows, there should be little 390 // penalty for splitting up the text. On the other hand, the buffer cannot 391 // be bigger than 4094 or the function will fail. 392 const int kMaxBufferLength = 256; 393 Vector<WORD, kMaxBufferLength> glyphs; 394 Vector<int, kMaxBufferLength> advances; 395 int glyphIndex = 0; // The starting glyph of the current chunk. 396 397 // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position 398 // of each glyph in floating point units and rounds to integer advances at the last possible moment. 399 400 float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph. 401 int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered. 402 while (glyphIndex < numGlyphs) { 403 // How many chars will be in this chunk? 404 int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); 405 glyphs.resize(curLen); 406 advances.resize(curLen); 407 408 float currentWidth = 0; 409 for (int i = 0; i < curLen; ++i, ++glyphIndex) { 410 glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); 411 horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex); 412 advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded; 413 lastHorizontalOffsetRounded += advances[i]; 414 currentWidth += glyphBuffer.advanceAt(from + glyphIndex); 415 416 // Bug 26088 - very large positive or negative runs can fail to 417 // render so we clamp the size here. In the specs, negative 418 // letter-spacing is implementation-defined, so this should be 419 // fine, and it matches Safari's implementation. The call actually 420 // seems to crash if kMaxNegativeRun is set to somewhere around 421 // -32830, so we give ourselves a little breathing room. 422 const int maxNegativeRun = -32768; 423 const int maxPositiveRun = 32768; 424 if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun)) 425 advances[i] = 0; 426 } 427 428 // Actually draw the glyphs (with retry on failure). 429 bool success = false; 430 for (int executions = 0; executions < 2; ++executions) { 431 success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], horizontalOffset - point.x() - currentWidth); 432 if (!success && executions == 0) { 433 // Ask the browser to load the font for us and retry. 434 PlatformBridge::ensureFontLoaded(font->platformData().hfont()); 435 continue; 436 } 437 break; 438 } 439 440 if (!success) 441 LOG_ERROR("Unable to draw the glyphs after second attempt"); 442 } 443 } 444 445 void Font::drawGlyphs(GraphicsContext* graphicsContext, 446 const SimpleFontData* font, 447 const GlyphBuffer& glyphBuffer, 448 int from, 449 int numGlyphs, 450 const FloatPoint& point) const 451 { 452 SkColor color = graphicsContext->platformContext()->effectiveFillColor(); 453 unsigned char alpha = SkColorGetA(color); 454 // Skip 100% transparent text; no need to draw anything. 455 if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke && !graphicsContext->hasShadow()) 456 return; 457 458 drawGlyphsWin(graphicsContext, font, glyphBuffer, from, numGlyphs, point); 459 } 460 461 FloatRect Font::selectionRectForComplexText(const TextRun& run, 462 const FloatPoint& point, 463 int h, 464 int from, 465 int to) const 466 { 467 UniscribeHelperTextRun state(run, *this); 468 float left = static_cast<float>(point.x() + state.characterToX(from)); 469 float right = static_cast<float>(point.x() + state.characterToX(to)); 470 471 // If the text is RTL, left will actually be after right. 472 if (left < right) 473 return FloatRect(left, point.y(), 474 right - left, static_cast<float>(h)); 475 476 return FloatRect(right, point.y(), 477 left - right, static_cast<float>(h)); 478 } 479 480 void Font::drawComplexText(GraphicsContext* graphicsContext, 481 const TextRun& run, 482 const FloatPoint& point, 483 int from, 484 int to) const 485 { 486 PlatformGraphicsContext* context = graphicsContext->platformContext(); 487 UniscribeHelperTextRun state(run, *this); 488 489 SkColor color = graphicsContext->platformContext()->effectiveFillColor(); 490 unsigned char alpha = SkColorGetA(color); 491 // Skip 100% transparent text; no need to draw anything. 492 if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) 493 return; 494 495 TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); 496 497 HDC hdc = painter.hdc(); 498 if (windowsCanHandleTextDrawing(graphicsContext) && !hdc) 499 return; 500 501 // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. 502 // Enforce non-transparent color. 503 color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); 504 if (hdc) { 505 SetTextColor(hdc, skia::SkColorToCOLORREF(color)); 506 SetBkMode(hdc, TRANSPARENT); 507 } 508 509 // If there is a non-blur shadow and both the fill color and shadow color 510 // are opaque, handle without skia. 511 FloatSize shadowOffset; 512 float shadowBlur; 513 Color shadowColor; 514 ColorSpace shadowColorSpace; 515 if (graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) && windowsCanHandleDrawTextShadow(graphicsContext)) { 516 COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); 517 COLORREF savedTextColor = GetTextColor(hdc); 518 SetTextColor(hdc, textColor); 519 state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(), 520 static_cast<int>(point.y() - fontMetrics().ascent()) + shadowOffset.height(), from, to); 521 SetTextColor(hdc, savedTextColor); 522 } 523 524 // Uniscribe counts the coordinates from the upper left, while WebKit uses 525 // the baseline, so we have to subtract off the ascent. 526 state.draw(graphicsContext, hdc, lroundf(point.x()), lroundf(point.y() - fontMetrics().ascent()), from, to); 527 528 skia::EndPlatformPaint(context->canvas()); 529 } 530 531 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const 532 { 533 notImplemented(); 534 } 535 536 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const 537 { 538 UniscribeHelperTextRun state(run, *this); 539 return static_cast<float>(state.width()); 540 } 541 542 int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, 543 bool includePartialGlyphs) const 544 { 545 // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers 546 // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. 547 int x = static_cast<int>(xFloat); 548 549 // Mac code ignores includePartialGlyphs, and they don't know what it's 550 // supposed to do, so we just ignore it as well. 551 UniscribeHelperTextRun state(run, *this); 552 int charIndex = state.xToCharacter(x); 553 554 // XToCharacter will return -1 if the position is before the first 555 // character (we get called like this sometimes). 556 if (charIndex < 0) 557 charIndex = 0; 558 return charIndex; 559 } 560 561 } // namespace WebCore 562