krita

kis_rotate_visitor.cc

00001 /*
00002  *  Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 #include <math.h>
00019 #include <qapplication.h>
00020 #include <qwmatrix.h>
00021 #include <qrect.h>
00022 
00023 #include <kdebug.h>
00024 #include <klocale.h>
00025 
00026 #include "kis_paint_device.h"
00027 #include "kis_rotate_visitor.h"
00028 #include "kis_progress_display_interface.h"
00029 #include "kis_iterators_pixel.h"
00030 #include "kis_selection.h"
00031 #include "kis_painter.h"
00032 
00033 void KisRotateVisitor::rotate(double angle, bool rotateAboutImageCentre, KisProgressDisplayInterface *progress)
00034 {
00035     KisPoint centreOfRotation;
00036 
00037     if (rotateAboutImageCentre) {
00038         centreOfRotation = KisPoint(m_dev->image()->width() / 2.0,  m_dev->image()->height() / 2.0);
00039     } else {
00040         QRect r = m_dev->exactBounds();
00041         centreOfRotation = KisPoint(r.x() + (r.width() / 2.0), r.y() + (r.height() / 2.0));
00042     }
00043 
00044     m_progress = progress;
00045 
00046     KisPaintDeviceSP rotated = rotate(m_dev, angle, centreOfRotation);
00047 
00048     if (!m_dev->hasSelection()) {
00049         // Clear everything
00050         m_dev->clear();
00051     } else {
00052         // Clear selected pixels
00053         m_dev->clearSelection();
00054     }
00055 
00056     KisPainter p(m_dev);
00057     QRect r = rotated->extent();
00058 
00059     // OVER ipv COPY
00060     p.bitBlt(r.x(), r.y(), COMPOSITE_OVER, rotated, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
00061     p.end();
00062 }
00063 
00064 void KisRotateVisitor::shear(double angleX, double angleY, KisProgressDisplayInterface *progress)
00065 {
00066     const double pi=3.1415926535897932385;
00067     double thetaX = angleX * pi / 180;
00068     double shearX = tan(thetaX);
00069     double thetaY = angleY * pi / 180;
00070     double shearY = tan(thetaY);
00071 
00072     QRect r = m_dev->exactBounds();
00073 
00074     const int xShearSteps = r.height();
00075     const int yShearSteps = r.width();
00076 
00077     m_progress = progress;
00078     initProgress(xShearSteps + yShearSteps);
00079 
00080 
00081     KisPaintDeviceSP sheared;
00082 
00083     if (m_dev->hasSelection()) {
00084         sheared = new KisPaintDevice(m_dev->colorSpace(), "sheared");
00085         KisPainter p1(sheared);
00086         p1.bltSelection(r.x(), r.y(), COMPOSITE_OVER, m_dev, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
00087         p1.end();
00088          sheared = xShear(sheared, shearX);
00089     }
00090     else {
00091         sheared = xShear(m_dev, shearX);
00092     }
00093 
00094      sheared = yShear(sheared, shearY);
00095 
00096      if (!m_dev->hasSelection()) {
00097         m_dev->clear();
00098      } else {
00099          // Clear selected pixels
00100          m_dev->clearSelection();
00101      }
00102 
00103     KisPainter p2(m_dev);
00104     r = sheared->extent();
00105 
00106     p2.bitBlt(r.x(), r.y(), COMPOSITE_OVER, sheared, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
00107     p2.end();
00108 
00109     setProgressDone();
00110 }
00111 
00112 KisPaintDeviceSP KisRotateVisitor::rotateRight90(KisPaintDeviceSP src)
00113 {
00114     KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateright90");
00115     dst->setX(src->getX());
00116     dst->setY(src->getY());
00117 
00118     Q_INT32 pixelSize = src->pixelSize();
00119     QRect r = src->exactBounds();
00120     Q_INT32 x = 0;
00121 
00122     for (Q_INT32 y = r.bottom(); y >= r.top(); --y) {
00123         KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false);
00124         KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true);
00125 
00126             while (!hit.isDone()) {
00127             if (hit.isSelected())  {
00128                 memcpy(vit.rawData(), hit.rawData(), pixelSize);
00129             }
00130             ++hit;
00131             ++vit;
00132         }
00133         ++x;
00134         incrementProgress();
00135     }
00136 
00137     return dst;
00138 }
00139 
00140 KisPaintDeviceSP KisRotateVisitor::rotateLeft90(KisPaintDeviceSP src)
00141 {
00142     KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateleft90");
00143 
00144     Q_INT32 pixelSize = src->pixelSize();
00145     QRect r = src->exactBounds();
00146     Q_INT32 x = 0;
00147 
00148     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00149         // Read the horizontal line from back to front, write onto the vertical column
00150         KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false);
00151         KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
00152 
00153         hit += r.width() - 1;
00154         while (!vit.isDone()) {
00155             if (hit.isSelected()) {
00156                 memcpy(vit.rawData(), hit.rawData(), pixelSize);
00157             }
00158             --hit;
00159             ++vit;
00160         }
00161         ++x;
00162         incrementProgress();
00163     }
00164 
00165     return dst;
00166 }
00167 
00168 KisPaintDeviceSP KisRotateVisitor::rotate180(KisPaintDeviceSP src)
00169 {
00170     KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotate180");
00171     dst->setX(src->getX());
00172     dst->setY(src->getY());
00173 
00174     Q_INT32 pixelSize = src->pixelSize();
00175     QRect r = src->exactBounds();
00176 
00177     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00178         KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), false);
00179         KisHLineIterator dstIt = dst->createHLineIterator( -r.x() - r.width(), -y, r.width(), true);
00180 
00181         srcIt += r.width() - 1;
00182         while (!dstIt.isDone()) {
00183             if (srcIt.isSelected())  {
00184                 memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize);
00185             }
00186             --srcIt;
00187             ++dstIt;
00188         }
00189         incrementProgress();
00190     }
00191 
00192     return dst;
00193 }
00194 
00195 KisPaintDeviceSP KisRotateVisitor::rotate(KisPaintDeviceSP src, double angle, KisPoint centreOfRotation)
00196 {
00197     const double pi = 3.1415926535897932385;
00198 
00199     if (angle >= 315 && angle < 360) {
00200         angle = angle - 360;
00201     } else if (angle > -360 && angle < -45) {
00202         angle = angle + 360;
00203     }
00204 
00205     QRect r = src->exactBounds();
00206 
00207     const int xShearSteps = r.height();
00208     const int yShearSteps = r.width();
00209     const int fixedRotateSteps = r.height();
00210 
00211     KisPaintDeviceSP dst;
00212 
00213     if (angle == 90) {
00214         initProgress(fixedRotateSteps);
00215         dst = rotateRight90(src);
00216     } else if (angle == 180) {
00217         initProgress(fixedRotateSteps);
00218         dst = rotate180(src);
00219     } else if (angle == 270) {
00220         initProgress(fixedRotateSteps);
00221         dst = rotateLeft90(src);
00222     } else {
00223         double theta;
00224 
00225         if (angle >= -45 && angle < 45) {
00226 
00227             theta = angle * pi / 180;
00228             dst = src;
00229             initProgress(yShearSteps + (2 * xShearSteps));
00230         }
00231         else if (angle >= 45 && angle < 135) {
00232 
00233             initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
00234             dst = rotateRight90(src);
00235             theta = (angle - 90) * pi / 180;
00236         }
00237         else if (angle >= 135 && angle < 225) {
00238 
00239             initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
00240             dst = rotate180(src);
00241             theta = (angle - 180) * pi / 180;
00242 
00243         } else {
00244 
00245             initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
00246             dst = rotateLeft90(src);
00247             theta = (angle - 270) * pi / 180;
00248         }
00249 
00250         double shearX = tan(theta / 2);
00251         double shearY = sin(theta);
00252 
00253         //first perform a shear along the x-axis by tan(theta/2)
00254         dst = xShear(dst, shearX);
00255         //next perform a shear along the y-axis by sin(theta)
00256         dst = yShear(dst, shearY);
00257         //again perform a shear along the x-axis by tan(theta/2)
00258         dst = xShear(dst, shearX);
00259     }
00260 
00261     double sinAngle = sin(angle * pi / 180);
00262     double cosAngle = cos(angle * pi / 180);
00263 
00264     KisPoint rotatedCentreOfRotation(
00265                                 centreOfRotation.x() * cosAngle - centreOfRotation.y() * sinAngle,
00266                                 centreOfRotation.x() * sinAngle + centreOfRotation.y() * cosAngle);
00267 
00268     dst->setX((Q_INT32)(dst->getX() + centreOfRotation.x() - rotatedCentreOfRotation.x()));
00269     dst->setY((Q_INT32)(dst->getY() + centreOfRotation.y() - rotatedCentreOfRotation.y()));
00270 
00271     setProgressDone();
00272 
00273     return dst;
00274 }
00275 
00276 KisPaintDeviceSP KisRotateVisitor::xShear(KisPaintDeviceSP src, double shearX)
00277 {
00278     KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "xShear");
00279     dst->setX(src->getX());
00280     dst->setY(src->getY());
00281 
00282     QRect r = src->exactBounds();
00283 
00284         double displacement;
00285         Q_INT32 displacementInt;
00286         double weight;
00287 
00288     for (Q_INT32 y = r.top(); y <= r.bottom(); y++) {
00289 
00290         //calculate displacement
00291         displacement = -y * shearX;
00292 
00293         displacementInt = (Q_INT32)(floor(displacement));
00294         weight = displacement - displacementInt;
00295 
00296         Q_UINT8 pixelWeights[2];
00297 
00298         pixelWeights[0] = static_cast<Q_UINT8>(weight * 255 + 0.5);
00299         pixelWeights[1] = 255 - pixelWeights[0];
00300 
00301         KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width() + 1, false);
00302         KisHLineIteratorPixel leftSrcIt = src->createHLineIterator(r.x() - 1, y, r.width() + 1, false);
00303         KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x() + displacementInt, y, r.width() + 1, true);
00304 
00305         while (!srcIt.isDone()) {
00306 
00307             const Q_UINT8 *pixelPtrs[2];
00308 
00309             pixelPtrs[0] = leftSrcIt.rawData();
00310             pixelPtrs[1] = srcIt.rawData();
00311 
00312             src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData());
00313 
00314             ++srcIt;
00315             ++leftSrcIt;
00316             ++dstIt;
00317         }
00318         incrementProgress();
00319     }
00320 
00321     return dst;
00322 }
00323 
00324 KisPaintDeviceSP KisRotateVisitor::yShear(KisPaintDeviceSP src, double shearY)
00325 {
00326     KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "yShear");
00327     dst->setX(src->getX());
00328     dst->setY(src->getY());
00329 
00330     QRect r = src->exactBounds();
00331 
00332         double displacement;
00333         Q_INT32 displacementInt;
00334         double weight;
00335 
00336     for (Q_INT32 x = r.left(); x <= r.right(); x++) {
00337 
00338         //calculate displacement
00339         displacement = x * shearY;
00340 
00341         displacementInt = (Q_INT32)(floor(displacement));
00342         weight = displacement - displacementInt;
00343 
00344         Q_UINT8 pixelWeights[2];
00345 
00346         pixelWeights[0] = static_cast<Q_UINT8>(weight * 255 + 0.5);
00347         pixelWeights[1] = 255 - pixelWeights[0];
00348 
00349         KisVLineIteratorPixel srcIt = src->createVLineIterator(x, r.y(), r.height() + 1, false);
00350         KisVLineIteratorPixel leftSrcIt = src->createVLineIterator(x, r.y() - 1, r.height() + 1, false);
00351         KisVLineIteratorPixel dstIt = dst->createVLineIterator(x, r.y() + displacementInt, r.height() + 1, true);
00352 
00353         while (!srcIt.isDone()) {
00354 
00355             const Q_UINT8 *pixelPtrs[2];
00356 
00357             pixelPtrs[0] = leftSrcIt.rawData();
00358             pixelPtrs[1] = srcIt.rawData();
00359 
00360             src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData());
00361 
00362             ++srcIt;
00363             ++leftSrcIt;
00364             ++dstIt;
00365         }
00366         incrementProgress();
00367     }
00368 
00369         return dst;
00370 }
00371 
00372 void KisRotateVisitor::initProgress(Q_INT32 totalSteps)
00373 {
00374     if (!m_progress) return;
00375 
00376     m_progressTotalSteps = totalSteps;
00377     m_progressStep = 0;
00378     m_lastProgressPerCent = 0;
00379 
00380 
00381     m_progress->setSubject(this, true, false);
00382     emit notifyProgress(0);
00383 
00384 }
00385 
00386 void KisRotateVisitor::incrementProgress()
00387 {
00388     if (!m_progress) return;
00389 
00390     m_progressStep++;
00391     Q_INT32 progressPerCent = (m_progressStep * 100) / m_progressTotalSteps;
00392 
00393     if (progressPerCent != m_lastProgressPerCent) {
00394         m_lastProgressPerCent = progressPerCent;
00395         emit notifyProgress(progressPerCent);
00396     }
00397 }
00398 
00399 void KisRotateVisitor::setProgressDone()
00400 {
00401     if (!m_progress) return;
00402 
00403     emit notifyProgressDone();
00404 }
00405 
00406 
KDE Home | KDE Accessibility Home | Description of Access Keys