You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
21 KiB
677 lines
21 KiB
/*
|
|
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de> filters
|
|
* Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
|
|
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> right angle rotators
|
|
*
|
|
* 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 <kdebug.h>
|
|
#include <tdelocale.h>
|
|
|
|
#include "kis_debug_areas.h"
|
|
#include "kis_paint_device.h"
|
|
#include "kis_selection.h"
|
|
#include "kis_transform_worker.h"
|
|
#include "kis_progress_display_interface.h"
|
|
#include "kis_iterators_pixel.h"
|
|
#include "kis_filter_strategy.h"
|
|
#include "kis_layer.h"
|
|
#include "kis_painter.h"
|
|
|
|
KisTransformWorker::KisTransformWorker(KisPaintDeviceSP dev, double xscale, double yscale,
|
|
double xshear, double yshear, double rotation,
|
|
TQ_INT32 xtranslate, TQ_INT32 ytranslate,
|
|
KisProgressDisplayInterface *progress, KisFilterStrategy *filter, bool fixBorderAlpha)
|
|
{
|
|
m_dev= dev;
|
|
m_xscale = xscale;
|
|
m_yscale = yscale;
|
|
m_xshear = xshear;
|
|
m_yshear = yshear;
|
|
m_rotation = rotation,
|
|
m_xtranslate = xtranslate;
|
|
m_ytranslate = ytranslate;
|
|
m_progress = progress;
|
|
m_filter = filter;
|
|
m_fixBorderAlpha = fixBorderAlpha;
|
|
}
|
|
|
|
void KisTransformWorker::rotateNone(KisPaintDeviceSP src, KisPaintDeviceSP dst)
|
|
{
|
|
KisSelectionSP dstSelection;
|
|
TQ_INT32 pixelSize = src->pixelSize();
|
|
TQRect r;
|
|
KisColorSpace *cs = src->colorSpace();
|
|
|
|
if(src->hasSelection())
|
|
{
|
|
r = src->selection()->selectedExactRect();
|
|
dstSelection = dst->selection();
|
|
}
|
|
else
|
|
{
|
|
r = src->exactBounds();
|
|
dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
|
|
}
|
|
|
|
KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), r.top(), r.width(), true);
|
|
KisHLineIterator vit = dst->createHLineIterator(r.x(), r.top(), r.width(), true);
|
|
KisHLineIterator dstSelIt = dstSelection->createHLineIterator(r.x(), r.top(), r.width(), true);
|
|
for (TQ_INT32 i = 0; i < r.height(); ++i) {
|
|
while (!hit.isDone()) {
|
|
if (hit.isSelected()) {
|
|
memcpy(vit.rawData(), hit.rawData(), pixelSize);
|
|
|
|
// XXX: Should set alpha = alpha*(1-selectedness)
|
|
cs->setAlpha(hit.rawData(), 0, 1);
|
|
}
|
|
*(dstSelIt.rawData()) = hit.selectedness();
|
|
++hit;
|
|
++vit;
|
|
++dstSelIt;
|
|
}
|
|
hit.nextRow();
|
|
vit.nextRow();
|
|
dstSelIt.nextRow();
|
|
|
|
//progress info
|
|
m_progressStep += r.width();
|
|
if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
|
|
{
|
|
m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
|
|
emit notifyProgress(m_lastProgressReport);
|
|
}
|
|
if (m_cancelRequested) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisTransformWorker::rotateRight90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
|
|
{
|
|
KisSelectionSP dstSelection;
|
|
TQ_INT32 pixelSize = src->pixelSize();
|
|
TQRect r;
|
|
KisColorSpace *cs = src->colorSpace();
|
|
|
|
if(src->hasSelection())
|
|
{
|
|
r = src->selection()->selectedExactRect();
|
|
dstSelection = dst->selection();
|
|
}
|
|
else
|
|
{
|
|
r = src->exactBounds();
|
|
dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
|
|
}
|
|
|
|
for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) {
|
|
KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true);
|
|
KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true);
|
|
KisVLineIterator dstSelIt = dstSelection->createVLineIterator(-y, r.x(), r.width(), true);
|
|
|
|
while (!hit.isDone()) {
|
|
if (hit.isSelected()) {
|
|
memcpy(vit.rawData(), hit.rawData(), pixelSize);
|
|
|
|
// XXX: Should set alpha = alpha*(1-selectedness)
|
|
cs->setAlpha(hit.rawData(), 0, 1);
|
|
}
|
|
*(dstSelIt.rawData()) = hit.selectedness();
|
|
++hit;
|
|
++vit;
|
|
++dstSelIt;
|
|
}
|
|
|
|
//progress info
|
|
m_progressStep += r.width();
|
|
if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
|
|
{
|
|
m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
|
|
emit notifyProgress(m_lastProgressReport);
|
|
}
|
|
if (m_cancelRequested) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisTransformWorker::rotateLeft90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
|
|
{
|
|
kdDebug() << "rotateLeft90 called\n";
|
|
KisSelectionSP dstSelection;
|
|
TQ_INT32 pixelSize = src->pixelSize();
|
|
TQRect r;
|
|
KisColorSpace *cs = src->colorSpace();
|
|
|
|
if(src->hasSelection())
|
|
{
|
|
r = src->selection()->selectedExactRect();
|
|
dstSelection = dst->selection();
|
|
}
|
|
else
|
|
{
|
|
r = src->exactBounds();
|
|
dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
|
|
}
|
|
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(), true);
|
|
KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
|
|
KisVLineIterator dstSelIt = dstSelection->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);
|
|
|
|
// XXX: Should set alpha = alpha*(1-selectedness)
|
|
cs->setAlpha(hit.rawData(), 0, 1);
|
|
}
|
|
*(dstSelIt.rawData()) = hit.selectedness();
|
|
--hit;
|
|
++vit;
|
|
++dstSelIt;
|
|
}
|
|
++x;
|
|
|
|
//progress info
|
|
m_progressStep += r.width();
|
|
if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
|
|
{
|
|
m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
|
|
emit notifyProgress(m_lastProgressReport);
|
|
}
|
|
if (m_cancelRequested) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst)
|
|
{
|
|
kdDebug() << "Rotating 180\n";
|
|
KisSelectionSP dstSelection;
|
|
TQ_INT32 pixelSize = src->pixelSize();
|
|
TQRect r;
|
|
KisColorSpace *cs = src->colorSpace();
|
|
|
|
if(src->hasSelection())
|
|
{
|
|
r = src->selection()->selectedExactRect();
|
|
dstSelection = dst->selection();
|
|
}
|
|
else
|
|
{
|
|
r = src->exactBounds();
|
|
dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
|
|
}
|
|
|
|
for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) {
|
|
KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), true);
|
|
KisHLineIterator dstIt = dst->createHLineIterator(-r.x() - r.width(), -y, r.width(), true);
|
|
KisHLineIterator dstSelIt = dstSelection->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);
|
|
|
|
// XXX: Should set alpha = alpha*(1-selectedness)
|
|
cs->setAlpha(srcIt.rawData(), 0, 1);
|
|
}
|
|
*(dstSelIt.rawData()) = srcIt.selectedness();
|
|
--srcIt;
|
|
++dstIt;
|
|
++dstSelIt;
|
|
}
|
|
|
|
//progress info
|
|
m_progressStep += r.width();
|
|
if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
|
|
{
|
|
m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
|
|
emit notifyProgress(m_lastProgressReport);
|
|
}
|
|
if (m_cancelRequested) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class iter> iter createIterator(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len);
|
|
|
|
template <> KisHLineIteratorPixel createIterator <KisHLineIteratorPixel>
|
|
(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len)
|
|
{
|
|
return dev->createHLineIterator(start, lineNum, len, true);
|
|
}
|
|
|
|
template <> KisVLineIteratorPixel createIterator <KisVLineIteratorPixel>
|
|
(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len)
|
|
{
|
|
return dev->createVLineIterator(lineNum, start, len, true);
|
|
}
|
|
|
|
template <class iter> void calcDimensions (KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines);
|
|
|
|
template <> void calcDimensions <KisHLineIteratorPixel>
|
|
(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines)
|
|
{
|
|
if(dev->hasSelection())
|
|
{
|
|
TQRect r = dev->selection()->selectedExactRect();
|
|
r.rect(&srcStart, &firstLine, &srcLen, &numLines);
|
|
}
|
|
else
|
|
dev->exactBounds(srcStart, firstLine, srcLen, numLines);
|
|
}
|
|
|
|
template <> void calcDimensions <KisVLineIteratorPixel>
|
|
(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines)
|
|
{
|
|
if(dev->hasSelection())
|
|
{
|
|
TQRect r = dev->selection()->selectedExactRect();
|
|
r.rect(&firstLine, &srcStart, &numLines, &srcLen);
|
|
}
|
|
else
|
|
dev->exactBounds(firstLine, srcStart, numLines, srcLen);
|
|
}
|
|
|
|
struct FilterValues
|
|
{
|
|
TQ_UINT8 numWeights;
|
|
TQ_UINT8 *weight;
|
|
~FilterValues() {delete [] weight;}
|
|
};
|
|
|
|
template <class T> void KisTransformWorker::transformPass(KisPaintDevice *src, KisPaintDevice *dst, double floatscale, double shear, TQ_INT32 dx, KisFilterStrategy *filterStrategy, bool fixBorderAlpha)
|
|
{
|
|
TQ_INT32 lineNum,srcStart,firstLine,srcLen,numLines;
|
|
TQ_INT32 center, begin, end; /* filter calculation variables */
|
|
TQ_UINT8 *data;
|
|
TQ_UINT8 pixelSize = src->pixelSize();
|
|
KisSelectionSP dstSelection;
|
|
KisColorSpace * cs = src->colorSpace();
|
|
TQ_INT32 scale;
|
|
TQ_INT32 scaleDenom;
|
|
TQ_INT32 shearFracOffset;
|
|
|
|
if(src->hasSelection())
|
|
dstSelection = dst->selection();
|
|
else
|
|
dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
|
|
|
|
calcDimensions <T>(src, srcStart, srcLen, firstLine, numLines);
|
|
|
|
scale = int(floatscale*srcLen);
|
|
scaleDenom = srcLen;
|
|
|
|
if(scaleDenom == 0)
|
|
return;
|
|
|
|
TQ_INT32 support = filterStrategy->intSupport();
|
|
TQ_INT32 dstLen, dstStart;
|
|
TQ_INT32 invfscale = 256;
|
|
|
|
// handle magnification/minification
|
|
if(abs(scale) < scaleDenom)
|
|
{
|
|
support *= scaleDenom;
|
|
support /= scale;
|
|
|
|
invfscale *= scale;
|
|
invfscale /= scaleDenom;
|
|
if(scale < 0) // handle mirroring
|
|
{
|
|
support = -support;
|
|
invfscale = -invfscale;
|
|
}
|
|
}
|
|
|
|
// handle mirroring
|
|
if(scale < 0)
|
|
dstLen = - scale;
|
|
else
|
|
dstLen = scale;
|
|
|
|
// Calculate extra length (in each side) needed due to shear
|
|
TQ_INT32 extraLen = (support+256)>>8 + 1;
|
|
|
|
TQ_UINT8 *tmpLine = new TQ_UINT8[(srcLen +2*extraLen)* pixelSize];
|
|
TQ_CHECK_PTR(tmpLine);
|
|
|
|
TQ_UINT8 *tmpSel = new TQ_UINT8[srcLen+2*extraLen];
|
|
TQ_CHECK_PTR(tmpSel);
|
|
|
|
//allocate space for colors
|
|
const TQ_UINT8 **colors = new const TQ_UINT8 *[2*support+1];
|
|
|
|
// Precalculate weights
|
|
FilterValues *filterWeights = new FilterValues[256];
|
|
|
|
for(int center = 0; center<256; ++center)
|
|
{
|
|
TQ_INT32 begin = (255 + center - support)>>8; // takes ceiling by adding 255
|
|
TQ_INT32 span = ((center + support)>>8) - begin + 1; // takes floor to get end. Subtracts begin to get span
|
|
TQ_INT32 t = (((begin<<8) - center) * invfscale)>>8;
|
|
TQ_INT32 dt = invfscale;
|
|
filterWeights[center].weight = new TQ_UINT8[span];
|
|
//printf("%d (",center);
|
|
TQ_UINT32 sum=0;
|
|
for(int num = 0; num<span; ++num)
|
|
{
|
|
TQ_UINT32 tmpw = filterStrategy->intValueAt(t) * invfscale;
|
|
|
|
tmpw >>=8;
|
|
filterWeights[center].weight[num] = tmpw;
|
|
//printf(" %d=%d,%d",t,filterWeights[center].weight[num],tmpw);
|
|
t += dt;
|
|
sum+=tmpw;
|
|
}
|
|
//printf(" )%d sum =%d",span,sum);
|
|
if(sum!=255)
|
|
{
|
|
double fixfactor= 255.0/sum;
|
|
sum=0;
|
|
for(int num = 0; num<span; ++num)
|
|
{
|
|
filterWeights[center].weight[num] = int(filterWeights[center].weight[num] * fixfactor);
|
|
sum+=filterWeights[center].weight[num];
|
|
}
|
|
}
|
|
|
|
//printf(" sum2 =%d",sum);
|
|
int num = 0;
|
|
while(sum<255 && num*2<span)
|
|
{
|
|
filterWeights[center].weight[span/2 + num]++;
|
|
++sum;
|
|
if(sum<255 && num<span/2)
|
|
{
|
|
filterWeights[center].weight[span/2 - num - 1]++;
|
|
++sum;
|
|
}
|
|
++num;
|
|
}
|
|
//printf(" sum3 =%d\n",sum);
|
|
|
|
filterWeights[center].numWeights = span;
|
|
}
|
|
|
|
for(lineNum = firstLine; lineNum < firstLine+numLines; lineNum++)
|
|
{
|
|
if(scale < 0)
|
|
dstStart = srcStart * scale / scaleDenom - dstLen + dx;
|
|
else
|
|
dstStart = (srcStart) * scale / scaleDenom + dx;
|
|
|
|
shearFracOffset = -int( 256 * (lineNum * shear - floor(lineNum * shear)));
|
|
dstStart += int(floor(lineNum * shear));
|
|
|
|
// Build a temporary line
|
|
T srcIt = createIterator <T>(src, srcStart - extraLen, lineNum, srcLen+2*extraLen);
|
|
TQ_INT32 i = 0;
|
|
while(!srcIt.isDone())
|
|
{
|
|
TQ_UINT8 *data;
|
|
|
|
data = srcIt.rawData();
|
|
memcpy(&tmpLine[i*pixelSize], data, pixelSize);
|
|
|
|
if(srcIt.isSelected())
|
|
{
|
|
// XXX: Should set alpha = alpha*(1-selectedness)
|
|
cs->setAlpha(data, 0, 1);
|
|
tmpSel[i] = 255;
|
|
}
|
|
else {
|
|
tmpSel[i] = 0;
|
|
}
|
|
++srcIt;
|
|
i++;
|
|
}
|
|
|
|
T dstIt = createIterator <T>(dst, dstStart, lineNum, dstLen);
|
|
T dstSelIt = createIterator <T>(dstSelection, dstStart, lineNum, dstLen);
|
|
|
|
i=0;
|
|
while(!dstIt.isDone())
|
|
{
|
|
if(scaleDenom<2500)
|
|
center = ((i<<8) * scaleDenom) / scale;
|
|
else
|
|
{
|
|
if(scaleDenom<46000) // real limit is actually 46340 pixels
|
|
center = ((i * scaleDenom) / scale)<<8;
|
|
else
|
|
center = ((i<<8)/scale * scaleDenom) / scale; // XXX fails for sizes over 2^23 pixels src width
|
|
}
|
|
|
|
if(scale < 0)
|
|
center += srcLen<<8;
|
|
|
|
center += 128*scaleDenom/scale;//xxx doesn't work for scale<0;
|
|
center += (extraLen<<8) + shearFracOffset;
|
|
|
|
// find contributing pixels
|
|
begin = (255 + center - support)>>8; // takes ceiling by adding 255
|
|
end = (center + support)>>8; // takes floor
|
|
|
|
////printf("sup=%d begin=%d end=%d",support,begin,end);
|
|
TQ_UINT8 selectedness = tmpSel[center>>8];
|
|
if(selectedness)
|
|
{
|
|
int num=0;
|
|
for(int srcpos = begin; srcpos <= end; ++srcpos)
|
|
{
|
|
colors[num] = &tmpLine[srcpos*pixelSize];
|
|
num++;
|
|
}
|
|
data = dstIt.rawData();
|
|
cs->mixColors(colors, filterWeights[center&255].weight, filterWeights[center&255].numWeights, data);
|
|
|
|
//possibly fix the alpha of the border if user wants it
|
|
if(fixBorderAlpha && (i==0 || i==dstLen-1))
|
|
cs->setAlpha(data, cs->getAlpha(&tmpLine[(center>>8)*pixelSize]), 1);
|
|
|
|
data = dstSelIt.rawData();
|
|
*data = selectedness;
|
|
}
|
|
|
|
++dstSelIt;
|
|
++dstIt;
|
|
i++;
|
|
}
|
|
|
|
//progress info
|
|
m_progressStep += dstLen;
|
|
if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
|
|
{
|
|
m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
|
|
emit notifyProgress(m_lastProgressReport);
|
|
}
|
|
if (m_cancelRequested) {
|
|
break;
|
|
}
|
|
}
|
|
delete [] colors;
|
|
delete [] tmpLine;
|
|
delete [] tmpSel;
|
|
delete [] filterWeights;
|
|
}
|
|
|
|
bool KisTransformWorker::run()
|
|
{
|
|
//progress info
|
|
m_cancelRequested = false;
|
|
if(m_progress)
|
|
m_progress->setSubject(this, true, true);
|
|
m_progressTotalSteps = 0;
|
|
m_progressStep = 0;
|
|
TQRect r;
|
|
if(m_dev->hasSelection())
|
|
r = m_dev->selection()->selectedExactRect();
|
|
else
|
|
r = m_dev->exactBounds();
|
|
|
|
KisPaintDeviceSP tmpdev1 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev1");;
|
|
KisPaintDeviceSP tmpdev2 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
|
|
KisPaintDeviceSP tmpdev3 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
|
|
KisPaintDeviceSP srcdev = m_dev;
|
|
|
|
double xscale = m_xscale;
|
|
double yscale = m_yscale;
|
|
double xshear = m_xshear;
|
|
double yshear = m_yshear;
|
|
double rotation = m_rotation;
|
|
TQ_INT32 xtranslate = m_xtranslate;
|
|
TQ_INT32 ytranslate = m_ytranslate;
|
|
|
|
if(rotation < 0.0)
|
|
rotation = -fmod(-rotation, 2*M_PI) + 2*M_PI;
|
|
else
|
|
rotation = fmod(rotation, 2*M_PI);
|
|
int rotQuadrant = int(rotation /(M_PI/2) + 0.5) & 3;
|
|
|
|
// Figure out how we will do the initial right angle rotations
|
|
double tmp;
|
|
switch(rotQuadrant)
|
|
{
|
|
default: // just to shut up the compiler
|
|
case 0:
|
|
m_progressTotalSteps = 0;
|
|
break;
|
|
case 1:
|
|
rotation -= M_PI/2;
|
|
tmp = xscale;
|
|
xscale=yscale;
|
|
yscale=tmp;
|
|
m_progressTotalSteps = r.width() * r.height();
|
|
break;
|
|
case 2:
|
|
rotation -= M_PI;
|
|
m_progressTotalSteps = r.width() * r.height();
|
|
break;
|
|
case 3:
|
|
rotation -= -M_PI/2 + 2*M_PI;
|
|
tmp = xscale;
|
|
xscale = yscale;
|
|
yscale = tmp;
|
|
m_progressTotalSteps = r.width() * r.height();
|
|
break;
|
|
}
|
|
|
|
// Calculate some auxillary values
|
|
yshear = sin(rotation);
|
|
xshear = -tan(rotation/2);
|
|
xtranslate -= int(xshear*ytranslate);
|
|
|
|
// Calculate progress steps
|
|
m_progressTotalSteps += int(yscale * r.width() * r.height());
|
|
m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width()*yshear));
|
|
|
|
m_lastProgressReport=0;
|
|
|
|
// Now that we have everything in place it's time to do the actual right angle rotations
|
|
switch(rotQuadrant)
|
|
{
|
|
default: // just to shut up the compiler
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
rotateRight90(srcdev, tmpdev1);
|
|
srcdev = tmpdev1;
|
|
break;
|
|
case 2:
|
|
rotate180(srcdev, tmpdev1);
|
|
srcdev = tmpdev1;
|
|
break;
|
|
case 3:
|
|
rotateLeft90(srcdev, tmpdev1);
|
|
srcdev = tmpdev1;
|
|
break;
|
|
}
|
|
|
|
// Handle simple move case possibly with rotation of 90,180,270
|
|
if(rotation == 0.0 && xscale == 1.0 && yscale == 1.0)
|
|
{
|
|
if(rotQuadrant==0)
|
|
{
|
|
// Though not nessesay in the general case because we make several passes
|
|
// We need to move (not just copy) the data to a temp dev so we can move them back
|
|
rotateNone(srcdev, tmpdev1);
|
|
srcdev = tmpdev1;
|
|
}
|
|
if(m_dev->hasSelection())
|
|
m_dev->selection()->clear();
|
|
|
|
srcdev->move(srcdev->getX() + xtranslate, srcdev->getY() + ytranslate);
|
|
rotateNone(srcdev, m_dev);
|
|
|
|
//progress info
|
|
emit notifyProgressDone();
|
|
m_dev->emitSelectionChanged();
|
|
|
|
return m_cancelRequested;
|
|
}
|
|
|
|
if ( m_cancelRequested) {
|
|
emit notifyProgressDone();
|
|
return false;
|
|
}
|
|
|
|
transformPass <KisHLineIteratorPixel>(srcdev, tmpdev2, xscale, yscale*xshear, 0, m_filter, m_fixBorderAlpha);
|
|
if(m_dev->hasSelection())
|
|
m_dev->selection()->clear();
|
|
|
|
if ( m_cancelRequested) {
|
|
emit notifyProgressDone();
|
|
return false;
|
|
}
|
|
|
|
// Now do the second pass
|
|
transformPass <KisVLineIteratorPixel>(tmpdev2.data(), tmpdev3.data(), yscale, yshear, ytranslate, m_filter, m_fixBorderAlpha);
|
|
|
|
if(m_dev->hasSelection())
|
|
m_dev->selection()->clear();
|
|
|
|
if ( m_cancelRequested) {
|
|
emit notifyProgressDone();
|
|
return false;
|
|
}
|
|
|
|
if (xshear != 0.0)
|
|
transformPass <KisHLineIteratorPixel>(tmpdev3, m_dev, 1.0, xshear, xtranslate, m_filter, m_fixBorderAlpha);
|
|
else
|
|
{
|
|
// No need to filter again when we are only scaling
|
|
tmpdev3->move(tmpdev3->getX() + xtranslate, tmpdev3->getY());
|
|
rotateNone(tmpdev3, m_dev);
|
|
}
|
|
|
|
if (m_dev->parentLayer()) {
|
|
m_dev->parentLayer()->setDirty();
|
|
}
|
|
//progress info
|
|
emit notifyProgressDone();
|
|
m_dev->emitSelectionChanged();
|
|
|
|
return m_cancelRequested;
|
|
}
|