summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <Armin.Le.Grand@me.com>2024-07-16 14:34:58 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2024-07-16 21:15:02 +0200
commitd1c6eeba13c2551e270e8ff0ba919fc9bac101c9 (patch)
tree0ddbf934ec2e5085d09e94103152f35963f6a4ee /drawinglayer
parentf7c9ecf3d740f53d7a860157aba4fa1a8e32cca8 (diff)
CairoSDPR: Direct support for RGBA Gradients (II)
This step wil now use the new primitive PolyPolygonRGBAGradientPrimitive2D inside the CairoSDPR/CairoPixelProcessor2D to directly render gradients as RGBA, without painting RGB (content) and A (alpha) separate and mixing the results. Note that this is only possible for gradients which use the same graphical definition, see the method 'sameDefinitionThanAlpha' and it's usages in the code. Change-Id: I6c46a211ea26d3e4b7bb39c7f2aaff09ec38dec5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170584 Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com> Tested-by: Jenkins
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx175
-rw-r--r--drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx6
2 files changed, 158 insertions, 23 deletions
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index 1de46473d790..5761d8f4bdb1 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -36,6 +36,7 @@
#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <drawinglayer/converters.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
@@ -1263,8 +1264,10 @@ void CairoPixelProcessor2D::processInvertPrimitive2D(
cairo_restore(mpRT);
}
-void CairoPixelProcessor2D::processMaskPrimitive2DPixel(
- const primitive2d::MaskPrimitive2D& rMaskCandidate)
+void CairoPixelProcessor2D::processMaskPrimitive2D(
+ const primitive2d::MaskPrimitive2D& rMaskCandidate,
+ const primitive2d::FillGradientPrimitive2D* pFillGradientPrimitive2D,
+ const attribute::FillGradientAttribute* pFillGradientAlpha)
{
if (rMaskCandidate.getChildren().empty())
{
@@ -1314,8 +1317,20 @@ void CairoPixelProcessor2D::processMaskPrimitive2DPixel(
cairo_clip(mpRT);
cairo_new_path(mpRT);
- // process sub-content (that shall be masked)
- process(rMaskCandidate.getChildren());
+ if (nullptr != pFillGradientPrimitive2D && nullptr != pFillGradientAlpha)
+ {
+ // special case: render given FillGradientPrimitive2D using
+ // FillGradientAlpha as RGBA gradient directly
+ // note that calling this method with nullptr != pFillGradientAlpha
+ // is only allowed internal from
+ // CairoPixelProcessor2D::processPolyPolygonRGBAGradientPrimitive2D
+ processFillGradientPrimitive2D(*pFillGradientPrimitive2D, pFillGradientAlpha);
+ }
+ else
+ {
+ // process sub-content (that shall be masked)
+ process(rMaskCandidate.getChildren());
+ }
cairo_restore(mpRT);
}
@@ -1922,7 +1937,8 @@ bool CairoPixelProcessor2D::processFillGradientPrimitive2D_isCompletelyBordered(
}
void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
- const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
+ const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D,
+ const attribute::FillGradientAttribute* pFillGradientAlpha)
{
assert(
(css::awt::GradientStyle_LINEAR == rFillGradientPrimitive2D.getFillGradient().getStyle()
@@ -1964,6 +1980,10 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
// get color stops (make copy, might have to be changed)
basegfx::BColorStops aBColorStops(rFillGradient.getColorStops());
+ basegfx::BColorStops aBColorStopsAlpha;
+ const bool bHasAlpha(nullptr != pFillGradientAlpha);
+ if (bHasAlpha)
+ aBColorStopsAlpha = pFillGradientAlpha->getColorStops();
const bool bAxial(css::awt::GradientStyle_AXIAL == rFillGradient.getStyle());
// get and apply border - create soace at start in gradient
@@ -1971,30 +1991,58 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
if (!basegfx::fTools::equalZero(fBorder))
{
if (bAxial)
+ {
aBColorStops.reverseColorStops();
+ if (bHasAlpha)
+ aBColorStopsAlpha.reverseColorStops();
+ }
+
aBColorStops.createSpaceAtStart(fBorder);
+ if (bHasAlpha)
+ aBColorStopsAlpha.createSpaceAtStart(fBorder);
+
if (bAxial)
+ {
aBColorStops.reverseColorStops();
+ if (bHasAlpha)
+ aBColorStopsAlpha.reverseColorStops();
+ }
}
if (bAxial)
{
// expand with mirrored ColorStops to create axial
aBColorStops.doApplyAxial();
+ if (bHasAlpha)
+ aBColorStopsAlpha.doApplyAxial();
}
// Apply steps if used to 'emulate' LO's 'discrete step' feature
if (rFillGradient.getSteps())
{
aBColorStops.doApplySteps(rFillGradient.getSteps());
+ if (bHasAlpha)
+ aBColorStopsAlpha.doApplySteps(rFillGradient.getSteps());
}
// add color stops
- for (const auto& aStop : aBColorStops)
+ for (size_t a(0); a < aBColorStops.size(); a++)
{
- const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(aStop.getStopColor()));
- cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), aColor.getRed(),
- aColor.getGreen(), aColor.getBlue());
+ const basegfx::BColorStop& rStop(aBColorStops[a]);
+ const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rStop.getStopColor()));
+
+ if (bHasAlpha)
+ {
+ const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor());
+ cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(),
+ aColor.getGreen(), aColor.getBlue(),
+ aAlpha.luminance());
+ }
+ else
+ {
+ cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
+ aColor.getGreen(), aColor.getBlue());
+ }
}
// draw OutRange
@@ -2273,7 +2321,8 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect(
}
void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical(
- const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
+ const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D,
+ const attribute::FillGradientAttribute* pFillGradientAlpha)
{
assert((css::awt::GradientStyle_RADIAL == rFillGradientPrimitive2D.getFillGradient().getStyle()
|| css::awt::GradientStyle_ELLIPTICAL
@@ -2337,26 +2386,46 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical(
// get color stops (make copy, might have to be changed)
basegfx::BColorStops aBColorStops(rFillGradient.getColorStops());
+ basegfx::BColorStops aBColorStopsAlpha;
+ const bool bHasAlpha(nullptr != pFillGradientAlpha);
+ if (bHasAlpha)
+ aBColorStopsAlpha = pFillGradientAlpha->getColorStops();
// get and apply border - create soace at start in gradient
const double fBorder(std::max(std::min(rFillGradient.getBorder(), 1.0), 0.0));
if (!basegfx::fTools::equalZero(fBorder))
{
aBColorStops.createSpaceAtStart(fBorder);
+ if (bHasAlpha)
+ aBColorStopsAlpha.createSpaceAtStart(fBorder);
}
// Apply steps if used to 'emulate' LO's 'discrete step' feature
if (rFillGradient.getSteps())
{
aBColorStops.doApplySteps(rFillGradient.getSteps());
+ if (bHasAlpha)
+ aBColorStopsAlpha.doApplySteps(rFillGradient.getSteps());
}
// add color stops
- for (const auto& aStop : aBColorStops)
+ for (size_t a(0); a < aBColorStops.size(); a++)
{
- const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(aStop.getStopColor()));
- cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), aColor.getRed(),
- aColor.getGreen(), aColor.getBlue());
+ const basegfx::BColorStop& rStop(aBColorStops[a]);
+ const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rStop.getStopColor()));
+
+ if (bHasAlpha)
+ {
+ const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor());
+ cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(),
+ aColor.getGreen(), aColor.getBlue(),
+ aAlpha.luminance());
+ }
+ else
+ {
+ cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
+ aColor.getGreen(), aColor.getBlue());
+ }
}
cairo_set_source(mpRT, pPattern);
@@ -2446,7 +2515,8 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_fallback_decompose(
}
void CairoPixelProcessor2D::processFillGradientPrimitive2D(
- const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
+ const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D,
+ const attribute::FillGradientAttribute* pFillGradientAlpha)
{
if (rFillGradientPrimitive2D.getDefinitionRange().isEmpty())
{
@@ -2494,13 +2564,21 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D(
return;
}
+ // for dfirect RGBA gradient render support: assert when the definition
+ // is not allowed, it HAS to fulfil the requested preconditions. Note that
+ // the form to call this function using nullptr != pFillGradientAlpha is
+ // only allowed locally in CairoPixelProcessor2D::processMaskPrimitive2D
+ assert(nullptr == pFillGradientAlpha
+ || rFillGradient.sameDefinitionThanAlpha(*pFillGradientAlpha));
+
switch (rFillGradient.getStyle())
{
case css::awt::GradientStyle_LINEAR:
case css::awt::GradientStyle_AXIAL:
{
// use specialized renderer for this cases - linear, axial
- processFillGradientPrimitive2D_linear_axial(rFillGradientPrimitive2D);
+ processFillGradientPrimitive2D_linear_axial(rFillGradientPrimitive2D,
+ pFillGradientAlpha);
return;
}
case css::awt::GradientStyle_RADIAL:
@@ -2517,13 +2595,20 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D(
// also rare. IF that should make problems reactivation of that case
// for the default case below is possible. main reason is that speed
// for direct rendering in cairo is much better.
- processFillGradientPrimitive2D_radial_elliptical(rFillGradientPrimitive2D);
+ processFillGradientPrimitive2D_radial_elliptical(rFillGradientPrimitive2D,
+ pFillGradientAlpha);
return;
}
case css::awt::GradientStyle_SQUARE:
case css::awt::GradientStyle_RECT:
{
// use specialized renderer for this cases - square, rect
+ // NOTE: *NO* support for FillGradientAlpha here. it is anyways
+ // hard to map these to direct rendering, but to do so the four
+ // trapezoids/sides are 'stitched' together, so painting RGBA
+ // directly will make the overlaps look bad and like errors.
+ // Anyways, these gradient types are only our internal heritage
+ // and rendering them directly is already much faster, will be okay.
processFillGradientPrimitive2D_square_rect(rFillGradientPrimitive2D);
return;
}
@@ -2538,6 +2623,53 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D(
}
}
+void CairoPixelProcessor2D::processPolyPolygonRGBAGradientPrimitive2D(
+ const primitive2d::PolyPolygonRGBAGradientPrimitive2D& rPolyPolygonRGBAGradientPrimitive2D)
+{
+ const attribute::FillGradientAttribute& rFill(
+ rPolyPolygonRGBAGradientPrimitive2D.getFillGradient());
+ const attribute::FillGradientAttribute& rAlpha(
+ rPolyPolygonRGBAGradientPrimitive2D.getFillGradientAlpha());
+
+ if (rFill.isDefault())
+ {
+ // no gradient definition, done
+ return;
+ }
+
+ // assert when the definition is not allowed, it HAS to fulfil the
+ // requested preconditions
+ assert(rFill.sameDefinitionThanAlpha(rAlpha));
+
+ // the gradient still needs to be masked to getB2DPolyPolygon() at the
+ // primitive, see PolyPolygonGradientPrimitive2D::create2DDecomposition.
+ // we could repeat here the code inside localprocessMaskPrimitive2D, but
+ // it is easier to just locally temporarily create the needed data structure
+ // and hand over the needed extra-data
+ const basegfx::B2DRange aPolyPolygonRange(
+ rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon().getB2DRange());
+ primitive2d::FillGradientPrimitive2D* pFillGradientPrimitive2D(
+ new primitive2d::FillGradientPrimitive2D(
+ aPolyPolygonRange, rPolyPolygonRGBAGradientPrimitive2D.getDefinitionRange(), rFill));
+ primitive2d::Primitive2DContainer aContent{ pFillGradientPrimitive2D };
+
+ // NOTE: I had this like
+ // const primitive2d::MaskPrimitive2D aMaskPrimitive2D(
+ // rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon(), std::move(aContent));
+ // but I got
+ // error: salhelper::SimpleReferenceObject subclass being directly stack managed, should
+ // be managed via rtl::Reference, const primitive2d::MaskPrimitive2D [loplugin:refcounting]
+ // thus I have *no choice* and have to use the heap here (?) It is no problem to use the stack
+ // and I wanted to do this here by purpose... sigh
+ primitive2d::MaskPrimitive2D* pMaskPrimitive2D(new primitive2d::MaskPrimitive2D(
+ rPolyPolygonRGBAGradientPrimitive2D.getB2DPolyPolygon(), std::move(aContent)));
+ primitive2d::Primitive2DContainer aMask{ pMaskPrimitive2D };
+ (void)aMask;
+
+ // render masked RGBA gradient
+ processMaskPrimitive2D(*pMaskPrimitive2D, pFillGradientPrimitive2D, &rAlpha);
+}
+
void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
switch (rCandidate.getPrimitive2DID())
@@ -2582,8 +2714,7 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit
}
case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
{
- processMaskPrimitive2DPixel(
- static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
break;
}
case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
@@ -2654,6 +2785,12 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit
static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate));
break;
}
+ case PRIMITIVE2D_ID_POLYPOLYGONRGBAGRADIENTPRIMITIVE2D:
+ {
+ processPolyPolygonRGBAGradientPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonRGBAGradientPrimitive2D&>(rCandidate));
+ break;
+ }
// continue with decompose
default:
diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
index 089106194754..e9bcc6c10423 100644
--- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
@@ -1269,8 +1269,7 @@ void D2DPixelProcessor2D::processUnifiedTransparencePrimitive2D(
increaseError();
}
-void D2DPixelProcessor2D::processMaskPrimitive2DPixel(
- const primitive2d::MaskPrimitive2D& rMaskCandidate)
+void D2DPixelProcessor2D::processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate)
{
if (rMaskCandidate.getChildren().empty())
{
@@ -2082,8 +2081,7 @@ void D2DPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
}
case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
{
- processMaskPrimitive2DPixel(
- static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
break;
}
case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: