krita

kis_transform_worker.cc

00001 /*
00002  *  Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de> filters
00003  *  Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
00004  *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org> right angle rotators
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 #include <kdebug.h>
00021 #include <klocale.h>
00022 
00023 #include "kis_debug_areas.h"
00024 #include "kis_paint_device.h"
00025 #include "kis_selection.h"
00026 #include "kis_transform_worker.h"
00027 #include "kis_progress_display_interface.h"
00028 #include "kis_iterators_pixel.h"
00029 #include "kis_filter_strategy.h"
00030 #include "kis_layer.h"
00031 
00032 KisTransformWorker::KisTransformWorker(KisPaintDeviceSP dev, double xscale, double yscale,
00033                     double xshear, double yshear, double rotation,
00034                     Q_INT32 xtranslate, Q_INT32 ytranslate,
00035                     KisProgressDisplayInterface *progress, KisFilterStrategy *filter)
00036 {
00037     m_dev= dev;
00038     m_xscale = xscale;
00039     m_yscale = yscale;
00040     m_xshear = xshear;
00041     m_yshear = yshear;
00042     m_rotation = rotation,
00043     m_xtranslate = xtranslate;
00044     m_ytranslate = ytranslate;
00045     m_progress = progress;
00046     m_filter = filter;
00047 }
00048 
00049 void KisTransformWorker::rotateRight90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
00050 {
00051     KisSelectionSP dstSelection;
00052     Q_INT32 pixelSize = src->pixelSize();
00053     QRect r;
00054     KisColorSpace *cs = src->colorSpace();
00055 
00056     if(src->hasSelection())
00057     {
00058         r = src->selection()->selectedExactRect();
00059         dstSelection = dst->selection();
00060     }
00061     else
00062     {
00063         r = src->exactBounds();
00064         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00065     }
00066 
00067     for (Q_INT32 y = r.bottom(); y >= r.top(); --y) {
00068         KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true);
00069         KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true);
00070         KisVLineIterator dstSelIt = dstSelection->createVLineIterator(-y, r.x(), r.width(), true);
00071 
00072             while (!hit.isDone()) {
00073             if (hit.isSelected())  {
00074                 memcpy(vit.rawData(), hit.rawData(), pixelSize);
00075 
00076                 // XXX: Should set alpha = alpha*(1-selectedness)
00077                 cs->setAlpha(hit.rawData(), 0, 1);
00078             }
00079             *(dstSelIt.rawData()) = hit.selectedness();
00080             ++hit;
00081             ++vit;
00082             ++dstSelIt;
00083         }
00084     }
00085 }
00086 
00087 void KisTransformWorker::rotateLeft90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
00088 {
00089     kdDebug() << "rotateLeft90 called\n";
00090     KisSelectionSP dstSelection;
00091     Q_INT32 pixelSize = src->pixelSize();
00092     QRect r;
00093     KisColorSpace *cs = src->colorSpace();
00094 
00095     if(src->hasSelection())
00096     {
00097         r = src->selection()->selectedExactRect();
00098         dstSelection = dst->selection();
00099     }
00100     else
00101     {
00102         r = src->exactBounds();
00103         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00104     }
00105     Q_INT32 x = 0;
00106 
00107     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00108         // Read the horizontal line from back to front, write onto the vertical column
00109         KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true);
00110         KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
00111         KisVLineIterator dstSelIt = dstSelection->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
00112 
00113         hit += r.width() - 1;
00114         while (!vit.isDone()) {
00115             if (hit.isSelected()) {
00116                 memcpy(vit.rawData(), hit.rawData(), pixelSize);
00117 
00118                 // XXX: Should set alpha = alpha*(1-selectedness)
00119                 cs->setAlpha(hit.rawData(), 0, 1);
00120             }
00121             *(dstSelIt.rawData()) = hit.selectedness();
00122             --hit;
00123             ++vit;
00124             ++dstSelIt;
00125         }
00126         ++x;
00127     }
00128 }
00129 
00130 void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst)
00131 {
00132     KisSelectionSP dstSelection;
00133     Q_INT32 pixelSize = src->pixelSize();
00134     QRect r;
00135     KisColorSpace *cs = src->colorSpace();
00136 
00137     if(src->hasSelection())
00138     {
00139         r = src->selection()->selectedExactRect();
00140         dstSelection = dst->selection();
00141     }
00142     else
00143     {
00144         r = src->exactBounds();
00145         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00146     }
00147 
00148     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00149         KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), true);
00150         KisHLineIterator dstIt = dst->createHLineIterator(-r.x() - r.width(), -y, r.width(), true);
00151         KisHLineIterator dstSelIt = dstSelection->createHLineIterator(-r.x() - r.width(), -y, r.width(), true);
00152 
00153         srcIt += r.width() - 1;
00154         while (!dstIt.isDone()) {
00155             if (srcIt.isSelected())  {
00156                 memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize);
00157 
00158                 // XXX: Should set alpha = alpha*(1-selectedness)
00159                 cs->setAlpha(srcIt.rawData(), 0, 1);
00160             }
00161             *(dstSelIt.rawData()) = srcIt.selectedness();
00162             --srcIt;
00163             ++dstIt;
00164             ++dstSelIt;
00165         }
00166     }
00167 }
00168 
00169 template <class iter> iter createIterator(KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len);
00170 
00171 template <> KisHLineIteratorPixel createIterator <KisHLineIteratorPixel>
00172 (KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len)
00173 {
00174     return dev->createHLineIterator(start, lineNum, len, true);
00175 }
00176 
00177 template <> KisVLineIteratorPixel createIterator <KisVLineIteratorPixel>
00178 (KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len)
00179 {
00180     return dev->createVLineIterator(lineNum, start, len, true);
00181 }
00182 
00183 template <class iter> void calcDimensions (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines, Q_INT32 &srcStartData, Q_INT32 &srcLenData);
00184 
00185 template <> void calcDimensions <KisHLineIteratorPixel>
00186         (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines, Q_INT32 &srcStartData, Q_INT32 &srcLenData)
00187 {
00188     dev->exactBounds(srcStartData, firstLine, srcLenData, numLines);
00189     if(dev->hasSelection())
00190     {
00191         QRect r = dev->selection()->selectedExactRect();
00192         r.rect(&srcStart, &firstLine, &srcLen, &numLines);
00193     }
00194     else
00195     {
00196         srcStart = srcStartData;
00197         srcLen = srcLenData;
00198     }
00199 }
00200 
00201 template <> void calcDimensions <KisVLineIteratorPixel>
00202         (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines, Q_INT32 &srcStartData, Q_INT32 &srcLenData)
00203 {
00204     dev->exactBounds(firstLine, srcStartData, numLines, srcLenData);
00205     if(dev->hasSelection())
00206     {
00207         QRect r = dev->selection()->selectedExactRect();
00208         r.rect(&firstLine, &srcStart, &numLines, &srcLen);
00209     }
00210     else
00211     {
00212         srcStart = srcStartData;
00213         srcLen = srcLenData;
00214     }
00215 }
00216 
00217 struct FilterValues
00218 {
00219     Q_UINT8 numWeights;
00220     Q_UINT8 *weight;
00221     ~FilterValues() {delete [] weight;}
00222 };
00223 
00224 template <class T> void KisTransformWorker::transformPass(KisPaintDevice *src, KisPaintDevice *dst, double floatscale, double shear, Q_INT32 dx, KisFilterStrategy *filterStrategy)
00225 {
00226     Q_INT32 lineNum,srcStart,firstLine,srcLen,numLines,srcStartData,srcLenData;
00227     Q_INT32 center, begin, end;    /* filter calculation variables */
00228     Q_UINT8 *data;
00229     Q_UINT8 pixelSize = src->pixelSize();
00230     KisSelectionSP dstSelection;
00231     KisColorSpace * cs = src->colorSpace();
00232     Q_INT32 scale;
00233     Q_INT32 scaleDenom;
00234     Q_INT32 shearFracOffset;
00235 
00236     if(src->hasSelection())
00237         dstSelection = dst->selection();
00238     else
00239         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00240 
00241     calcDimensions <T>(src, srcStart, srcLen, firstLine, numLines,srcStartData,srcLenData);
00242 
00243     scale = int(floatscale*srcLen);
00244     scaleDenom = srcLen;
00245 
00246     if(scaleDenom == 0)
00247         return;
00248 
00249     Q_INT32 support = filterStrategy->intSupport();
00250     Q_INT32 dstLen, dstStart;
00251     Q_INT32 invfscale = 256;
00252 
00253     // handle magnification/minification
00254     if(abs(scale) < scaleDenom)
00255     {
00256         support *= scaleDenom;
00257         support /= scale;
00258 
00259         invfscale *= scale;
00260         invfscale /= scaleDenom;
00261         if(scale < 0) // handle mirroring
00262         {
00263             support = -support;
00264             invfscale = -invfscale;
00265         }
00266     }
00267 
00268     // handle mirroring
00269     if(scale < 0)
00270         dstLen = - scale;
00271     else
00272         dstLen = scale;
00273 
00274     // Calculate extra length (in each side) needed due to shear
00275     Q_INT32 extraLen = (support+256)>>8 + 1;
00276 
00277     Q_UINT8 *tmpLine = new Q_UINT8[(srcLen +2*extraLen)* pixelSize];
00278     Q_CHECK_PTR(tmpLine);
00279 
00280     Q_UINT8 *tmpSel = new Q_UINT8[srcLen+2*extraLen];
00281     Q_CHECK_PTR(tmpSel);
00282 
00283     //allocate space for colors
00284     const Q_UINT8 **colors = new const Q_UINT8 *[2*support+1];
00285 
00286     // Precalculate weights
00287     FilterValues *filterWeights = new FilterValues[256];
00288 
00289     for(int center = 0; center<256; ++center)
00290     {
00291         Q_INT32 begin = (255 + center - support)>>8; // takes ceiling by adding 255
00292         Q_INT32 span = ((center + support)>>8) - begin + 1; // takes floor to get end. Subtracts begin to get span
00293         Q_INT32 t = (((begin<<8) - center) * invfscale)>>8;
00294         Q_INT32 dt = invfscale;
00295         filterWeights[center].weight = new Q_UINT8[span];
00296 //printf("%d (",center);
00297         Q_UINT32 sum=0;
00298         for(int num = 0; num<span; ++num)
00299         {
00300             Q_UINT32 tmpw = filterStrategy->intValueAt(t) * invfscale;
00301 
00302             tmpw >>=8;
00303             filterWeights[center].weight[num] = tmpw;
00304 //printf(" %d=%d,%d",t,filterWeights[center].weight[num],tmpw);
00305             t += dt;
00306             sum+=tmpw;
00307         }
00308 //printf(" )%d sum =%d",span,sum);
00309         if(sum!=255)
00310         {
00311             double fixfactor= 255.0/sum;
00312             sum=0;
00313             for(int num = 0; num<span; ++num)
00314             {
00315                 filterWeights[center].weight[num] = int(filterWeights[center].weight[num] * fixfactor);
00316                 sum+=filterWeights[center].weight[num];
00317             }
00318         }
00319 
00320 //printf("  sum2 =%d",sum);
00321         int num = 0; 
00322         while(sum<255 && num*2<span)
00323         {
00324             filterWeights[center].weight[span/2 + num]++;
00325             ++sum;
00326             if(sum<255 && num<span/2)
00327             {
00328                 filterWeights[center].weight[span/2 - num - 1]++;
00329                 ++sum;
00330             }
00331             ++num;
00332         }
00333 //printf("  sum3 =%d\n",sum);
00334 
00335         filterWeights[center].numWeights = span;
00336     }
00337 
00338     for(lineNum = firstLine; lineNum < firstLine+numLines; lineNum++)
00339     {
00340         if(scale < 0)
00341             dstStart = srcStart * scale / scaleDenom - dstLen + dx;
00342         else
00343             dstStart = (srcStart) * scale / scaleDenom + dx;
00344 
00345         shearFracOffset = -int( 256 * (lineNum * shear - floor(lineNum * shear)));
00346         dstStart += int(floor(lineNum * shear));
00347 
00348         // Build a temporary line
00349         T srcIt = createIterator <T>(src, QMAX(srcStart - extraLen, srcStartData), lineNum, srcLen+2*extraLen);
00350         Q_INT32 i = 0;
00351         Q_INT32 x = srcStart - extraLen;
00352         while(i < srcLen + 2*extraLen)
00353         {
00354             Q_UINT8 *data;
00355 
00356             data = srcIt.rawData();
00357             memcpy(&tmpLine[i*pixelSize], data, pixelSize);
00358             if(srcIt.isSelected())
00359             {
00360                 tmpSel[i] = 255;
00361             }
00362             else
00363             {
00364                 tmpSel[i] = 0;
00365             }
00366             if(x >= srcStartData && x < srcStartData + srcLenData - 1)
00367             {
00368                 // XXX: Should set alpha = alpha*(1-selectedness)
00369                 cs->setAlpha(data, 0, 1);
00370                 ++srcIt;
00371             }
00372             i++;
00373             x++;
00374         }
00375 
00376         T dstIt = createIterator <T>(dst, dstStart, lineNum, dstLen);
00377         T dstSelIt = createIterator <T>(dstSelection, dstStart, lineNum, dstLen);
00378 
00379         i=0;
00380         while(!dstIt.isDone())
00381         {
00382             if(scaleDenom<2500)
00383                 center = ((i<<8) * scaleDenom) / scale;
00384             else
00385             {
00386                 if(scaleDenom<46000) // real limit is actually 46340 pixels
00387                     center = ((i * scaleDenom) / scale)<<8;
00388                 else
00389                     center = ((i<<8)/scale * scaleDenom) / scale; // XXX fails for sizes over 2^23 pixels src width
00390             }
00391 
00392             if(scale < 0)
00393                 center += srcLen<<8;
00394 
00395             center += 128*scaleDenom/scale;//xxx doesn't work for scale<0;
00396             center += (extraLen<<8) + shearFracOffset;
00397 
00398             // find contributing pixels
00399             begin = (255 + center - support)>>8; // takes ceiling by adding 255
00400             end = (center + support)>>8; // takes floor
00401 
00403             Q_UINT8 selectedness = tmpSel[center>>8];
00404             if(selectedness)
00405             {
00406                 int num=0;
00407                 for(int srcpos = begin; srcpos <= end; ++srcpos)
00408                 {
00409                     colors[num] = &tmpLine[srcpos*pixelSize];
00410                     num++;
00411                 }
00412                 data = dstIt.rawData();
00413                 cs->mixColors(colors, filterWeights[center&255].weight, filterWeights[center&255].numWeights, data);
00414                 data = dstSelIt.rawData();
00415                 *data = selectedness;
00416             }
00417 
00418             ++dstSelIt;
00419             ++dstIt;
00420             i++;
00421         }
00422 
00423         //progress info
00424         m_progressStep += dstLen;
00425         if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
00426         {
00427             m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
00428             emit notifyProgress(m_lastProgressReport);
00429         }
00430         if (m_cancelRequested) {
00431             break;
00432         }
00433     }
00434     delete [] colors;
00435     delete [] tmpLine;
00436     delete [] tmpSel;
00437     delete [] filterWeights;
00438 }
00439 
00440 bool KisTransformWorker::run()
00441 {
00442     //progress info
00443     m_cancelRequested = false;
00444     if(m_progress)
00445         m_progress->setSubject(this, true, true);
00446     m_progressTotalSteps = 0;
00447     m_progressStep = 0;
00448     QRect r;
00449     if(m_dev->hasSelection())
00450         r = m_dev->selection()->selectedExactRect();
00451     else
00452         r = m_dev->exactBounds();
00453 
00454     KisPaintDeviceSP tmpdev1 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev1");;
00455     KisPaintDeviceSP tmpdev2 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
00456     KisPaintDeviceSP tmpdev3 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
00457     KisPaintDeviceSP srcdev = m_dev;
00458 
00459     double xscale = m_xscale;
00460     double yscale = m_yscale;
00461     double xshear = m_xshear;
00462     double yshear = m_yshear;
00463     double rotation = m_rotation;
00464     Q_INT32 xtranslate = m_xtranslate;
00465     Q_INT32 ytranslate = m_ytranslate;
00466 
00467     if(rotation < 0.0)
00468         rotation = -fmod(-rotation, 2*M_PI) + 2*M_PI;
00469     else
00470         rotation = fmod(rotation, 2*M_PI);
00471     int rotQuadrant = int(rotation /(M_PI/2) + 0.5) & 3;
00472 
00473     double tmp;
00474     switch(rotQuadrant)
00475     {
00476         case 0:
00477             break;
00478         case 1:
00479             rotateRight90(srcdev, tmpdev1);
00480             srcdev = tmpdev1;
00481             rotation -= M_PI/2;
00482             tmp = xscale;
00483             xscale=yscale;
00484             yscale=tmp;
00485             break;
00486         case 2:
00487             rotate180(srcdev, tmpdev1);
00488             srcdev = tmpdev1;
00489             rotation -= M_PI;
00490             break;
00491         case 3:
00492             rotateLeft90(srcdev, tmpdev1);
00493             srcdev = tmpdev1;
00494             rotation += M_PI/2 + 2*M_PI;
00495             tmp = xscale;
00496             xscale = yscale;
00497             yscale = tmp;
00498             break;
00499         default:
00500             break;
00501     }
00502 
00503     yshear = sin(rotation);
00504     xshear = -tan(rotation/2);
00505     xtranslate -= int(xshear*ytranslate);
00506 
00507     m_progressTotalSteps = int(yscale * r.width() * r.height());
00508     m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width()*yshear));
00509 
00510     m_lastProgressReport=0;
00511 
00512     if ( m_cancelRequested) {
00513         emit notifyProgressDone();
00514         return false;
00515     }
00516 
00517     transformPass <KisHLineIteratorPixel>(srcdev, tmpdev2, xscale, yscale*xshear, 0, m_filter);
00518     if(m_dev->hasSelection())
00519         m_dev->selection()->clear();
00520 
00521     if ( m_cancelRequested) {
00522         emit notifyProgressDone();
00523         return false;
00524     }
00525 
00526     if(xshear==0.0)
00527         // Not going to do the third transform when scaling
00528         transformPass <KisVLineIteratorPixel>(tmpdev2.data(), m_dev.data(), yscale, yshear, ytranslate, m_filter);
00529     else
00530         transformPass <KisVLineIteratorPixel>(tmpdev2.data(), tmpdev3.data(), yscale, yshear, ytranslate, m_filter);
00531 
00532     if(m_dev->hasSelection())
00533         m_dev->selection()->clear();
00534 
00535     if ( m_cancelRequested) {
00536         emit notifyProgressDone();
00537         return false;
00538     }
00539 
00540     if (xshear != 0.0)
00541         transformPass <KisHLineIteratorPixel>(tmpdev3, m_dev, 1.0, xshear, xtranslate, m_filter);
00542     if (m_dev->parentLayer()) {
00543         m_dev->parentLayer()->setDirty();
00544     }
00545     //progress info
00546     emit notifyProgressDone();
00547     m_dev->emitSelectionChanged();
00548 
00549     return m_cancelRequested;
00550 }
KDE Home | KDE Accessibility Home | Description of Access Keys