summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx320
1 files changed, 320 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
new file mode 100644
index 0000000000..f39daccc43
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
@@ -0,0 +1,320 @@
+/* -*- 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/fillgradientprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <utility>
+#include <algorithm>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ // Get the OuterColor. Take into account that for css::awt::GradientStyle_AXIAL
+ // this is the last one due to inverted gradient usage (see constructor there)
+ basegfx::BColor FillGradientPrimitive2D::getOuterColor() const
+ {
+ if (getFillGradient().getColorStops().empty())
+ return basegfx::BColor();
+
+ if (css::awt::GradientStyle_AXIAL == getFillGradient().getStyle())
+ return getFillGradient().getColorStops().back().getStopColor();
+
+ return getFillGradient().getColorStops().front().getStopColor();
+ }
+
+ // Get the needed UnitPolygon dependent on the GradientStyle
+ basegfx::B2DPolygon FillGradientPrimitive2D::getUnitPolygon() const
+ {
+ if (css::awt::GradientStyle_RADIAL == getFillGradient().getStyle()
+ || css::awt::GradientStyle_ELLIPTICAL == getFillGradient().getStyle())
+ {
+ return basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
+ }
+
+ return basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
+ }
+
+ void FillGradientPrimitive2D::generateMatricesAndColors(
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) const
+ {
+ switch(getFillGradient().getStyle())
+ {
+ default: // GradientStyle_MAKE_FIXED_SIZE
+ case css::awt::GradientStyle_LINEAR:
+ {
+ texture::GeoTexSvxGradientLinear aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL:
+ {
+ texture::GeoTexSvxGradientAxial aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL:
+ {
+ texture::GeoTexSvxGradientRadial aGradient(
+ getDefinitionRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL:
+ {
+ texture::GeoTexSvxGradientElliptical aGradient(
+ getDefinitionRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ case css::awt::GradientStyle_SQUARE:
+ {
+ texture::GeoTexSvxGradientSquare aGradient(
+ getDefinitionRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ case css::awt::GradientStyle_RECT:
+ {
+ texture::GeoTexSvxGradientRect aGradient(
+ getDefinitionRange(),
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(aCallback);
+ break;
+ }
+ }
+ }
+
+ void FillGradientPrimitive2D::createFill(Primitive2DContainer& rContainer, bool bOverlapping) const
+ {
+ if (bOverlapping)
+ {
+ // OverlappingFill: create solid fill with outmost color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getOutputRange())),
+ getOuterColor()));
+
+ // create solid fill steps by providing callback as lambda
+ auto aCallback([&rContainer,this](
+ const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor)
+ {
+ // create part polygon
+ basegfx::B2DPolygon aNewPoly(getUnitPolygon());
+ aNewPoly.transform(rMatrix);
+
+ // create solid fill
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNewPoly),
+ rColor));
+ });
+
+ // call value generator to trigger callbacks
+ generateMatricesAndColors(aCallback);
+ }
+ else
+ {
+ // NonOverlappingFill
+ if (getFillGradient().getColorStops().size() < 2)
+ {
+ // not really a gradient, we need to create a start primitive
+ // entry using the single color and the covered area
+ const basegfx::B2DRange aOutmostRange(getOutputRange());
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aOutmostRange)),
+ getOuterColor()));
+ }
+ else
+ {
+ // gradient with stops, prepare CombinedPolyPoly, use callback
+ basegfx::B2DPolyPolygon aCombinedPolyPoly;
+ basegfx::BColor aLastColor;
+
+ auto aCallback([&rContainer,&aCombinedPolyPoly,&aLastColor,this](
+ const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor)
+ {
+ if (rContainer.empty())
+ {
+ // 1st callback, init CombinedPolyPoly & create 1st entry
+ basegfx::B2DRange aOutmostRange(getOutputRange());
+
+ // expand aOutmostRange with transformed first polygon
+ // to ensure confinement
+ basegfx::B2DPolygon aFirstPoly(getUnitPolygon());
+ aFirstPoly.transform(rMatrix);
+ aOutmostRange.expand(aFirstPoly.getB2DRange());
+
+ // build 1st combined polygon; outmost range 1st, then
+ // the shaped, transformed polygon
+ aCombinedPolyPoly.append(basegfx::utils::createPolygonFromRect(aOutmostRange));
+ aCombinedPolyPoly.append(aFirstPoly);
+
+ // create first primitive
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ getOuterColor()));
+
+ // save first polygon for re-use in next call, it's the second
+ // one, so remove 1st
+ aCombinedPolyPoly.remove(0);
+
+ // remember color for next primitive creation
+ aLastColor = rColor;
+ }
+ else
+ {
+ // regular n-th callback, create combined entry by re-using
+ // CombinedPolyPoly and aLastColor
+ basegfx::B2DPolygon aNextPoly(getUnitPolygon());
+ aNextPoly.transform(rMatrix);
+ aCombinedPolyPoly.append(aNextPoly);
+
+ // create primitive with correct color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ aLastColor));
+
+ // prepare re-use of inner polygon, save color
+ aCombinedPolyPoly.remove(0);
+ aLastColor = rColor;
+ }
+ });
+
+ // call value generator to trigger callbacks
+ generateMatricesAndColors(aCallback);
+
+ // add last inner polygon with last color
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ aLastColor));
+ }
+ }
+ }
+
+ void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // default creates overlapping fill which works with AntiAliasing and without.
+ // The non-overlapping version does not create single filled polygons, but
+ // PolyPolygons where each one describes a 'ring' for the gradient such
+ // that the rings will not overlap. This is useful for the old XOR-paint
+ // 'trick' of VCL which is recorded in Metafiles; so this version may be
+ // used from the MetafilePrimitive2D in its decomposition.
+
+ if(!getFillGradient().isDefault())
+ {
+ createFill(rContainer, /*bOverlapping*/true);
+ }
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ attribute::FillGradientAttribute aFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rOutputRange),
+ maFillGradient(std::move(aFillGradient))
+ {
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::B2DRange& rDefinitionRange,
+ attribute::FillGradientAttribute aFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rDefinitionRange),
+ maFillGradient(std::move(aFillGradient))
+ {
+ }
+
+ bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive);
+
+ return (getOutputRange() == rCompare.getOutputRange()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGradient() == rCompare.getFillGradient());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return the geometrically visible area
+ return getOutputRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 FillGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */