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