diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2019-10-25 16:29:49 +0200 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2019-11-27 09:55:12 +0100 |
commit | 5ac9a62f3a354db80837bdd1c95b763989b303bb (patch) | |
tree | 94a5bd6875589fa3827c20b84a5edee9da60090b /vcl | |
parent | aa08385d1a07c530d32de91b633dbe087a3848ba (diff) |
fix Skia Windows text rendering
There are two cases in WinSalGraphics::DrawTextLayout(), with and
without cached glyphs:
- Cached case DeferredTextDraw() gets data as BGRA with the glyph
drawn in white, it just needs to be modulated to the proper color
and drawn.
- Uncached case DrawTextMask() gets data as BGRA with A invalid,
it must be used as mask for the color to drawn, but without
the inverse alpha VCL idiosyncracy that DrawMask() handles.
Change-Id: I05dcec994df68d5986cd85cffa42a8f9f23c42c4
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/opengl/win/gdiimpl.hxx | 6 | ||||
-rw-r--r-- | vcl/inc/skia/win/gdiimpl.hxx | 8 | ||||
-rw-r--r-- | vcl/inc/win/salgdi.h | 7 | ||||
-rw-r--r-- | vcl/inc/win/wingdiimpl.hxx | 4 | ||||
-rw-r--r-- | vcl/opengl/win/gdiimpl.cxx | 4 | ||||
-rw-r--r-- | vcl/skia/win/gdiimpl.cxx | 79 | ||||
-rw-r--r-- | vcl/win/gdi/winlayout.cxx | 7 |
7 files changed, 90 insertions, 25 deletions
diff --git a/vcl/inc/opengl/win/gdiimpl.hxx b/vcl/inc/opengl/win/gdiimpl.hxx index 02b8b3850cab..2130654a3951 100644 --- a/vcl/inc/opengl/win/gdiimpl.hxx +++ b/vcl/inc/opengl/win/gdiimpl.hxx @@ -26,8 +26,8 @@ class OpenGLCompatibleDC : public CompatibleDC public: OpenGLCompatibleDC(SalGraphics &rGraphics, int x, int y, int width, int height); - virtual std::unique_ptr<Texture> getTexture() override; - // overload, caller must delete + virtual std::unique_ptr<Texture> getAsMaskTexture() override; + // caller must delete OpenGLTexture* getOpenGLTexture(); virtual bool copyToTexture(Texture& aTexture) override; @@ -68,7 +68,7 @@ public: virtual bool UseTextDraw() const override { return true; } virtual void PreDrawText() override; virtual void PostDrawText() override; - virtual void DrawMask( CompatibleDC::Texture* rTexture, Color nMaskColor, const SalTwoRect& rPosAry ) override; + virtual void DrawTextMask( CompatibleDC::Texture* rTexture, Color nMaskColor, const SalTwoRect& rPosAry ) override; using OpenGLSalGraphicsImpl::DrawMask; virtual void DeferredTextDraw(const CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry) override; diff --git a/vcl/inc/skia/win/gdiimpl.hxx b/vcl/inc/skia/win/gdiimpl.hxx index bc177337b3de..6bd52b073aa5 100644 --- a/vcl/inc/skia/win/gdiimpl.hxx +++ b/vcl/inc/skia/win/gdiimpl.hxx @@ -28,10 +28,12 @@ class SkiaCompatibleDC : public CompatibleDC public: SkiaCompatibleDC(SalGraphics& rGraphics, int x, int y, int width, int height); - virtual std::unique_ptr<Texture> getTexture() override; + virtual std::unique_ptr<Texture> getAsMaskTexture() override; virtual bool copyToTexture(Texture& aTexture) override; + virtual bool wantsTextColorWhite() const override { return true; } + struct Texture; }; @@ -65,8 +67,8 @@ public: virtual bool UseTextDraw() const override { return true; } virtual void PreDrawText() override; virtual void PostDrawText() override; - virtual void DrawMask(CompatibleDC::Texture* rTexture, Color nMaskColor, - const SalTwoRect& rPosAry) override; + virtual void DrawTextMask(CompatibleDC::Texture* rTexture, Color nMaskColor, + const SalTwoRect& rPosAry) override; virtual void DeferredTextDraw(const CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry) override; diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h index f7fb206624b5..fc12bc823b93 100644 --- a/vcl/inc/win/salgdi.h +++ b/vcl/inc/win/salgdi.h @@ -138,11 +138,14 @@ public: /// Base texture class (OpenGL and Skia will provide their implementations). struct Texture; - /// Obtain the texture. - virtual std::unique_ptr<Texture> getTexture() { abort(); }; + /// Obtain the texture in format for WinSalGraphicsImplBase::DrawTextMask(). + virtual std::unique_ptr<Texture> getAsMaskTexture() { abort(); }; /// Copy bitmap data to the texture. Texture must be initialized and the correct size to hold the bitmap. virtual bool copyToTexture(Texture& /*aTexture*/) { abort(); }; + + /// Return true if text glyphs should be drawn as white instead of black. + virtual bool wantsTextColorWhite() const { return false; } }; struct CompatibleDC::Texture diff --git a/vcl/inc/win/wingdiimpl.hxx b/vcl/inc/win/wingdiimpl.hxx index 2264eecf353d..84884220318f 100644 --- a/vcl/inc/win/wingdiimpl.hxx +++ b/vcl/inc/win/wingdiimpl.hxx @@ -39,8 +39,8 @@ public: virtual bool UseTextDraw() const { return false; } virtual void PreDrawText() {} virtual void PostDrawText() {} - virtual void DrawMask(CompatibleDC::Texture* /*rTexture*/, Color /*nMaskColor*/, - const SalTwoRect& /*rPosAry*/) + virtual void DrawTextMask(CompatibleDC::Texture* /*rTexture*/, Color /*nMaskColor*/, + const SalTwoRect& /*rPosAry*/) { abort(); }; diff --git a/vcl/opengl/win/gdiimpl.cxx b/vcl/opengl/win/gdiimpl.cxx index d71a03714198..bb6e5bf0a16c 100644 --- a/vcl/opengl/win/gdiimpl.cxx +++ b/vcl/opengl/win/gdiimpl.cxx @@ -770,7 +770,7 @@ OpenGLTexture* OpenGLCompatibleDC::getOpenGLTexture() return new OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData); } -std::unique_ptr<CompatibleDC::Texture> OpenGLCompatibleDC::getTexture() +std::unique_ptr<CompatibleDC::Texture> OpenGLCompatibleDC::getAsMaskTexture() { auto ret = std::make_unique<OpenGLCompatibleDC::Texture>(); ret->texture = OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData); @@ -881,7 +881,7 @@ void WinOpenGLSalGraphicsImpl::DeferredTextDraw(const CompatibleDC::Texture* pTe PostBatchDraw(); } -void WinOpenGLSalGraphicsImpl::DrawMask( CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry ) +void WinOpenGLSalGraphicsImpl::DrawTextMask( CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry ) { assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(pTexture)); DrawMask( static_cast<OpenGLCompatibleDC::Texture*>(pTexture)->texture, nMaskColor, rPosAry ); diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx index 9a41ffc7da27..e583cea86f3c 100644 --- a/vcl/skia/win/gdiimpl.cxx +++ b/vcl/skia/win/gdiimpl.cxx @@ -12,6 +12,9 @@ #include <tools/sk_app/win/WindowContextFactory_win.h> #include <tools/sk_app/WindowContext.h> +#include <SkColorFilter.h> +#include <SkPixelRef.h> + WinSkiaSalGraphicsImpl::WinSkiaSalGraphicsImpl(WinSalGraphics& rGraphics, SalGeometryProvider* mpProvider) : SkiaSalGraphicsImpl(rGraphics, mpProvider) @@ -102,18 +105,54 @@ void WinSkiaSalGraphicsImpl::PreDrawText() { preDraw(); } void WinSkiaSalGraphicsImpl::PostDrawText() { postDraw(); } +SkColor toSkColor(Color color) +{ + return SkColorSetARGB(255 - color.GetTransparency(), color.GetRed(), color.GetGreen(), + color.GetBlue()); +} + void WinSkiaSalGraphicsImpl::DeferredTextDraw(const CompatibleDC::Texture* pTexture, Color aMaskColor, const SalTwoRect& rPosAry) { assert(dynamic_cast<const SkiaCompatibleDC::Texture*>(pTexture)); - drawMask(rPosAry, static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->bitmap, aMaskColor); + preDraw(); + SkPaint paint; + // The glyph is painted as white, modulate it to be of the appropriate color. + // SkiaCompatibleDC::wantsTextColorWhite() ensures the glyph is white. + // TODO maybe other black/white in WinFontInstance::CacheGlyphToAtlas() should be swapped. + paint.setColorFilter(SkColorFilters::Blend(toSkColor(aMaskColor), SkBlendMode::kModulate)); + mSurface->getCanvas()->drawBitmapRect( + static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->bitmap, + SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight), + SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, + rPosAry.mnDestHeight), + &paint); + postDraw(); } -void WinSkiaSalGraphicsImpl::DrawMask(CompatibleDC::Texture* pTexture, Color nMaskColor, - const SalTwoRect& rPosAry) +void WinSkiaSalGraphicsImpl::DrawTextMask(CompatibleDC::Texture* pTexture, Color nMaskColor, + const SalTwoRect& rPosAry) { assert(dynamic_cast<SkiaCompatibleDC::Texture*>(pTexture)); - drawMask(rPosAry, static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->bitmap, nMaskColor); + const SkBitmap& bitmap = static_cast<const SkiaCompatibleDC::Texture*>(pTexture)->bitmap; + preDraw(); + SkBitmap tmpBitmap; + if (!tmpBitmap.tryAllocN32Pixels(bitmap.width(), bitmap.height())) + abort(); + tmpBitmap.eraseColor(toSkColor(nMaskColor)); + SkPaint paint; + // Draw the color with the given mask. + // TODO figure out the right blend mode to avoid the temporary bitmap + paint.setBlendMode(SkBlendMode::kDstOut); + SkCanvas canvas(tmpBitmap); + canvas.drawBitmap(bitmap, 0, 0, &paint); + mSurface->getCanvas()->drawBitmapRect( + tmpBitmap, + SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight), + SkRect::MakeXYWH(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, + rPosAry.mnDestHeight), + nullptr); + postDraw(); } SkiaCompatibleDC::SkiaCompatibleDC(SalGraphics& rGraphics, int x, int y, int width, int height) @@ -121,15 +160,35 @@ SkiaCompatibleDC::SkiaCompatibleDC(SalGraphics& rGraphics, int x, int y, int wid { } -std::unique_ptr<CompatibleDC::Texture> SkiaCompatibleDC::getTexture() +std::unique_ptr<CompatibleDC::Texture> SkiaCompatibleDC::getAsMaskTexture() { auto ret = std::make_unique<SkiaCompatibleDC::Texture>(); - // TODO is this correct? - // TODO make copy of data? - if (!ret->bitmap.installPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight, - kBGRA_8888_SkColorType, kUnpremul_SkAlphaType), - mpData, maRects.mnSrcWidth * 4)) + // mpData is in the BGRA format, with A unused (and set to 0), and RGB are grey, + // so convert it to Skia format, then to 8bit and finally use as alpha mask + SkBitmap tmpBitmap; + if (!tmpBitmap.installPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight, + kBGRA_8888_SkColorType, kOpaque_SkAlphaType), + mpData, maRects.mnSrcWidth * 4)) + abort(); + SkBitmap bitmap8; + if (!bitmap8.tryAllocPixels(SkImageInfo::Make(maRects.mnSrcWidth, maRects.mnSrcHeight, + kGray_8_SkColorType, kOpaque_SkAlphaType))) abort(); + SkCanvas canvas8(bitmap8); + SkPaint paint8; + paint8.setBlendMode(SkBlendMode::kSrc); // copy and convert depth + // The data we got is upside-down. + SkMatrix matrix; + matrix.preTranslate(0, maRects.mnSrcHeight); + matrix.setConcat(matrix, SkMatrix::MakeScale(1, -1)); + canvas8.concat(matrix); + canvas8.drawBitmap(tmpBitmap, 0, 0, &paint8); + // use the 8bit data as an alpha channel + SkBitmap alpha; + alpha.setInfo(bitmap8.info().makeColorType(kAlpha_8_SkColorType), bitmap8.rowBytes()); + alpha.setPixelRef(sk_ref_sp(bitmap8.pixelRef()), bitmap8.pixelRefOrigin().x(), + bitmap8.pixelRefOrigin().y()); + ret->bitmap = alpha; return ret; } diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx index 06189c6a7643..cf264425ae17 100644 --- a/vcl/win/gdi/winlayout.cxx +++ b/vcl/win/gdi/winlayout.cxx @@ -171,7 +171,8 @@ bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex, auto pRT = pTxt->GetRenderTarget(); ID2D1SolidColorBrush* pBrush = nullptr; - if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush))) + D2D1::ColorF textColor = aDC->wantsTextColorWhite() ? D2D1::ColorF::White : D2D1::ColorF::Black; + if (!SUCCEEDED(pRT->CreateSolidColorBrush(textColor, &pBrush))) return false; D2D1_POINT_2F baseline = { @@ -622,9 +623,9 @@ void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) // the actual drawing DrawTextLayout(rLayout, aDC->getCompatibleHDC(), !bForceGDI); - std::unique_ptr<CompatibleDC::Texture> xTexture(aDC->getTexture()); + std::unique_ptr<CompatibleDC::Texture> xTexture(aDC->getAsMaskTexture()); if (xTexture) - pImpl->DrawMask(xTexture.get(), salColor, aDC->getTwoRect()); + pImpl->DrawTextMask(xTexture.get(), salColor, aDC->getTwoRect()); ::SelectFont(aDC->getCompatibleHDC(), hOrigFont); |