vcl/Library_vclplug_osx.mk | 1 vcl/inc/quartz/salgdi.h | 14 +++++- vcl/inc/skia/osx/bitmap.hxx | 26 ++++++++++++ vcl/inc/skia/osx/gdiimpl.hxx | 5 ++ vcl/osx/salinst.cxx | 6 ++ vcl/quartz/salgdi.cxx | 38 ++++++++++-------- vcl/skia/osx/bitmap.cxx | 88 +++++++++++++++++++++++++++++++++++++++++++ vcl/skia/osx/gdiimpl.cxx | 51 ++++++++++++++++++++++++ 8 files changed, 209 insertions(+), 20 deletions(-)
New commits: commit ab9c627bc8f16dd679a19b0946863cebab7b03bc Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Tue Aug 17 17:05:55 2021 +0200 Commit: Luboš Luňák <l.lu...@collabora.com> CommitDate: Mon Aug 23 15:00:45 2021 +0200 add CreateCGImage() variant for Mac/Skia Needed at least for 'recent documents' icons in the Mac menubar. Change-Id: I5a22cf64ff5c5aba2c70ca2556fd0b66c425bafd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120811 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lu...@collabora.com> diff --git a/vcl/Library_vclplug_osx.mk b/vcl/Library_vclplug_osx.mk index 3e0e0ff481be..ddc3a3608f1b 100644 --- a/vcl/Library_vclplug_osx.mk +++ b/vcl/Library_vclplug_osx.mk @@ -142,6 +142,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ vcl/quartz/utils \ vcl/quartz/AquaGraphicsBackend \ $(if $(filter SKIA,$(BUILD_TYPE)), \ + vcl/skia/osx/bitmap \ vcl/skia/osx/gdiimpl \ vcl/skia/osx/rastercontext \ ) \ diff --git a/vcl/inc/skia/osx/bitmap.hxx b/vcl/inc/skia/osx/bitmap.hxx new file mode 100644 index 000000000000..0c0b1ee09be3 --- /dev/null +++ b/vcl/inc/skia/osx/bitmap.hxx @@ -0,0 +1,26 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_VCL_INC_SKIA_OSX_BITMAP_HXX +#define INCLUDED_VCL_INC_SKIA_OSX_BITMAP_HXX + +#include <vcl/dllapi.h> + +#include <osx/osxvcltypes.h> + +class Image; + +namespace SkiaHelper +{ +VCL_PLUGIN_PUBLIC CGImageRef createCGImage(const Image& rImage); +}; + +#endif // INCLUDED_VCL_INC_SKIA_OSX_BITMAP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx index 08fae66b4ad3..dbdb8038b493 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -78,6 +78,7 @@ #include <vcl/skia/SkiaHelper.hxx> #include <skia/salbmp.hxx> #include <skia/osx/gdiimpl.hxx> +#include <skia/osx/bitmap.hxx> #endif extern "C" { @@ -918,6 +919,11 @@ OUString AquaSalInstance::getOSVersion() CGImageRef CreateCGImage( const Image& rImage ) { +#if HAVE_FEATURE_SKIA + if (SkiaHelper::isVCLSkiaEnabled()) + return SkiaHelper::createCGImage( rImage ); +#endif + BitmapEx aBmpEx( rImage.GetBitmapEx() ); Bitmap aBmp( aBmpEx.GetBitmap() ); diff --git a/vcl/skia/osx/bitmap.cxx b/vcl/skia/osx/bitmap.cxx new file mode 100644 index 000000000000..77e34697aa50 --- /dev/null +++ b/vcl/skia/osx/bitmap.cxx @@ -0,0 +1,88 @@ +/* -*- 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/. + * + * Some of this code is based on Skia source code, covered by the following + * license notice (see readlicense_oo for the full license): + * + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + */ + +#include <skia/osx/bitmap.hxx> + +#include <vcl/bitmapex.hxx> +#include <vcl/image.hxx> + +#include <skia/salbmp.hxx> +#include <osx/saldata.hxx> + +#include <SkBitmap.h> +#include <SkCanvas.h> + +using namespace SkiaHelper; + +namespace SkiaHelper +{ +CGImageRef createCGImage(const Image& rImage) +{ + BitmapEx bitmapEx(rImage.GetBitmapEx()); + Bitmap bitmap(bitmapEx.GetBitmap()); + + if (bitmap.IsEmpty() || !bitmap.ImplGetSalBitmap()) + return nullptr; + + assert(dynamic_cast<SkiaSalBitmap*>(bitmap.ImplGetSalBitmap().get()) != nullptr); + SkiaSalBitmap* skiaBitmap = static_cast<SkiaSalBitmap*>(bitmap.ImplGetSalBitmap().get()); + + SkBitmap targetBitmap; + if (!targetBitmap.tryAllocPixels( + SkImageInfo::Make(bitmap.GetSizePixel().getWidth(), bitmap.GetSizePixel().getHeight(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType))) + return nullptr; + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); // set as is + SkMatrix matrix; // The image is needed upside-down. + matrix.preTranslate(0, targetBitmap.height()); + matrix.setConcat(matrix, SkMatrix::Scale(1, -1)); + + if (!bitmapEx.IsAlpha()) + { + SkCanvas canvas(targetBitmap); + canvas.concat(matrix); + canvas.drawImage(skiaBitmap->GetSkImage(), 0, 0, SkSamplingOptions(), &paint); + } + else + { + AlphaMask alpha(bitmapEx.GetAlpha()); + Bitmap alphaBitmap(alpha.GetBitmap()); + assert(dynamic_cast<SkiaSalBitmap*>(alphaBitmap.ImplGetSalBitmap().get()) != nullptr); + SkiaSalBitmap* skiaAlpha + = static_cast<SkiaSalBitmap*>(alphaBitmap.ImplGetSalBitmap().get()); + paint.setShader(SkShaders::Blend(SkBlendMode::kDstOut, + skiaBitmap->GetSkShader(SkSamplingOptions()), + skiaAlpha->GetAlphaSkShader(SkSamplingOptions()))); + SkCanvas canvas(targetBitmap); + canvas.concat(matrix); + canvas.drawPaint(paint); + } + + CGContextRef context = CGBitmapContextCreate( + targetBitmap.getAddr32(0, 0), targetBitmap.width(), targetBitmap.height(), 8, + targetBitmap.rowBytes(), GetSalData()->mxRGBSpace, kCGImageAlphaPremultipliedLast); + if (!context) + return nullptr; + CGImageRef screenImage = CGBitmapContextCreateImage(context); + CFRelease(context); + return screenImage; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 55422ec895d3014ba0507757870ebdec9ac1a2a9 Author: Luboš Luňák <l.lu...@collabora.com> AuthorDate: Tue Aug 17 13:10:36 2021 +0200 Commit: Luboš Luňák <l.lu...@collabora.com> CommitDate: Mon Aug 23 15:00:30 2021 +0200 implement text rendering on Mac using Skia There may be still small problems (CJK needs checking), but this is already usable. Change-Id: Ic9381c22ca55d9e6320152ffebeae740fd90f796 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120810 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lu...@collabora.com> diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 6cedf6f09b29..80b79a6ec720 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -123,6 +123,8 @@ public: void AnnounceFonts( PhysicalFontCollection& ) const; CoreTextFontFace* GetFontDataFromId( sal_IntPtr nFontId ) const; + CTFontCollectionRef fontCollection() { return mpCTFontCollection; } + private: CTFontCollectionRef mpCTFontCollection; CFArrayRef mpCTFontArray; @@ -170,6 +172,10 @@ struct AquaSharedAttributes int mnXorMode; // 0: off 1: on 2: invert only int mnBitmapDepth; // zero unless bitmap + Color maTextColor; + /// allows text to be rendered without antialiasing + bool mbNonAntialiasedText; + std::unique_ptr<XorEmulation> mpXorEmulation; AquaSharedAttributes() @@ -188,6 +194,8 @@ struct AquaSharedAttributes , mnHeight(0) , mnXorMode(0) , mnBitmapDepth(0) + , maTextColor( COL_BLACK ) + , mbNonAntialiasedText( false ) {} void unsetClipPath() @@ -287,6 +295,7 @@ public: const tools::Rectangle &rControlRegion, ControlState nState, const ImplControlValue &aValue) = 0; + virtual void drawTextLayout(const GenericSalLayout& layout) = 0; protected: static bool performDrawNativeControl(ControlType nType, ControlPart nPart, @@ -433,6 +442,8 @@ public: ControlState nState, const ImplControlValue &aValue) override; + virtual void drawTextLayout(const GenericSalLayout& layout) override; + bool supportsOperation(OutDevSupportType eType) const override; }; @@ -447,9 +458,6 @@ class AquaSalGraphics : public SalGraphicsAutoDelegateToImpl // Device Font settings rtl::Reference<CoreTextStyle> mpTextStyle[MAX_FALLBACK]; - RGBAColor maTextColor; - /// allows text to be rendered without antialiasing - bool mbNonAntialiasedText; public: AquaSalGraphics(); diff --git a/vcl/inc/skia/osx/gdiimpl.hxx b/vcl/inc/skia/osx/gdiimpl.hxx index e42126cfae46..cc291bd38764 100644 --- a/vcl/inc/skia/osx/gdiimpl.hxx +++ b/vcl/inc/skia/osx/gdiimpl.hxx @@ -17,6 +17,8 @@ #include <skia/gdiimpl.hxx> #include <skia/utils.hxx> +#include <SkFontMgr.h> + class VCL_PLUGIN_PUBLIC AquaSkiaSalGraphicsImpl final : public SkiaSalGraphicsImpl, public AquaGraphicsBackendBase { @@ -38,11 +40,14 @@ public: const tools::Rectangle& rControlRegion, ControlState nState, const ImplControlValue& aValue) override; + virtual void drawTextLayout(const GenericSalLayout& layout) override; + private: virtual void createWindowContext(bool forceRaster = false) override; virtual void performFlush() override; void flushToScreen(const SkIRect& rect); friend std::unique_ptr<sk_app::WindowContext> createVulkanWindowContext(bool); + static inline sk_sp<SkFontMgr> fontManager; }; #endif // INCLUDED_VCL_INC_SKIA_OSX_GDIIMPL_HXX diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index 1fa3d425a6d4..53b10ab9533c 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -194,8 +194,6 @@ bool CoreTextFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilit AquaSalGraphics::AquaSalGraphics() : mnRealDPIX( 0 ) , mnRealDPIY( 0 ) - , maTextColor( COL_BLACK ) - , mbNonAntialiasedText( false ) { SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this ); @@ -250,8 +248,8 @@ SalGraphicsImpl* AquaSalGraphics::GetImpl() const void AquaSalGraphics::SetTextColor( Color nColor ) { - maTextColor = RGBAColor( nColor ); - // SAL_ DEBUG(std::hex << nColor << std::dec << "={" << maTextColor.GetRed() << ", " << maTextColor.GetGreen() << ", " << maTextColor.GetBlue() << ", " << maTextColor.GetAlpha() << "}"); + maShared.maTextColor = nColor; + // SAL_ DEBUG(std::hex << nColor << std::dec << "={" << maShared.maTextColor.GetRed() << ", " << maShared.maTextColor.GetGreen() << ", " << maShared.maTextColor.GetBlue() << ", " << maShared.maTextColor.GetAlpha() << "}"); } void AquaSalGraphics::GetFontMetric(ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel) @@ -361,9 +359,14 @@ bool AquaSalGraphics::AddTempDevFont( PhysicalFontCollection*, } void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) +{ + mpBackend->drawTextLayout(rLayout); +} + +void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout) { #ifdef IOS - if (!maShared.checkContext()) + if (!mrShared.checkContext()) { SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout() without context"); return; @@ -437,20 +440,21 @@ void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) std::cerr << "]\n"; #endif - maShared.maContextHolder.saveState(); + mrShared.maContextHolder.saveState(); + RGBAColor textColor( mrShared.maTextColor ); // The view is vertically flipped (no idea why), flip it back. - CGContextScaleCTM(maShared.maContextHolder.get(), 1.0, -1.0); - CGContextSetShouldAntialias(maShared.maContextHolder.get(), !mbNonAntialiasedText); - CGContextSetFillColor(maShared.maContextHolder.get(), maTextColor.AsArray()); + CGContextScaleCTM(mrShared.maContextHolder.get(), 1.0, -1.0); + CGContextSetShouldAntialias(mrShared.maContextHolder.get(), !mrShared.mbNonAntialiasedText); + CGContextSetFillColor(mrShared.maContextHolder.get(), textColor.AsArray()); if (rStyle.mbFauxBold) { float fSize = rFontSelect.mnHeight / 23.0f; - CGContextSetStrokeColor(maShared.maContextHolder.get(), maTextColor.AsArray()); - CGContextSetLineWidth(maShared.maContextHolder.get(), fSize); - CGContextSetTextDrawingMode(maShared.maContextHolder.get(), kCGTextFillStroke); + CGContextSetStrokeColor(mrShared.maContextHolder.get(), textColor.AsArray()); + CGContextSetLineWidth(mrShared.maContextHolder.get(), fSize); + CGContextSetTextDrawingMode(mrShared.maContextHolder.get(), kCGTextFillStroke); } auto aIt = aGlyphOrientation.cbegin(); @@ -463,18 +467,18 @@ void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout) size_t nStartIndex = std::distance(aGlyphOrientation.cbegin(), aIt); size_t nLen = std::distance(aIt, aNext); - maShared.maContextHolder.saveState(); + mrShared.maContextHolder.saveState(); if (rStyle.mfFontRotation && !bUprightGlyph) { - CGContextRotateCTM(maShared.maContextHolder.get(), rStyle.mfFontRotation); + CGContextRotateCTM(mrShared.maContextHolder.get(), rStyle.mfFontRotation); } - CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, maShared.maContextHolder.get()); - maShared.maContextHolder.restoreState(); + CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, mrShared.maContextHolder.get()); + mrShared.maContextHolder.restoreState(); aIt = aNext; } - maShared.maContextHolder.restoreState(); + mrShared.maContextHolder.restoreState(); } void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel) diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx index c5e8911d339e..8a879e2f7788 100644 --- a/vcl/skia/osx/gdiimpl.cxx +++ b/vcl/skia/osx/gdiimpl.cxx @@ -23,7 +23,12 @@ #include <skia/osx/rastercontext.hxx> +#include <quartz/ctfonts.hxx> + #include <SkCanvas.h> +#include <SkFont.h> +#include <SkFontMgr_mac_ct.h> +#include <SkTypeface_mac.h> using namespace SkiaHelper; @@ -173,6 +178,52 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart n return bOK; } +void AquaSkiaSalGraphicsImpl::drawTextLayout(const GenericSalLayout& rLayout) +{ + const CoreTextStyle& rStyle = *static_cast<const CoreTextStyle*>(&rLayout.GetFont()); + const FontSelectPattern& rFontSelect = rStyle.GetFontSelectPattern(); + int nHeight = rFontSelect.mnHeight; + int nWidth = rFontSelect.mnWidth ? rFontSelect.mnWidth : nHeight; + if (nWidth == 0 || nHeight == 0) + { + SAL_WARN("vcl.skia", "DrawTextLayout(): rFontSelect.mnHeight is zero!?"); + return; + } + + if (!fontManager) + { + SystemFontList* fontList = GetCoretextFontList(); + if (fontList == nullptr) + { + SAL_WARN("vcl.skia", "DrawTextLayout(): No coretext font list"); + fontManager = SkFontMgr_New_CoreText(nullptr); + } + else + { + fontManager = SkFontMgr_New_CoreText(fontList->fontCollection()); + } + } + + CTFontRef pFont + = static_cast<CTFontRef>(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName)); + sk_sp<SkTypeface> typeface = SkMakeTypefaceFromCTFont(pFont); + SkFont font(typeface); + font.setSize(nHeight); + // font.setScaleX(rStyle.mfFontStretch); TODO + if (rStyle.mbFauxBold) + font.setEmbolden(true); + font.setEdging(!mrShared.mbNonAntialiasedText ? SkFont::Edging::kAntiAlias + : SkFont::Edging::kAlias); + + // Vertical font, use width as "height". + SkFont verticalFont(font); + verticalFont.setSize(nHeight); + // verticalFont.setSize(nWidth); TODO + // verticalFont.setScaleX(1.0 * nHeight / nWidth); + + drawGenericLayout(rLayout, mrShared.maTextColor, font, verticalFont); +} + std::unique_ptr<sk_app::WindowContext> createVulkanWindowContext(bool /*temporary*/) { return nullptr;