summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2020-03-16 15:08:11 +0100
committerLuboš Luňák <l.lunak@collabora.com>2020-03-18 10:48:19 +0100
commit271e8c99a2a1e28b7eb9fdc5fe16f8be1e8ee763 (patch)
tree9235764f08b015ec92716c1f6f1cf777e55ca3cc
parent2ff6fce634ff173b9eb8a703b7f2f265f6e3ecb1 (diff)
implement text rendering using directly Skia (Windows)
The Windows code needed for Skia text rendering. Like with the X11 code, the font is slightly lighter than with Skia disabled, but otherwise it seems to work. And like the X11 code this also requires patching Skia to use the font we want. Change-Id: Ib5ba52e4ba51b6523617072b77ed5446e7343f46 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/90582 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
-rw-r--r--external/skia/UnpackedTarball_skia.mk1
-rw-r--r--external/skia/windows-hfont-typeface.patch.0134
-rw-r--r--vcl/inc/skia/win/gdiimpl.hxx3
-rw-r--r--vcl/inc/win/wingdiimpl.hxx3
-rw-r--r--vcl/skia/win/gdiimpl.cxx55
-rw-r--r--vcl/win/gdi/winlayout.cxx6
6 files changed, 200 insertions, 2 deletions
diff --git a/external/skia/UnpackedTarball_skia.mk b/external/skia/UnpackedTarball_skia.mk
index 6ffde2006590..42276177ab33 100644
--- a/external/skia/UnpackedTarball_skia.mk
+++ b/external/skia/UnpackedTarball_skia.mk
@@ -28,6 +28,7 @@ skia_patches := \
msvc-vectorcall-sse.patch.1 \
clang11-flax-vector-conversion.patch.0 \
fontconfig-get-typeface.patch.0 \
+ windows-hfont-typeface.patch.0 \
$(eval $(call gb_UnpackedTarball_set_patchlevel,skia,1))
diff --git a/external/skia/windows-hfont-typeface.patch.0 b/external/skia/windows-hfont-typeface.patch.0
new file mode 100644
index 000000000000..459595a7b3d6
--- /dev/null
+++ b/external/skia/windows-hfont-typeface.patch.0
@@ -0,0 +1,134 @@
+--- ./include/ports/SkTypeface_win.h.sav 2019-09-19 11:38:00.943185300 +0200
++++ ./include/ports/SkTypeface_win.h 2020-03-16 15:11:38.347067100 +0100
+@@ -28,6 +28,8 @@
+ */
+ SK_API SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
+
++SK_API SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&, HFONT);
++
+ /**
+ * Copy the LOGFONT associated with this typeface into the lf parameter. Note
+ * that the lfHeight will need to be set afterwards, since the typeface does
+--- ./src/ports/SkFontHost_win.cpp.sav 2020-03-16 15:22:02.620518100 +0100
++++ ./src/ports/SkFontHost_win.cpp 2020-03-16 15:27:12.733594400 +0100
+@@ -215,6 +215,11 @@
+ , fFont(::CreateFontIndirect(&lf))
+ , fSavefont((HFONT)::SelectObject(fHdc, fFont))
+ { }
++ explicit SkAutoHDC(const HFONT hf)
++ : fHdc(::CreateCompatibleDC(nullptr))
++ , fFont(nullptr)
++ , fSavefont((HFONT)::SelectObject(fHdc, hf))
++ { }
+ ~SkAutoHDC() {
+ if (fHdc) {
+ ::SelectObject(fHdc, fSavefont);
+@@ -238,8 +243,22 @@
+ : SkTypeface(style, false)
+ , fLogFont(lf)
+ , fSerializeAsStream(serializeAsStream)
++ , hFont(nullptr)
+ {
+ SkAutoHDC hdc(fLogFont);
++ init(hdc, style, lf);
++ }
++ LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, HFONT hf, bool serializeAsStream)
++ : SkTypeface(style, false)
++ , fLogFont(lf)
++ , fSerializeAsStream(serializeAsStream)
++ , hFont(hf)
++ {
++ SkAutoHDC hdc(hFont);
++ init(hdc, style, lf);
++ }
++ void init(SkAutoHDC& hdc, const SkFontStyle& style, const LOGFONT& lf)
++ {
+ TEXTMETRIC textMetric;
+ if (0 == GetTextMetrics(hdc, &textMetric)) {
+ call_ensure_accessible(lf);
+@@ -260,6 +279,7 @@
+ }
+
+ LOGFONT fLogFont;
++ HFONT hFont;
+ bool fSerializeAsStream;
+ bool fCanBeLCD;
+
+@@ -267,6 +287,10 @@
+ return sk_sp<LogFontTypeface>(new LogFontTypeface(get_style(lf), lf, false));
+ }
+
++ static sk_sp<LogFontTypeface> Make(const LOGFONT& lf, HFONT hf) {
++ return sk_sp<LogFontTypeface>(new LogFontTypeface(get_style(lf), lf, hf, false));
++ }
++
+ static void EnsureAccessible(const SkTypeface* face) {
+ call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
+ }
+@@ -348,7 +372,7 @@
+ */
+ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
+ LOGFONT lf = origLF;
+- make_canonical(&lf);
++// make_canonical(&lf);
+ sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
+ if (!face) {
+ face = LogFontTypeface::Make(lf);
+@@ -357,12 +381,33 @@
+ return face.release();
+ }
+
++static bool FindByLogFontAndHFont(SkTypeface* face, void* ctx) {
++ LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
++ const std::pair<LOGFONT*,HFONT>* data = reinterpret_cast<const std::pair<LOGFONT*,HFONT>*>(ctx);
++ const LOGFONT* lf = data->first;
++ const HFONT hf = data->second;
++
++ return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT)) && lface->hFont == hf;
++}
++
++SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF, HFONT hFont) {
++ LOGFONT lf = origLF;
++// make_canonical(&lf);
++ std::pair<LOGFONT*,HFONT> data = std::make_pair(&lf,hFont);
++ sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(FindByLogFontAndHFont, &data);
++ if (!face) {
++ face = LogFontTypeface::Make(lf,hFont);
++ SkTypefaceCache::Add(face);
++ }
++ return face.release();
++}
++
+ /**
+ * The created SkTypeface takes ownership of fontMemResource.
+ */
+ sk_sp<SkTypeface> SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
+ LOGFONT lf = origLF;
+- make_canonical(&lf);
++// make_canonical(&lf);
+ // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
+ return FontMemResourceTypeface::Make(lf, fontMemResource);
+ }
+@@ -686,7 +731,10 @@
+ LOGFONT lf = typeface->fLogFont;
+ lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
+ lf.lfQuality = compute_quality(fRec);
+- fFont = CreateFontIndirect(&lf);
++ if(typeface->hFont != nullptr)
++ fFont = typeface->hFont;
++ else
++ fFont = CreateFontIndirect(&lf);
+ if (!fFont) {
+ return;
+ }
+@@ -788,7 +836,9 @@
+ ::DeleteDC(fDDC);
+ }
+ if (fFont) {
+- ::DeleteObject(fFont);
++ LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
++ if(typeface->hFont != fFont)
++ ::DeleteObject(fFont);
+ }
+ if (fSC) {
+ ::ScriptFreeCache(&fSC);
diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx
index 676e743151e7..3d6c680f2af4 100644
--- a/vcl/inc/skia/win/gdiimpl.hxx
+++ b/vcl/inc/skia/win/gdiimpl.hxx
@@ -73,6 +73,9 @@ public:
virtual bool RenderAndCacheNativeControl(CompatibleDC& rWhite, CompatibleDC& rBlack, int nX,
int nY, ControlCacheKey& aControlCacheKey) override;
+ virtual bool DrawTextLayout(const GenericSalLayout& layout) override;
+ // TODO This method of text drawing can probably be removed once DrawTextLayout()
+ // is fully usable.
virtual bool UseTextDraw() const override { return true; }
virtual void PreDrawText() override;
virtual void PostDrawText() override;
diff --git a/vcl/inc/win/wingdiimpl.hxx b/vcl/inc/win/wingdiimpl.hxx
index 84884220318f..e81e35201413 100644
--- a/vcl/inc/win/wingdiimpl.hxx
+++ b/vcl/inc/win/wingdiimpl.hxx
@@ -35,6 +35,9 @@ public:
abort();
};
+ // Implementation for WinSalGraphics::DrawTextLayout().
+ // Returns true if handled, if false, then WinSalGraphics will handle it itself.
+ virtual bool DrawTextLayout(const GenericSalLayout&) { return false; }
// If true is returned, the following functions are used for text rendering.
virtual bool UseTextDraw() const { return false; }
virtual void PreDrawText() {}
diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index 84aed4f05b92..b7014ed33b48 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -13,12 +13,17 @@
#include <vcl/skia/SkiaHelper.hxx>
#include <skia/utils.hxx>
#include <skia/zone.hxx>
+#include <win/winlayout.hxx>
#include <SkColorFilter.h>
#include <SkPixelRef.h>
+#include <SkTypeface_win.h>
+#include <SkFont.h>
#include <tools/sk_app/win/WindowContextFactory_win.h>
#include <tools/sk_app/WindowContext.h>
+#include <windows.h>
+
WinSkiaSalGraphicsImpl::WinSkiaSalGraphicsImpl(WinSalGraphics& rGraphics,
SalGeometryProvider* mpProvider)
: SkiaSalGraphicsImpl(rGraphics, mpProvider)
@@ -104,6 +109,56 @@ bool WinSkiaSalGraphicsImpl::RenderAndCacheNativeControl(CompatibleDC& rWhite, C
return true;
}
+bool WinSkiaSalGraphicsImpl::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ const WinFontInstance& rWinFont = static_cast<const WinFontInstance&>(rLayout.GetFont());
+ float fHScale = rWinFont.getHScale();
+
+ assert(dynamic_cast<const WinFontInstance*>(&rLayout.GetFont()));
+ const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont());
+ const HFONT hLayoutFont = pWinFont->GetHFONT();
+ LOGFONT logFont;
+ if (::GetObjectW(hLayoutFont, sizeof(logFont), &logFont) == 0)
+ {
+ assert(false);
+ return false;
+ }
+ // Wrap the font in Skia's SkTypeFace subclass that's been patched
+ // to use it.
+ sk_sp<SkTypeface> typeface(SkCreateTypefaceFromLOGFONT(logFont, hLayoutFont));
+ // lfHeight actually depends on DPI, so it's not really font height as such,
+ // but for LOGFONT-based typefaces Skia simply sets lfHeight back to this value
+ // directly.
+ // This is probably not necessary since we pass also the HFONT itself, but better
+ // forward that information too, in case SkFont uses it somehow.
+ double fontHeight = logFont.lfHeight;
+ if (fontHeight < 0)
+ fontHeight = -fontHeight;
+ SkFont font(typeface, fontHeight, fHScale, 0);
+ // Skia needs to be explicitly told what kind of antialiasing should be used,
+ // get it from system settings. This does not actually matter for the text
+ // rendering itself, since it will use the font passed to Skia in the code above
+ // (and that one uses DEFAULT_QUALITY, so Windows will select the appropriate AA setting),
+ // but Skia internally chooses the format to which the glyphs will be rendered
+ // based on this setting (subpixel AA requires colors, others do not).
+ BOOL set;
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &set, 0) && set)
+ {
+ UINT set2;
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &set2, 0)
+ && set2 == FE_FONTSMOOTHINGCLEARTYPE)
+ font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
+ else
+ font.setEdging(SkFont::Edging::kAntiAlias);
+ }
+ assert(dynamic_cast<SkiaSalGraphicsImpl*>(mWinParent.GetImpl()));
+ SkiaSalGraphicsImpl* impl = static_cast<SkiaSalGraphicsImpl*>(mWinParent.GetImpl());
+ COLORREF color = ::GetTextColor(mWinParent.getHDC());
+ Color salColor(GetRValue(color), GetGValue(color), GetBValue(color));
+ impl->drawGenericLayout(rLayout, salColor, font);
+ return true;
+}
+
void WinSkiaSalGraphicsImpl::PreDrawText() { preDraw(); }
void WinSkiaSalGraphicsImpl::PostDrawText() { postDraw(); }
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index e13d6930df64..636cac595bf1 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -543,11 +543,13 @@ void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bo
void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
{
- HDC hDC = getHDC();
+ 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();
- WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
bool bUseClassic = !pImpl->UseTextDraw() || mbPrinter;
// Our DirectWrite renderer is incomplete, skip it for vertical text where glyphs are not