summaryrefslogtreecommitdiffstats
path: root/include/basegfx/utils/bgradient.hxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /include/basegfx/utils/bgradient.hxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'include/basegfx/utils/bgradient.hxx')
-rw-r--r--include/basegfx/utils/bgradient.hxx329
1 files changed, 329 insertions, 0 deletions
diff --git a/include/basegfx/utils/bgradient.hxx b/include/basegfx/utils/bgradient.hxx
new file mode 100644
index 0000000000..7d360beee4
--- /dev/null
+++ b/include/basegfx/utils/bgradient.hxx
@@ -0,0 +1,329 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/basegfxdllapi.h>
+#include <vector>
+#include <com/sun/star/awt/GradientStyle.hpp>
+#include <tools/degree.hxx>
+#include <boost/property_tree/ptree_fwd.hpp>
+
+namespace basegfx
+{
+/* MCGR: Provide ColorStop definition
+
+ This is the needed combination of offset and color:
+
+ Offset is defined as:
+ - being in the range of [0.0 .. 1.0] (unit range)
+ - offsets outside are an error
+ - lowest/1st value equivalent to StartColor
+ - highest/last value equivalent to EndColor
+ - missing 0.0/1.0 entries are allowed
+ - at least one value (usually 0.0, StartColor) is required
+ - this allows to avoid massive testing in all places where
+ this data has to be accessed
+
+ Color is defined as:
+ - RGB with unit values [0.0 .. 1.0]
+
+ These definitions are packed in a std::vector<ColorStop> ColorStops,
+ see typedef below.
+ */
+class BASEGFX_DLLPUBLIC BColorStop
+{
+private:
+ // offset in the range of [0.0 .. 1.0]
+ double mfStopOffset;
+
+ // RGB color of ColorStop entry
+ BColor maStopColor;
+
+public:
+ // constructor - defaults are needed to have a default constructor
+ // e.g. for usage in std::vector::insert (even when only reducing)
+ // ensure [0.0 .. 1.0] range for mfStopOffset
+ BColorStop(double fStopOffset = 0.0, const BColor& rStopColor = BColor())
+ : mfStopOffset(fStopOffset)
+ , maStopColor(rStopColor)
+ {
+ // NOTE: I originally *corrected* mfStopOffset here by using
+ // mfStopOffset(std::max(0.0, std::min(fOffset, 1.0)))
+ // While that is formally correct, it moves an invalid
+ // entry to 0.0 or 1.0, thus creating additional wrong
+ // Start/EndColor entries. That may then 'overlay' the
+ // correct entry when corrections are applied to the
+ // vector of entries (see sortAndCorrectColorStops)
+ // which leads to getting the wanted Start/EndColor
+ // to be factically deleted, what is an error.
+ }
+
+ double getStopOffset() const { return mfStopOffset; }
+ const BColor& getStopColor() const { return maStopColor; }
+
+ // needed for std::sort
+ bool operator<(const BColorStop& rCandidate) const
+ {
+ return getStopOffset() < rCandidate.getStopOffset();
+ }
+
+ bool operator==(const BColorStop& rCandidate) const
+ {
+ return getStopOffset() == rCandidate.getStopOffset()
+ && getStopColor() == rCandidate.getStopColor();
+ }
+
+ bool operator!=(const BColorStop& rCandidate) const { return !(*this == rCandidate); }
+};
+
+/* MCGR: Provide ColorStops definition to the FillGradientAttribute
+
+ This array should be sorted ascending by offsets, from lowest to
+ highest. Since all the primitive data definition where it is used
+ is read-only, this can/will be guaranteed by forcing/checking this
+ in the constructor, see ::FillGradientAttribute
+ */
+class BASEGFX_DLLPUBLIC BColorStops final : public std::vector<BColorStop>
+{
+public:
+ explicit BColorStops()
+ : vector()
+ {
+ }
+ BColorStops(const BColorStops& other)
+ : vector(other)
+ {
+ }
+ BColorStops(BColorStops&& other) noexcept
+ : vector(std::move(other))
+ {
+ }
+ BColorStops(std::initializer_list<BColorStop> init)
+ : vector(init)
+ {
+ }
+ BColorStops(const_iterator first, const_iterator last)
+ : vector(first, last)
+ {
+ }
+
+ // constructor with two colors to explicitly create a
+ // BColorStops for StartColor @0.0 & EndColor @1.0
+ BColorStops(const BColor& rStart, const BColor& rEnd);
+
+ BColorStops& operator=(const BColorStops& r)
+ {
+ vector::operator=(r);
+ return *this;
+ }
+ BColorStops& operator=(BColorStops&& r) noexcept
+ {
+ vector::operator=(std::move(r));
+ return *this;
+ }
+
+ // helper data struct to support buffering entries in
+ // gradient texture mapping, see usages for more info
+ struct BColorStopRange
+ {
+ basegfx::BColor maColorStart;
+ basegfx::BColor maColorEnd;
+ double mfOffsetStart;
+ double mfOffsetEnd;
+
+ BColorStopRange()
+ : maColorStart()
+ , maColorEnd()
+ , mfOffsetStart(0.0)
+ , mfOffsetEnd(0.0)
+ {
+ }
+ };
+
+ /* Helper to grep the correct ColorStop out of
+ ColorStops and interpolate as needed for given
+ relative value in fPosition in the range of [0.0 .. 1.0].
+ It also takes care of evtl. given RequestedSteps.
+ */
+ BColor getInterpolatedBColor(double fPosition, sal_uInt32 nRequestedSteps,
+ BColorStopRange& rLastColorStopRange) const;
+
+ /* Tooling method that allows to replace the StartColor in a
+ vector of ColorStops. A vector in 'ordered state' is expected,
+ so you may use/have used sortAndCorrect.
+ This method is for convenience & backwards compatibility, please
+ think about handling multi-colored gradients directly.
+ */
+ void replaceStartColor(const BColor& rStart);
+
+ /* Tooling method that allows to replace the EndColor in a
+ vector of ColorStops. A vector in 'ordered state' is expected,
+ so you may use/have used sortAndCorrect.
+ This method is for convenience & backwards compatibility, please
+ think about handling multi-colored gradients directly.
+ */
+ void replaceEndColor(const BColor& rEnd);
+
+ /* Tooling method to linearly blend the Colors contained in
+ a given ColorStop vector against a given Color using the
+ given intensity values.
+ The intensity values fStartIntensity, fEndIntensity are
+ in the range of [0.0 .. 1.0] and describe how much the
+ blend is supposed to be done at the start color position
+ and the end color position respectively, where 0.0 means
+ to fully use the given BlendColor, 1.0 means to not change
+ the existing color in the ColorStop.
+ Every color entry in the given ColorStop is blended
+ relative to it's StopPosition, interpolating the
+ given intensities with the range [0.0 .. 1.0] to do so.
+ */
+ void blendToIntensity(double fStartIntensity, double fEndIntensity, const BColor& rBlendColor);
+
+ /* Tooling method to guarantee sort and correctness for
+ the given ColorStops vector.
+ A vector fulfilling these conditions is called to be
+ in 'ordered state'.
+
+ At return, the following conditions are guaranteed:
+ - contains no ColorStops with offset < 0.0 (will
+ be removed)
+ - contains no ColorStops with offset > 1.0 (will
+ be removed)
+ - ColorStops with identical offsets are now allowed
+ - will be sorted from lowest offset to highest
+
+ Some more notes:
+ - It can happen that the result is empty
+ - It is allowed to have consecutive entries with
+ the same color, this represents single-color
+ regions inside the gradient
+ - A entry with 0.0 is not required or forced, so
+ no 'StartColor' is technically required
+ - A entry with 1.0 is not required or forced, so
+ no 'EndColor' is technically required
+
+ All this is done in one run (sort + O(N)) without
+ creating a copy of the data in any form
+ */
+ void sortAndCorrect();
+
+ // check if we need last-ColorStop-correction. This returns true if the last
+ // two ColorStops have the same offset but different Colors. In that case the
+ // tessellation for gradients does have to create an extra ending/closing entry
+ bool checkPenultimate() const;
+
+ /* Tooling method to check if a ColorStop vector is defined
+ by a single color. It returns true if this is the case.
+ If true is returned, rSingleColor contains that single
+ color for convenience.
+ NOTE: If no ColorStop is defined, a fallback to BColor-default
+ (which is black) and true will be returned
+ */
+ bool isSingleColor(BColor& rSingleColor) const;
+
+ /* Tooling method to reverse ColorStops, including offsets.
+ When also mirroring offsets a valid sort keeps valid.
+ */
+ void reverseColorStops();
+
+ // createSpaceAtStart creates fOffset space at start by
+ // translating/scaling all entries to the right
+ void createSpaceAtStart(double fOffset);
+
+ // removeSpaceAtStart removes fOffset space from start by
+ // translating/scaling entries more or equal to fOffset
+ // to the left. Entries less than fOffset will be removed
+ void removeSpaceAtStart(double fOffset);
+
+ // try to detect if an empty/no-color-change area exists
+ // at the start and return offset to it. Returns 0.0 if not.
+ double detectPossibleOffsetAtStart() const;
+
+ // returns true if the color stops are symmetrical in color and offset, otherwise false.
+ bool isSymmetrical() const;
+ // assume that the color stops represent an Axial gradient
+ // and replace with gradient stops to represent the same
+ // gradient as linear gradient
+ void doApplyAxial();
+
+ // apply Steps as 'hard' color stops
+ void doApplySteps(sal_uInt16 nStepCount);
+};
+
+class BASEGFX_DLLPUBLIC BGradient final
+{
+private:
+ css::awt::GradientStyle eStyle;
+
+ // MCGS: ColorStops in the range [0.0 .. 1.0], including StartColor/EndColor
+ basegfx::BColorStops aColorStops;
+
+ Degree10 nAngle;
+ sal_uInt16 nBorder;
+ sal_uInt16 nOfsX;
+ sal_uInt16 nOfsY;
+ sal_uInt16 nIntensStart;
+ sal_uInt16 nIntensEnd;
+ sal_uInt16 nStepCount;
+
+ static std::string GradientStyleToString(css::awt::GradientStyle eStyle);
+
+public:
+ BGradient();
+ BGradient(const basegfx::BColorStops& rColorStops,
+ css::awt::GradientStyle eStyle = css::awt::GradientStyle_LINEAR,
+ Degree10 nAngle = 0_deg10, sal_uInt16 nXOfs = 50, sal_uInt16 nYOfs = 50,
+ sal_uInt16 nBorder = 0, sal_uInt16 nStartIntens = 100, sal_uInt16 nEndIntens = 100,
+ sal_uInt16 nSteps = 0);
+
+ bool operator==(const BGradient& rGradient) const;
+
+ void SetGradientStyle(css::awt::GradientStyle eNewStyle) { eStyle = eNewStyle; }
+ void SetColorStops(const basegfx::BColorStops& rSteps);
+ void SetAngle(Degree10 nNewAngle) { nAngle = nNewAngle; }
+ void SetBorder(sal_uInt16 nNewBorder) { nBorder = nNewBorder; }
+ void SetXOffset(sal_uInt16 nNewOffset) { nOfsX = nNewOffset; }
+ void SetYOffset(sal_uInt16 nNewOffset) { nOfsY = nNewOffset; }
+ void SetStartIntens(sal_uInt16 nNewIntens) { nIntensStart = nNewIntens; }
+ void SetEndIntens(sal_uInt16 nNewIntens) { nIntensEnd = nNewIntens; }
+ void SetSteps(sal_uInt16 nSteps) { nStepCount = nSteps; }
+
+ css::awt::GradientStyle GetGradientStyle() const { return eStyle; }
+ const basegfx::BColorStops& GetColorStops() const { return aColorStops; }
+ Degree10 GetAngle() const { return nAngle; }
+ sal_uInt16 GetBorder() const { return nBorder; }
+ sal_uInt16 GetXOffset() const { return nOfsX; }
+ sal_uInt16 GetYOffset() const { return nOfsY; }
+ sal_uInt16 GetStartIntens() const { return nIntensStart; }
+ sal_uInt16 GetEndIntens() const { return nIntensEnd; }
+ sal_uInt16 GetSteps() const { return nStepCount; }
+
+ boost::property_tree::ptree dumpAsJSON() const;
+ static BGradient fromJSON(std::u16string_view rJSON);
+
+ // Tooling to handle
+ // - border correction/integration
+ // - apply StartStopIntensity to color stops
+ // - convert type from 'axial' to linear
+ // - apply Steps as 'hard' color stops
+ void tryToRecreateBorder(basegfx::BColorStops* pAssociatedTransparencyStops = nullptr);
+ void tryToApplyBorder();
+ void tryToApplyStartEndIntensity();
+
+ // If a linear gradient is symmetrical it is converted to an axial gradient.
+ // Does nothing in other cases and for other gradient types.
+ void tryToConvertToAxial();
+ void tryToApplyAxial();
+ void tryToApplySteps();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */