summaryrefslogtreecommitdiffstats
path: root/chalk/core/kis_rotate_visitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/core/kis_rotate_visitor.cpp')
-rw-r--r--chalk/core/kis_rotate_visitor.cpp406
1 files changed, 406 insertions, 0 deletions
diff --git a/chalk/core/kis_rotate_visitor.cpp b/chalk/core/kis_rotate_visitor.cpp
new file mode 100644
index 000000000..abb47a056
--- /dev/null
+++ b/chalk/core/kis_rotate_visitor.cpp
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <math.h>
+#include <tqapplication.h>
+#include <tqwmatrix.h>
+#include <tqrect.h>
+
+#include <kdebug.h>
+#include <tdelocale.h>
+
+#include "kis_paint_device.h"
+#include "kis_rotate_visitor.h"
+#include "kis_progress_display_interface.h"
+#include "kis_iterators_pixel.h"
+#include "kis_selection.h"
+#include "kis_painter.h"
+
+void KisRotateVisitor::rotate(double angle, bool rotateAboutImageCentre, KisProgressDisplayInterface *progress)
+{
+ KisPoint centreOfRotation;
+
+ if (rotateAboutImageCentre) {
+ centreOfRotation = KisPoint(m_dev->image()->width() / 2.0, m_dev->image()->height() / 2.0);
+ } else {
+ TQRect r = m_dev->exactBounds();
+ centreOfRotation = KisPoint(r.x() + (r.width() / 2.0), r.y() + (r.height() / 2.0));
+ }
+
+ m_progress = progress;
+
+ KisPaintDeviceSP rotated = rotate(m_dev, angle, centreOfRotation);
+
+ if (!m_dev->hasSelection()) {
+ // Clear everything
+ m_dev->clear();
+ } else {
+ // Clear selected pixels
+ m_dev->clearSelection();
+ }
+
+ KisPainter p(m_dev);
+ TQRect r = rotated->extent();
+
+ // OVER ipv COPY
+ p.bitBlt(r.x(), r.y(), COMPOSITE_OVER, rotated, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
+ p.end();
+}
+
+void KisRotateVisitor::shear(double angleX, double angleY, KisProgressDisplayInterface *progress)
+{
+ const double pi=3.1415926535897932385;
+ double thetaX = angleX * pi / 180;
+ double shearX = tan(thetaX);
+ double thetaY = angleY * pi / 180;
+ double shearY = tan(thetaY);
+
+ TQRect r = m_dev->exactBounds();
+
+ const int xShearSteps = r.height();
+ const int yShearSteps = r.width();
+
+ m_progress = progress;
+ initProgress(xShearSteps + yShearSteps);
+
+
+ KisPaintDeviceSP sheared;
+
+ if (m_dev->hasSelection()) {
+ sheared = new KisPaintDevice(m_dev->colorSpace(), "sheared");
+ KisPainter p1(sheared);
+ p1.bltSelection(r.x(), r.y(), COMPOSITE_OVER, m_dev, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
+ p1.end();
+ sheared = xShear(sheared, shearX);
+ }
+ else {
+ sheared = xShear(m_dev, shearX);
+ }
+
+ sheared = yShear(sheared, shearY);
+
+ if (!m_dev->hasSelection()) {
+ m_dev->clear();
+ } else {
+ // Clear selected pixels
+ m_dev->clearSelection();
+ }
+
+ KisPainter p2(m_dev);
+ r = sheared->extent();
+
+ p2.bitBlt(r.x(), r.y(), COMPOSITE_OVER, sheared, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
+ p2.end();
+
+ setProgressDone();
+}
+
+KisPaintDeviceSP KisRotateVisitor::rotateRight90(KisPaintDeviceSP src)
+{
+ KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateright90");
+ dst->setX(src->getX());
+ dst->setY(src->getY());
+
+ TQ_INT32 pixelSize = src->pixelSize();
+ TQRect r = src->exactBounds();
+ TQ_INT32 x = 0;
+
+ for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) {
+ KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false);
+ KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true);
+
+ while (!hit.isDone()) {
+ if (hit.isSelected()) {
+ memcpy(vit.rawData(), hit.rawData(), pixelSize);
+ }
+ ++hit;
+ ++vit;
+ }
+ ++x;
+ incrementProgress();
+ }
+
+ return dst;
+}
+
+KisPaintDeviceSP KisRotateVisitor::rotateLeft90(KisPaintDeviceSP src)
+{
+ KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateleft90");
+
+ TQ_INT32 pixelSize = src->pixelSize();
+ TQRect r = src->exactBounds();
+ TQ_INT32 x = 0;
+
+ for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) {
+ // Read the horizontal line from back to front, write onto the vertical column
+ KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false);
+ KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
+
+ hit += r.width() - 1;
+ while (!vit.isDone()) {
+ if (hit.isSelected()) {
+ memcpy(vit.rawData(), hit.rawData(), pixelSize);
+ }
+ --hit;
+ ++vit;
+ }
+ ++x;
+ incrementProgress();
+ }
+
+ return dst;
+}
+
+KisPaintDeviceSP KisRotateVisitor::rotate180(KisPaintDeviceSP src)
+{
+ KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotate180");
+ dst->setX(src->getX());
+ dst->setY(src->getY());
+
+ TQ_INT32 pixelSize = src->pixelSize();
+ TQRect r = src->exactBounds();
+
+ for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), false);
+ KisHLineIterator dstIt = dst->createHLineIterator( -r.x() - r.width(), -y, r.width(), true);
+
+ srcIt += r.width() - 1;
+ while (!dstIt.isDone()) {
+ if (srcIt.isSelected()) {
+ memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize);
+ }
+ --srcIt;
+ ++dstIt;
+ }
+ incrementProgress();
+ }
+
+ return dst;
+}
+
+KisPaintDeviceSP KisRotateVisitor::rotate(KisPaintDeviceSP src, double angle, KisPoint centreOfRotation)
+{
+ const double pi = 3.1415926535897932385;
+
+ if (angle >= 315 && angle < 360) {
+ angle = angle - 360;
+ } else if (angle > -360 && angle < -45) {
+ angle = angle + 360;
+ }
+
+ TQRect r = src->exactBounds();
+
+ const int xShearSteps = r.height();
+ const int yShearSteps = r.width();
+ const int fixedRotateSteps = r.height();
+
+ KisPaintDeviceSP dst;
+
+ if (angle == 90) {
+ initProgress(fixedRotateSteps);
+ dst = rotateRight90(src);
+ } else if (angle == 180) {
+ initProgress(fixedRotateSteps);
+ dst = rotate180(src);
+ } else if (angle == 270) {
+ initProgress(fixedRotateSteps);
+ dst = rotateLeft90(src);
+ } else {
+ double theta;
+
+ if (angle >= -45 && angle < 45) {
+
+ theta = angle * pi / 180;
+ dst = src;
+ initProgress(yShearSteps + (2 * xShearSteps));
+ }
+ else if (angle >= 45 && angle < 135) {
+
+ initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
+ dst = rotateRight90(src);
+ theta = (angle - 90) * pi / 180;
+ }
+ else if (angle >= 135 && angle < 225) {
+
+ initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
+ dst = rotate180(src);
+ theta = (angle - 180) * pi / 180;
+
+ } else {
+
+ initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps));
+ dst = rotateLeft90(src);
+ theta = (angle - 270) * pi / 180;
+ }
+
+ double shearX = tan(theta / 2);
+ double shearY = sin(theta);
+
+ //first perform a shear along the x-axis by tan(theta/2)
+ dst = xShear(dst, shearX);
+ //next perform a shear along the y-axis by sin(theta)
+ dst = yShear(dst, shearY);
+ //again perform a shear along the x-axis by tan(theta/2)
+ dst = xShear(dst, shearX);
+ }
+
+ double sinAngle = sin(angle * pi / 180);
+ double cosAngle = cos(angle * pi / 180);
+
+ KisPoint rotatedCentreOfRotation(
+ centreOfRotation.x() * cosAngle - centreOfRotation.y() * sinAngle,
+ centreOfRotation.x() * sinAngle + centreOfRotation.y() * cosAngle);
+
+ dst->setX((TQ_INT32)(dst->getX() + centreOfRotation.x() - rotatedCentreOfRotation.x()));
+ dst->setY((TQ_INT32)(dst->getY() + centreOfRotation.y() - rotatedCentreOfRotation.y()));
+
+ setProgressDone();
+
+ return dst;
+}
+
+KisPaintDeviceSP KisRotateVisitor::xShear(KisPaintDeviceSP src, double shearX)
+{
+ KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "xShear");
+ dst->setX(src->getX());
+ dst->setY(src->getY());
+
+ TQRect r = src->exactBounds();
+
+ double displacement;
+ TQ_INT32 displacementInt;
+ double weight;
+
+ for (TQ_INT32 y = r.top(); y <= r.bottom(); y++) {
+
+ //calculate displacement
+ displacement = -y * shearX;
+
+ displacementInt = (TQ_INT32)(floor(displacement));
+ weight = displacement - displacementInt;
+
+ TQ_UINT8 pixelWeights[2];
+
+ pixelWeights[0] = static_cast<TQ_UINT8>(weight * 255 + 0.5);
+ pixelWeights[1] = 255 - pixelWeights[0];
+
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width() + 1, false);
+ KisHLineIteratorPixel leftSrcIt = src->createHLineIterator(r.x() - 1, y, r.width() + 1, false);
+ KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x() + displacementInt, y, r.width() + 1, true);
+
+ while (!srcIt.isDone()) {
+
+ const TQ_UINT8 *pixelPtrs[2];
+
+ pixelPtrs[0] = leftSrcIt.rawData();
+ pixelPtrs[1] = srcIt.rawData();
+
+ src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData());
+
+ ++srcIt;
+ ++leftSrcIt;
+ ++dstIt;
+ }
+ incrementProgress();
+ }
+
+ return dst;
+}
+
+KisPaintDeviceSP KisRotateVisitor::yShear(KisPaintDeviceSP src, double shearY)
+{
+ KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "yShear");
+ dst->setX(src->getX());
+ dst->setY(src->getY());
+
+ TQRect r = src->exactBounds();
+
+ double displacement;
+ TQ_INT32 displacementInt;
+ double weight;
+
+ for (TQ_INT32 x = r.left(); x <= r.right(); x++) {
+
+ //calculate displacement
+ displacement = x * shearY;
+
+ displacementInt = (TQ_INT32)(floor(displacement));
+ weight = displacement - displacementInt;
+
+ TQ_UINT8 pixelWeights[2];
+
+ pixelWeights[0] = static_cast<TQ_UINT8>(weight * 255 + 0.5);
+ pixelWeights[1] = 255 - pixelWeights[0];
+
+ KisVLineIteratorPixel srcIt = src->createVLineIterator(x, r.y(), r.height() + 1, false);
+ KisVLineIteratorPixel leftSrcIt = src->createVLineIterator(x, r.y() - 1, r.height() + 1, false);
+ KisVLineIteratorPixel dstIt = dst->createVLineIterator(x, r.y() + displacementInt, r.height() + 1, true);
+
+ while (!srcIt.isDone()) {
+
+ const TQ_UINT8 *pixelPtrs[2];
+
+ pixelPtrs[0] = leftSrcIt.rawData();
+ pixelPtrs[1] = srcIt.rawData();
+
+ src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData());
+
+ ++srcIt;
+ ++leftSrcIt;
+ ++dstIt;
+ }
+ incrementProgress();
+ }
+
+ return dst;
+}
+
+void KisRotateVisitor::initProgress(TQ_INT32 totalSteps)
+{
+ if (!m_progress) return;
+
+ m_progressTotalSteps = totalSteps;
+ m_progressStep = 0;
+ m_lastProgressPerCent = 0;
+
+
+ m_progress->setSubject(this, true, false);
+ emit notifyProgress(0);
+
+}
+
+void KisRotateVisitor::incrementProgress()
+{
+ if (!m_progress) return;
+
+ m_progressStep++;
+ TQ_INT32 progressPerCent = (m_progressStep * 100) / m_progressTotalSteps;
+
+ if (progressPerCent != m_lastProgressPerCent) {
+ m_lastProgressPerCent = progressPerCent;
+ emit notifyProgress(progressPerCent);
+ }
+}
+
+void KisRotateVisitor::setProgressDone()
+{
+ if (!m_progress) return;
+
+ emit notifyProgressDone();
+}
+
+