1 2 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 3 /* 4 * This file is part of the LibreOffice project. 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * This file incorporates work covered by the following license notice: 11 * 12 * Licensed to the Apache Software Foundation (ASF) under one or more 13 * contributor license agreements. See the NOTICE file distributed 14 * with this work for additional information regarding copyright 15 * ownership. The ASF licenses this file to you under the Apache 16 * License, Version 2.0 (the "License"); you may not use this file 17 * except in compliance with the License. You may obtain a copy of 18 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 19 */ 20 21 #include <config_features.h> 22 23 #include <memory> 24 #include <osl/module.h> 25 #include <osl/file.h> 26 #include <sal/log.hxx> 27 28 #include <comphelper/windowserrorstring.hxx> 29 #include <comphelper/scopeguard.hxx> 30 31 #include <opengl/win/gdiimpl.hxx> 32 #include <opengl/win/winlayout.hxx> 33 34 #include <vcl/opengl/OpenGLHelper.hxx> 35 #include <win/salgdi.h> 36 #include <win/saldata.hxx> 37 #include <win/wingdiimpl.hxx> 38 #include <outdev.h> 39 40 #include <win/DWriteTextRenderer.hxx> 41 #include <win/scoped_gdi.hxx> 42 43 #include <sft.hxx> 44 #include <sallayout.hxx> 45 46 #include <cstdio> 47 #include <cstdlib> 48 49 #include <rtl/character.hxx> 50 51 #include <boost/functional/hash.hpp> 52 #include <algorithm> 53 54 #include <shlwapi.h> 55 #include <winver.h> 56 57 GlobalWinGlyphCache * GlobalWinGlyphCache::get() 58 { 59 SalData *data = GetSalData(); 60 if (!data->m_pGlobalWinGlyphCache) 61 { 62 if (OpenGLHelper::isVCLOpenGLEnabled()) 63 data->m_pGlobalWinGlyphCache.reset(new OpenGLGlobalWinGlyphCache); 64 } 65 return data->m_pGlobalWinGlyphCache.get(); 66 } 67 68 bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, 69 SalGraphics& rGraphics, const GenericSalLayout& rLayout) 70 { 71 WinGlyphDrawElement aElement; 72 73 ScopedHDC aHDC(CreateCompatibleDC(hDC)); 74 75 if (!aHDC) 76 { 77 SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError())); 78 return false; 79 } 80 81 const HFONT hOrigFont = static_cast<HFONT>(SelectObject(aHDC.get(), hFont)); 82 if (hOrigFont == nullptr) 83 { 84 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError())); 85 return false; 86 } 87 const ::comphelper::ScopeGuard aHFONTrestoreScopeGuard( 88 [&aHDC,hOrigFont]() { SelectFont(aHDC.get(), hOrigFont); }); 89 90 // For now we assume DWrite is present and we won't bother with fallback paths. 91 D2DWriteTextOutRenderer * pTxt = dynamic_cast<D2DWriteTextOutRenderer *>(&TextOutRenderer::get(true)); 92 if (!pTxt) 93 return false; 94 95 pTxt->changeTextAntiAliasMode(D2DTextAntiAliasMode::AntiAliased); 96 97 if (!pTxt->BindFont(aHDC.get())) 98 { 99 SAL_WARN("vcl.gdi", "Binding of font failed. The font might not be supported by DirectWrite."); 100 return false; 101 } 102 const ::comphelper::ScopeGuard aFontReleaseScopeGuard([&pTxt]() { pTxt->ReleaseFont(); }); 103 104 std::vector<WORD> aGlyphIndices(1); 105 aGlyphIndices[0] = nGlyphIndex; 106 // Fetch the ink boxes and calculate the size of the atlas. 107 tools::Rectangle bounds(0, 0, 0, 0); 108 auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1); 109 if (aInkBoxes.empty()) 110 return false; 111 112 for (auto &box : aInkBoxes) 113 bounds.Union(box + Point(bounds.Right(), 0)); 114 115 // bounds.Top() is the offset from the baseline at (0,0) to the top of the 116 // inkbox. 117 aElement.mnBaselineOffset = -bounds.Top(); 118 aElement.mnHeight = bounds.getHeight(); 119 aElement.mbVertical = false; 120 121 // Try hard to avoid overlap as we want to be able to use 122 // individual rectangles for each glyph. The ABC widths don't 123 // take anti-aliasing into consideration. Let's hope that leaving 124 // "extra" space between glyphs will help. 125 std::vector<float> aGlyphAdv(1); // offsets between glyphs 126 std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, {0.0f, 0.0f}); 127 std::vector<int> aEnds(1); // end of each glyph box 128 float fHScale = getHScale(); 129 float totWidth = 0; 130 { 131 int overhang = aInkBoxes[0].Left(); 132 int blackWidth = aInkBoxes[0].getWidth() * fHScale; // width of non-AA pixels 133 aElement.maLeftOverhangs = overhang; 134 135 aGlyphAdv[0] = blackWidth + aElement.getExtraSpace(); 136 aGlyphOffset[0].advanceOffset = -overhang; 137 138 totWidth += aGlyphAdv[0]; 139 aEnds[0] = totWidth; 140 } 141 // Leave extra space also at top and bottom 142 int nBitmapWidth = totWidth; 143 int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace(); 144 145 UINT nPos = 0; 146 147 aElement.maLocation.SetLeft(nPos); 148 aElement.maLocation.SetRight(aEnds[0]); 149 aElement.maLocation.SetTop(0); 150 aElement.maLocation.SetBottom(bounds.getHeight() + aElement.getExtraSpace()); 151 nPos = aEnds[0]; 152 153 std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight)); 154 155 SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0)); 156 SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255)); 157 158 aDC->fill(RGB(0xff, 0xff, 0xff)); 159 160 pTxt->BindDC(aDC->getCompatibleHDC(), tools::Rectangle(0, 0, nBitmapWidth, nBitmapHeight)); 161 auto pRT = pTxt->GetRenderTarget(); 162 163 ID2D1SolidColorBrush* pBrush = nullptr; 164 if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush))) 165 return false; 166 167 D2D1_POINT_2F baseline = { 168 static_cast<FLOAT>(aElement.getExtraOffset()), 169 static_cast<FLOAT>(aElement.getExtraOffset() + aElement.mnBaselineOffset) 170 }; 171 172 DWRITE_GLYPH_RUN glyphs = { 173 pTxt->GetFontFace(), 174 pTxt->GetEmHeight(), 175 1, 176 aGlyphIndices.data(), 177 aGlyphAdv.data(), 178 aGlyphOffset.data(), 179 false, 180 0 181 }; 182 183 WinFontTransformGuard aTransformGuard(pRT, fHScale, rLayout, baseline); 184 pRT->BeginDraw(); 185 pRT->DrawGlyphRun(baseline, &glyphs, pBrush); 186 HRESULT hResult = pRT->EndDraw(); 187 188 pBrush->Release(); 189 190 switch (hResult) 191 { 192 case S_OK: 193 break; 194 case D2DERR_RECREATE_TARGET: 195 pTxt->CreateRenderTarget(); 196 break; 197 default: 198 SAL_WARN("vcl.gdi", "DrawGlyphRun-EndDraw failed: " << WindowsErrorString(GetLastError())); 199 return false; 200 } 201 202 if (!GlobalWinGlyphCache::get()->AllocateTexture(aElement, aDC.get())) 203 return false; 204 205 maWinGlyphCache.PutDrawElementInCache(std::move(aElement), nGlyphIndex); 206 207 return true; 208 } 209 210 TextOutRenderer & TextOutRenderer::get(bool bUseDWrite) 211 { 212 SalData *const pSalData = GetSalData(); 213 214 if (!pSalData) 215 { // don't call this after DeInitVCL() 216 fprintf(stderr, "TextOutRenderer fatal error: no SalData"); 217 abort(); 218 } 219 220 if (bUseDWrite) 221 { 222 if (!pSalData->m_pD2DWriteTextOutRenderer) 223 { 224 pSalData->m_pD2DWriteTextOutRenderer.reset(new D2DWriteTextOutRenderer()); 225 } 226 return *pSalData->m_pD2DWriteTextOutRenderer; 227 } 228 if (!pSalData->m_pExTextOutRenderer) 229 { 230 pSalData->m_pExTextOutRenderer.reset(new ExTextOutRenderer); 231 } 232 return *pSalData->m_pExTextOutRenderer; 233 } 234 235 236 bool ExTextOutRenderer::operator ()(GenericSalLayout const &rLayout, 237 SalGraphics & /*rGraphics*/, 238 HDC hDC) 239 { 240 HFONT hFont = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT )); 241 ScopedHFONT hAltFont; 242 bool bUseAltFont = false; 243 bool bShift = false; 244 if (rLayout.GetFont().GetFontSelectPattern().mbVertical) 245 { 246 LOGFONTW aLogFont; 247 GetObjectW(hFont, sizeof(aLogFont), &aLogFont); 248 if (aLogFont.lfFaceName[0] == '@') 249 { 250 memmove(&aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], 251 sizeof(aLogFont.lfFaceName)-sizeof(aLogFont.lfFaceName[0])); 252 hAltFont.reset(CreateFontIndirectW(&aLogFont)); 253 } 254 else 255 { 256 bShift = true; 257 aLogFont.lfEscapement += 2700; 258 aLogFont.lfOrientation = aLogFont.lfEscapement; 259 hAltFont.reset(CreateFontIndirectW(&aLogFont)); 260 } 261 } 262 263 UINT nTextAlign = GetTextAlign ( hDC ); 264 int nStart = 0; 265 Point aPos(0, 0); 266 const GlyphItem* pGlyph; 267 while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) 268 { 269 WORD glyphWStr[] = { pGlyph->glyphId() }; 270 if (hAltFont && pGlyph->IsVertical() == bUseAltFont) 271 { 272 bUseAltFont = !bUseAltFont; 273 SelectFont(hDC, bUseAltFont ? hAltFont.get() : hFont); 274 } 275 if (bShift && pGlyph->IsVertical()) 276 SetTextAlign(hDC, TA_TOP|TA_LEFT); 277 278 ExtTextOutW(hDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, nullptr, LPCWSTR(&glyphWStr), 1, nullptr); 279 280 if (bShift && pGlyph->IsVertical()) 281 SetTextAlign(hDC, nTextAlign); 282 } 283 if (hAltFont) 284 { 285 if (bUseAltFont) 286 SelectFont(hDC, hFont); 287 } 288 289 return true; 290 } 291 292 std::unique_ptr<GenericSalLayout> WinSalGraphics::GetTextLayout(int nFallbackLevel) 293 { 294 assert(mpWinFontEntry[nFallbackLevel]); 295 if (!mpWinFontEntry[nFallbackLevel]) 296 return nullptr; 297 298 assert(mpWinFontEntry[nFallbackLevel]->GetFontFace()); 299 300 mpWinFontEntry[nFallbackLevel]->SetGraphics(this); 301 return std::make_unique<GenericSalLayout>(*mpWinFontEntry[nFallbackLevel]); 302 } 303 304 WinFontInstance::WinFontInstance(const WinFontFace& rPFF, const FontSelectPattern& rFSP) 305 : LogicalFontInstance(rPFF, rFSP) 306 , m_pGraphics(nullptr) 307 , m_hFont(nullptr) 308 , m_fScale(1.0f) 309 { 310 } 311 312 WinFontInstance::~WinFontInstance() 313 { 314 if (m_hFont) 315 ::DeleteFont(m_hFont); 316 } 317 318 bool WinFontInstance::hasHScale() const 319 { 320 const FontSelectPattern &rPattern = GetFontSelectPattern(); 321 int nHeight(rPattern.mnHeight); 322 int nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight); 323 return nWidth != nHeight; 324 } 325 326 float WinFontInstance::getHScale() const 327 { 328 const FontSelectPattern& rPattern = GetFontSelectPattern(); 329 int nHeight(rPattern.mnHeight); 330 if (!nHeight) 331 return 1.0; 332 float nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight); 333 return nWidth / nHeight; 334 } 335 336 namespace { 337 338 struct BlobReference 339 { 340 hb_blob_t* mpBlob; 341 BlobReference(hb_blob_t* pBlob) : mpBlob(pBlob) 342 { 343 hb_blob_reference(mpBlob); 344 } 345 BlobReference(BlobReference const & other) 346 : mpBlob(other.mpBlob) 347 { 348 hb_blob_reference(mpBlob); 349 } 350 ~BlobReference() { hb_blob_destroy(mpBlob); } 351 }; 352 353 } 354 355 using BlobCacheKey = std::pair<rtl::Reference<PhysicalFontFace>, hb_tag_t>; 356 357 namespace { 358 359 struct BlobCacheKeyHash 360 { 361 std::size_t operator()(BlobCacheKey const& rKey) const 362 { 363 std::size_t seed = 0; 364 boost::hash_combine(seed, rKey.first.get()); 365 boost::hash_combine(seed, rKey.second); 366 return seed; 367 } 368 }; 369 370 } 371 372 static hb_blob_t* getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData) 373 { 374 static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50); 375 376 WinFontInstance* pFont = static_cast<WinFontInstance*>(pUserData); 377 HDC hDC = pFont->GetGraphics()->getHDC(); 378 HFONT hFont = pFont->GetHFONT(); 379 assert(hDC); 380 assert(hFont); 381 382 BlobCacheKey cacheKey { rtl::Reference<PhysicalFontFace>(pFont->GetFontFace()), nTableTag }; 383 auto it = gCache.find(cacheKey); 384 if (it != gCache.end()) 385 { 386 hb_blob_reference(it->second.mpBlob); 387 return it->second.mpBlob; 388 } 389 390 sal_uLong nLength = 0; 391 unsigned char* pBuffer = nullptr; 392 393 HGDIOBJ hOrigFont = SelectObject(hDC, hFont); 394 nLength = ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, nullptr, 0); 395 if (nLength > 0 && nLength != GDI_ERROR) 396 { 397 pBuffer = new unsigned char[nLength]; 398 ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, pBuffer, nLength); 399 } 400 SelectObject(hDC, hOrigFont); 401 402 if (!pBuffer) 403 return nullptr; 404 405 hb_blob_t* pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, 406 pBuffer, [](void* data){ delete[] static_cast<unsigned char*>(data); }); 407 if (!pBlob) 408 return pBlob; 409 gCache.insert({cacheKey, BlobReference(pBlob)}); 410 return pBlob; 411 } 412 413 hb_font_t* WinFontInstance::ImplInitHbFont() 414 { 415 assert(m_pGraphics); 416 hb_font_t* pHbFont = InitHbFont(hb_face_create_for_tables(getFontTable, this, nullptr)); 417 418 // Calculate the AverageWidthFactor, see LogicalFontInstance::GetScale(). 419 if (GetFontSelectPattern().mnWidth) 420 { 421 double nUPEM = hb_face_get_upem(hb_font_get_face(pHbFont)); 422 423 LOGFONTW aLogFont; 424 GetObjectW(m_hFont, sizeof(LOGFONTW), &aLogFont); 425 426 // Set the height (font size) to EM to minimize rounding errors. 427 aLogFont.lfHeight = -nUPEM; 428 // Set width to the default to get the original value in the metrics. 429 aLogFont.lfWidth = 0; 430 431 TEXTMETRICW aFontMetric; 432 { 433 // Get the font metrics. 434 HDC hDC = m_pGraphics->getHDC(); 435 ScopedSelectedHFONT hFont(hDC, CreateFontIndirectW(&aLogFont)); 436 GetTextMetricsW(hDC, &aFontMetric); 437 } 438 439 SetAverageWidthFactor(nUPEM / aFontMetric.tmAveCharWidth); 440 } 441 442 return pHbFont; 443 } 444 445 void WinFontInstance::SetGraphics(WinSalGraphics *pGraphics) 446 { 447 m_pGraphics = pGraphics; 448 if (m_hFont) 449 return; 450 HFONT hOrigFont; 451 m_hFont = m_pGraphics->ImplDoSetFont(GetFontSelectPattern(), GetFontFace(), m_fScale, hOrigFont); 452 SelectObject(m_pGraphics->getHDC(), hOrigFont); 453 } 454 455 bool WinSalGraphics::CacheGlyphs(const GenericSalLayout& rLayout) 456 { 457 static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == nullptr); 458 if (!bDoGlyphCaching) 459 return false; 460 461 if (rLayout.GetOrientation()) 462 // Our caching is incomplete, skip it for non-horizontal text. 463 return false; 464 465 HDC hDC = getHDC(); 466 WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont()); 467 HFONT hFONT = rFont.GetHFONT(); 468 469 int nStart = 0; 470 Point aPos(0, 0); 471 const GlyphItem* pGlyph; 472 while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) 473 { 474 if (!rFont.GetWinGlyphCache().IsGlyphCached(pGlyph->glyphId())) 475 { 476 if (!rFont.CacheGlyphToAtlas(hDC, hFONT, pGlyph->glyphId(), *this, rLayout)) 477 return false; 478 } 479 } 480 481 return true; 482 } 483 484 bool WinSalGraphics::DrawCachedGlyphs(const GenericSalLayout& rLayout) 485 { 486 HDC hDC = getHDC(); 487 488 tools::Rectangle aRect; 489 rLayout.GetBoundRect(aRect); 490 491 COLORREF color = GetTextColor(hDC); 492 Color salColor(GetRValue(color), GetGValue(color), GetBValue(color)); 493 494 WinSalGraphicsImplBase *pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get()); 495 if (!pImpl->UseTextDraw()) 496 return false; 497 498 WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont()); 499 500 int nStart = 0; 501 Point aPos(0, 0); 502 const GlyphItem* pGlyph; 503 while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart)) 504 { 505 WinGlyphDrawElement& rElement(rFont.GetWinGlyphCache().GetDrawElement(pGlyph->glyphId())); 506 const CompatibleDC::Texture* texture = rElement.maTexture.get(); 507 508 if (!texture || !texture->isValid()) 509 return false; 510 511 SalTwoRect a2Rects(0, 0, 512 texture->GetWidth(), texture->GetHeight(), 513 aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs, 514 aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(), 515 texture->GetWidth(), texture->GetHeight()); 516 517 pImpl->DeferredTextDraw(texture, salColor, a2Rects); 518 } 519 520 return true; 521 } 522 523 static void PruneGlyphCache() 524 { 525 GlobalWinGlyphCache::get()->Prune(); 526 } 527 528 void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite) 529 { 530 TextOutRenderer &render = TextOutRenderer::get(bUseDWrite); 531 render(rLayout, *this, hDC); 532 } 533 534 void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) 535 { 536 WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get()); 537 if( !mbPrinter && pImpl->DrawTextLayout(rLayout)) 538 return; // handled by pImpl 539 540 HDC hDC = getHDC(); 541 const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont()); 542 const HFONT hLayoutFont = pWinFont->GetHFONT(); 543 bool bUseClassic = !pImpl->UseTextDraw() || mbPrinter; 544 545 // Our DirectWrite renderer is incomplete, skip it for vertical text where glyphs are not 546 // rotated. 547 bool bForceGDI = rLayout.GetFont().GetFontSelectPattern().mbVertical; 548 549 if (bUseClassic) 550 { 551 // no OpenGL, just classic rendering 552 const HFONT hOrigFont = ::SelectFont(hDC, hLayoutFont); 553 DrawTextLayout(rLayout, hDC, false); 554 ::SelectFont(hDC, hOrigFont); 555 } 556 // if we can't draw the cached OpenGL glyphs, try to draw a full OpenGL layout 557 else if (!bForceGDI && CacheGlyphs(rLayout) && DrawCachedGlyphs(rLayout)) 558 { 559 PruneGlyphCache(); 560 } 561 else 562 { 563 PruneGlyphCache(); // prune the cache from the failed calls above 564 565 // We have to render the text to a hidden texture, and draw it. 566 // 567 // Note that Windows GDI does not really support the alpha correctly 568 // when drawing - ie. it draws nothing to the alpha channel when 569 // rendering the text, even the antialiasing is done as 'real' pixels, 570 // not alpha... 571 // 572 // Luckily, this does not really limit us: 573 // 574 // To blend properly, we draw the texture, but then use it as an alpha 575 // channel for solid color (that will define the text color). This 576 // destroys the subpixel antialiasing - turns it into 'classic' 577 // antialiasing - but that is the best we can do, because the subpixel 578 // antialiasing needs to know what is in the background: When the 579 // background is white, or white-ish, it does the subpixel, but when 580 // there is a color, it just darkens the color (and does this even 581 // when part of the character is on a colored background, and part on 582 // white). It has to work this way, the results would look strange 583 // otherwise. 584 // 585 // For the GL rendering to work even with the subpixel antialiasing, 586 // we would need to get the current texture from the screen, let GDI 587 // draw the text to it (so that it can decide well where to use the 588 // subpixel and where not), and draw the result - but in that case we 589 // don't need alpha anyway. 590 // 591 // TODO: check the performance of this 2nd approach at some stage and 592 // switch to that if it performs well. 593 594 tools::Rectangle aRect; 595 rLayout.GetBoundRect(aRect); 596 if( aRect.IsEmpty()) 597 return; 598 599 pImpl->PreDrawText(); 600 601 std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(*this, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight())); 602 603 // we are making changes to the DC, make sure we got a new one 604 assert(aDC->getCompatibleHDC() != hDC); 605 606 RECT aWinRect = { aRect.Left(), aRect.Top(), aRect.Left() + aRect.GetWidth(), aRect.Top() + aRect.GetHeight() }; 607 ::FillRect(aDC->getCompatibleHDC(), &aWinRect, static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH))); 608 609 // setup the hidden DC with black color and white background, we will 610 // use the result of the text drawing later as a mask only 611 const HFONT hOrigFont = ::SelectFont(aDC->getCompatibleHDC(), hLayoutFont); 612 613 ::SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0)); 614 ::SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255)); 615 616 UINT nTextAlign = ::GetTextAlign(hDC); 617 ::SetTextAlign(aDC->getCompatibleHDC(), nTextAlign); 618 619 COLORREF color = ::GetTextColor(hDC); 620 Color salColor(GetRValue(color), GetGValue(color), GetBValue(color)); 621 622 // the actual drawing 623 DrawTextLayout(rLayout, aDC->getCompatibleHDC(), !bForceGDI); 624 625 std::unique_ptr<CompatibleDC::Texture> xTexture(aDC->getAsMaskTexture()); 626 if (xTexture) 627 pImpl->DrawTextMask(xTexture.get(), salColor, aDC->getTwoRect()); 628 629 ::SelectFont(aDC->getCompatibleHDC(), hOrigFont); 630 631 pImpl->PostDrawText(); 632 } 633 } 634 635 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 636
