diff options
Diffstat (limited to 'chalk/core/kis_transform_worker.cpp')
-rw-r--r-- | chalk/core/kis_transform_worker.cpp | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/chalk/core/kis_transform_worker.cpp b/chalk/core/kis_transform_worker.cpp new file mode 100644 index 000000000..0d4a98b4c --- /dev/null +++ b/chalk/core/kis_transform_worker.cpp @@ -0,0 +1,676 @@ +/* + * 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; +} |