Warning, file /office/calligra/libs/flake/KoShapeShadow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net> 0003 * Copyright (C) 2010 Thomas Zander <zander@kde.org> 0004 * Copyright (C) 2010 Ariya Hidayat <ariya.hidayat@gmail.com> 0005 * Copyright (C) 2010-2011 Yue Liu <yue.liu@mail.com> 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Library General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Library General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Library General Public License 0018 * along with this library; see the file COPYING.LIB. If not, write to 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "KoShapeShadow.h" 0024 #include "KoShapeGroup.h" 0025 #include "KoSelection.h" 0026 #include "KoShapeSavingContext.h" 0027 #include "KoShapeStrokeModel.h" 0028 #include "KoShape.h" 0029 #include "KoInsets.h" 0030 #include "KoPathShape.h" 0031 #include <KoGenStyle.h> 0032 #include <KoViewConverter.h> 0033 #include <FlakeDebug.h> 0034 #include <QPainter> 0035 #include <QPainterPath> 0036 #include <QAtomicInt> 0037 #include <QImage> 0038 #include <QRectF> 0039 0040 class Q_DECL_HIDDEN KoShapeShadow::Private 0041 { 0042 public: 0043 Private() 0044 : offset(2, 2), color(Qt::black), blur(8), visible(true), refCount(0) { 0045 } 0046 QPointF offset; 0047 QColor color; 0048 qreal blur; 0049 bool visible; 0050 QAtomicInt refCount; 0051 0052 /** 0053 * Paints the shadow of the shape group to the buffer image. 0054 * @param group the shape group to paint around 0055 * @param painter the painter to paint on the image 0056 * @param converter to convert between internal and view coordinates. 0057 */ 0058 void paintGroupShadow(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter); 0059 /** 0060 * Paints the shadow of the shape to the buffer image. 0061 * @param shape the shape to paint around 0062 * @param painter the painter to paint on the image 0063 * @param converter to convert between internal and view coordinates. 0064 */ 0065 void paintShadow(KoShape *shape, QPainter &painter, const KoViewConverter &converter); 0066 void blurShadow(QImage &image, int radius, const QColor& shadowColor); 0067 }; 0068 0069 void KoShapeShadow::Private::paintGroupShadow(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter) 0070 { 0071 QList<KoShape*> shapes = group->shapes(); 0072 foreach(KoShape *child, shapes) { 0073 // we paint recursively here, so we do not have to check recursively for visibility 0074 if (!child->isVisible()) 0075 continue; 0076 painter.save(); 0077 //apply group child's transformation 0078 painter.setTransform(child->absoluteTransformation(&converter), true); 0079 paintShadow(child, painter, converter); 0080 painter.restore(); 0081 } 0082 } 0083 0084 void KoShapeShadow::Private::paintShadow(KoShape *shape, QPainter &painter, const KoViewConverter &converter) 0085 { 0086 QPainterPath path(shape->shadowOutline()); 0087 if (!path.isEmpty()) { 0088 painter.save(); 0089 KoShape::applyConversion(painter, converter); 0090 painter.setBrush(QBrush(color)); 0091 0092 // Make sure the shadow has the same fill rule as the shape. 0093 KoPathShape * pathShape = dynamic_cast<KoPathShape*>(shape); 0094 if (pathShape) 0095 path.setFillRule(pathShape->fillRule()); 0096 0097 painter.drawPath(path); 0098 painter.restore(); 0099 } 0100 0101 if (shape->stroke()) { 0102 shape->stroke()->paint(shape, painter, converter); 0103 } 0104 } 0105 0106 /* You can also find a BSD version to this method from 0107 * http://gitorious.org/ofi-labs/x2/blobs/master/graphics/shadowblur/ 0108 */ 0109 void KoShapeShadow::Private::blurShadow(QImage &image, int radius, const QColor& shadowColor) 0110 { 0111 static const int BlurSumShift = 15; 0112 0113 // Check http://www.w3.org/TR/SVG/filters.html# 0114 // As noted in the SVG filter specification, ru 0115 // approximates a real gaussian blur nicely. 0116 // See comments in http://webkit.org/b/40793, it seems sensible 0117 // to follow Skia's limit of 128 pixels for the blur radius. 0118 if (radius > 128) 0119 radius = 128; 0120 0121 int channels[4] = { 3, 0, 1, 3 }; 0122 int dmax = radius >> 1; 0123 int dmin = dmax - 1 + (radius & 1); 0124 if (dmin < 0) 0125 dmin = 0; 0126 0127 // Two stages: horizontal and vertical 0128 for (int k = 0; k < 2; ++k) { 0129 0130 unsigned char* pixels = image.bits(); 0131 int stride = (k == 0) ? 4 : image.bytesPerLine(); 0132 int delta = (k == 0) ? image.bytesPerLine() : 4; 0133 int jfinal = (k == 0) ? image.height() : image.width(); 0134 int dim = (k == 0) ? image.width() : image.height(); 0135 0136 for (int j = 0; j < jfinal; ++j, pixels += delta) { 0137 0138 // For each step, we blur the alpha in a channel and store the result 0139 // in another channel for the subsequent step. 0140 // We use sliding window algorithm to accumulate the alpha values. 0141 // This is much more efficient than computing the sum of each pixels 0142 // covered by the box kernel size for each x. 0143 0144 for (int step = 0; step < 3; ++step) { 0145 int side1 = (step == 0) ? dmin : dmax; 0146 int side2 = (step == 1) ? dmin : dmax; 0147 int pixelCount = side1 + 1 + side2; 0148 int invCount = ((1 << BlurSumShift) + pixelCount - 1) / pixelCount; 0149 int ofs = 1 + side2; 0150 int alpha1 = pixels[channels[step]]; 0151 int alpha2 = pixels[(dim - 1) * stride + channels[step]]; 0152 unsigned char* ptr = pixels + channels[step + 1]; 0153 unsigned char* prev = pixels + stride + channels[step]; 0154 unsigned char* next = pixels + ofs * stride + channels[step]; 0155 0156 int i; 0157 int sum = side1 * alpha1 + alpha1; 0158 int limit = (dim < side2 + 1) ? dim : side2 + 1; 0159 for (i = 1; i < limit; ++i, prev += stride) 0160 sum += *prev; 0161 if (limit <= side2) 0162 sum += (side2 - limit + 1) * alpha2; 0163 0164 limit = (side1 < dim) ? side1 : dim; 0165 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { 0166 *ptr = (sum * invCount) >> BlurSumShift; 0167 sum += ((ofs < dim) ? *next : alpha2) - alpha1; 0168 } 0169 prev = pixels + channels[step]; 0170 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { 0171 *ptr = (sum * invCount) >> BlurSumShift; 0172 sum += (*next) - (*prev); 0173 } 0174 for (; i < dim; ptr += stride, prev += stride, ++i) { 0175 *ptr = (sum * invCount) >> BlurSumShift; 0176 sum += alpha2 - (*prev); 0177 } 0178 } 0179 } 0180 } 0181 0182 // "Colorize" with the right shadow color. 0183 QPainter p(&image); 0184 p.setCompositionMode(QPainter::CompositionMode_SourceIn); 0185 p.fillRect(image.rect(), shadowColor); 0186 p.end(); 0187 } 0188 0189 0190 // ---------------------------------------------------------------- 0191 // KoShapeShadow 0192 0193 0194 KoShapeShadow::KoShapeShadow() 0195 : d(new Private()) 0196 { 0197 } 0198 0199 KoShapeShadow::~KoShapeShadow() 0200 { 0201 delete d; 0202 } 0203 0204 KoShapeShadow::KoShapeShadow(const KoShapeShadow &rhs) 0205 : d(new Private(*rhs.d)) 0206 { 0207 d->refCount = 0; 0208 } 0209 0210 KoShapeShadow& KoShapeShadow::operator=(const KoShapeShadow &rhs) 0211 { 0212 *d = *rhs.d; 0213 d->refCount = 0; 0214 return *this; 0215 } 0216 0217 void KoShapeShadow::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) 0218 { 0219 Q_UNUSED(context); 0220 0221 style.addProperty("draw:shadow", d->visible ? "visible" : "hidden", KoGenStyle::GraphicType); 0222 style.addProperty("draw:shadow-color", d->color.name(), KoGenStyle::GraphicType); 0223 if (d->color.alphaF() != 1.0) 0224 style.addProperty("draw:shadow-opacity", QString("%1%").arg(d->color.alphaF() * 100.0), KoGenStyle::GraphicType); 0225 style.addProperty("draw:shadow-offset-x", QString("%1pt").arg(d->offset.x()), KoGenStyle::GraphicType); 0226 style.addProperty("draw:shadow-offset-y", QString("%1pt").arg(d->offset.y()), KoGenStyle::GraphicType); 0227 if (d->blur != 0) 0228 style.addProperty("calligra:shadow-blur-radius", QString("%1pt").arg(d->blur), KoGenStyle::GraphicType); 0229 } 0230 0231 void KoShapeShadow::paint(KoShape *shape, QPainter &painter, const KoViewConverter &converter) 0232 { 0233 if (! d->visible) 0234 return; 0235 0236 // So the approach we are taking here is to draw into a buffer image the size of boundingRect 0237 // We offset by the shadow offset at the time we draw into the buffer 0238 // Then we filter the image and draw it at the position of the bounding rect on canvas 0239 0240 //the boundingRect of the shape or the KoSelection boundingRect of the group 0241 QRectF shadowRect = shape->boundingRect(); 0242 QRectF zoomedClipRegion = converter.documentToView(shadowRect); 0243 0244 // Init the buffer image 0245 QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied); 0246 sourceGraphic.fill(qRgba(0,0,0,0)); 0247 // Init the buffer painter 0248 QPainter imagePainter(&sourceGraphic); 0249 imagePainter.setPen(Qt::NoPen); 0250 imagePainter.setBrush(Qt::NoBrush); 0251 imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing)); 0252 // Since our imagebuffer and the canvas don't align we need to offset our drawings 0253 imagePainter.translate(-1.0f*converter.documentToView(shadowRect.topLeft())); 0254 0255 // Handle the shadow offset 0256 imagePainter.translate(converter.documentToView(offset())); 0257 0258 KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape); 0259 if (group) { 0260 d->paintGroupShadow(group, imagePainter, converter); 0261 } else { 0262 //apply shape's transformation 0263 imagePainter.setTransform(shape->absoluteTransformation(&converter), true); 0264 0265 d->paintShadow(shape, imagePainter, converter); 0266 } 0267 imagePainter.end(); 0268 0269 // Blur the shadow (well the entire buffer) 0270 d->blurShadow(sourceGraphic, converter.documentToViewX(d->blur), d->color); 0271 0272 // Paint the result 0273 painter.save(); 0274 // The painter is initialized for us with canvas transform 'plus' shape transform 0275 // we are only interested in the canvas transform so 'subtract' the shape transform part 0276 painter.setTransform(shape->absoluteTransformation(&converter).inverted() * painter.transform()); 0277 painter.drawImage(zoomedClipRegion.topLeft(), sourceGraphic); 0278 painter.restore(); 0279 } 0280 0281 void KoShapeShadow::setOffset(const QPointF & offset) 0282 { 0283 d->offset = offset; 0284 } 0285 0286 QPointF KoShapeShadow::offset() const 0287 { 0288 return d->offset; 0289 } 0290 0291 void KoShapeShadow::setColor(const QColor &color) 0292 { 0293 d->color = color; 0294 } 0295 0296 QColor KoShapeShadow::color() const 0297 { 0298 return d->color; 0299 } 0300 0301 void KoShapeShadow::setBlur(qreal blur) 0302 { 0303 // force positive blur radius 0304 d->blur = qAbs(blur); 0305 } 0306 0307 qreal KoShapeShadow::blur() const 0308 { 0309 return d->blur; 0310 } 0311 0312 void KoShapeShadow::setVisible(bool visible) 0313 { 0314 d->visible = visible; 0315 } 0316 0317 bool KoShapeShadow::isVisible() const 0318 { 0319 return d->visible; 0320 } 0321 0322 void KoShapeShadow::insets(KoInsets &insets) const 0323 { 0324 if (!d->visible) { 0325 insets.top = 0; 0326 insets.bottom = 0; 0327 insets.left = 0; 0328 insets.right = 0; 0329 return; 0330 } 0331 0332 qreal expand = d->blur; 0333 0334 insets.left = (d->offset.x() < 0.0) ? qAbs(d->offset.x()) : 0.0; 0335 insets.top = (d->offset.y() < 0.0) ? qAbs(d->offset.y()) : 0.0; 0336 insets.right = (d->offset.x() > 0.0) ? d->offset.x() : 0.0; 0337 insets.bottom = (d->offset.y() > 0.0) ? d->offset.y() : 0.0; 0338 0339 insets.left += expand; 0340 insets.top += expand; 0341 insets.right += expand; 0342 insets.bottom += expand; 0343 } 0344 0345 bool KoShapeShadow::ref() 0346 { 0347 return d->refCount.ref(); 0348 } 0349 0350 bool KoShapeShadow::deref() 0351 { 0352 return d->refCount.deref(); 0353 } 0354 0355 int KoShapeShadow::useCount() const 0356 { 0357 return d->refCount; 0358 }