summaryrefslogtreecommitdiffstats
path: root/vcl/win/gdi/winlayout.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/win/gdi/winlayout.cxx635
1 files changed, 635 insertions, 0 deletions
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
new file mode 100644
index 000000000..056540921
--- /dev/null
+++ b/vcl/win/gdi/winlayout.cxx
@@ -0,0 +1,635 @@
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <memory>
+#include <osl/module.h>
+#include <osl/file.h>
+#include <sal/log.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <opengl/win/gdiimpl.hxx>
+#include <opengl/win/winlayout.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/wingdiimpl.hxx>
+#include <outdev.h>
+
+#include <win/DWriteTextRenderer.hxx>
+#include <win/scoped_gdi.hxx>
+
+#include <sft.hxx>
+#include <sallayout.hxx>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <rtl/character.hxx>
+
+#include <boost/functional/hash.hpp>
+#include <algorithm>
+
+#include <shlwapi.h>
+#include <winver.h>
+
+GlobalWinGlyphCache * GlobalWinGlyphCache::get()
+{
+ SalData *data = GetSalData();
+ if (!data->m_pGlobalWinGlyphCache)
+ {
+ if (OpenGLHelper::isVCLOpenGLEnabled())
+ data->m_pGlobalWinGlyphCache.reset(new OpenGLGlobalWinGlyphCache);
+ }
+ return data->m_pGlobalWinGlyphCache.get();
+}
+
+bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex,
+ SalGraphics& rGraphics, const GenericSalLayout& rLayout)
+{
+ WinGlyphDrawElement aElement;
+
+ ScopedHDC aHDC(CreateCompatibleDC(hDC));
+
+ if (!aHDC)
+ {
+ SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ const HFONT hOrigFont = static_cast<HFONT>(SelectObject(aHDC.get(), hFont));
+ if (hOrigFont == nullptr)
+ {
+ SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ const ::comphelper::ScopeGuard aHFONTrestoreScopeGuard(
+ [&aHDC,hOrigFont]() { SelectFont(aHDC.get(), hOrigFont); });
+
+ // For now we assume DWrite is present and we won't bother with fallback paths.
+ D2DWriteTextOutRenderer * pTxt = dynamic_cast<D2DWriteTextOutRenderer *>(&TextOutRenderer::get(true));
+ if (!pTxt)
+ return false;
+
+ pTxt->changeTextAntiAliasMode(D2DTextAntiAliasMode::AntiAliased);
+
+ if (!pTxt->BindFont(aHDC.get()))
+ {
+ SAL_WARN("vcl.gdi", "Binding of font failed. The font might not be supported by DirectWrite.");
+ return false;
+ }
+ const ::comphelper::ScopeGuard aFontReleaseScopeGuard([&pTxt]() { pTxt->ReleaseFont(); });
+
+ std::vector<WORD> aGlyphIndices(1);
+ aGlyphIndices[0] = nGlyphIndex;
+ // Fetch the ink boxes and calculate the size of the atlas.
+ tools::Rectangle bounds(0, 0, 0, 0);
+ auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1);
+ if (aInkBoxes.empty())
+ return false;
+
+ for (auto &box : aInkBoxes)
+ bounds.Union(box + Point(bounds.Right(), 0));
+
+ // bounds.Top() is the offset from the baseline at (0,0) to the top of the
+ // inkbox.
+ aElement.mnBaselineOffset = -bounds.Top();
+ aElement.mnHeight = bounds.getHeight();
+ aElement.mbVertical = false;
+
+ // Try hard to avoid overlap as we want to be able to use
+ // individual rectangles for each glyph. The ABC widths don't
+ // take anti-aliasing into consideration. Let's hope that leaving
+ // "extra" space between glyphs will help.
+ std::vector<float> aGlyphAdv(1); // offsets between glyphs
+ std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, {0.0f, 0.0f});
+ std::vector<int> aEnds(1); // end of each glyph box
+ float fHScale = getHScale();
+ float totWidth = 0;
+ {
+ int overhang = aInkBoxes[0].Left();
+ int blackWidth = aInkBoxes[0].getWidth() * fHScale; // width of non-AA pixels
+ aElement.maLeftOverhangs = overhang;
+
+ aGlyphAdv[0] = blackWidth + aElement.getExtraSpace();
+ aGlyphOffset[0].advanceOffset = -overhang;
+
+ totWidth += aGlyphAdv[0];
+ aEnds[0] = totWidth;
+ }
+ // Leave extra space also at top and bottom
+ int nBitmapWidth = totWidth;
+ int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace();
+
+ UINT nPos = 0;
+
+ aElement.maLocation.SetLeft(nPos);
+ aElement.maLocation.SetRight(aEnds[0]);
+ aElement.maLocation.SetTop(0);
+ aElement.maLocation.SetBottom(bounds.getHeight() + aElement.getExtraSpace());
+ nPos = aEnds[0];
+
+ std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight));
+
+ SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0));
+ SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255));
+
+ aDC->fill(RGB(0xff, 0xff, 0xff));
+
+ pTxt->BindDC(aDC->getCompatibleHDC(), tools::Rectangle(0, 0, nBitmapWidth, nBitmapHeight));
+ auto pRT = pTxt->GetRenderTarget();
+
+ ID2D1SolidColorBrush* pBrush = nullptr;
+ if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush)))
+ return false;
+
+ D2D1_POINT_2F baseline = {
+ static_cast<FLOAT>(aElement.getExtraOffset()),
+ static_cast<FLOAT>(aElement.getExtraOffset() + aElement.mnBaselineOffset)
+ };
+
+ DWRITE_GLYPH_RUN glyphs = {
+ pTxt->GetFontFace(),
+ pTxt->GetEmHeight(),
+ 1,
+ aGlyphIndices.data(),
+ aGlyphAdv.data(),
+ aGlyphOffset.data(),
+ false,
+ 0
+ };
+
+ WinFontTransformGuard aTransformGuard(pRT, fHScale, rLayout, baseline);
+ pRT->BeginDraw();
+ pRT->DrawGlyphRun(baseline, &glyphs, pBrush);
+ HRESULT hResult = pRT->EndDraw();
+
+ pBrush->Release();
+
+ switch (hResult)
+ {
+ case S_OK:
+ break;
+ case D2DERR_RECREATE_TARGET:
+ pTxt->CreateRenderTarget();
+ break;
+ default:
+ SAL_WARN("vcl.gdi", "DrawGlyphRun-EndDraw failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ if (!GlobalWinGlyphCache::get()->AllocateTexture(aElement, aDC.get()))
+ return false;
+
+ maWinGlyphCache.PutDrawElementInCache(std::move(aElement), nGlyphIndex);
+
+ return true;
+}
+
+TextOutRenderer & TextOutRenderer::get(bool bUseDWrite)
+{
+ SalData *const pSalData = GetSalData();
+
+ if (!pSalData)
+ { // don't call this after DeInitVCL()
+ fprintf(stderr, "TextOutRenderer fatal error: no SalData");
+ abort();
+ }
+
+ if (bUseDWrite)
+ {
+ if (!pSalData->m_pD2DWriteTextOutRenderer)
+ {
+ pSalData->m_pD2DWriteTextOutRenderer.reset(new D2DWriteTextOutRenderer());
+ }
+ return *pSalData->m_pD2DWriteTextOutRenderer;
+ }
+ if (!pSalData->m_pExTextOutRenderer)
+ {
+ pSalData->m_pExTextOutRenderer.reset(new ExTextOutRenderer);
+ }
+ return *pSalData->m_pExTextOutRenderer;
+}
+
+
+bool ExTextOutRenderer::operator ()(GenericSalLayout const &rLayout,
+ SalGraphics & /*rGraphics*/,
+ HDC hDC)
+{
+ HFONT hFont = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT ));
+ ScopedHFONT hAltFont;
+ bool bUseAltFont = false;
+ bool bShift = false;
+ if (rLayout.GetFont().GetFontSelectPattern().mbVertical)
+ {
+ LOGFONTW aLogFont;
+ GetObjectW(hFont, sizeof(aLogFont), &aLogFont);
+ if (aLogFont.lfFaceName[0] == '@')
+ {
+ memmove(&aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1],
+ sizeof(aLogFont.lfFaceName)-sizeof(aLogFont.lfFaceName[0]));
+ hAltFont.reset(CreateFontIndirectW(&aLogFont));
+ }
+ else
+ {
+ bShift = true;
+ aLogFont.lfEscapement += 2700;
+ aLogFont.lfOrientation = aLogFont.lfEscapement;
+ hAltFont.reset(CreateFontIndirectW(&aLogFont));
+ }
+ }
+
+ UINT nTextAlign = GetTextAlign ( hDC );
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ WORD glyphWStr[] = { pGlyph->glyphId() };
+ if (hAltFont && pGlyph->IsVertical() == bUseAltFont)
+ {
+ bUseAltFont = !bUseAltFont;
+ SelectFont(hDC, bUseAltFont ? hAltFont.get() : hFont);
+ }
+ if (bShift && pGlyph->IsVertical())
+ SetTextAlign(hDC, TA_TOP|TA_LEFT);
+
+ ExtTextOutW(hDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, nullptr, LPCWSTR(&glyphWStr), 1, nullptr);
+
+ if (bShift && pGlyph->IsVertical())
+ SetTextAlign(hDC, nTextAlign);
+ }
+ if (hAltFont)
+ {
+ if (bUseAltFont)
+ SelectFont(hDC, hFont);
+ }
+
+ return true;
+}
+
+std::unique_ptr<GenericSalLayout> WinSalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ assert(mpWinFontEntry[nFallbackLevel]);
+ if (!mpWinFontEntry[nFallbackLevel])
+ return nullptr;
+
+ assert(mpWinFontEntry[nFallbackLevel]->GetFontFace());
+
+ mpWinFontEntry[nFallbackLevel]->SetGraphics(this);
+ return std::make_unique<GenericSalLayout>(*mpWinFontEntry[nFallbackLevel]);
+}
+
+WinFontInstance::WinFontInstance(const WinFontFace& rPFF, const FontSelectPattern& rFSP)
+ : LogicalFontInstance(rPFF, rFSP)
+ , m_pGraphics(nullptr)
+ , m_hFont(nullptr)
+ , m_fScale(1.0f)
+{
+}
+
+WinFontInstance::~WinFontInstance()
+{
+ if (m_hFont)
+ ::DeleteFont(m_hFont);
+}
+
+bool WinFontInstance::hasHScale() const
+{
+ const FontSelectPattern &rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ int nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth != nHeight;
+}
+
+float WinFontInstance::getHScale() const
+{
+ const FontSelectPattern& rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ if (!nHeight)
+ return 1.0;
+ float nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth / nHeight;
+}
+
+namespace {
+
+struct BlobReference
+{
+ hb_blob_t* mpBlob;
+ BlobReference(hb_blob_t* pBlob) : mpBlob(pBlob)
+ {
+ hb_blob_reference(mpBlob);
+ }
+ BlobReference(BlobReference const & other)
+ : mpBlob(other.mpBlob)
+ {
+ hb_blob_reference(mpBlob);
+ }
+ ~BlobReference() { hb_blob_destroy(mpBlob); }
+};
+
+}
+
+using BlobCacheKey = std::pair<rtl::Reference<PhysicalFontFace>, hb_tag_t>;
+
+namespace {
+
+struct BlobCacheKeyHash
+{
+ std::size_t operator()(BlobCacheKey const& rKey) const
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, rKey.first.get());
+ boost::hash_combine(seed, rKey.second);
+ return seed;
+ }
+};
+
+}
+
+static hb_blob_t* getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
+{
+ static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
+
+ WinFontInstance* pFont = static_cast<WinFontInstance*>(pUserData);
+ HDC hDC = pFont->GetGraphics()->getHDC();
+ HFONT hFont = pFont->GetHFONT();
+ assert(hDC);
+ assert(hFont);
+
+ BlobCacheKey cacheKey { rtl::Reference<PhysicalFontFace>(pFont->GetFontFace()), nTableTag };
+ auto it = gCache.find(cacheKey);
+ if (it != gCache.end())
+ {
+ hb_blob_reference(it->second.mpBlob);
+ return it->second.mpBlob;
+ }
+
+ sal_uLong nLength = 0;
+ unsigned char* pBuffer = nullptr;
+
+ HGDIOBJ hOrigFont = SelectObject(hDC, hFont);
+ nLength = ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, nullptr, 0);
+ if (nLength > 0 && nLength != GDI_ERROR)
+ {
+ pBuffer = new unsigned char[nLength];
+ ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, pBuffer, nLength);
+ }
+ SelectObject(hDC, hOrigFont);
+
+ if (!pBuffer)
+ return nullptr;
+
+ hb_blob_t* pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
+ pBuffer, [](void* data){ delete[] static_cast<unsigned char*>(data); });
+ if (!pBlob)
+ return pBlob;
+ gCache.insert({cacheKey, BlobReference(pBlob)});
+ return pBlob;
+}
+
+hb_font_t* WinFontInstance::ImplInitHbFont()
+{
+ assert(m_pGraphics);
+ hb_font_t* pHbFont = InitHbFont(hb_face_create_for_tables(getFontTable, this, nullptr));
+
+ // Calculate the AverageWidthFactor, see LogicalFontInstance::GetScale().
+ if (GetFontSelectPattern().mnWidth)
+ {
+ double nUPEM = hb_face_get_upem(hb_font_get_face(pHbFont));
+
+ LOGFONTW aLogFont;
+ GetObjectW(m_hFont, sizeof(LOGFONTW), &aLogFont);
+
+ // Set the height (font size) to EM to minimize rounding errors.
+ aLogFont.lfHeight = -nUPEM;
+ // Set width to the default to get the original value in the metrics.
+ aLogFont.lfWidth = 0;
+
+ TEXTMETRICW aFontMetric;
+ {
+ // Get the font metrics.
+ HDC hDC = m_pGraphics->getHDC();
+ ScopedSelectedHFONT hFont(hDC, CreateFontIndirectW(&aLogFont));
+ GetTextMetricsW(hDC, &aFontMetric);
+ }
+
+ SetAverageWidthFactor(nUPEM / aFontMetric.tmAveCharWidth);
+ }
+
+ return pHbFont;
+}
+
+void WinFontInstance::SetGraphics(WinSalGraphics *pGraphics)
+{
+ m_pGraphics = pGraphics;
+ if (m_hFont)
+ return;
+ HFONT hOrigFont;
+ m_hFont = m_pGraphics->ImplDoSetFont(GetFontSelectPattern(), GetFontFace(), hOrigFont);
+ SelectObject(m_pGraphics->getHDC(), hOrigFont);
+}
+
+bool WinSalGraphics::CacheGlyphs(const GenericSalLayout& rLayout)
+{
+ static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == nullptr);
+ if (!bDoGlyphCaching)
+ return false;
+
+ if (rLayout.GetOrientation())
+ // Our caching is incomplete, skip it for non-horizontal text.
+ return false;
+
+ HDC hDC = getHDC();
+ WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont());
+ HFONT hFONT = rFont.GetHFONT();
+
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ if (!rFont.GetWinGlyphCache().IsGlyphCached(pGlyph->glyphId()))
+ {
+ if (!rFont.CacheGlyphToAtlas(hDC, hFONT, pGlyph->glyphId(), *this, rLayout))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool WinSalGraphics::DrawCachedGlyphs(const GenericSalLayout& rLayout)
+{
+ HDC hDC = getHDC();
+
+ tools::Rectangle aRect;
+ rLayout.GetBoundRect(aRect);
+
+ COLORREF color = GetTextColor(hDC);
+ Color salColor(GetRValue(color), GetGValue(color), GetBValue(color));
+
+ WinSalGraphicsImplBase *pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
+ if (!pImpl->UseTextDraw())
+ return false;
+
+ WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont());
+
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ WinGlyphDrawElement& rElement(rFont.GetWinGlyphCache().GetDrawElement(pGlyph->glyphId()));
+ const CompatibleDC::Texture* texture = rElement.maTexture.get();
+
+ if (!texture || !texture->isValid())
+ return false;
+
+ SalTwoRect a2Rects(0, 0,
+ texture->GetWidth(), texture->GetHeight(),
+ aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+ aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+ texture->GetWidth(), texture->GetHeight());
+
+ pImpl->DeferredTextDraw(texture, salColor, a2Rects);
+ }
+
+ return true;
+}
+
+static void PruneGlyphCache()
+{
+ GlobalWinGlyphCache::get()->Prune();
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite)
+{
+ TextOutRenderer &render = TextOutRenderer::get(bUseDWrite);
+ render(rLayout, *this, hDC);
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
+ if( !mbPrinter && pImpl->DrawTextLayout(rLayout))
+ return; // handled by pImpl
+
+ HDC hDC = getHDC();
+ const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont());
+ const HFONT hLayoutFont = pWinFont->GetHFONT();
+ bool bUseClassic = !pImpl->UseTextDraw() || mbPrinter;
+
+ // Our DirectWrite renderer is incomplete, skip it for vertical text where glyphs are not
+ // rotated.
+ bool bForceGDI = rLayout.GetFont().GetFontSelectPattern().mbVertical;
+
+ if (bUseClassic)
+ {
+ // no OpenGL, just classic rendering
+ const HFONT hOrigFont = ::SelectFont(hDC, hLayoutFont);
+ DrawTextLayout(rLayout, hDC, false);
+ ::SelectFont(hDC, hOrigFont);
+ }
+ // if we can't draw the cached OpenGL glyphs, try to draw a full OpenGL layout
+ else if (!bForceGDI && CacheGlyphs(rLayout) && DrawCachedGlyphs(rLayout))
+ {
+ PruneGlyphCache();
+ }
+ else
+ {
+ PruneGlyphCache(); // prune the cache from the failed calls above
+
+ // We have to render the text to a hidden texture, and draw it.
+ //
+ // Note that Windows GDI does not really support the alpha correctly
+ // when drawing - ie. it draws nothing to the alpha channel when
+ // rendering the text, even the antialiasing is done as 'real' pixels,
+ // not alpha...
+ //
+ // Luckily, this does not really limit us:
+ //
+ // To blend properly, we draw the texture, but then use it as an alpha
+ // channel for solid color (that will define the text color). This
+ // destroys the subpixel antialiasing - turns it into 'classic'
+ // antialiasing - but that is the best we can do, because the subpixel
+ // antialiasing needs to know what is in the background: When the
+ // background is white, or white-ish, it does the subpixel, but when
+ // there is a color, it just darkens the color (and does this even
+ // when part of the character is on a colored background, and part on
+ // white). It has to work this way, the results would look strange
+ // otherwise.
+ //
+ // For the GL rendering to work even with the subpixel antialiasing,
+ // we would need to get the current texture from the screen, let GDI
+ // draw the text to it (so that it can decide well where to use the
+ // subpixel and where not), and draw the result - but in that case we
+ // don't need alpha anyway.
+ //
+ // TODO: check the performance of this 2nd approach at some stage and
+ // switch to that if it performs well.
+
+ tools::Rectangle aRect;
+ rLayout.GetBoundRect(aRect);
+ if( aRect.IsEmpty())
+ return;
+
+ pImpl->PreDrawText();
+
+ std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(*this, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight()));
+
+ // we are making changes to the DC, make sure we got a new one
+ assert(aDC->getCompatibleHDC() != hDC);
+
+ RECT aWinRect = { aRect.Left(), aRect.Top(), aRect.Left() + aRect.GetWidth(), aRect.Top() + aRect.GetHeight() };
+ ::FillRect(aDC->getCompatibleHDC(), &aWinRect, static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
+
+ // setup the hidden DC with black color and white background, we will
+ // use the result of the text drawing later as a mask only
+ const HFONT hOrigFont = ::SelectFont(aDC->getCompatibleHDC(), hLayoutFont);
+
+ ::SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0));
+ ::SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255));
+
+ UINT nTextAlign = ::GetTextAlign(hDC);
+ ::SetTextAlign(aDC->getCompatibleHDC(), nTextAlign);
+
+ COLORREF color = ::GetTextColor(hDC);
+ Color salColor(GetRValue(color), GetGValue(color), GetBValue(color));
+
+ // the actual drawing
+ DrawTextLayout(rLayout, aDC->getCompatibleHDC(), !bForceGDI);
+
+ std::unique_ptr<CompatibleDC::Texture> xTexture(aDC->getAsMaskTexture());
+ if (xTexture)
+ pImpl->DrawTextMask(xTexture.get(), salColor, aDC->getTwoRect());
+
+ ::SelectFont(aDC->getCompatibleHDC(), hOrigFont);
+
+ pImpl->PostDrawText();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */