Hello, In recent days I'm trying to fix the shape group shadow issues but I think I have some misunderstandings and made things even worse. Hope I can get some help.
My design: get the shadow rectangle by shadowRect = shape->boundingRect(); this will include the insets of shadow offsets and shadow blur caused shadow expansion. I think after that I'll get a rectangle with shape's origin inside it. create a buffer image with shadow rectangle's size in view zoomlevel. QRectF zoomedClipRegion = converter.documentToView(shadowRect); QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied); apply a transform to th qpainter which paints the shadow onto the image. this part confuse me very much, currently i did: //offset transformation on the buffer image in pixel, shape's own coordinate QTransform tm; tm.translate(d->offset.x(), d->offset.y()); QTransform tr = shape->absoluteTransformation(&converter); QTransform offsetMatrix = tr * tm * tr.inverted(); //set the absolute transformation to shape so it can draw in it's own coordinate system in pixel KoShape::applyConversion(painter, converter); // apply the offset transformation so it draws on the correct position on buffer image painter.setTransform(offsetMatrix * painter.transform()); //process the image and paste the image to the canvas QPointF clippingOffset = zoomedClipRegion.topLeft(); painter.drawImage(clippingOffset, sourceGraphic); But the result goes very starnge, the shadow rectangle changes a lot when shape is moving, and the shadow position is incorrect. I must have get something wrong. Anyone knows what's wrong with the implementation? in the attachment shape group part are commented since I haven't figure out the single shape part. Best Regards, Yue Liu
/* This file is part of the KDE project * Copyright (C) 2008-2009 Jan Hambrecht <ja...@gmx.net> * Copyright (C) 2010 Thomas Zander <zan...@kde.org> * Copyright (C) 2010 Ariya Hidayat <ariya.hida...@gmail.com> * Copyright (C) 2010 Yue Liu <opusp...@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoShapeShadow.h" #include "KoShapeGroup.h" #include "KoShapeSavingContext.h" #include "KoShapeBorderModel.h" #include "KoShape.h" #include "KoInsets.h" #include "KoPathShape.h" #include <KoGenStyle.h> #include <KoViewConverter.h> #include <kdebug.h> #include <QtGui/QPainter> #include <QtCore/QAtomicInt> #include <QImage> class KoShapeShadow::Private { public: Private() : offset(2, 2), color(Qt::black), blur(8), visible(true), refCount(0) { } QPointF offset; QColor color; qreal blur; bool visible; QAtomicInt refCount; }; KoShapeShadow::KoShapeShadow() : d(new Private()) { } KoShapeShadow::~KoShapeShadow() { delete d; } void KoShapeShadow::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) { Q_UNUSED(context); style.addProperty("draw:shadow", d->visible ? "visible" : "hidden"); style.addProperty("draw:shadow-color", d->color.name()); if (d->color.alphaF() != 1.0) style.addProperty("draw:shadow-opacity", QString("%1%").arg(d->color.alphaF() * 100.0)); style.addProperty("draw:shadow-offset-x", QString("%1pt").arg(d->offset.x())); style.addProperty("draw:shadow-offset-y", QString("%1pt").arg(d->offset.y())); if (d->blur != 0) style.addProperty("calligra:shadow-blur-radius", QString("%1").arg(d->blur)); } void KoShapeShadow::paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter, QTransform &offsetMatrix) { QList<KoShape*> shapes = group->shapes(); foreach(KoShape *child, shapes) { // we paint recursively here, so we do not have to check recursively for visibility if (!child->isVisible()) continue; KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(child); if (childGroup) { paintGroup(childGroup, painter, converter, offsetMatrix); } else { painter.save(); paintShape(child, painter, converter, offsetMatrix); painter.restore(); } } } //offsetMatrix left void KoShapeShadow::paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, QTransform &offsetMatrix) { if (shape->background()) { painter.save(); KoShape::applyConversion(painter, converter); // the shadow direction is independent of the shapes transformation // please only change if you know what you are doing painter.setTransform(offsetMatrix * painter.transform()); painter.setBrush(QBrush(d->color)); QPainterPath path(shape->outline()); KoPathShape * pathShape = dynamic_cast<KoPathShape*>(shape); if (pathShape) path.setFillRule(pathShape->fillRule()); painter.drawPath(path); painter.restore(); } if (shape->border()) { painter.save(); QTransform oldPainterMatrix = painter.transform(); KoShape::applyConversion(painter, converter); QTransform newPainterMatrix = painter.transform(); // the shadow direction is independent of the shapes transformation // please only change if you know what you are doing painter.setTransform(offsetMatrix * painter.transform()); // compensate applyConversion call in paint QTransform scaleMatrix = newPainterMatrix * oldPainterMatrix.inverted(); painter.setTransform(scaleMatrix.inverted() * painter.transform()); shape->border()->paint(shape, painter, converter); painter.restore(); } } void KoShapeShadow::paint(KoShape *shape, QPainter &painter, const KoViewConverter &converter) { if (! d->visible) return; // calculate the shadow offset independent of shape transformation QTransform tm; tm.translate(d->offset.x(), d->offset.y()); QTransform tr = shape->absoluteTransformation(&converter); QTransform offsetMatrix = tr * tm * tr.inverted(); //The shadowRect includes the region that both the shape, shadow, and blur expansion needed QRectF shadowRect(0, 0, 0, 0); /*KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape); if (group) { foreach(KoShape *child, group->shapes()) { shadowRect.united(child->boundingRect()); } } else {*/ shadowRect = shape->boundingRect(); //} //convert relative radius to absolute radius qreal absRadius = converter.documentToViewX(d->blur); QRectF zoomedClipRegion = converter.documentToView(shadowRect); QPointF clippingOffset = zoomedClipRegion.topLeft(); // Init the buffer image QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied); sourceGraphic.fill(qRgba(0,0,0,0)); // Init the buffer painter QPainter bufferPainter(&sourceGraphic); //bufferPainter.translate(-1.0f*clippingOffset); bufferPainter.setPen(Qt::NoPen); bufferPainter.setBrush(Qt::NoBrush); bufferPainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing)); /* if (group) paintGroup(group, bufferPainter, converter, offsetMatrix); else*/ paintShape(shape, bufferPainter, converter, offsetMatrix); bufferPainter.end(); blurShadow(sourceGraphic, absRadius, d->color); // Paint the result painter.save(); painter.drawImage(clippingOffset, sourceGraphic); painter.restore(); } void KoShapeShadow::paintBuffer(QPointF &clippingOffset, QImage image, QPainter &painter, const KoViewConverter &converter) { } void KoShapeShadow::setOffset(const QPointF & offset) { d->offset = offset; } QPointF KoShapeShadow::offset() const { return d->offset; } void KoShapeShadow::setColor(const QColor &color) { d->color = color; } QColor KoShapeShadow::color() const { return d->color; } void KoShapeShadow::setBlur(const qreal &blur) { d->blur = blur; } qreal KoShapeShadow::blur() const { return d->blur; } void KoShapeShadow::setVisible(bool visible) { d->visible = visible; } bool KoShapeShadow::isVisible() const { return d->visible; } void KoShapeShadow::insets(KoInsets &insets) const { if (!d->visible) { insets.top = 0; insets.bottom = 0; insets.left = 0; insets.right = 0; return; } insets.left = (d->offset.x() - d->blur*3 < 0.0) ? qAbs(d->offset.x() - d->blur*3) : 0.0; insets.top = (d->offset.y() - d->blur*3 < 0.0) ? qAbs(d->offset.y() - d->blur*3) : 0.0; insets.right = (d->offset.x() + d->blur*3 > 0.0) ? d->offset.x() + d->blur*3 : 0.0; insets.bottom = (d->offset.y() + d->blur*3 > 0.0) ? d->offset.y() + d->blur*3 : 0.0; } bool KoShapeShadow::ref() { return d->refCount.ref(); } bool KoShapeShadow::deref() { return d->refCount.deref(); } int KoShapeShadow::useCount() const { return d->refCount; } /* You can also find a BSD version to this method from * http://gitorious.org/ofi-labs/x2/blobs/master/graphics/shadowblur/ */ void KoShapeShadow::blurShadow(QImage &image, int radius, const QColor& shadowColor) { static const int BlurSumShift = 15; // Check http://www.w3.org/TR/SVG/filters.html# // As noted in the SVG filter specification, ru // approximates a real gaussian blur nicely. // See comments in http://webkit.org/b/40793, it seems sensible // to follow Skia's limit of 128 pixels for the blur radius. if (radius > 128) radius = 128; int channels[4] = { 3, 0, 1, 3 }; int dmax = radius >> 1; int dmin = dmax - 1 + (radius & 1); if (dmin < 0) dmin = 0; // Two stages: horizontal and vertical for (int k = 0; k < 2; ++k) { unsigned char* pixels = image.bits(); int stride = (k == 0) ? 4 : image.bytesPerLine(); int delta = (k == 0) ? image.bytesPerLine() : 4; int jfinal = (k == 0) ? image.height() : image.width(); int dim = (k == 0) ? image.width() : image.height(); for (int j = 0; j < jfinal; ++j, pixels += delta) { // For each step, we blur the alpha in a channel and store the result // in another channel for the subsequent step. // We use sliding window algorithm to accumulate the alpha values. // This is much more efficient than computing the sum of each pixels // covered by the box kernel size for each x. for (int step = 0; step < 3; ++step) { int side1 = (step == 0) ? dmin : dmax; int side2 = (step == 1) ? dmin : dmax; int pixelCount = side1 + 1 + side2; int invCount = ((1 << BlurSumShift) + pixelCount - 1) / pixelCount; int ofs = 1 + side2; int alpha1 = pixels[channels[step]]; int alpha2 = pixels[(dim - 1) * stride + channels[step]]; unsigned char* ptr = pixels + channels[step + 1]; unsigned char* prev = pixels + stride + channels[step]; unsigned char* next = pixels + ofs * stride + channels[step]; int i; int sum = side1 * alpha1 + alpha1; int limit = (dim < side2 + 1) ? dim : side2 + 1; for (i = 1; i < limit; ++i, prev += stride) sum += *prev; if (limit <= side2) sum += (side2 - limit + 1) * alpha2; limit = (side1 < dim) ? side1 : dim; for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { *ptr = (sum * invCount) >> BlurSumShift; sum += ((ofs < dim) ? *next : alpha2) - alpha1; } prev = pixels + channels[step]; for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { *ptr = (sum * invCount) >> BlurSumShift; sum += (*next) - (*prev); } for (; i < dim; ptr += stride, prev += stride, ++i) { *ptr = (sum * invCount) >> BlurSumShift; sum += alpha2 - (*prev); } } } } // "Colorize" with the right shadow color. QPainter p(&image); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(image.rect(), shadowColor); p.end(); }
_______________________________________________ calligra-devel mailing list calligra-devel@kde.org https://mail.kde.org/mailman/listinfo/calligra-devel