File indexing completed on 2024-04-28 04:20:23
0001 0002 /* 0003 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org> 0004 All rights reserved. 0005 0006 Redistribution and use in source and binary forms, with or without 0007 modification, are permitted provided that the following conditions 0008 are met: 0009 0010 1. Redistributions of source code must retain the above copyright 0011 notice, this list of conditions and the following disclaimer. 0012 2. Redistributions in binary form must reproduce the above copyright 0013 notice, this list of conditions and the following disclaimer in the 0014 documentation and/or other materials provided with the distribution. 0015 0016 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 0017 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0018 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 0019 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 0020 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 0021 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 0022 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 0023 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 0025 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0026 */ 0027 0028 0029 #define DEBUG_KP_PIXMAP_FX 0 0030 0031 0032 #include "kpPixmapFX.h" 0033 0034 #include <QtMath> 0035 0036 #include <QPainter> 0037 #include <QImage> 0038 #include <QPoint> 0039 #include <QRect> 0040 0041 #include "kpLogCategories.h" 0042 0043 #include "layers/selections/kpAbstractSelection.h" 0044 #include "imagelib/kpColor.h" 0045 #include "kpDefs.h" 0046 0047 //--------------------------------------------------------------------- 0048 0049 // public static 0050 void kpPixmapFX::resize (QImage *destPtr, int w, int h, 0051 const kpColor &backgroundColor) 0052 { 0053 #if DEBUG_KP_PIXMAP_FX && 1 0054 qCDebug(kpLogPixmapfx) << "kpPixmapFX::resize()"; 0055 #endif 0056 0057 if (!destPtr) { 0058 return; 0059 } 0060 0061 const int oldWidth = destPtr->width (); 0062 const int oldHeight = destPtr->height (); 0063 0064 if (w == oldWidth && h == oldHeight) { 0065 return; 0066 } 0067 0068 QImage newImage (w, h, QImage::Format_ARGB32_Premultiplied); 0069 0070 // Would have new undefined areas? 0071 if (w > oldWidth || h > oldHeight) { 0072 newImage.fill (backgroundColor.toQRgb ()); 0073 } 0074 0075 // Copy over old pixmap. 0076 QPainter painter(&newImage); 0077 painter.setCompositionMode(QPainter::CompositionMode_Source); 0078 painter.drawImage(0, 0, *destPtr); 0079 painter.end(); 0080 0081 // Replace pixmap with new one. 0082 *destPtr = std::move(newImage); 0083 } 0084 0085 //--------------------------------------------------------------------- 0086 0087 // public static 0088 QImage kpPixmapFX::resize (const QImage &image, int w, int h, 0089 const kpColor &backgroundColor) 0090 { 0091 QImage ret = image; 0092 kpPixmapFX::resize (&ret, w, h, backgroundColor); 0093 return ret; 0094 } 0095 0096 //--------------------------------------------------------------------- 0097 0098 // public static 0099 void kpPixmapFX::scale (QImage *destPtr, int w, int h, bool pretty) 0100 { 0101 if (!destPtr) { 0102 return; 0103 } 0104 0105 *destPtr = kpPixmapFX::scale (*destPtr, w, h, pretty); 0106 } 0107 0108 //--------------------------------------------------------------------- 0109 0110 // public static 0111 QImage kpPixmapFX::scale (const QImage &image, int w, int h, bool pretty) 0112 { 0113 #if DEBUG_KP_PIXMAP_FX && 0 0114 qCDebug(kpLogPixmapfx) << "kpPixmapFX::scale(oldRect=" << image.rect () 0115 << ",w=" << w 0116 << ",h=" << h 0117 << ",pretty=" << pretty 0118 << ")"; 0119 #endif 0120 0121 if (w == image.width () && h == image.height ()) { 0122 return image; 0123 } 0124 0125 return image.scaled(w, h, Qt::IgnoreAspectRatio, 0126 pretty ? Qt::SmoothTransformation : Qt::FastTransformation); 0127 } 0128 0129 //--------------------------------------------------------------------- 0130 0131 // public static 0132 const double kpPixmapFX::AngleInDegreesEpsilon = 0133 qRadiansToDegrees (std::tan (1.0 / 10000.0)) 0134 / (2.0/*max error allowed*/ * 2.0/*for good measure*/); 0135 0136 0137 static void MatrixDebug (const QString& matrixName, const QTransform &matrix, 0138 int srcPixmapWidth = -1, int srcPixmapHeight = -1) 0139 { 0140 #if DEBUG_KP_PIXMAP_FX 0141 const int w = srcPixmapWidth, h = srcPixmapHeight; 0142 0143 qCDebug(kpLogPixmapfx) << matrixName << "=" << matrix; 0144 // Sometimes this precision lets us see unexpected rounding errors. 0145 fprintf (stderr, "m11=%.24f m12=%.24f m21=%.24f m22=%.24f dx=%.24f dy=%.24f\n", 0146 matrix.m11 (), matrix.m12 (), 0147 matrix.m21 (), matrix.m22 (), 0148 matrix.dx (), matrix.dy ()); 0149 if (w > 0 && h > 0) 0150 { 0151 qCDebug(kpLogPixmapfx) << "(0,0) ->" << matrix.map (QPoint (0, 0)); 0152 qCDebug(kpLogPixmapfx) << "(w-1,0) ->" << matrix.map (QPoint (w - 1, 0)); 0153 qCDebug(kpLogPixmapfx) << "(0,h-1) ->" << matrix.map (QPoint (0, h - 1)); 0154 qCDebug(kpLogPixmapfx) << "(w-1,h-1) ->" << matrix.map (QPoint (w - 1, h - 1)); 0155 } 0156 0157 #else 0158 0159 Q_UNUSED (matrixName); 0160 Q_UNUSED (matrix); 0161 Q_UNUSED (srcPixmapWidth); 0162 Q_UNUSED (srcPixmapHeight); 0163 0164 #endif // DEBUG_KP_PIXMAP_FX 0165 } 0166 0167 //--------------------------------------------------------------------- 0168 0169 // Theoretically, this should act the same as QPixmap::trueMatrix() but 0170 // it doesn't. As an example, if you rotate tests/transforms.png by 90 0171 // degrees clockwise, this returns the correct <dx> of 26 but 0172 // QPixmap::trueMatrix() returns 27. 0173 // 0174 // You should use the returned matrix to map points accurately (e.g. selection 0175 // borders). For QPainter::drawPixmap()/drawImage() + setWorldMatrix() 0176 // rendering accuracy, pass the returned matrix through QPixmap::trueMatrix() 0177 // and use that. 0178 // 0179 // TODO: If you put the flipMatrix() of tests/transforms.png through this, 0180 // the output is the same as QPixmap::trueMatrix(): <dy> is one off 0181 // (dy=27 instead of 26). 0182 // SYNC: I bet this is a Qt4 bug. 0183 static QTransform MatrixWithZeroOrigin (const QTransform &matrix, int width, int height) 0184 { 0185 #if DEBUG_KP_PIXMAP_FX 0186 qCDebug(kpLogPixmapfx) << "matrixWithZeroOrigin(w=" << width << ",h=" << height << ")"; 0187 qCDebug(kpLogPixmapfx) << "\tmatrix: m11=" << matrix.m11 () 0188 << "m12=" << matrix.m12 () 0189 << "m21=" << matrix.m21 () 0190 << "m22=" << matrix.m22 () 0191 << "dx=" << matrix.dx () 0192 << "dy=" << matrix.dy (); 0193 #endif 0194 0195 QRect mappedRect = matrix.mapRect (QRect (0, 0, width, height)); 0196 #if DEBUG_KP_PIXMAP_FX 0197 qCDebug(kpLogPixmapfx) << "\tmappedRect=" << mappedRect; 0198 #endif 0199 0200 QTransform translatedMatrix ( 0201 matrix.m11 (), matrix.m12 (), 0202 matrix.m21 (), matrix.m22 (), 0203 matrix.dx () - mappedRect.left (), matrix.dy () - mappedRect.top ()); 0204 0205 #if DEBUG_KP_PIXMAP_FX 0206 qCDebug(kpLogPixmapfx) << "\treturning" << translatedMatrix; 0207 qCDebug(kpLogPixmapfx) << "(0,0) ->" << translatedMatrix.map (QPoint (0, 0)); 0208 qCDebug(kpLogPixmapfx) << "(w-1,0) ->" << translatedMatrix.map (QPoint (width - 1, 0)); 0209 qCDebug(kpLogPixmapfx) << "(0,h-1) ->" << translatedMatrix.map (QPoint (0, height - 1)); 0210 qCDebug(kpLogPixmapfx) << "(w-1,h-1) ->" << translatedMatrix.map (QPoint (width - 1, height - 1)); 0211 #endif 0212 0213 return translatedMatrix; 0214 } 0215 0216 //--------------------------------------------------------------------- 0217 0218 static double TrueMatrixEpsilon = 0.000001; 0219 0220 // An attempt to reverse tiny rounding errors introduced by QPixmap::trueMatrix() 0221 // when skewing tests/transforms.png by 45% horizontally (with TransformPixmap() 0222 // using a QPixmap painter, prior to the 2007-10-09 change -- did not test after 0223 // the change). 0224 // Unfortunately, this does not work enough to stop the rendering errors 0225 // that follow. But it was worth a try and might still help us given the 0226 // sometimes excessive aliasing QPainter::draw{Pixmap,Image}() gives us, when 0227 // QPainter::SmoothPixmapTransform is disabled. 0228 static double TrueMatrixFixInts (double x) 0229 { 0230 if (std::fabs (x - qRound (x)) < TrueMatrixEpsilon) { 0231 return qRound (x); 0232 } 0233 0234 return x; 0235 } 0236 0237 //--------------------------------------------------------------------- 0238 0239 static QTransform TrueMatrix (const QTransform &matrix, int srcPixmapWidth, int srcPixmapHeight) 0240 { 0241 ::MatrixDebug (QStringLiteral("TrueMatrix(): org"), matrix); 0242 0243 const QTransform truMat = QPixmap::trueMatrix (matrix, srcPixmapWidth, srcPixmapHeight); 0244 ::MatrixDebug (QStringLiteral("TrueMatrix(): passed through QPixmap::trueMatrix()"), truMat); 0245 0246 const QTransform retMat ( 0247 ::TrueMatrixFixInts (truMat.m11 ()), 0248 ::TrueMatrixFixInts (truMat.m12 ()), 0249 ::TrueMatrixFixInts (truMat.m21 ()), 0250 ::TrueMatrixFixInts (truMat.m22 ()), 0251 ::TrueMatrixFixInts (truMat.dx ()), 0252 ::TrueMatrixFixInts (truMat.dy ())); 0253 ::MatrixDebug (QStringLiteral("TrueMatrix(): fixed ints"), retMat); 0254 0255 return retMat; 0256 } 0257 0258 //--------------------------------------------------------------------- 0259 0260 // Like QPixmap::transformed() but fills new areas with <backgroundColor> 0261 // (unless <backgroundColor> is invalid) and works around internal QTransform 0262 // floating point -> integer oddities, that would otherwise give fatally 0263 // incorrect results. If you don't believe me on this latter point, compare 0264 // QPixmap::transformed() to us using a flip matrix or a rotate-by-multiple-of-90 0265 // matrix on tests/transforms.png -- QPixmap::transformed()'s output is 1 0266 // pixel too high or low depending on whether the matrix is passed through 0267 // QPixmap::trueMatrix(). 0268 // 0269 // Use <targetWidth> and <targetHeight> to specify the intended output size 0270 // of the pixmap. -1 if don't care. 0271 static QImage TransformPixmap (const QImage &pm, const QTransform &transformMatrix_, 0272 const kpColor &backgroundColor, 0273 int targetWidth, int targetHeight) 0274 { 0275 QTransform transformMatrix = transformMatrix_; 0276 0277 #if DEBUG_KP_PIXMAP_FX && 1 0278 qCDebug(kpLogPixmapfx) << "kppixmapfx.cpp: TransformPixmap(pm.size=" << pm.size () 0279 << ",targetWidth=" << targetWidth 0280 << ",targetHeight=" << targetHeight 0281 << ")"; 0282 #endif 0283 0284 QRect newRect = transformMatrix.mapRect (pm.rect ()); 0285 #if DEBUG_KP_PIXMAP_FX && 1 0286 qCDebug(kpLogPixmapfx) << "\tmappedRect=" << newRect; 0287 0288 #endif 0289 0290 QTransform scaleMatrix; 0291 if (targetWidth > 0 && targetWidth != newRect.width ()) 0292 { 0293 #if DEBUG_KP_PIXMAP_FX && 1 0294 qCDebug(kpLogPixmapfx) << "\tadjusting for targetWidth"; 0295 #endif 0296 scaleMatrix.scale (double (targetWidth) / double (newRect.width ()), 1); 0297 } 0298 0299 if (targetHeight > 0 && targetHeight != newRect.height ()) 0300 { 0301 #if DEBUG_KP_PIXMAP_FX && 1 0302 qCDebug(kpLogPixmapfx) << "\tadjusting for targetHeight"; 0303 #endif 0304 scaleMatrix.scale (1, double (targetHeight) / double (newRect.height ())); 0305 } 0306 0307 if (!scaleMatrix.isIdentity ()) 0308 { 0309 #if DEBUG_KP_PIXMAP_FX && 1 0310 // TODO: What is going on here??? Why isn't matrix * working properly? 0311 QTransform wrongMatrix = transformMatrix * scaleMatrix; 0312 QTransform oldHat = transformMatrix; 0313 0314 if (targetWidth > 0 && targetWidth != newRect.width ()) { 0315 oldHat.scale (double (targetWidth) / double (newRect.width ()), 1); 0316 } 0317 if (targetHeight > 0 && targetHeight != newRect.height ()) { 0318 oldHat.scale (1, double (targetHeight) / double (newRect.height ())); 0319 } 0320 0321 QTransform altHat = transformMatrix; 0322 0323 altHat.scale ((targetWidth > 0 && targetWidth != newRect.width ()) ? double (targetWidth) / double (newRect.width ()) : 1, 0324 (targetHeight > 0 && targetHeight != newRect.height ()) ? double (targetHeight) / double (newRect.height ()) : 1); 0325 QTransform correctMatrix = scaleMatrix * transformMatrix; 0326 0327 qCDebug(kpLogPixmapfx) << "\tsupposedlyWrongMatrix: m11=" << wrongMatrix.m11 () // <<<---- this is the correct matrix??? 0328 << " m12=" << wrongMatrix.m12 () 0329 << " m21=" << wrongMatrix.m21 () 0330 << " m22=" << wrongMatrix.m22 () 0331 << " dx=" << wrongMatrix.dx () 0332 << " dy=" << wrongMatrix.dy () 0333 << " rect=" << wrongMatrix.mapRect (pm.rect ()) 0334 << "\n" 0335 << "\ti_used_to_use_thisMatrix: m11=" << oldHat.m11 () 0336 << " m12=" << oldHat.m12 () 0337 << " m21=" << oldHat.m21 () 0338 << " m22=" << oldHat.m22 () 0339 << " dx=" << oldHat.dx () 0340 << " dy=" << oldHat.dy () 0341 << " rect=" << oldHat.mapRect (pm.rect ()) 0342 << "\n" 0343 << "\tabove but scaled at the same time: m11=" << altHat.m11 () 0344 << " m12=" << altHat.m12 () 0345 << " m21=" << altHat.m21 () 0346 << " m22=" << altHat.m22 () 0347 << " dx=" << altHat.dx () 0348 << " dy=" << altHat.dy () 0349 << " rect=" << altHat.mapRect (pm.rect ()) 0350 << "\n" 0351 << "\tsupposedlyCorrectMatrix: m11=" << correctMatrix.m11 () 0352 << " m12=" << correctMatrix.m12 () 0353 << " m21=" << correctMatrix.m21 () 0354 << " m22=" << correctMatrix.m22 () 0355 << " dx=" << correctMatrix.dx () 0356 << " dy=" << correctMatrix.dy () 0357 << " rect=" << correctMatrix.mapRect (pm.rect ()); 0358 #endif 0359 0360 transformMatrix = transformMatrix * scaleMatrix; 0361 0362 newRect = transformMatrix.mapRect (pm.rect ()); 0363 #if DEBUG_KP_PIXMAP_FX && 1 0364 qCDebug(kpLogPixmapfx) << "\tnewRect after targetWidth,targetHeight adjust=" << newRect; 0365 #endif 0366 } 0367 0368 0369 ::MatrixDebug (QStringLiteral("TransformPixmap(): before trueMatrix"), transformMatrix, 0370 pm.width (), pm.height ()); 0371 #if DEBUG_KP_PIXMAP_FX && 1 0372 QMatrix oldMatrix = transformMatrix; 0373 #endif 0374 0375 // Translate the matrix to account for Qt rounding errors, 0376 // so that flipping (if it used this method) and rotating by a multiple 0377 // of 90 degrees actually work as expected (try tests/transforms.png). 0378 // 0379 // SYNC: This was not required with Qt3 so we are actually working 0380 // around a Qt4 bug/feature. 0381 // 0382 // COMPAT: Qt4's rendering with a matrix enabled is low quality anyway 0383 // but does this reduce quality even further? 0384 // 0385 // With or without it, skews by 45 degrees with the QImage 0386 // painter below look bad (with it, you get an extra transparent 0387 // line on the right; without, you get only about 1/4 of a source 0388 // line on the left). In Qt3, with TrueMatrix(), the source 0389 // image is translated 1 pixel off the destination image. 0390 // 0391 // Also, if you skew a rectangular selection, the skewed selection 0392 // border does not line up with the skewed image data. 0393 // TODO: do we need to pass <newRect> through this new matrix? 0394 transformMatrix = ::TrueMatrix (transformMatrix, 0395 pm.width (), pm.height ()); 0396 0397 #if DEBUG_KP_PIXMAP_FX && 1 0398 qCDebug(kpLogPixmapfx) << "trueMatrix changed matrix?" << (oldMatrix == transformMatrix); 0399 #endif 0400 ::MatrixDebug (QStringLiteral("TransformPixmap(): after trueMatrix"), transformMatrix, 0401 pm.width (), pm.height ()); 0402 0403 0404 QImage newQImage (targetWidth > 0 ? targetWidth : newRect.width (), 0405 targetHeight > 0 ? targetHeight : newRect.height (), 0406 QImage::Format_ARGB32_Premultiplied); 0407 0408 if ((targetWidth > 0 && targetWidth != newRect.width ()) || 0409 (targetHeight > 0 && targetHeight != newRect.height ())) 0410 { 0411 #if DEBUG_KP_PIXMAP_FX && 1 0412 qCDebug(kpLogPixmapfx) << "kppixmapfx.cpp: TransformPixmap(pm.size=" << pm.size () 0413 << ",targetWidth=" << targetWidth 0414 << ",targetHeight=" << targetHeight 0415 << ") newRect=" << newRect 0416 << " (you are a victim of rounding error)"; 0417 #endif 0418 } 0419 0420 0421 #if DEBUG_KP_PIXMAP_FX && 0 0422 qCDebug(kpLogPixmapfx) << "\ttranslate top=" << painter.xForm (QPoint (0, 0)); 0423 qCDebug(kpLogPixmapfx) << "\tmatrix: m11=" << painter.worldMatrix ().m11 () 0424 << " m12=" << painter.worldMatrix ().m12 () 0425 << " m21=" << painter.worldMatrix ().m21 () 0426 << " m22=" << painter.worldMatrix ().m22 () 0427 << " dx=" << painter.worldMatrix ().dx () 0428 << " dy=" << painter.worldMatrix ().dy () 0429 << endl; 0430 #endif 0431 0432 0433 // Note: Do _not_ use "p.setRenderHints (QPainter::SmoothPixmapTransform);" 0434 // as the user does not want their image to get blurier every 0435 // time they e.g. rotate it (especially important for multiples 0436 // of 90 degrees but also true for every other angle). Being a 0437 // pixel-based program, we generally like to preserve RGB values 0438 // and avoid unnecessary blurs -- in the worst case, we'd rather 0439 // drop pixels, than blur. 0440 QPainter p (&newQImage); 0441 { 0442 // Make sure transparent pixels are drawn into the destination image. 0443 p.setCompositionMode (QPainter::CompositionMode_Source); 0444 0445 // Fill the entire new image with the background color. 0446 if (backgroundColor.isValid ()) 0447 { 0448 p.fillRect (newQImage.rect (), backgroundColor.toQColor ()); 0449 } 0450 0451 p.setWorldTransform (transformMatrix); 0452 p.drawImage (QPoint (0, 0), pm); 0453 } 0454 p.end (); 0455 0456 #if DEBUG_KP_PIXMAP_FX && 1 0457 qCDebug(kpLogPixmapfx) << "Done"; 0458 #endif 0459 0460 return newQImage; 0461 } 0462 0463 //--------------------------------------------------------------------- 0464 0465 // public static 0466 QTransform kpPixmapFX::skewMatrix (int width, int height, double hangle, double vangle) 0467 { 0468 if (std::fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && 0469 std::fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon) 0470 { 0471 return {}; 0472 } 0473 0474 0475 /* Diagram for completeness :) 0476 * 0477 * |---------- w ----------| 0478 * (0,0) 0479 * _ _______________________ (w,0) 0480 * | |\~_ va | 0481 * | | \ ~_ | 0482 * | |ha\ ~__ | 0483 * | \ ~__ | dy 0484 * h | \ ~___ | 0485 * | \ ~___ | 0486 * | | \ ~___| (w,w*tan(va)=dy) 0487 * | | \ * \ 0488 * _ |________\________|_____|\ vertical shear factor 0489 * (0,h) dx ^~_ | \ | 0490 * | ~_ \________\________ General Point (x,y) V 0491 * | ~__ \ Skewed Point (x + y*tan(ha),y + x*tan(va)) 0492 * (h*tan(ha)=dx,h) ~__ \ ^ 0493 * ~___ \ | 0494 * ~___ \ horizontal shear factor 0495 * Key: ~___\ 0496 * ha = hangle (w + h*tan(ha)=w+dx,h + w*tan(va)=w+dy) 0497 * va = vangle 0498 * 0499 * Skewing really just twists a rectangle into a parallelogram. 0500 * 0501 */ 0502 0503 //QTransform matrix (1, tan (KP_DEGREES_TO_RADIANS (vangle)), tan (KP_DEGREES_TO_RADIANS (hangle)), 1, 0, 0); 0504 // I think this is clearer than above :) 0505 QTransform matrix; 0506 matrix.shear (std::tan (qDegreesToRadians (hangle)), 0507 std::tan (qDegreesToRadians (vangle))); 0508 0509 return ::MatrixWithZeroOrigin (matrix, width, height); 0510 } 0511 0512 //--------------------------------------------------------------------- 0513 0514 // public static 0515 QTransform kpPixmapFX::skewMatrix (const QImage &pixmap, double hangle, double vangle) 0516 { 0517 return kpPixmapFX::skewMatrix (pixmap.width (), pixmap.height (), hangle, vangle); 0518 } 0519 0520 //--------------------------------------------------------------------- 0521 0522 0523 // public static 0524 void kpPixmapFX::skew (QImage *destPtr, double hangle, double vangle, 0525 const kpColor &backgroundColor, 0526 int targetWidth, int targetHeight) 0527 { 0528 if (!destPtr) { 0529 return; 0530 } 0531 0532 *destPtr = kpPixmapFX::skew (*destPtr, hangle, vangle, 0533 backgroundColor, 0534 targetWidth, targetHeight); 0535 } 0536 0537 //--------------------------------------------------------------------- 0538 0539 // public static 0540 QImage kpPixmapFX::skew (const QImage &pm, double hangle, double vangle, 0541 const kpColor &backgroundColor, 0542 int targetWidth, int targetHeight) 0543 { 0544 #if DEBUG_KP_PIXMAP_FX 0545 qCDebug(kpLogPixmapfx) << "kpPixmapFX::skew() pm.width=" << pm.width () 0546 << " pm.height=" << pm.height () 0547 << " hangle=" << hangle 0548 << " vangle=" << vangle 0549 << " targetWidth=" << targetWidth 0550 << " targetHeight=" << targetHeight; 0551 #endif 0552 0553 if (std::fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && 0554 std::fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && 0555 (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/) 0556 { 0557 return pm; 0558 } 0559 0560 if (std::fabs (hangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon || 0561 std::fabs (vangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon) 0562 { 0563 qCCritical(kpLogPixmapfx) << "kpPixmapFX::skew() passed hangle and/or vangle out of range (-90 < x < 90)"; 0564 return pm; 0565 } 0566 0567 0568 QTransform matrix = skewMatrix (pm, hangle, vangle); 0569 0570 return ::TransformPixmap (pm, matrix, backgroundColor, targetWidth, targetHeight); 0571 } 0572 0573 //--------------------------------------------------------------------- 0574 0575 0576 // public static 0577 QTransform kpPixmapFX::rotateMatrix (int width, int height, double angle) 0578 { 0579 if (std::fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon) 0580 { 0581 return {}; 0582 } 0583 0584 QTransform matrix; 0585 matrix.translate (width / 2, height / 2); 0586 matrix.rotate (angle); 0587 0588 return ::MatrixWithZeroOrigin (matrix, width, height); 0589 } 0590 0591 //--------------------------------------------------------------------- 0592 0593 // public static 0594 QTransform kpPixmapFX::rotateMatrix (const QImage &pixmap, double angle) 0595 { 0596 return kpPixmapFX::rotateMatrix (pixmap.width (), pixmap.height (), angle); 0597 } 0598 0599 //--------------------------------------------------------------------- 0600 0601 0602 // public static 0603 bool kpPixmapFX::isLosslessRotation (double angle) 0604 { 0605 const double angleIn = angle; 0606 0607 // Reflect angle into positive if negative 0608 if (angle < 0) { 0609 angle = -angle; 0610 } 0611 0612 // Remove multiples of 90 to make sure 0 <= angle <= 90 0613 angle -= (static_cast<int> (angle)) / 90 * 90; 0614 0615 // "Impossible" situation? 0616 if (angle < 0 || angle > 90) 0617 { 0618 qCCritical(kpLogPixmapfx) << "kpPixmapFX::isLosslessRotation(" << angleIn 0619 << ") result=" << angle; 0620 return false; // better safe than sorry 0621 } 0622 0623 const bool ret = (angle < kpPixmapFX::AngleInDegreesEpsilon || 0624 90 - angle < kpPixmapFX::AngleInDegreesEpsilon); 0625 #if DEBUG_KP_PIXMAP_FX 0626 qCDebug(kpLogPixmapfx) << "kpPixmapFX::isLosslessRotation(" << angleIn << ")" 0627 << " residual angle=" << angle 0628 << " returning " << ret; 0629 #endif 0630 return ret; 0631 } 0632 0633 //--------------------------------------------------------------------- 0634 0635 0636 // public static 0637 void kpPixmapFX::rotate (QImage *destPtr, double angle, 0638 const kpColor &backgroundColor, 0639 int targetWidth, int targetHeight) 0640 { 0641 if (!destPtr) { 0642 return; 0643 } 0644 0645 *destPtr = kpPixmapFX::rotate (*destPtr, angle, 0646 backgroundColor, 0647 targetWidth, targetHeight); 0648 } 0649 0650 //--------------------------------------------------------------------- 0651 0652 // public static 0653 QImage kpPixmapFX::rotate (const QImage &pm, double angle, 0654 const kpColor &backgroundColor, 0655 int targetWidth, int targetHeight) 0656 { 0657 if (std::fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon && 0658 (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/) 0659 { 0660 return pm; 0661 } 0662 0663 0664 QTransform matrix = rotateMatrix (pm, angle); 0665 0666 return ::TransformPixmap (pm, matrix, backgroundColor, targetWidth, targetHeight); 0667 } 0668 0669 //---------------------------------------------------------------------