Warning, file /office/calligra/libs/flake/KoPatternBackground.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 Jan Hambrecht <jaham@gmx.net> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Library General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this library; see the file COPYING.LIB. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KoPatternBackground.h" 0021 #include "KoShapeBackground_p.h" 0022 #include "KoShapeSavingContext.h" 0023 #include "KoImageData.h" 0024 #include "KoImageCollection.h" 0025 #include <KoStyleStack.h> 0026 #include <KoGenStyle.h> 0027 #include <KoGenStyles.h> 0028 #include <KoXmlNS.h> 0029 #include <KoOdfLoadingContext.h> 0030 #include <KoOdfGraphicStyles.h> 0031 #include <KoOdfStylesReader.h> 0032 #include <KoUnit.h> 0033 #include <KoViewConverter.h> 0034 0035 #include <FlakeDebug.h> 0036 0037 #include <QBrush> 0038 #include <QPainter> 0039 #include <QPainterPath> 0040 0041 class KoPatternBackgroundPrivate : public KoShapeBackgroundPrivate 0042 { 0043 public: 0044 KoPatternBackgroundPrivate() 0045 : repeat(KoPatternBackground::Tiled) 0046 , refPoint(KoPatternBackground::TopLeft) 0047 , imageCollection(0) 0048 , imageData(0) 0049 { 0050 } 0051 0052 ~KoPatternBackgroundPrivate() override { 0053 delete imageData; 0054 } 0055 0056 QSizeF targetSize() const { 0057 QSizeF size = imageData->imageSize(); 0058 if (targetImageSizePercent.width() > 0.0) 0059 size.setWidth(0.01 * targetImageSizePercent.width() * size.width()); 0060 else if (targetImageSize.width() > 0.0) 0061 size.setWidth(targetImageSize.width()); 0062 if (targetImageSizePercent.height() > 0.0) 0063 size.setHeight(0.01 * targetImageSizePercent.height() * size.height()); 0064 else if (targetImageSize.height() > 0.0) 0065 size.setHeight(targetImageSize.height()); 0066 0067 return size; 0068 } 0069 0070 QPointF offsetFromRect(const QRectF &fillRect, const QSizeF &imageSize) const { 0071 QPointF offset; 0072 switch (refPoint) { 0073 case KoPatternBackground::TopLeft: 0074 offset = fillRect.topLeft(); 0075 break; 0076 case KoPatternBackground::Top: 0077 offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); 0078 offset.setY(fillRect.top()); 0079 break; 0080 case KoPatternBackground::TopRight: 0081 offset.setX(fillRect.right() - imageSize.width()); 0082 offset.setY(fillRect.top()); 0083 break; 0084 case KoPatternBackground::Left: 0085 offset.setX(fillRect.left()); 0086 offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); 0087 break; 0088 case KoPatternBackground::Center: 0089 offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); 0090 offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); 0091 break; 0092 case KoPatternBackground::Right: 0093 offset.setX(fillRect.right() - imageSize.width()); 0094 offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); 0095 break; 0096 case KoPatternBackground::BottomLeft: 0097 offset.setX(fillRect.left()); 0098 offset.setY(fillRect.bottom() - imageSize.height()); 0099 break; 0100 case KoPatternBackground::Bottom: 0101 offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); 0102 offset.setY(fillRect.bottom() - imageSize.height()); 0103 break; 0104 case KoPatternBackground::BottomRight: 0105 offset.setX(fillRect.right() - imageSize.width()); 0106 offset.setY(fillRect.bottom() - imageSize.height()); 0107 break; 0108 default: 0109 break; 0110 } 0111 if (refPointOffsetPercent.x() > 0.0) 0112 offset += QPointF(0.01 * refPointOffsetPercent.x() * imageSize.width(), 0); 0113 if (refPointOffsetPercent.y() > 0.0) 0114 offset += QPointF(0, 0.01 * refPointOffsetPercent.y() * imageSize.height()); 0115 0116 return offset; 0117 } 0118 0119 QTransform matrix; 0120 KoPatternBackground::PatternRepeat repeat; 0121 KoPatternBackground::ReferencePoint refPoint; 0122 QSizeF targetImageSize; 0123 QSizeF targetImageSizePercent; 0124 QPointF refPointOffsetPercent; 0125 QPointF tileRepeatOffsetPercent; 0126 KoImageCollection * imageCollection; 0127 KoImageData * imageData; 0128 }; 0129 0130 0131 // ---------------------------------------------------------------- 0132 0133 0134 KoPatternBackground::KoPatternBackground(KoImageCollection * imageCollection) 0135 : KoShapeBackground(*(new KoPatternBackgroundPrivate())) 0136 { 0137 Q_D(KoPatternBackground); 0138 d->imageCollection = imageCollection; 0139 Q_ASSERT(d->imageCollection); 0140 } 0141 0142 KoPatternBackground::~KoPatternBackground() 0143 { 0144 //Q_D(KoPatternBackground); 0145 } 0146 0147 void KoPatternBackground::setTransform(const QTransform &matrix) 0148 { 0149 Q_D(KoPatternBackground); 0150 d->matrix = matrix; 0151 } 0152 0153 QTransform KoPatternBackground::transform() const 0154 { 0155 Q_D(const KoPatternBackground); 0156 return d->matrix; 0157 } 0158 0159 void KoPatternBackground::setPattern(const QImage &pattern) 0160 { 0161 Q_D(KoPatternBackground); 0162 delete d->imageData; 0163 d->imageData = d->imageCollection->createImageData(pattern); 0164 } 0165 0166 void KoPatternBackground::setPattern(KoImageData *imageData) 0167 { 0168 Q_D(KoPatternBackground); 0169 delete d->imageData; 0170 0171 d->imageData = imageData; 0172 } 0173 0174 QImage KoPatternBackground::pattern() const 0175 { 0176 Q_D(const KoPatternBackground); 0177 if (d->imageData) 0178 return d->imageData->image(); 0179 return QImage(); 0180 } 0181 0182 void KoPatternBackground::setRepeat(PatternRepeat repeat) 0183 { 0184 Q_D(KoPatternBackground); 0185 d->repeat = repeat; 0186 } 0187 0188 KoPatternBackground::PatternRepeat KoPatternBackground::repeat() const 0189 { 0190 Q_D(const KoPatternBackground); 0191 return d->repeat; 0192 } 0193 0194 KoPatternBackground::ReferencePoint KoPatternBackground::referencePoint() const 0195 { 0196 Q_D(const KoPatternBackground); 0197 return d->refPoint; 0198 } 0199 0200 void KoPatternBackground::setReferencePoint(ReferencePoint referencePoint) 0201 { 0202 Q_D(KoPatternBackground); 0203 d->refPoint = referencePoint; 0204 } 0205 0206 QPointF KoPatternBackground::referencePointOffset() const 0207 { 0208 Q_D(const KoPatternBackground); 0209 return d->refPointOffsetPercent; 0210 } 0211 0212 void KoPatternBackground::setReferencePointOffset(const QPointF &offset) 0213 { 0214 Q_D(KoPatternBackground); 0215 qreal ox = qMax(qreal(0.0), qMin(qreal(100.0), offset.x())); 0216 qreal oy = qMax(qreal(0.0), qMin(qreal(100.0), offset.y())); 0217 0218 d->refPointOffsetPercent = QPointF(ox, oy); 0219 } 0220 0221 QPointF KoPatternBackground::tileRepeatOffset() const 0222 { 0223 Q_D(const KoPatternBackground); 0224 return d->tileRepeatOffsetPercent; 0225 } 0226 0227 void KoPatternBackground::setTileRepeatOffset(const QPointF &offset) 0228 { 0229 Q_D(KoPatternBackground); 0230 d->tileRepeatOffsetPercent = offset; 0231 } 0232 0233 QSizeF KoPatternBackground::patternDisplaySize() const 0234 { 0235 Q_D(const KoPatternBackground); 0236 return d->targetSize(); 0237 } 0238 0239 void KoPatternBackground::setPatternDisplaySize(const QSizeF &size) 0240 { 0241 Q_D(KoPatternBackground); 0242 d->targetImageSizePercent = QSizeF(); 0243 d->targetImageSize = size; 0244 } 0245 0246 QSizeF KoPatternBackground::patternOriginalSize() const 0247 { 0248 Q_D(const KoPatternBackground); 0249 return d->imageData->imageSize(); 0250 } 0251 0252 void KoPatternBackground::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const 0253 { 0254 Q_D(const KoPatternBackground); 0255 if (! d->imageData) 0256 return; 0257 0258 painter.save(); 0259 0260 if (d->repeat == Tiled) { 0261 // calculate scaling of pixmap 0262 QSizeF targetSize = d->targetSize(); 0263 QSizeF imageSize = d->imageData->imageSize(); 0264 qreal scaleX = targetSize.width() / imageSize.width(); 0265 qreal scaleY = targetSize.height() / imageSize.height(); 0266 0267 QRectF targetRect = fillPath.boundingRect(); 0268 // undo scaling on target rectangle 0269 targetRect.setWidth(targetRect.width() / scaleX); 0270 targetRect.setHeight(targetRect.height() / scaleY); 0271 0272 // determine pattern offset 0273 QPointF offset = d->offsetFromRect(targetRect, imageSize); 0274 0275 // create matrix for pixmap scaling 0276 QTransform matrix; 0277 matrix.scale(scaleX, scaleY); 0278 0279 painter.setClipPath(fillPath); 0280 painter.setWorldTransform(matrix, true); 0281 painter.drawTiledPixmap(targetRect, d->imageData->pixmap(imageSize.toSize()), -offset); 0282 } else if (d->repeat == Original) { 0283 QRectF sourceRect(QPointF(0, 0), d->imageData->imageSize()); 0284 QRectF targetRect(QPoint(0, 0), d->targetSize()); 0285 targetRect.moveCenter(fillPath.boundingRect().center()); 0286 painter.setClipPath(fillPath); 0287 painter.drawPixmap(targetRect, d->imageData->pixmap(sourceRect.size().toSize()), sourceRect); 0288 } else if (d->repeat == Stretched) { 0289 painter.setClipPath(fillPath); 0290 // undo conversion of the scaling so that we can use a nicely scaled image of the correct size 0291 qreal zoomX, zoomY; 0292 converter.zoom(&zoomX, &zoomY); 0293 zoomX = zoomX ? 1 / zoomX : zoomX; 0294 zoomY = zoomY ? 1 / zoomY : zoomY; 0295 painter.scale(zoomX, zoomY); 0296 0297 QRectF targetRect = converter.documentToView(fillPath.boundingRect()); 0298 painter.drawPixmap(targetRect.topLeft(), d->imageData->pixmap(targetRect.size().toSize())); 0299 } 0300 0301 painter.restore(); 0302 } 0303 0304 void KoPatternBackground::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) 0305 { 0306 Q_D(KoPatternBackground); 0307 if (! d->imageData) 0308 return; 0309 0310 switch (d->repeat) { 0311 case Original: 0312 style.addProperty("style:repeat", "no-repeat"); 0313 break; 0314 case Tiled: 0315 style.addProperty("style:repeat", "repeat"); 0316 break; 0317 case Stretched: 0318 style.addProperty("style:repeat", "stretch"); 0319 break; 0320 } 0321 0322 if (d->repeat == Tiled) { 0323 QString refPointId = "top-left"; 0324 switch (d->refPoint) { 0325 case TopLeft: refPointId = "top-left"; break; 0326 case Top: refPointId = "top"; break; 0327 case TopRight: refPointId = "top-right"; break; 0328 case Left: refPointId = "left"; break; 0329 case Center: refPointId = "center"; break; 0330 case Right: refPointId = "right"; break; 0331 case BottomLeft: refPointId = "bottom-left"; break; 0332 case Bottom: refPointId = "bottom"; break; 0333 case BottomRight: refPointId = "bottom-right"; break; 0334 } 0335 style.addProperty("draw:fill-image-ref-point", refPointId); 0336 if (d->refPointOffsetPercent.x() > 0.0) 0337 style.addProperty("draw:fill-image-ref-point-x", QString("%1%").arg(d->refPointOffsetPercent.x())); 0338 if (d->refPointOffsetPercent.y() > 0.0) 0339 style.addProperty("draw:fill-image-ref-point-y", QString("%1%").arg(d->refPointOffsetPercent.y())); 0340 } 0341 0342 if (d->repeat != Stretched) { 0343 QSizeF targetSize = d->targetSize(); 0344 QSizeF imageSize = d->imageData->imageSize(); 0345 if (targetSize.height() != imageSize.height()) 0346 style.addPropertyPt("draw:fill-image-height", targetSize.height()); 0347 if (targetSize.width() != imageSize.width()) 0348 style.addPropertyPt("draw:fill-image-width", targetSize.width()); 0349 } 0350 0351 KoGenStyle patternStyle(KoGenStyle::FillImageStyle /*no family name*/); 0352 patternStyle.addAttribute("xlink:show", "embed"); 0353 patternStyle.addAttribute("xlink:actuate", "onLoad"); 0354 patternStyle.addAttribute("xlink:type", "simple"); 0355 patternStyle.addAttribute("xlink:href", context.imageHref(d->imageData)); 0356 0357 QString patternStyleName = context.mainStyles().insert(patternStyle, "picture"); 0358 style.addProperty("draw:fill", "bitmap"); 0359 style.addProperty("draw:fill-image-name", patternStyleName); 0360 0361 context.addDataCenter(d->imageCollection); 0362 } 0363 0364 bool KoPatternBackground::loadStyle(KoOdfLoadingContext &context, const QSizeF &) 0365 { 0366 Q_D(KoPatternBackground); 0367 KoStyleStack &styleStack = context.styleStack(); 0368 if (! styleStack.hasProperty(KoXmlNS::draw, "fill")) 0369 return false; 0370 0371 QString fillStyle = styleStack.property(KoXmlNS::draw, "fill"); 0372 if (fillStyle != "bitmap") 0373 return false; 0374 0375 QString styleName = styleStack.property(KoXmlNS::draw, "fill-image-name"); 0376 0377 KoXmlElement* e = context.stylesReader().drawStyles("fill-image").value(styleName); 0378 if (! e) 0379 return false; 0380 0381 const QString href = e->attributeNS(KoXmlNS::xlink, "href", QString()); 0382 if (href.isEmpty()) 0383 return false; 0384 0385 delete d->imageData; 0386 d->imageData = d->imageCollection->createImageData(href, context.store()); 0387 if (! d->imageData) 0388 return false; 0389 0390 // read the pattern repeat style 0391 QString style = styleStack.property(KoXmlNS::style, "repeat"); 0392 if (style == "stretch") 0393 d->repeat = Stretched; 0394 else if (style == "no-repeat") 0395 d->repeat = Original; 0396 else 0397 d->repeat = Tiled; 0398 0399 if (style != "stretch") { 0400 // optional attributes which can override original image size 0401 if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-height")) { 0402 QString height = styleStack.property(KoXmlNS::draw, "fill-image-height"); 0403 if (height.endsWith('%')) 0404 d->targetImageSizePercent.setHeight(height.remove('%').toDouble()); 0405 else 0406 d->targetImageSize.setHeight(KoUnit::parseValue(height)); 0407 } 0408 if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-width")) { 0409 QString width = styleStack.property(KoXmlNS::draw, "fill-image-width"); 0410 if (width.endsWith('%')) 0411 d->targetImageSizePercent.setWidth(width.remove('%').toDouble()); 0412 else 0413 d->targetImageSize.setWidth(KoUnit::parseValue(width)); 0414 } 0415 } 0416 0417 if (style == "repeat") { 0418 if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point")) { 0419 // align pattern to the given size 0420 QString align = styleStack.property(KoXmlNS::draw, "fill-image-ref-point"); 0421 if (align == "top-left") 0422 d->refPoint = TopLeft; 0423 else if (align == "top") 0424 d->refPoint = Top; 0425 else if (align == "top-right") 0426 d->refPoint = TopRight; 0427 else if (align == "left") 0428 d->refPoint = Left; 0429 else if (align == "center") 0430 d->refPoint = Center; 0431 else if (align == "right") 0432 d->refPoint = Right; 0433 else if (align == "bottom-left") 0434 d->refPoint = BottomLeft; 0435 else if (align == "bottom") 0436 d->refPoint = Bottom; 0437 else if (align == "bottom-right") 0438 d->refPoint = BottomRight; 0439 } 0440 if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-x")) { 0441 QString pointX = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-x"); 0442 d->refPointOffsetPercent.setX(pointX.remove('%').toDouble()); 0443 } 0444 if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-y")) { 0445 QString pointY = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-y"); 0446 d->refPointOffsetPercent.setY(pointY.remove('%').toDouble()); 0447 } 0448 if (styleStack.hasProperty(KoXmlNS::draw, "tile-repeat-offset")) { 0449 QString repeatOffset = styleStack.property(KoXmlNS::draw, "tile-repeat-offset"); 0450 QStringList tokens = repeatOffset.split('%'); 0451 if (tokens.count() == 2) { 0452 QString direction = tokens[1].simplified(); 0453 if (direction == "horizontal") 0454 d->tileRepeatOffsetPercent.setX(tokens[0].toDouble()); 0455 else if (direction == "vertical") 0456 d->tileRepeatOffsetPercent.setY(tokens[0].toDouble()); 0457 } 0458 } 0459 } 0460 0461 return true; 0462 } 0463 0464 QRectF KoPatternBackground::patternRectFromFillSize(const QSizeF &size) 0465 { 0466 Q_D(KoPatternBackground); 0467 QRectF rect; 0468 0469 switch (d->repeat) { 0470 case Tiled: 0471 rect.setTopLeft(d->offsetFromRect(QRectF(QPointF(), size), d->targetSize())); 0472 rect.setSize(d->targetSize()); 0473 break; 0474 case Original: 0475 rect.setLeft(0.5 * (size.width() - d->targetSize().width())); 0476 rect.setTop(0.5 * (size.height() - d->targetSize().height())); 0477 rect.setSize(d->targetSize()); 0478 break; 0479 case Stretched: 0480 rect.setTopLeft(QPointF(0.0, 0.0)); 0481 rect.setSize(size); 0482 break; 0483 } 0484 0485 return rect; 0486 }