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 }