File indexing completed on 2024-05-12 15:56:45

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2008 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KoPatternBackground.h"
0008 #include "KoShapeSavingContext.h"
0009 #include <KoXmlNS.h>
0010 #include <KoUnit.h>
0011 
0012 #include <FlakeDebug.h>
0013 
0014 #include <QBrush>
0015 #include <QPainter>
0016 #include <QPainterPath>
0017 #include <QPointer>
0018 #include <QSharedData>
0019 
0020 class KoPatternBackground::Private : public QSharedData
0021 {
0022 public:
0023     Private()
0024         : QSharedData()
0025         , repeat(KoPatternBackground::Tiled)
0026         , refPoint(KoPatternBackground::TopLeft)
0027     {
0028     }
0029 
0030     ~Private()
0031     {
0032     }
0033 
0034     QSizeF targetSize() const {
0035         QSizeF size = pattern.size();
0036         if (targetImageSizePercent.width() > 0.0)
0037             size.setWidth(0.01 * targetImageSizePercent.width() * size.width());
0038         else if (targetImageSize.width() > 0.0)
0039             size.setWidth(targetImageSize.width());
0040         if (targetImageSizePercent.height() > 0.0)
0041             size.setHeight(0.01 * targetImageSizePercent.height() * size.height());
0042         else if (targetImageSize.height() > 0.0)
0043             size.setHeight(targetImageSize.height());
0044 
0045         return size;
0046     }
0047 
0048     QPointF offsetFromRect(const QRectF &fillRect, const QSizeF &imageSize) const {
0049         QPointF offset;
0050         switch (refPoint) {
0051         case KoPatternBackground::TopLeft:
0052             offset = fillRect.topLeft();
0053             break;
0054         case KoPatternBackground::Top:
0055             offset.setX(fillRect.center().x() - 0.5 * imageSize.width());
0056             offset.setY(fillRect.top());
0057             break;
0058         case KoPatternBackground::TopRight:
0059             offset.setX(fillRect.right() - imageSize.width());
0060             offset.setY(fillRect.top());
0061             break;
0062         case KoPatternBackground::Left:
0063             offset.setX(fillRect.left());
0064             offset.setY(fillRect.center().y() - 0.5 * imageSize.height());
0065             break;
0066         case KoPatternBackground::Center:
0067             offset.setX(fillRect.center().x() - 0.5 * imageSize.width());
0068             offset.setY(fillRect.center().y() - 0.5 * imageSize.height());
0069             break;
0070         case KoPatternBackground::Right:
0071             offset.setX(fillRect.right() - imageSize.width());
0072             offset.setY(fillRect.center().y() - 0.5 * imageSize.height());
0073             break;
0074         case KoPatternBackground::BottomLeft:
0075             offset.setX(fillRect.left());
0076             offset.setY(fillRect.bottom() - imageSize.height());
0077             break;
0078         case KoPatternBackground::Bottom:
0079             offset.setX(fillRect.center().x() - 0.5 * imageSize.width());
0080             offset.setY(fillRect.bottom() - imageSize.height());
0081             break;
0082         case KoPatternBackground::BottomRight:
0083             offset.setX(fillRect.right() - imageSize.width());
0084             offset.setY(fillRect.bottom() - imageSize.height());
0085             break;
0086         default:
0087             break;
0088         }
0089         if (refPointOffsetPercent.x() > 0.0)
0090             offset += QPointF(0.01 * refPointOffsetPercent.x() * imageSize.width(), 0);
0091         if (refPointOffsetPercent.y() > 0.0)
0092             offset += QPointF(0, 0.01 * refPointOffsetPercent.y() * imageSize.height());
0093 
0094         return offset;
0095     }
0096 
0097     QTransform matrix;
0098     KoPatternBackground::PatternRepeat repeat;
0099     KoPatternBackground::ReferencePoint refPoint;
0100     QSizeF targetImageSize;
0101     QSizeF targetImageSizePercent;
0102     QPointF refPointOffsetPercent;
0103     QPointF tileRepeatOffsetPercent;
0104     QImage pattern;
0105 };
0106 
0107 
0108 // ----------------------------------------------------------------
0109 
0110 
0111 KoPatternBackground::KoPatternBackground()
0112     : KoShapeBackground()
0113     , d(new Private)
0114 {
0115 }
0116 
0117 KoPatternBackground::~KoPatternBackground()
0118 {
0119 }
0120 
0121 KoPatternBackground::KoPatternBackground(const KoPatternBackground &rhs)
0122     : d(new Private(*rhs.d))
0123 {
0124 }
0125 
0126 KoPatternBackground& KoPatternBackground::operator=(const KoPatternBackground &rhs)
0127 {
0128     d = rhs.d;
0129     return *this;
0130 }
0131 
0132 bool KoPatternBackground::compareTo(const KoShapeBackground *other) const
0133 {
0134     Q_UNUSED(other);
0135     return false;
0136 }
0137 
0138 void KoPatternBackground::setTransform(const QTransform &matrix)
0139 {
0140     d->matrix = matrix;
0141 }
0142 
0143 QTransform KoPatternBackground::transform() const
0144 {
0145     return d->matrix;
0146 }
0147 
0148 void KoPatternBackground::setPattern(const QImage &pattern)
0149 {
0150     d->pattern = pattern;
0151 }
0152 
0153 QImage KoPatternBackground::pattern() const
0154 {
0155     return d->pattern;
0156 }
0157 
0158 void KoPatternBackground::setRepeat(PatternRepeat repeat)
0159 {
0160     d->repeat = repeat;
0161 }
0162 
0163 KoPatternBackground::PatternRepeat KoPatternBackground::repeat() const
0164 {
0165     return d->repeat;
0166 }
0167 
0168 KoPatternBackground::ReferencePoint KoPatternBackground::referencePoint() const
0169 {
0170     return d->refPoint;
0171 }
0172 
0173 void KoPatternBackground::setReferencePoint(ReferencePoint referencePoint)
0174 {
0175     d->refPoint = referencePoint;
0176 }
0177 
0178 QPointF KoPatternBackground::referencePointOffset() const
0179 {
0180     return d->refPointOffsetPercent;
0181 }
0182 
0183 void KoPatternBackground::setReferencePointOffset(const QPointF &offset)
0184 {
0185     qreal ox = qMax(qreal(0.0), qMin(qreal(100.0), offset.x()));
0186     qreal oy = qMax(qreal(0.0), qMin(qreal(100.0), offset.y()));
0187 
0188     d->refPointOffsetPercent = QPointF(ox, oy);
0189 }
0190 
0191 QPointF KoPatternBackground::tileRepeatOffset() const
0192 {
0193     return d->tileRepeatOffsetPercent;
0194 }
0195 
0196 void KoPatternBackground::setTileRepeatOffset(const QPointF &offset)
0197 {
0198     d->tileRepeatOffsetPercent = offset;
0199 }
0200 
0201 QSizeF KoPatternBackground::patternDisplaySize() const
0202 {
0203     return d->targetSize();
0204 }
0205 
0206 void KoPatternBackground::setPatternDisplaySize(const QSizeF &size)
0207 {
0208     d->targetImageSizePercent = QSizeF();
0209     d->targetImageSize = size;
0210 }
0211 
0212 QSizeF KoPatternBackground::patternOriginalSize() const
0213 {
0214     return d->pattern.size();
0215 }
0216 
0217 void KoPatternBackground::paint(QPainter &painter, const QPainterPath &fillPath) const
0218 {
0219     if (d->pattern.isNull()) {
0220         return;
0221     }
0222 
0223     painter.save();
0224     if (d->repeat == Tiled) {
0225         // calculate scaling of pixmap
0226         QSizeF targetSize = d->targetSize();
0227         QSizeF imageSize = d->pattern.size();
0228         qreal scaleX = targetSize.width() / imageSize.width();
0229         qreal scaleY = targetSize.height() / imageSize.height();
0230 
0231         QRectF targetRect = fillPath.boundingRect();
0232         // undo scaling on target rectangle
0233         targetRect.setWidth(targetRect.width() / scaleX);
0234         targetRect.setHeight(targetRect.height() / scaleY);
0235 
0236         // determine pattern offset
0237         QPointF offset = d->offsetFromRect(targetRect, imageSize);
0238 
0239         // create matrix for pixmap scaling
0240         QTransform matrix;
0241         matrix.scale(scaleX, scaleY);
0242 
0243         painter.setClipPath(fillPath);
0244         painter.setWorldTransform(matrix, true);
0245         painter.drawTiledPixmap(targetRect, QPixmap::fromImage(d->pattern), -offset);
0246     } else if (d->repeat == Original) {
0247         QRectF sourceRect(QPointF(0, 0), d->pattern.size());
0248         QRectF targetRect(QPoint(0, 0), d->targetSize());
0249         targetRect.moveCenter(fillPath.boundingRect().center());
0250         painter.setClipPath(fillPath);
0251         painter.drawPixmap(targetRect, QPixmap::fromImage(d->pattern).scaled(sourceRect.size().toSize()), sourceRect);
0252     } else if (d->repeat == Stretched) {
0253         painter.setClipPath(fillPath);
0254         // undo conversion of the scaling so that we can use a nicely scaled image of the correct size
0255         qWarning() << "WARNING: stretched KoPatternBackground painting code is abandoned. The result might be not correct";
0256         const QRectF targetRect = fillPath.boundingRect();
0257         painter.drawPixmap(targetRect.topLeft(), QPixmap::fromImage(d->pattern).scaled(targetRect.size().toSize()));
0258     }
0259 
0260     painter.restore();
0261 }
0262 
0263 
0264 QRectF KoPatternBackground::patternRectFromFillSize(const QSizeF &size)
0265 {
0266     QRectF rect;
0267 
0268     switch (d->repeat) {
0269     case Tiled:
0270         rect.setTopLeft(d->offsetFromRect(QRectF(QPointF(), size), d->targetSize()));
0271         rect.setSize(d->targetSize());
0272         break;
0273     case Original:
0274         rect.setLeft(0.5 * (size.width() - d->targetSize().width()));
0275         rect.setTop(0.5 * (size.height() - d->targetSize().height()));
0276         rect.setSize(d->targetSize());
0277         break;
0278     case Stretched:
0279         rect.setTopLeft(QPointF(0.0, 0.0));
0280         rect.setSize(size);
0281         break;
0282     }
0283 
0284     return rect;
0285 }