1 /* This file is part of the KDE project
2  * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "KoGradientBackground.h"
21 #include "KoFlake.h"
22 #include <KoStyleStack.h>
23 #include <KoXmlNS.h>
24 #include <KoOdfLoadingContext.h>
25 #include <KoOdfGraphicStyles.h>
26 #include <KoShapeSavingContext.h>
27 
28 #include <FlakeDebug.h>
29 
30 #include <QSharedPointer>
31 #include <QBrush>
32 #include <QPainter>
33 #include <QSharedData>
34 #include <QPainterPath>
35 
36 class KoGradientBackground::Private : public QSharedData
37 {
38 public:
Private()39     Private()
40         : QSharedData()
41         , gradient(0)
42     {}
43 
44     QGradient *gradient;
45     QTransform matrix;
46 };
47 
KoGradientBackground(QGradient * gradient,const QTransform & matrix)48 KoGradientBackground::KoGradientBackground(QGradient * gradient, const QTransform &matrix)
49     : KoShapeBackground()
50     , d(new Private)
51 {
52     d->gradient = gradient;
53     d->matrix = matrix;
54     Q_ASSERT(d->gradient);
55 }
56 
KoGradientBackground(const QGradient & gradient,const QTransform & matrix)57 KoGradientBackground::KoGradientBackground(const QGradient & gradient, const QTransform &matrix)
58     : KoShapeBackground()
59     , d(new Private)
60 {
61     d->gradient = KoFlake::cloneGradient(&gradient);
62     d->matrix = matrix;
63     Q_ASSERT(d->gradient);
64 }
65 
~KoGradientBackground()66 KoGradientBackground::~KoGradientBackground()
67 {
68     delete d->gradient;
69 }
70 
compareTo(const KoShapeBackground * other) const71 bool KoGradientBackground::compareTo(const KoShapeBackground *other) const
72 {
73     const KoGradientBackground *otherGradient = dynamic_cast<const KoGradientBackground*>(other);
74 
75     return otherGradient &&
76         d->matrix == otherGradient->d->matrix &&
77         *d->gradient == *otherGradient->d->gradient;
78 }
79 
setTransform(const QTransform & matrix)80 void KoGradientBackground::setTransform(const QTransform &matrix)
81 {
82     d->matrix = matrix;
83 }
84 
transform() const85 QTransform KoGradientBackground::transform() const
86 {
87     return d->matrix;
88 }
89 
setGradient(const QGradient & gradient)90 void KoGradientBackground::setGradient(const QGradient &gradient)
91 {
92     delete d->gradient;
93 
94     d->gradient = KoFlake::cloneGradient(&gradient);
95     Q_ASSERT(d->gradient);
96 }
97 
gradient() const98 const QGradient * KoGradientBackground::gradient() const
99 {
100     return d->gradient;
101 }
102 
paint(QPainter & painter,KoShapePaintingContext &,const QPainterPath & fillPath) const103 void KoGradientBackground::paint(QPainter &painter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const
104 {
105     if (!d->gradient) return;
106 
107     if (d->gradient->coordinateMode() == QGradient::ObjectBoundingMode) {
108 
109         /**
110          * NOTE: important hack!
111          *
112          * Qt has different notation of QBrush::setTransform() in comparison
113          * to what SVG defines. SVG defines gradientToUser matrix to be postmultiplied
114          * by QBrush::transform(), but Qt does exactly reverse!
115          *
116          * That most probably has beed caused by the fact that Qt uses transposed
117          * matrices and someone just mistyped the stuff long ago :(
118          *
119          * So here we basically emulate this feature by converting the gradient into
120          * QGradient::LogicalMode and doing transformations manually.
121          */
122 
123         const QRectF boundingRect = fillPath.boundingRect();
124         QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
125                                   boundingRect.x(), boundingRect.y());
126 
127         // TODO: how about slicing the object?
128         QGradient g = *d->gradient;
129         g.setCoordinateMode(QGradient::LogicalMode);
130 
131         QBrush b(g);
132         b.setTransform(d->matrix * gradientToUser);
133         painter.setBrush(b);
134     } else {
135         QBrush b(*d->gradient);
136         b.setTransform(d->matrix);
137         painter.setBrush(b);
138     }
139 
140     painter.drawPath(fillPath);
141 }
142 
fillStyle(KoGenStyle & style,KoShapeSavingContext & context)143 void KoGradientBackground::fillStyle(KoGenStyle &style, KoShapeSavingContext &context)
144 {
145     if (!d->gradient) return;
146     QBrush brush(*d->gradient);
147     brush.setTransform(d->matrix);
148     KoOdfGraphicStyles::saveOdfFillStyle(style, context.mainStyles(), brush);
149 }
150 
loadStyle(KoOdfLoadingContext & context,const QSizeF & shapeSize)151 bool KoGradientBackground::loadStyle(KoOdfLoadingContext &context, const QSizeF &shapeSize)
152 {
153     KoStyleStack &styleStack = context.styleStack();
154     if (! styleStack.hasProperty(KoXmlNS::draw, "fill"))
155         return false;
156 
157     QString fillStyle = styleStack.property(KoXmlNS::draw, "fill");
158     if (fillStyle == "gradient") {
159         QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyle(styleStack, context.stylesReader(), shapeSize);
160         const QGradient * gradient = brush.gradient();
161         if (gradient) {
162             d->gradient = KoFlake::cloneGradient(gradient);
163             d->matrix = brush.transform();
164 
165             //Gopalakrishna Bhat: If the brush has transparency then we ignore the draw:opacity property and use the brush transparency.
166             // Brush will have transparency if the svg:linearGradient stop point has stop-opacity property otherwise it is opaque
167             if (brush.isOpaque() && styleStack.hasProperty(KoXmlNS::draw, "opacity")) {
168                 QString opacityPercent = styleStack.property(KoXmlNS::draw, "opacity");
169                 if (! opacityPercent.isEmpty() && opacityPercent.right(1) == "%") {
170                     float opacity = qMin(opacityPercent.left(opacityPercent.length() - 1).toDouble(), 100.0) / 100;
171                     QGradientStops stops;
172                     Q_FOREACH (QGradientStop stop, d->gradient->stops()) {
173                         stop.second.setAlphaF(opacity);
174                         stops << stop;
175                     }
176                     d->gradient->setStops(stops);
177                 }
178             }
179 
180             return true;
181         }
182     }
183     return false;
184 }
185