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