Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
koffice/chalk/core/kis_rotate_visitor.cc

407 righe
12 KiB

/*
* 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();
}