CairoSDPR: Support direct transparency for gradients

FillGradientPrimitive2D/ and PolyPolygonGradientPrimitive2D
now support both alphas, a gradientAlpha and a unified one.
This allows a processor to directly handle a gradient with
a unifiedAlpha if he wants. Adapted other places accordingly.

NOTE: In VclMetafileProcessor2D handling of the primitive
PolyPolygonGradientPrimitive2D has to do a local compromize,
see comment there.

Change-Id: I536f4935dafde0369f768dbd281d547b7bb08eb4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171052
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Tested-by: Jenkins
This commit is contained in:
Armin Le Grand (Collabora)
2024-07-26 10:56:33 +02:00
committed by Armin Le Grand
parent 2a9ae69483
commit 4461173a4d
9 changed files with 141 additions and 44 deletions
@@ -40,7 +40,7 @@ Primitive2DReference PolyPolygonGradientPrimitive2D::create2DDecomposition(
const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
rtl::Reference<FillGradientPrimitive2D> pNewGradient = new FillGradientPrimitive2D(
aPolyPolygonRange, getDefinitionRange(), getFillGradient(),
hasAlphaGradient() ? &getAlphaGradient() : nullptr);
hasAlphaGradient() ? &getAlphaGradient() : nullptr, getTransparency());
Primitive2DContainer aSubSequence{ pNewGradient };
// create mask primitive
@@ -56,17 +56,19 @@ PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
, maDefinitionRange(rPolyPolygon.getB2DRange())
, maFillGradient(rFillGradient)
, maAlphaGradient()
, mfTransparency(0.0)
{
}
PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
const attribute::FillGradientAttribute& rFillGradient,
const attribute::FillGradientAttribute* pAlphaGradient)
const attribute::FillGradientAttribute* pAlphaGradient, double fTransparency)
: maPolyPolygon(std::move(aPolyPolygon))
, maDefinitionRange(rDefinitionRange)
, maFillGradient(rFillGradient)
, maAlphaGradient()
, mfTransparency(fTransparency)
{
// copy alpha gradient if we got one
if (nullptr != pAlphaGradient)
@@ -93,6 +95,9 @@ bool PolyPolygonGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitiv
if (maAlphaGradient != rCompare.maAlphaGradient)
return false;
if (!basegfx::fTools::equal(getTransparency(), rCompare.getTransparency()))
return false;
return true;
}
@@ -25,6 +25,7 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <utility>
#include <algorithm>
@@ -264,22 +265,33 @@ namespace drawinglayer::primitive2d
// SDPR: support alpha directly now. If a primitive processor
// cannot deal with it, use it's decomposition. For that purpose
// this decomposition has two stages now: This 1st one will
// separate content and alpha into a TransparencePrimitive2D,
// (if needed) separate content and alpha into a TransparencePrimitive2D
// and (if needed) embed that to a UnifiedTransparencePrimitive2D,
// so all processors can work as before
if (hasAlphaGradient())
if (hasAlphaGradient() || hasTransparency())
{
Primitive2DContainer aContent{ new FillGradientPrimitive2D(
getOutputRange(),
getDefinitionRange(),
getFillGradient()) };
Primitive2DReference aRetval(
new FillGradientPrimitive2D(
getOutputRange(),
getDefinitionRange(),
getFillGradient()));
Primitive2DContainer aAlpha{ new FillGradientPrimitive2D(
getOutputRange(),
getDefinitionRange(),
getAlphaGradient()) };
if (hasAlphaGradient())
{
Primitive2DContainer aAlpha{ new FillGradientPrimitive2D(
getOutputRange(),
getDefinitionRange(),
getAlphaGradient()) };
return Primitive2DReference{ new TransparencePrimitive2D(std::move(aContent),
std::move(aAlpha)) };
aRetval = new TransparencePrimitive2D(Primitive2DContainer{ aRetval }, std::move(aAlpha));
}
if (hasTransparency())
{
aRetval = new UnifiedTransparencePrimitive2D(Primitive2DContainer{ aRetval }, getTransparency());
}
return aRetval;
}
// default creates overlapping fill which works with AntiAliasing and without.
@@ -303,6 +315,7 @@ namespace drawinglayer::primitive2d
, maDefinitionRange(rOutputRange)
, maFillGradient(rFillGradient)
, maAlphaGradient()
, mfTransparency(0.0)
{
}
@@ -310,11 +323,13 @@ namespace drawinglayer::primitive2d
const basegfx::B2DRange& rOutputRange,
const basegfx::B2DRange& rDefinitionRange,
const attribute::FillGradientAttribute& rFillGradient,
const attribute::FillGradientAttribute* pAlphaGradient)
const attribute::FillGradientAttribute* pAlphaGradient,
double fTransparency)
: maOutputRange(rOutputRange)
, maDefinitionRange(rDefinitionRange)
, maFillGradient(rFillGradient)
, maAlphaGradient()
, mfTransparency(fTransparency)
{
// copy alpha gradient if we got one
if (nullptr != pAlphaGradient)
@@ -340,6 +355,9 @@ namespace drawinglayer::primitive2d
if (maAlphaGradient != rCompare.maAlphaGradient)
return false;
if (!basegfx::fTools::equal(getTransparency(), rCompare.getTransparency()))
return false;
return true;
}
@@ -2151,8 +2151,17 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
}
else
{
cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue());
if (rFillGradientPrimitive2D.hasTransparency())
{
cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue(),
1.0 - rFillGradientPrimitive2D.getTransparency());
}
else
{
cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue());
}
}
}
@@ -2177,9 +2186,12 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial(
void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect(
const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
{
if (rFillGradientPrimitive2D.hasAlphaGradient())
if (rFillGradientPrimitive2D.hasAlphaGradient() || rFillGradientPrimitive2D.hasTransparency())
{
// process recursively
// Do not use direct alpha for this: It paints using four trapez that
// do not add up at edges due to being painted AntiAliased; that means
// common pixels do not add up, but blend by transparency, so leaving
// visual traces -> process recursively
process(rFillGradientPrimitive2D);
return;
}
@@ -2542,8 +2554,17 @@ void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical(
}
else
{
cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue());
if (rFillGradientPrimitive2D.hasTransparency())
{
cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue(),
1.0 - rFillGradientPrimitive2D.getTransparency());
}
else
{
cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(),
aColor.getGreen(), aColor.getBlue());
}
}
}
@@ -1940,7 +1940,7 @@ void D2DPixelProcessor2D::processFillGraphicPrimitive2D(
void D2DPixelProcessor2D::processFillGradientPrimitive2D(
const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
{
if (rFillGradientPrimitive2D.hasAlphaGradient())
if (rFillGradientPrimitive2D.hasAlphaGradient() || rFillGradientPrimitive2D.hasTransparency())
{
// SDPR: As long as direct alpha is not supported by this
// renderer we need to work on the decomposition, so call it
@@ -1984,15 +1984,45 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
{
bool useDecompose(false);
// SDPR: Caution: metafile export cannot handle added TransparencyGradient
if (rGradientCandidate.hasAlphaGradient())
if (rGradientCandidate.hasAlphaGradient() || rGradientCandidate.hasTransparency())
{
// SDPR: metafile cannot handle this, use decompose
useDecompose = true;
// if it has alpha added directly we need to use the decomposition.
// unfortunately VclMetafileProcessor2D does *not* support the
// primitive created in the decomposition, the FillGradientPrimitive2D.
// at the same time extra stuff like adding gradient info to the
// metafile (BGRAD_SEQ_BEGIN) only is done HERE. To solve that and to
// not add PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D now, create a temporary
// decomposition to again get a PolyPolygonGradientPrimitive2D, but
// *without* directly added alpha
primitive2d::Primitive2DReference aRetval(new primitive2d::PolyPolygonGradientPrimitive2D(
rGradientCandidate.getB2DPolyPolygon(), rGradientCandidate.getDefinitionRange(),
rGradientCandidate.getFillGradient()));
if (rGradientCandidate.hasAlphaGradient())
{
const basegfx::B2DRange aPolyPolygonRange(
rGradientCandidate.getB2DPolyPolygon().getB2DRange());
primitive2d::Primitive2DContainer aAlpha{ new primitive2d::FillGradientPrimitive2D(
aPolyPolygonRange, rGradientCandidate.getDefinitionRange(),
rGradientCandidate.getAlphaGradient()) };
aRetval = new primitive2d::TransparencePrimitive2D(
primitive2d::Primitive2DContainer{ aRetval }, std::move(aAlpha));
}
if (rGradientCandidate.hasTransparency())
{
aRetval = new primitive2d::UnifiedTransparencePrimitive2D(
primitive2d::Primitive2DContainer{ aRetval }, rGradientCandidate.getTransparency());
}
process(primitive2d::Primitive2DContainer{ aRetval });
return;
}
bool useDecompose(false);
if (!useDecompose)
{
basegfx::B2DVector aScale, aTranslate;
@@ -930,7 +930,7 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim
void VclPixelProcessor2D::processFillGradientPrimitive2D(
const primitive2d::FillGradientPrimitive2D& rPrimitive)
{
if (rPrimitive.hasAlphaGradient())
if (rPrimitive.hasAlphaGradient() || rPrimitive.hasTransparency())
{
// SDPR: As long as direct alpha is not supported by this
// renderer we need to work on the decomposition, so call it
@@ -49,6 +49,9 @@ private:
/// evtl. fitting alphaGradient definition
attribute::FillGradientAttribute maAlphaGradient;
/// the transparency in range [0.0 .. 1.0]
double mfTransparency;
/// local decomposition.
virtual Primitive2DReference
create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override;
@@ -60,16 +63,20 @@ public:
PolyPolygonGradientPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
const basegfx::B2DRange& rDefinitionRange,
const attribute::FillGradientAttribute& rFillGradient,
const attribute::FillGradientAttribute* pAlphaGradient
= nullptr);
const attribute::FillGradientAttribute* pAlphaGradient = nullptr,
double fTransparency = 0.0);
/// data read access
const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
const basegfx::B2DRange& getDefinitionRange() const { return maDefinitionRange; }
const attribute::FillGradientAttribute& getFillGradient() const { return maFillGradient; }
const attribute::FillGradientAttribute& getAlphaGradient() const { return maAlphaGradient; }
bool hasAlphaGradient() const { return !maAlphaGradient.isDefault(); }
double getTransparency() const { return mfTransparency; }
bool hasTransparency() const { return !basegfx::fTools::equalZero(mfTransparency); }
/// compare operator
virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
@@ -70,6 +70,8 @@ namespace drawinglayer::primitive2d
/// evtl. fitting alphaGradient definition
attribute::FillGradientAttribute maAlphaGradient;
/// the transparency in range [0.0 .. 1.0]
double mfTransparency;
protected:
/// local helper
@@ -93,15 +95,20 @@ namespace drawinglayer::primitive2d
const basegfx::B2DRange& rOutputRange,
const basegfx::B2DRange& rDefinitionRange,
const attribute::FillGradientAttribute& rFillGradient,
const attribute::FillGradientAttribute* pAlphaGradient = nullptr);
const attribute::FillGradientAttribute* pAlphaGradient = nullptr,
double fTransparency = 0.0);
/// data read access
const basegfx::B2DRange& getOutputRange() const { return maOutputRange; }
const basegfx::B2DRange& getDefinitionRange() const { return maDefinitionRange; }
const attribute::FillGradientAttribute& getFillGradient() const { return maFillGradient; }
const attribute::FillGradientAttribute& getAlphaGradient() const { return maAlphaGradient; }
bool hasAlphaGradient() const { return !maAlphaGradient.isDefault(); }
double getTransparency() const { return mfTransparency; }
bool hasTransparency() const { return !basegfx::fTools::equalZero(mfTransparency); }
/// compare operator
virtual bool operator==(const BasePrimitive2D& rPrimitive) const override final;
@@ -404,25 +404,34 @@ sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
if(!rFillGradient.isDefault())
{
// SDPR: check early if we have a gradient and an alpha
// gradient that 'fits' in its geometric definition
// so that it can be rendered as RGBA directly. If yes,
// create it and return early
const bool bIncludeAlpha(
0.0 == rFill.getTransparence()
const bool bHasTransparency(!basegfx::fTools::equalZero(rFill.getTransparence()));
// note: need to use !bHasTransparency to do the same as below
// where embedding to transparency is done. There, simple transparency
// gets priority over gradient transparency (and none). Thus here only one
// option is used. Note that the implementation of FillGradientPrimitive2D
// and PolyPolygonGradientPrimitive2D do support both alphas being used
const bool bHasAlphaGradient(!bHasTransparency
&& !rAlphaGradient.isDefault()
&& rFillGradient.sameDefinitionThanAlpha(rAlphaGradient));
if(bHasTransparency || bHasAlphaGradient)
{
// SDPR: check early if we have a gradient and an alpha
// gradient that 'fits' in its geometric definition
// so that it can be rendered as RGBA directly. If yes,
// create it and return early
return new PolyPolygonGradientPrimitive2D(
rPolyPolygon,
rDefinitionRange,
rFillGradient,
bHasAlphaGradient ? &rAlphaGradient : nullptr,
bHasTransparency ? rFill.getTransparence() : 0.0);
}
pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
rPolyPolygon,
rDefinitionRange,
rFillGradient,
bIncludeAlpha ? &rAlphaGradient : nullptr);
if (bIncludeAlpha)
{
return pNewFillPrimitive;
}
rFillGradient);
}
else if(!rFill.getHatch().isDefault())
{