diff --git a/libs/flake/KoClipMask.cpp b/libs/flake/KoClipMask.cpp index 8a21648f32..7e2eb17bed 100644 --- a/libs/flake/KoClipMask.cpp +++ b/libs/flake/KoClipMask.cpp @@ -1,176 +1,174 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoClipMask.h" #include #include #include +#include #include #include "kis_algebra_2d.h" #include #include -struct Q_DECL_HIDDEN KoClipMask::Private { +struct Q_DECL_HIDDEN KoClipMask::Private : public QSharedData +{ Private() {} Private(const Private &rhs) : coordinates(rhs.coordinates), contentCoordinates(rhs.contentCoordinates), maskRect(rhs.maskRect), extraShapeTransform(rhs.extraShapeTransform) { + // XXX: Use KisDescendent instead of this Q_FOREACH (KoShape *shape, rhs.shapes) { KoShape *clonedShape = shape->cloneShape(); KIS_ASSERT_RECOVER(clonedShape) { continue; } shapes << clonedShape; } } ~Private() { qDeleteAll(shapes); shapes.clear(); } KoFlake::CoordinateSystem coordinates = KoFlake::ObjectBoundingBox; KoFlake::CoordinateSystem contentCoordinates = KoFlake::UserSpaceOnUse; QRectF maskRect = QRectF(-0.1, -0.1, 1.2, 1.2); QList shapes; QTransform extraShapeTransform; // TODO: not used anymore, use direct shape transform instead }; KoClipMask::KoClipMask() : m_d(new Private) { } KoClipMask::~KoClipMask() { } -KoClipMask::KoClipMask(const KoClipMask &rhs) - : m_d(new Private(*rhs.m_d)) -{ -} - KoClipMask *KoClipMask::clone() const { return new KoClipMask(*this); } KoFlake::CoordinateSystem KoClipMask::coordinates() const { return m_d->coordinates; } void KoClipMask::setCoordinates(KoFlake::CoordinateSystem value) { m_d->coordinates = value; } KoFlake::CoordinateSystem KoClipMask::contentCoordinates() const { return m_d->contentCoordinates; } void KoClipMask::setContentCoordinates(KoFlake::CoordinateSystem value) { m_d->contentCoordinates = value; } QRectF KoClipMask::maskRect() const { return m_d->maskRect; } void KoClipMask::setMaskRect(const QRectF &value) { m_d->maskRect = value; } QList KoClipMask::shapes() const { return m_d->shapes; } void KoClipMask::setShapes(const QList &value) { m_d->shapes = value; } bool KoClipMask::isEmpty() const { return m_d->shapes.isEmpty(); } void KoClipMask::setExtraShapeOffset(const QPointF &value) { /** * TODO: when we implement source shapes sharing, please wrap the shapes * into a group and apply this transform to the group instead */ if (m_d->contentCoordinates == KoFlake::UserSpaceOnUse) { const QTransform t = QTransform::fromTranslate(value.x(), value.y()); Q_FOREACH (KoShape *shape, m_d->shapes) { shape->applyAbsoluteTransformation(t); } } if (m_d->coordinates == KoFlake::UserSpaceOnUse) { m_d->maskRect.translate(value); } } void KoClipMask::drawMask(QPainter *painter, KoShape *shape) { painter->save(); QPainterPath clipPathInShapeSpace; if (m_d->coordinates == KoFlake::ObjectBoundingBox) { QTransform relativeToShape = KisAlgebra2D::mapToRect(shape->outlineRect()); clipPathInShapeSpace.addPolygon(relativeToShape.map(m_d->maskRect)); } else { clipPathInShapeSpace.addRect(m_d->maskRect); clipPathInShapeSpace = m_d->extraShapeTransform.map(clipPathInShapeSpace); } painter->setClipPath(clipPathInShapeSpace, Qt::IntersectClip); if (m_d->contentCoordinates == KoFlake::ObjectBoundingBox) { QTransform relativeToShape = KisAlgebra2D::mapToRect(shape->outlineRect()); painter->setTransform(relativeToShape, true); } else { painter->setTransform(m_d->extraShapeTransform, true); } KoViewConverter converter; KoShapePainter p; p.setShapes(m_d->shapes); p.paint(*painter, converter); painter->restore(); } diff --git a/libs/flake/KoClipMask.h b/libs/flake/KoClipMask.h index e66af53d35..e2e875d945 100644 --- a/libs/flake/KoClipMask.h +++ b/libs/flake/KoClipMask.h @@ -1,68 +1,66 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOCLIPMASK_H #define KOCLIPMASK_H #include "kritaflake_export.h" #include -#include #include +#include class KoShape; class QRectF; class QTransform; class QPointF; class QPainter; class KRITAFLAKE_EXPORT KoClipMask { public: KoClipMask(); ~KoClipMask(); KoClipMask *clone() const; KoFlake::CoordinateSystem coordinates() const; void setCoordinates(KoFlake::CoordinateSystem value); KoFlake::CoordinateSystem contentCoordinates() const; void setContentCoordinates(KoFlake::CoordinateSystem value); QRectF maskRect() const; void setMaskRect(const QRectF &value); QList shapes() const; void setShapes(const QList &value); bool isEmpty() const; void setExtraShapeOffset(const QPointF &value); void drawMask(QPainter *painter, KoShape *shape); private: - KoClipMask(const KoClipMask &rhs); - struct Private; - const QScopedPointer m_d; + QSharedDataPointer m_d; }; #endif // KOCLIPMASK_H diff --git a/libs/flake/KoClipPath.cpp b/libs/flake/KoClipPath.cpp index fdb7631c8d..2ce35345a9 100644 --- a/libs/flake/KoClipPath.cpp +++ b/libs/flake/KoClipPath.cpp @@ -1,240 +1,236 @@ /* This file is part of the KDE project * Copyright (C) 2011 Jan Hambrecht * * 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 "KoClipPath.h" #include "KoPathShape.h" #include "KoViewConverter.h" #include "KoShapeGroup.h" #include #include #include #include +#include #include QTransform scaleToPercent(const QSizeF &size) { const qreal w = qMax(static_cast(1e-5), size.width()); const qreal h = qMax(static_cast(1e-5), size.height()); return QTransform().scale(1.0/w, 1.0/h); } QTransform scaleFromPercent(const QSizeF &size) { const qreal w = qMax(static_cast(1e-5), size.width()); const qreal h = qMax(static_cast(1e-5), size.height()); return QTransform().scale(w/1.0, h/1.0); } -class Q_DECL_HIDDEN KoClipPath::Private +class Q_DECL_HIDDEN KoClipPath::Private : public QSharedData { public: Private() {} Private(const Private &rhs) : clipPath(rhs.clipPath), clipRule(rhs.clipRule), coordinates(rhs.coordinates), initialTransformToShape(rhs.initialTransformToShape), initialShapeSize(rhs.initialShapeSize) { Q_FOREACH (KoShape *shape, rhs.shapes) { KoShape *clonedShape = shape->cloneShape(); KIS_ASSERT_RECOVER(clonedShape) { continue; } shapes.append(clonedShape); } } ~Private() { qDeleteAll(shapes); shapes.clear(); } void collectShapePath(QPainterPath *result, const KoShape *shape) { if (const KoPathShape *pathShape = dynamic_cast(shape)) { // different shapes add up to the final path using Windind Fill rule (acc. to SVG 1.1) QTransform t = pathShape->absoluteTransformation(0); result->addPath(t.map(pathShape->outline())); } else if (const KoShapeGroup *groupShape = dynamic_cast(shape)) { QList shapes = groupShape->shapes(); std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); Q_FOREACH (const KoShape *child, shapes) { collectShapePath(result, child); } } } void compileClipPath() { QList clipShapes = this->shapes; if (clipShapes.isEmpty()) return; clipPath = QPainterPath(); clipPath.setFillRule(Qt::WindingFill); std::sort(clipShapes.begin(), clipShapes.end(), KoShape::compareShapeZIndex); Q_FOREACH (KoShape *path, clipShapes) { if (!path) continue; collectShapePath(&clipPath, path); } } QList shapes; QPainterPath clipPath; ///< the compiled clip path in shape coordinates of the clipped shape Qt::FillRule clipRule = Qt::WindingFill; KoFlake::CoordinateSystem coordinates = KoFlake::ObjectBoundingBox; QTransform initialTransformToShape; ///< initial transformation to shape coordinates of the clipped shape QSizeF initialShapeSize; ///< initial size of clipped shape }; KoClipPath::KoClipPath(QList clipShapes, KoFlake::CoordinateSystem coordinates) : d(new Private()) { d->shapes = clipShapes; d->coordinates = coordinates; d->compileClipPath(); } -KoClipPath::KoClipPath(const KoClipPath &rhs) - : d(new Private(*rhs.d)) -{ -} - KoClipPath::~KoClipPath() { } KoClipPath *KoClipPath::clone() const { return new KoClipPath(*this); } void KoClipPath::setClipRule(Qt::FillRule clipRule) { d->clipRule = clipRule; } Qt::FillRule KoClipPath::clipRule() const { return d->clipRule; } KoFlake::CoordinateSystem KoClipPath::coordinates() const { return d->coordinates; } void KoClipPath::applyClipping(KoShape *clippedShape, QPainter &painter, const KoViewConverter &converter) { QPainterPath clipPath; KoShape *shape = clippedShape; while (shape) { if (shape->clipPath()) { QPainterPath path = shape->clipPath()->path(); QTransform t; if (shape->clipPath()->coordinates() == KoFlake::ObjectBoundingBox) { const QRectF shapeLocalBoundingRect = shape->outline().boundingRect(); t = KisAlgebra2D::mapToRect(shapeLocalBoundingRect) * shape->absoluteTransformation(0); } else { t = shape->absoluteTransformation(0); } path = t.map(path); if (clipPath.isEmpty()) { clipPath = path; } else { clipPath &= path; } } shape = shape->parent(); } if (!clipPath.isEmpty()) { QTransform viewMatrix; qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); viewMatrix.scale(zoomX, zoomY); painter.setClipPath(viewMatrix.map(clipPath), Qt::IntersectClip); } } QPainterPath KoClipPath::path() const { return d->clipPath; } QPainterPath KoClipPath::pathForSize(const QSizeF &size) const { return scaleFromPercent(size).map(d->clipPath); } QList KoClipPath::clipPathShapes() const { // TODO: deprecate this method! QList shapes; Q_FOREACH (KoShape *shape, d->shapes) { KoPathShape *pathShape = dynamic_cast(shape); if (pathShape) { shapes << pathShape; } } return shapes; } QList KoClipPath::clipShapes() const { return d->shapes; } QTransform KoClipPath::clipDataTransformation(KoShape *clippedShape) const { if (!clippedShape) return d->initialTransformToShape; // the current transformation of the clipped shape QTransform currentShapeTransform = clippedShape->absoluteTransformation(0); // calculate the transformation which represents any resizing of the clipped shape const QSizeF currentShapeSize = clippedShape->outline().boundingRect().size(); const qreal sx = currentShapeSize.width() / d->initialShapeSize.width(); const qreal sy = currentShapeSize.height() / d->initialShapeSize.height(); QTransform scaleTransform = QTransform().scale(sx, sy); // 1. transform to initial clipped shape coordinates // 2. apply resizing transformation // 3. convert to current clipped shape document coordinates return d->initialTransformToShape * scaleTransform * currentShapeTransform; } diff --git a/libs/flake/KoClipPath.h b/libs/flake/KoClipPath.h index ffa7011dbd..8db2c15b43 100644 --- a/libs/flake/KoClipPath.h +++ b/libs/flake/KoClipPath.h @@ -1,93 +1,91 @@ /* This file is part of the KDE project * Copyright (C) 2011 Jan Hambrecht * * 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. */ #ifndef KOCLIPPATH_H #define KOCLIPPATH_H #include "kritaflake_export.h" #include #include +#include #include #include class KoShape; class KoPathShape; class KoViewConverter; class QPainter; class QTransform; class QPainterPath; class QSizeF; /// Clip path used to clip shapes class KRITAFLAKE_EXPORT KoClipPath { public: /** * Create a new shape clipping using the given clip data * @param clipShapes define the clipping shapes, owned by KoClipPath! * @param coordinates shows if ObjectBoundingBox or UserSpaceOnUse coordinate * system is used. */ KoClipPath(QList clipShapes, KoFlake::CoordinateSystem coordinates); ~KoClipPath(); KoClipPath *clone() const; KoFlake::CoordinateSystem coordinates() const; /// Sets the clip rule to be used for the clip path void setClipRule(Qt::FillRule clipRule); /// Returns the current clip rule Qt::FillRule clipRule() const; /// Returns the current clip path with coordinates in percent of the clipped shape size QPainterPath path() const; /// Returns the current clip path scaled to match the specified shape size QPainterPath pathForSize(const QSizeF &size) const; /// Returns the clip path shapes QList clipPathShapes() const; QList clipShapes() const; /** * Returns the transformation from the clip data path shapes to the * current document coordinates of the specified clipped shape. * If the specified clipped shape is null, the transformation * from clip data path shapes to shape coordinates of the clipped shape * at the time of creating this clip path is being returned. */ QTransform clipDataTransformation(KoShape *clippedShape) const; /// Applies the clipping to the given painter static void applyClipping(KoShape *clippedShape, QPainter &painter, const KoViewConverter &converter); -private: - KoClipPath(const KoClipPath &rhs); - private: class Private; - const QScopedPointer d; + QSharedDataPointer d; }; #endif // KOCLIPPATH_H diff --git a/libs/flake/KoImageData.cpp b/libs/flake/KoImageData.cpp index 006aa419fd..992110947e 100644 --- a/libs/flake/KoImageData.cpp +++ b/libs/flake/KoImageData.cpp @@ -1,379 +1,372 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009 Thomas Zander * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 C. Boemann * Copyright (C) 2008 Thorsten Zachmann * * 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 "KoImageData.h" #include "KoImageData_p.h" #include "KoImageCollection.h" #include #include #include #include #include #include #include #include /// the maximum amount of bytes the image can be while we store it in memory instead of /// spooling it to disk in a temp-file. #define MAX_MEMORY_IMAGESIZE 90000 KoImageData::KoImageData() - : d(0) { } KoImageData::KoImageData(const KoImageData &imageData) : KoShapeUserData(), - d(imageData.d) + d(imageData.d) { - if (d) - d->refCount.ref(); } KoImageData::KoImageData(KoImageDataPrivate *priv) : d(priv) { - d->refCount.ref(); } KoImageData::~KoImageData() { - if (d && !d->refCount.deref()) - delete d; } QPixmap KoImageData::pixmap(const QSize &size) { if (!d) return QPixmap(); QSize wantedSize = size; if (! wantedSize.isValid()) { if (d->pixmap.isNull()) // we have a problem, Houston.. wantedSize = QSize(100, 100); else wantedSize = d->pixmap.size(); } if (d->pixmap.isNull() || d->pixmap.size() != wantedSize) { switch (d->dataStoreState) { case KoImageDataPrivate::StateEmpty: { #if 0 // this is not possible as it gets called during the paint method // and will crash. Therefore create a tmp pixmap and return it. d->pixmap = QPixmap(1, 1); QPainter p(&d->pixmap); p.setPen(QPen(Qt::gray, 0)); p.drawPoint(0, 0); p.end(); break; #endif QPixmap tmp(1, 1); tmp.fill(Qt::gray); return tmp; } case KoImageDataPrivate::StateNotLoaded: image(); // forces load Q_FALLTHROUGH(); case KoImageDataPrivate::StateImageLoaded: case KoImageDataPrivate::StateImageOnly: if (!d->image.isNull()) { // create pixmap from image. // this is the highest quality and lowest memory usage way of doing the conversion. d->pixmap = QPixmap::fromImage(d->image.scaled(wantedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } } if (d->dataStoreState == KoImageDataPrivate::StateImageLoaded) { - if (d->cleanCacheTimer.isActive()) - d->cleanCacheTimer.stop(); + if (d->cleanCacheTimer->isActive()) + d->cleanCacheTimer->stop(); // schedule an auto-unload of the big QImage in a second. - d->cleanCacheTimer.start(); + d->cleanCacheTimer->start(); } } return d->pixmap; } bool KoImageData::hasCachedPixmap() const { return d && !d->pixmap.isNull(); } QSizeF KoImageData::imageSize() { if (!d->imageSize.isValid()) { // The imagesize have not yet been calculated if (image().isNull()) // auto loads the image return QSizeF(100, 100); if (d->image.dotsPerMeterX()) d->imageSize.setWidth(DM_TO_POINT(d->image.width() / (qreal) d->image.dotsPerMeterX() * 10.0)); else d->imageSize.setWidth(d->image.width() / 72.0); if (d->image.dotsPerMeterY()) d->imageSize.setHeight(DM_TO_POINT(d->image.height() / (qreal) d->image.dotsPerMeterY() * 10.0)); else d->imageSize.setHeight(d->image.height() / 72.0); } return d->imageSize; } +// XXX: why const here? QImage KoImageData::image() const { if (d->dataStoreState == KoImageDataPrivate::StateNotLoaded) { // load image if (d->temporaryFile) { bool r = d->temporaryFile->open(); if (!r) { d->errorCode = OpenFailed; } else if (d->errorCode == Success && !d->image.load(d->temporaryFile->fileName(), d->suffix.toLatin1())) { d->errorCode = OpenFailed; } d->temporaryFile->close(); } else { if (d->errorCode == Success && !d->image.load(d->imageLocation.toLocalFile())) { d->errorCode = OpenFailed; } } if (d->errorCode == Success) { d->dataStoreState = KoImageDataPrivate::StateImageLoaded; } } return d->image; } bool KoImageData::hasCachedImage() const { return d && !d->image.isNull(); } void KoImageData::setImage(const QImage &image, KoImageCollection *collection) { qint64 oldKey = 0; if (d) { oldKey = d->key; } Q_ASSERT(!image.isNull()); if (collection) { // let the collection first check if it already has one. If it doesn't it'll call this method // again and well go to the other clause KoImageData *other = collection->createImageData(image); this->operator=(*other); delete other; } else { if (d == 0) { - d = new KoImageDataPrivate(this); - d->refCount.ref(); + d = new KoImageDataPrivate(); } delete d->temporaryFile; d->temporaryFile = 0; d->clear(); d->suffix = "png"; // good default for non-lossy storage. if (image.byteCount() > MAX_MEMORY_IMAGESIZE) { // store image QBuffer buffer; buffer.open(QIODevice::WriteOnly); if (!image.save(&buffer, d->suffix.toLatin1())) { warnFlake << "Write temporary file failed"; d->errorCode = StorageFailed; delete d->temporaryFile; d->temporaryFile = 0; return; } buffer.close(); buffer.open(QIODevice::ReadOnly); d->copyToTemporary(buffer); } else { d->image = image; d->dataStoreState = KoImageDataPrivate::StateImageOnly; QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); // use .png for images we get as QImage QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(ba); d->key = KoImageDataPrivate::generateKey(md5.result()); } if (oldKey != 0 && d->collection) { d->collection->update(oldKey, d->key); } } } void KoImageData::setImage(const QString &url, KoStore *store, KoImageCollection *collection) { if (collection) { // Let the collection first check if it already has one. If it // doesn't it'll call this method again and we'll go to the // other clause. KoImageData *other = collection->createImageData(url, store); this->operator=(*other); delete other; } else { if (d == 0) { - d = new KoImageDataPrivate(this); - d->refCount.ref(); + d = new KoImageDataPrivate(); } else { d->clear(); } d->setSuffix(url); if (store->open(url)) { struct Finalizer { ~Finalizer() { store->close(); } KoStore *store; }; Finalizer closer; closer.store = store; KoStoreDevice device(store); const bool lossy = url.endsWith(".jpg", Qt::CaseInsensitive) || url.endsWith(".gif", Qt::CaseInsensitive); if (!lossy && device.size() < MAX_MEMORY_IMAGESIZE) { QByteArray data = device.readAll(); if (d->image.loadFromData(data)) { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(data); qint64 oldKey = d->key; d->key = KoImageDataPrivate::generateKey(md5.result()); if (oldKey != 0 && d->collection) { d->collection->update(oldKey, d->key); } d->dataStoreState = KoImageDataPrivate::StateImageOnly; return; } } if (!device.open(QIODevice::ReadOnly)) { warnFlake << "open file from store " << url << "failed"; d->errorCode = OpenFailed; return; } d->copyToTemporary(device); } else { warnFlake << "Find file in store " << url << "failed"; d->errorCode = OpenFailed; return; } } } void KoImageData::setImage(const QByteArray &imageData, KoImageCollection *collection) { if (collection) { // let the collection first check if it already has one. If it doesn't it'll call this method // again and we'll go to the other clause KoImageData *other = collection->createImageData(imageData); this->operator=(*other); delete other; } else { if (d == 0) { - d = new KoImageDataPrivate(this); - d->refCount.ref(); + d = new KoImageDataPrivate(); } d->suffix = "png"; // good default for non-lossy storage. if (imageData.size() <= MAX_MEMORY_IMAGESIZE) { QImage image; if (!image.loadFromData(imageData)) { // mark the image as invalid, but keep the data in memory // even if Calligra cannot handle the format, the data should // be retained d->errorCode = OpenFailed; } d->image = image; d->dataStoreState = KoImageDataPrivate::StateImageOnly; } if (imageData.size() > MAX_MEMORY_IMAGESIZE || d->errorCode == OpenFailed) { d->image = QImage(); // store image data QBuffer buffer; buffer.setData(imageData); buffer.open(QIODevice::ReadOnly); d->copyToTemporary(buffer); } QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(imageData); qint64 oldKey = d->key; d->key = KoImageDataPrivate::generateKey(md5.result()); if (oldKey != 0 && d->collection) { d->collection->update(oldKey, d->key); } } } bool KoImageData::isValid() const { return d && d->dataStoreState != KoImageDataPrivate::StateEmpty && d->errorCode == Success; } +KoImageDataPrivate *KoImageData::priv() +{ + return d.data(); +} + bool KoImageData::operator==(const KoImageData &other) const { return other.d == d; } KoImageData &KoImageData::operator=(const KoImageData &other) { - if (other.d) - other.d->refCount.ref(); - if (d && !d->refCount.deref()) - delete d; d = other.d; return *this; } KoShapeUserData *KoImageData::clone() const { return new KoImageData(*this); } qint64 KoImageData::key() const { return d->key; } QString KoImageData::suffix() const { return d->suffix; } KoImageData::ErrorCode KoImageData::errorCode() const { return d->errorCode; } bool KoImageData::saveData(QIODevice &device) { return d->saveData(device); } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoImageData.cpp" diff --git a/libs/flake/KoImageData.h b/libs/flake/KoImageData.h index 9369e7067a..1648523f19 100644 --- a/libs/flake/KoImageData.h +++ b/libs/flake/KoImageData.h @@ -1,141 +1,141 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009 Thomas Zander * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 Thorsten Zachmann * * 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. */ #ifndef KOIMAGEDATA_H #define KOIMAGEDATA_H #include "kritaflake_export.h" #include #include +#include #include class QIODevice; class QPixmap; class QImage; class QSizeF; class QUrl; class KoImageCollection; class KoImageDataPrivate; class KoStore; /** * This class is meant to represent the image data so it can be shared between image shapes. * * This class inherits from KoShapeUserData which means you can set it on any KoShape using * KoShape::setUserData() and get it using KoShape::userData(). The pictureshape plugin * uses this class to show its image data. * * Plugins should not make a copy of the pixmap data, but use the pixmap() method, which * handles caching. */ class KRITAFLAKE_EXPORT KoImageData : public KoShapeUserData { Q_OBJECT public: /// Various error codes representing what has gone wrong enum ErrorCode { Success, OpenFailed, StorageFailed, ///< This is set if the image data has to be stored on disk in a temporary file, but we failed to do so LoadFailed }; /// default constructor, creates an invalid imageData object KoImageData(); ~KoImageData() override; KoImageData(const KoImageData &imageData); KoImageData &operator=(const KoImageData &other); inline bool operator!=(const KoImageData &other) const { return !operator==(other); } bool operator==(const KoImageData &other) const; KoShapeUserData* clone() const override; void setImage(const QString &location, KoStore *store, KoImageCollection *collection = 0); /** * Renders a pixmap the first time you request it is called and returns it. * @returns the cached pixmap * @see isValid(), hasCachedPixmap() */ QPixmap pixmap(const QSize &targetSize = QSize()); /** * Return the internal store of the image. * @see isValid(), hasCachedImage() */ QImage image() const; /** * The size of the image in points */ QSizeF imageSize(); /** * Save the image data to the param device. * The full file is saved. * @param device the device that is used to get the data from. * @return returns true if load was successful. */ bool saveData(QIODevice &device); /** * Get a unique key of the image data */ qint64 key() const; /// @return the original image file's extension, e.g. "png" or "gif" QString suffix() const; ErrorCode errorCode() const; /// returns if this is a valid imageData with actual image data present on it. bool isValid() const; /// \internal - KoImageDataPrivate *priv() { return d; } + KoImageDataPrivate *priv(); private: friend class KoImageCollection; friend class TestImageCollection; KoImageData(KoImageDataPrivate *priv); /// returns true only if pixmap() would return immediately with a cached pixmap bool hasCachedPixmap() const; /// returns true only if image() would return immediately with a cached image bool hasCachedImage() const; void setImage(const QImage &image, KoImageCollection *collection = 0); void setImage(const QByteArray &imageData, KoImageCollection *collection = 0); private: - KoImageDataPrivate *d; - Q_PRIVATE_SLOT(d, void cleanupImageCache()) + QSharedDataPointer d; }; Q_DECLARE_METATYPE(KoImageData*) #endif diff --git a/libs/flake/KoImageData_p.cpp b/libs/flake/KoImageData_p.cpp index c01fca9fcc..a138e303ea 100644 --- a/libs/flake/KoImageData_p.cpp +++ b/libs/flake/KoImageData_p.cpp @@ -1,164 +1,163 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009 Thomas Zander * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 C. Boemann * Copyright (C) 2008 Thorsten Zachmann * * 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 "KoImageData_p.h" #include "KoImageCollection.h" #include #include #include #include #include #include #include -KoImageDataPrivate::KoImageDataPrivate(KoImageData *q) +KoImageDataPrivate::KoImageDataPrivate() : collection(0), errorCode(KoImageData::Success), key(0), - refCount(0), dataStoreState(StateEmpty), temporaryFile(0) { - cleanCacheTimer.setSingleShot(true); - cleanCacheTimer.setInterval(1000); - QObject::connect(&cleanCacheTimer, SIGNAL(timeout()), q, SLOT(cleanupImageCache())); + cleanCacheTimer->setSingleShot(true); + cleanCacheTimer->setInterval(1000); + QObject::connect(cleanCacheTimer.data(), &QTimer::timeout, [&]() { cleanupImageCache(); }); } KoImageDataPrivate::~KoImageDataPrivate() { if (collection) collection->removeOnKey(key); delete temporaryFile; } // called from the collection bool KoImageDataPrivate::saveData(QIODevice &device) { // if we have a temp file save that to the store. This is needed as to not lose data when // saving lossy formats. Also writing out gif is not supported by qt so saving temp file // also fixes the problem that gif images are empty after saving. if (temporaryFile) { if (!temporaryFile->open()) { warnFlake << "Read file from temporary store failed"; return false; } char buf[4096]; while (true) { temporaryFile->waitForReadyRead(-1); qint64 bytes = temporaryFile->read(buf, sizeof(buf)); if (bytes <= 0) break; // done! do { qint64 nWritten = device.write(buf, bytes); if (nWritten == -1) { temporaryFile->close(); return false; } bytes -= nWritten; } while (bytes > 0); } temporaryFile->close(); return true; } switch (dataStoreState) { case KoImageDataPrivate::StateEmpty: return false; case KoImageDataPrivate::StateNotLoaded: // we should not reach this state as above this will already be saved. Q_ASSERT(temporaryFile); return true; case KoImageDataPrivate::StateImageLoaded: case KoImageDataPrivate::StateImageOnly: { // save image QBuffer buffer; QImageWriter writer(&buffer, suffix.toLatin1()); bool result = writer.write(image); device.write(buffer.data(), buffer.size()); return result; } } return false; } void KoImageDataPrivate::setSuffix(const QString &name) { QFileInfo fi(name); suffix = fi.suffix(); } void KoImageDataPrivate::copyToTemporary(QIODevice &device) { delete temporaryFile; temporaryFile = new QTemporaryFile(QDir::tempPath() + "/" + qAppName() + QLatin1String("_XXXXXX")); if (!temporaryFile->open()) { warnFlake << "open temporary file for writing failed"; errorCode = KoImageData::StorageFailed; return; } QCryptographicHash md5(QCryptographicHash::Md5); char buf[8096]; while (true) { device.waitForReadyRead(-1); qint64 bytes = device.read(buf, sizeof(buf)); if (bytes <= 0) break; // done! md5.addData(buf, bytes); do { bytes -= temporaryFile->write(buf, bytes); } while (bytes > 0); } key = KoImageDataPrivate::generateKey(md5.result()); temporaryFile->close(); dataStoreState = StateNotLoaded; } void KoImageDataPrivate::cleanupImageCache() { if (dataStoreState == KoImageDataPrivate::StateImageLoaded) { image = QImage(); dataStoreState = KoImageDataPrivate::StateNotLoaded; } } void KoImageDataPrivate::clear() { errorCode = KoImageData::Success; dataStoreState = StateEmpty; imageLocation.clear(); imageSize = QSizeF(); key = 0; image = QImage(); pixmap = QPixmap(); } qint64 KoImageDataPrivate::generateKey(const QByteArray &bytes) { qint64 answer = 1; const int max = qMin(8, bytes.count()); for (int x = 0; x < max; ++x) answer += qint64(bytes[x] << (8 * x)); return answer; } diff --git a/libs/flake/KoImageData_p.h b/libs/flake/KoImageData_p.h index 38502e52cb..53b24c3b03 100644 --- a/libs/flake/KoImageData_p.h +++ b/libs/flake/KoImageData_p.h @@ -1,91 +1,93 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009 Thomas Zander * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 C. Boemann * Copyright (C) 2008 Thorsten Zachmann * * 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. */ #ifndef KOIMAGEDATA_P_H #define KOIMAGEDATA_P_H #include #include #include #include #include #include +#include + +#include #include "KoImageData.h" class KoImageCollection; class QTemporaryFile; -class KoImageDataPrivate +class KoImageDataPrivate : public QSharedData { public: - explicit KoImageDataPrivate(KoImageData *q); + KoImageDataPrivate(); virtual ~KoImageDataPrivate(); /** * Save the image data to the param device. * The full file is saved. * @param device the device that is used to get the data from. * @return returns true if load was successful. */ bool saveData(QIODevice &device); /// store the suffix based on the full filename. void setSuffix(const QString &fileName); /// take the data from \a device and store it in the temporaryFile void copyToTemporary(QIODevice &device); /// clean the image cache. void cleanupImageCache(); void clear(); static qint64 generateKey(const QByteArray &bytes); enum DataStoreState { StateEmpty, ///< No image data, either as url or as QImage StateNotLoaded, ///< Image data is set as Url StateImageLoaded,///< Image data is loaded from Url, so both are present. StateImageOnly ///< Image data is stored in a QImage. There is no external storage. }; KoImageCollection *collection; - KoImageData::ErrorCode errorCode; + mutable KoImageData::ErrorCode errorCode; QSizeF imageSize; qint64 key; QString suffix; // the suffix of the picture e.g. png TODO use a QByteArray ? - QTimer cleanCacheTimer; - - QAtomicInt refCount; + KisNewOnCopy cleanCacheTimer; // Image data store. - DataStoreState dataStoreState; + mutable DataStoreState dataStoreState; QUrl imageLocation; - QImage image; + // XXX this should not be needed + mutable QImage image; /// screen optimized cached version. QPixmap pixmap; QTemporaryFile *temporaryFile; }; #endif /* KOIMAGEDATA_P_H */ diff --git a/libs/flake/KoShape_p.h b/libs/flake/KoShape_p.h index 09c6fb955c..a1b05e486f 100644 --- a/libs/flake/KoShape_p.h +++ b/libs/flake/KoShape_p.h @@ -1,128 +1,129 @@ /* This file is part of the KDE project * Copyright (C) 2009 Thomas Zander * Copyright (C) 2010 Boudewijn Rempt * * 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. */ #ifndef KOSHAPEPRIVATE_H #define KOSHAPEPRIVATE_H #include "KoShape.h" #include #include #include #include #include class KoBorder; class KoShapeManager; class KoShapePrivate { public: explicit KoShapePrivate(KoShape *shape); virtual ~KoShapePrivate(); explicit KoShapePrivate(const KoShapePrivate &rhs, KoShape *q); /** * Notify the shape that a change was done. To be used by inheriting shapes. * @param type the change type */ void shapeChanged(KoShape::ChangeType type); void addShapeManager(KoShapeManager *manager); void removeShapeManager(KoShapeManager *manager); /** * Fills the style stack and returns the value of the given style property (e.g fill, stroke). */ static QString getStyleProperty(const char *property, KoShapeLoadingContext &context); /// Loads the shadow style KoShapeShadow *loadOdfShadow(KoShapeLoadingContext &context) const; // Loads the border style. KoBorder *loadOdfBorder(KoShapeLoadingContext &context) const; public: // Members KoShape *q_ptr; // Points the shape that owns this class. mutable QSizeF size; // size in pt QString shapeId; QString name; ///< the shapes names QTransform localMatrix; ///< the shapes local transformation matrix KoConnectionPoints connectors; ///< glue point id to data mapping KoShapeContainer *parent; QSet shapeManagers; QSet toolDelegates; QScopedPointer userData; QSharedPointer stroke; ///< points to a stroke, or 0 if there is no stroke QSharedPointer fill; ///< Stands for the background color / fill etc. bool inheritBackground = false; bool inheritStroke = false; QList dependees; ///< list of shape dependent on this shape QList listeners; KoShapeShadow * shadow; ///< the current shape shadow KoBorder *border; ///< the current shape border + // XXX: change this to instance instead of pointer QScopedPointer clipPath; ///< the current clip path QScopedPointer clipMask; ///< the current clip mask QMap additionalAttributes; QMap additionalStyleAttributes; KoFilterEffectStack *filterEffectStack; ///< stack of filter effects applied to the shape qreal transparency; ///< the shapes transparency QString hyperLink; //hyperlink for this shape int zIndex : 16; // keep maxZIndex in sync! int runThrough : 16; int visible : 1; int printable : 1; int geometryProtected : 1; int keepAspect : 1; int selectable : 1; int detectCollision : 1; int protectContent : 1; KoShape::TextRunAroundSide textRunAroundSide; qreal textRunAroundDistanceLeft; qreal textRunAroundDistanceTop; qreal textRunAroundDistanceRight; qreal textRunAroundDistanceBottom; qreal textRunAroundThreshold; KoShape::TextRunAroundContour textRunAroundContour; public: /// Connection point converters /// Convert connection point position from shape coordinates, taking alignment into account void convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const; /// Convert connection point position to shape coordinates, taking alignment into account void convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const; Q_DECLARE_PUBLIC(KoShape) }; #endif diff --git a/libs/global/KisNewOnCopy.h b/libs/global/KisNewOnCopy.h new file mode 100644 index 0000000000..9ba1641ed8 --- /dev/null +++ b/libs/global/KisNewOnCopy.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Tusooa Zhu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_NEW_ON_COPY_H_ +#define KIS_NEW_ON_COPY_H_ + +/** + * This class wraps around some type T that is not copiable. + * When the copy-constructor or assignment of KisNewOnCopy is called, + * it default-constructs an instance of T. + */ +template +class KisNewOnCopy +{ +public: + KisNewOnCopy() : instance() {} + KisNewOnCopy(const KisNewOnCopy &) : instance() {} + + // KisNewOnCopy &operator=(const KisNewOnCopy &) { return *this; } + + const T *data() const { return &instance; } + const T *constData() { return &instance; } + T *data() { return &instance; } + const T *operator->() const { return &instance; } + T *operator->() { return &instance; } + +private: + T instance; +}; + +#endif