diff options
author | Armin Le Grand (Collabora) <Armin.Le.Grand@me.com> | 2024-08-08 14:08:22 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2024-08-08 18:27:16 +0200 |
commit | ebe1543250b9729a6560545e58f50ea68e62be11 (patch) | |
tree | 8a95640098b09a669f3cad9e4404e2ba019b481c /drawinglayer | |
parent | b1fcc20db80e7b1ba86594b3eef199965057d499 (diff) |
CairoSDPR: Support ColorPolygon with AlphaGradient
Cairo can render RGBA gradients directly and the
CairoSDPR supports that. If we have a PolyPolygon
filled with single color combined with a gradient
alpha the renderer could map RGB to that color
and combine with the real alpha gradient steps.
To support that I added another Primitive called
PolyPolygonAlphaGradientPrimitive2D. It decomposes
as needed (TransparencePrimitive2D if needed), so
no other renderers have to be touched.
The Cairo renderer supports it directly, though,
what makes it much faster.
Change-Id: Ie90c8bd84d6458d12443db815ced55bdea93c15c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171628
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'drawinglayer')
6 files changed, 206 insertions, 3 deletions
diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk index 7529c34a43b7..e8e3dde7c952 100644 --- a/drawinglayer/Library_drawinglayer.mk +++ b/drawinglayer/Library_drawinglayer.mk @@ -144,6 +144,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D \ + drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D \ diff --git a/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.cxx new file mode 100644 index 000000000000..c65785e78bb8 --- /dev/null +++ b/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.cxx @@ -0,0 +1,106 @@ +/* -*- 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 <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <basegfx/utils/bgradient.hxx> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +Primitive2DReference PolyPolygonAlphaGradientPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (0 == getB2DPolyPolygon().count()) + { + // no geometry, done + return nullptr; + } + + if (getAlphaGradient().isDefault()) + { + // default is a single ColorStop at 0.0 with black (0, 0, 0). The + // luminance is then 0.0, too -> not transparent at all + return new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), getBColor()); + } + + basegfx::BColor aSingleColor; + if (getAlphaGradient().getColorStops().isSingleColor(aSingleColor)) + { + // no real taransparence gradient, only unified alpha, + // we can use PolyPolygonRGBAPrimitive2D + return new PolyPolygonRGBAPrimitive2D(getB2DPolyPolygon(), getBColor(), + aSingleColor.luminance()); + } + + // transparency gradient is a real gradient, create TransparencePrimitive2D + Primitive2DContainer aContent{ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), + getBColor()) }; + + Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( + basegfx::utils::getRange(getB2DPolyPolygon()), getAlphaGradient()) }; + + return new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)); +} + +PolyPolygonAlphaGradientPrimitive2D::PolyPolygonAlphaGradientPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor, + const attribute::FillGradientAttribute& rAlphaGradient) + : maPolyPolygon(rPolyPolygon) + , maBColor(rBColor) + , maAlphaGradient(rAlphaGradient) +{ +} + +bool PolyPolygonAlphaGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const PolyPolygonAlphaGradientPrimitive2D& rCompare + = static_cast<const PolyPolygonAlphaGradientPrimitive2D&>(rPrimitive); + + return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() + && getBColor() == rCompare.getBColor() + && getAlphaGradient() == rCompare.getAlphaGradient()); + } + + return false; +} + +basegfx::B2DRange PolyPolygonAlphaGradientPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // return range - without decompose + return basegfx::utils::getRange(getB2DPolyPolygon()); +} + +// provide unique ID +sal_uInt32 PolyPolygonAlphaGradientPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D; +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx index eb453116c97c..fb230af8712e 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx @@ -58,10 +58,10 @@ Primitive2DReference PolyPolygonRGBAPrimitive2D::create2DDecomposition( getTransparency()) }; } -PolyPolygonRGBAPrimitive2D::PolyPolygonRGBAPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, +PolyPolygonRGBAPrimitive2D::PolyPolygonRGBAPrimitive2D(const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor, double fTransparency) - : maPolyPolygon(std::move(aPolyPolygon)) + : maPolyPolygon(rPolyPolygon) , maBColor(rBColor) , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) { @@ -85,7 +85,7 @@ bool PolyPolygonRGBAPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) c basegfx::B2DRange PolyPolygonRGBAPrimitive2D::getB2DRange( const geometry::ViewInformation2D& /*rViewInformation*/) const { - // return range + // return range - without decompose return basegfx::utils::getRange(getB2DPolyPolygon()); } diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx index 2a4314c70ec4..52545212b8cc 100644 --- a/drawinglayer/source/primitive2d/Tools.cxx +++ b/drawinglayer/source/primitive2d/Tools.cxx @@ -239,6 +239,8 @@ OUString idToString(sal_uInt32 nId) return u"POLYPOLYGONRGBAPRIMITIVE2D"_ustr; case PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D: return u"BITMAPALPHAPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + return u"POLYPOLYGONALPHAGRADIENTPRIMITIVE2D"_ustr; default: return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF); } diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index 62e009e62bc9..31387125c259 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -38,6 +38,7 @@ #include <drawinglayer/primitive2d/invertprimitive2d.hxx> #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> #include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> @@ -2786,6 +2787,80 @@ void CairoPixelProcessor2D::processPolyPolygonRGBAPrimitive2D( rPolyPolygonRGBAPrimitive2D.getTransparency()); } +void CairoPixelProcessor2D::processPolyPolygonAlphaGradientPrimitive2D( + const primitive2d::PolyPolygonAlphaGradientPrimitive2D& rPolyPolygonAlphaGradientPrimitive2D) +{ + const basegfx::B2DPolyPolygon& rPolyPolygon( + rPolyPolygonAlphaGradientPrimitive2D.getB2DPolyPolygon()); + if (0 == rPolyPolygon.count()) + { + // no geometry, done + return; + } + + const basegfx::BColor& rColor(rPolyPolygonAlphaGradientPrimitive2D.getBColor()); + const attribute::FillGradientAttribute& rAlphaGradient( + rPolyPolygonAlphaGradientPrimitive2D.getAlphaGradient()); + if (rAlphaGradient.isDefault()) + { + // default is a single ColorStop at 0.0 with black (0, 0, 0). The + // luminance is then 0.0, too -> not transparent at all + paintPolyPoylgonRGBA(rPolyPolygon, rColor); + return; + } + + basegfx::BColor aSingleColor; + const basegfx::BColorStops& rAlphaStops(rAlphaGradient.getColorStops()); + if (rAlphaStops.isSingleColor(aSingleColor)) + { + // draw with alpha directly + paintPolyPoylgonRGBA(rPolyPolygon, rColor, aSingleColor.luminance()); + return; + } + + const css::awt::GradientStyle aStyle(rAlphaGradient.getStyle()); + if (css::awt::GradientStyle_SQUARE == aStyle || css::awt::GradientStyle_RECT == aStyle) + { + // direct paint cannot be used for thse styles since they get 'stitched' + // by multiple parts, so *need* singhle alpha for multiple pieces, go + // with decompose/recursion + process(rPolyPolygonAlphaGradientPrimitive2D); + return; + } + + // render as FillGradientPrimitive2D. The idea is to create BColorStops + // with the same number of entries, but all the same color, using the + // polygon's traget fill color, so we can directly paint gradients as + // RGBA in Cairo + basegfx::BColorStops aColorStops; + + // create ColorStops at same stops but single color + aColorStops.reserve(rAlphaStops.size()); + for (const auto& entry : rAlphaStops) + aColorStops.emplace_back(entry.getStopOffset(), rColor); + + // create FillGradient using that single-color ColorStops + const attribute::FillGradientAttribute aFillGradient( + rAlphaGradient.getStyle(), rAlphaGradient.getBorder(), rAlphaGradient.getOffsetX(), + rAlphaGradient.getOffsetY(), rAlphaGradient.getAngle(), aColorStops, + rAlphaGradient.getSteps()); + + // create temporary FillGradientPrimitive2D, but do not forget + // to embed to MaskPrimitive2D to get the PolyPolygon form + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + const primitive2d::Primitive2DContainer aContainerMaskedFillGradient{ + rtl::Reference<primitive2d::MaskPrimitive2D>(new primitive2d::MaskPrimitive2D( + rPolyPolygon, + primitive2d::Primitive2DContainer{ rtl::Reference<primitive2d::FillGradientPrimitive2D>( + new primitive2d::FillGradientPrimitive2D(aRange, // OutputRange + aRange, // DefinitionRange + aFillGradient, &rAlphaGradient)) })) + }; + + // render this + process(aContainerMaskedFillGradient); +} + void CairoPixelProcessor2D::processBitmapAlphaPrimitive2D( const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaPrimitive2D) { @@ -3070,6 +3145,12 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit static_cast<const primitive2d::BitmapAlphaPrimitive2D&>(rCandidate)); break; } + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + { + processPolyPolygonAlphaGradientPrimitive2D( + static_cast<const primitive2d::PolyPolygonAlphaGradientPrimitive2D&>(rCandidate)); + break; + } case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: { processTextSimplePortionPrimitive2D( diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx index 55c581dab510..527889cd7399 100644 --- a/drawinglayer/source/tools/primitive2dxmldump.cxx +++ b/drawinglayer/source/tools/primitive2dxmldump.cxx @@ -43,6 +43,7 @@ #include <drawinglayer/primitive2d/sceneprimitive2d.hxx> #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> #include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/attribute/lineattribute.hxx> #include <drawinglayer/attribute/fontattribute.hxx> @@ -1229,6 +1230,18 @@ void Primitive2dXmlDump::decomposeAndWrite( break; } + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + { + const PolyPolygonAlphaGradientPrimitive2D& rPolyPolygonAlphaGradientPrimitive2D + = dynamic_cast<const PolyPolygonAlphaGradientPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polypolygonalphagradient"); + rWriter.attribute("color", convertColorToString( + rPolyPolygonAlphaGradientPrimitive2D.getBColor())); + writePolyPolygon(rWriter, rPolyPolygonAlphaGradientPrimitive2D.getB2DPolyPolygon()); + rWriter.endElement(); + break; + } + default: { rWriter.startElement("unhandled"); |