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     KisSelectionSP dstSelection;
00090     Q_INT32 pixelSize = src->pixelSize();
00091     QRect r;
00092     KisColorSpace *cs = src->colorSpace();
00093 
00094     if(src->hasSelection())
00095     {
00096         r = src->selection()->selectedExactRect();
00097         dstSelection = dst->selection();
00098     }
00099     else
00100     {
00101         r = src->exactBounds();
00102         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00103     }
00104     Q_INT32 x = 0;
00105 
00106     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00107         // Read the horizontal line from back to front, write onto the vertical column
00108         KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true);
00109         KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
00110         KisVLineIterator dstSelIt = dstSelection->createVLineIterator(y, -r.x() - r.width(), r.width(), true);
00111 
00112         hit += r.width() - 1;
00113         while (!vit.isDone()) {
00114             if (hit.isSelected()) {
00115                 memcpy(vit.rawData(), hit.rawData(), pixelSize);
00116 
00117                 // XXX: Should set alpha = alpha*(1-selectedness)
00118                 cs->setAlpha(hit.rawData(), 0, 1);
00119             }
00120             *(dstSelIt.rawData()) = hit.selectedness();
00121             --hit;
00122             ++vit;
00123             ++dstSelIt;
00124         }
00125         ++x;
00126     }
00127 }
00128 
00129 void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst)
00130 {
00131     KisSelectionSP dstSelection;
00132     Q_INT32 pixelSize = src->pixelSize();
00133     QRect r;
00134     KisColorSpace *cs = src->colorSpace();
00135 
00136     if(src->hasSelection())
00137     {
00138         r = src->selection()->selectedExactRect();
00139         dstSelection = dst->selection();
00140     }
00141     else
00142     {
00143         r = src->exactBounds();
00144         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00145     }
00146 
00147     for (Q_INT32 y = r.top(); y <= r.bottom(); ++y) {
00148         KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), false);
00149         KisHLineIterator dstIt = dst->createHLineIterator(-r.x() - r.width(), -y, r.width(), true);
00150         KisHLineIterator dstSelIt = dstSelection->createHLineIterator(-r.x() - r.width(), -y, r.width(), true);
00151 
00152         srcIt += r.width() - 1;
00153         while (!dstIt.isDone()) {
00154             if (srcIt.isSelected())  {
00155                 memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize);
00156 
00157                 // XXX: Should set alpha = alpha*(1-selectedness)
00158                 cs->setAlpha(srcIt.rawData(), 0, 1);
00159             }
00160             *(dstSelIt.rawData()) = srcIt.selectedness();
00161             --srcIt;
00162             ++dstIt;
00163             ++dstSelIt;
00164         }
00165     }
00166 }
00167 
00168 template <class iter> iter createIterator(KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len);
00169 
00170 template <> KisHLineIteratorPixel createIterator <KisHLineIteratorPixel>
00171 (KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len)
00172 {
00173     return dev->createHLineIterator(start, lineNum, len, true);
00174 }
00175 
00176 template <> KisVLineIteratorPixel createIterator <KisVLineIteratorPixel>
00177 (KisPaintDevice *dev, Q_INT32 start, Q_INT32 lineNum, Q_INT32 len)
00178 {
00179     return dev->createVLineIterator(lineNum, start, len, true);
00180 }
00181 
00182 template <class iter> void calcDimensions (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines);
00183 
00184 template <> void calcDimensions <KisHLineIteratorPixel>
00185 (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines)
00186 {
00187     if(dev->hasSelection())
00188     {
00189         QRect r = dev->selection()->selectedExactRect();
00190         r.rect(&srcStart, &firstLine, &srcLen, &numLines);
00191     }
00192     else
00193         dev->exactBounds(srcStart, firstLine, srcLen, numLines);
00194 }
00195 
00196 template <> void calcDimensions <KisVLineIteratorPixel>
00197 (KisPaintDevice *dev, Q_INT32 &srcStart, Q_INT32 &srcLen, Q_INT32 &firstLine, Q_INT32 &numLines)
00198 {
00199     if(dev->hasSelection())
00200     {
00201         QRect r = dev->selection()->selectedExactRect();
00202         r.rect(&firstLine, &srcStart, &numLines, &srcLen);
00203     }
00204     else
00205         dev->exactBounds(firstLine, srcStart, numLines, srcLen);
00206 }
00207 
00208 struct FilterValues
00209 {
00210     Q_UINT8 numWeights;
00211     Q_UINT8 *weight;
00212     ~FilterValues() {delete [] weight;}
00213 };
00214 
00215 template <class T> void KisTransformWorker::transformPass(KisPaintDevice *src, KisPaintDevice *dst, double floatscale, double shear, Q_INT32 dx, KisFilterStrategy *filterStrategy)
00216 {
00217     Q_INT32 lineNum,srcStart,firstLine,srcLen,numLines;
00218     Q_INT32 center, begin, end;    /* filter calculation variables */
00219     Q_UINT8 *data;
00220     Q_UINT8 pixelSize = src->pixelSize();
00221     KisSelectionSP dstSelection;
00222     KisColorSpace * cs = src->colorSpace();
00223     Q_INT32 scale;
00224     Q_INT32 scaleDenom;
00225     Q_INT32 shearFracOffset;
00226 
00227     if(src->hasSelection())
00228         dstSelection = dst->selection();
00229     else
00230         dstSelection = new KisSelection(dst); // essentially a dummy to be deleted
00231 
00232     calcDimensions <T>(src, srcStart, srcLen, firstLine, numLines);
00233 
00234     scale = int(floatscale*srcLen);
00235     scaleDenom = srcLen;
00236 
00237     if(scaleDenom == 0)
00238         return;
00239 
00240     Q_INT32 support = filterStrategy->intSupport();
00241     Q_INT32 dstLen, dstStart;
00242     Q_INT32 invfscale = 256;
00243 
00244     // handle magnification/minification
00245     if(abs(scale) < scaleDenom)
00246     {
00247         support *= scaleDenom;
00248         support /= scale;
00249 
00250         invfscale *= scale;
00251         invfscale /= scaleDenom;
00252         if(scale < 0) // handle mirroring
00253         {
00254             support = -support;
00255             invfscale = -invfscale;
00256         }
00257     }
00258 
00259     // handle mirroring
00260     if(scale < 0)
00261         dstLen = - scale;
00262     else
00263         dstLen = scale;
00264 
00265     // Calculate extra length (in each side) needed due to shear
00266     Q_INT32 extraLen = (support+256)>>8;
00267 
00268     Q_UINT8 *tmpLine = new Q_UINT8[(srcLen +2*extraLen)* pixelSize];
00269     Q_CHECK_PTR(tmpLine);
00270 
00271     Q_UINT8 *tmpSel = new Q_UINT8[srcLen+2*extraLen];
00272     Q_CHECK_PTR(tmpSel);
00273 
00274     //allocate space for colors
00275     const Q_UINT8 **colors = new const Q_UINT8 *[2*support+1];
00276 
00277     // Precalculate weights
00278     FilterValues *filterWeights = new FilterValues[256];
00279 
00280     for(int center = 0; center<256; ++center)
00281     {
00282         Q_INT32 begin = (255 + center - support)>>8; // takes ceiling by adding 255
00283         Q_INT32 span = ((center + support)>>8) - begin + 1; // takes floor to get end. Subtracts begin to get span
00284         Q_INT32 t = (((begin<<8) - center) * invfscale)>>8;
00285         Q_INT32 dt = invfscale;
00286         filterWeights[center].weight = new Q_UINT8[span];
00287 //printf("%d (",center);
00288         Q_UINT32 sum=0;
00289         for(int num = 0; num<span; ++num)
00290         {
00291             Q_UINT32 tmpw = filterStrategy->intValueAt(t) * invfscale;
00292 
00293             tmpw >>=8;
00294             filterWeights[center].weight[num] = tmpw;
00295 //printf(" %d=%d,%d",t,filterWeights[center].weight[num],tmpw);
00296             t += dt;
00297             sum+=tmpw;
00298         }
00299 //printf(" )%d sum =%d",span,sum);
00300         if(sum!=255)
00301         {
00302             double fixfactor= 255.0/sum;
00303             sum=0;
00304             for(int num = 0; num<span; ++num)
00305             {
00306                 filterWeights[center].weight[num] = int(filterWeights[center].weight[num] * fixfactor);
00307                 sum+=filterWeights[center].weight[num];
00308             }
00309         }
00310 
00311 //printf("  sum2 =%d",sum);
00312         int num = 0; 
00313         while(sum<255 && num*2<span)
00314         {
00315             filterWeights[center].weight[span/2 + num]++;
00316             ++sum;
00317             if(sum<255 && num<span/2)
00318             {
00319                 filterWeights[center].weight[span/2 - num - 1]++;
00320                 ++sum;
00321             }
00322             ++num;
00323         }
00324 //printf("  sum3 =%d\n",sum);
00325 
00326         filterWeights[center].numWeights = span;
00327     }
00328 
00329     for(lineNum = firstLine; lineNum < firstLine+numLines; lineNum++)
00330     {
00331         if(scale < 0)
00332             dstStart = srcStart * scale / scaleDenom - dstLen + dx;
00333         else
00334             dstStart = (srcStart) * scale / scaleDenom + dx;
00335 
00336         shearFracOffset = -int( 256 * (lineNum * shear - floor(lineNum * shear)));
00337         dstStart += int(floor(lineNum * shear));
00338 
00339         // Build a temporary line
00340         T srcIt = createIterator <T>(src, srcStart - extraLen, lineNum, srcLen+2*extraLen);
00341         Q_INT32 i = 0;
00342         while(!srcIt.isDone())
00343         {
00344             Q_UINT8 *data;
00345 
00346             if(srcIt.isSelected())
00347             {
00348                 data = srcIt.rawData();
00349                 memcpy(&tmpLine[i*pixelSize], data, pixelSize);
00350 
00351                 // XXX: Should set alpha = alpha*(1-selectedness)
00352                 cs->setAlpha(data, 0, 1);
00353 
00354                 tmpSel[i] = 255;
00355             }
00356             else
00357                 tmpSel[i] = 0;
00358             ++srcIt;
00359             i++;
00360         }
00361 
00362         T dstIt = createIterator <T>(dst, dstStart, lineNum, dstLen);
00363         T dstSelIt = createIterator <T>(dstSelection, dstStart, lineNum, dstLen);
00364 
00365         i=0;
00366         while(!dstIt.isDone())
00367         {
00368             if(scaleDenom<2500)
00369                 center = ((i<<8) * scaleDenom) / scale;
00370             else
00371             {
00372                 if(scaleDenom<46000) // real limit is actually 46340 pixels
00373                     center = ((i * scaleDenom) / scale)<<8;
00374                 else
00375                     center = ((i<<8)/scale * scaleDenom) / scale; // XXX fails for sizes over 2^23 pixels src width
00376             }
00377 
00378             if(scale < 0)
00379                 center += srcLen<<8;
00380 
00381             center += 128*scaleDenom/scale;//xxx doesn't work for scale<0;
00382             center += (extraLen<<8) + shearFracOffset;
00383 
00384             // find contributing pixels
00385             begin = (255 + center - support)>>8; // takes ceiling by adding 255
00386             end = (center + support)>>8; // takes floor
00387 
00389             Q_UINT8 selectedness = tmpSel[center>>8];
00390             if(selectedness)
00391             {
00392                 int num=0;
00393                 for(int srcpos = begin; srcpos <= end; ++srcpos)
00394                 {
00395                     colors[num] = &tmpLine[srcpos*pixelSize];
00396                     num++;
00397                 }
00398                 data = dstIt.rawData();
00399                 cs->mixColors(colors, filterWeights[center&255].weight, filterWeights[center&255].numWeights, data);
00400                 data = dstSelIt.rawData();
00401                 *data = selectedness;
00402             }
00403 
00404             ++dstSelIt;
00405             ++dstIt;
00406             i++;
00407         }
00408 
00409         //progress info
00410         m_progressStep += dstLen;
00411         if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps)
00412         {
00413             m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps;
00414             emit notifyProgress(m_lastProgressReport);
00415         }
00416         if (m_cancelRequested) {
00417             break;
00418         }
00419     }
00420     delete [] colors;
00421     delete [] tmpLine;
00422     delete [] tmpSel;
00423     delete [] filterWeights;
00424 }
00425 
00426 bool KisTransformWorker::run()
00427 {
00428     //progress info
00429     m_cancelRequested = false;
00430     if(m_progress)
00431         m_progress->setSubject(this, true, true);
00432     m_progressTotalSteps = 0;
00433     m_progressStep = 0;
00434     QRect r;
00435     if(m_dev->hasSelection())
00436         r = m_dev->selection()->selectedExactRect();
00437     else
00438         r = m_dev->exactBounds();
00439 
00440     KisPaintDeviceSP tmpdev1 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev1");;
00441     KisPaintDeviceSP tmpdev2 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
00442     KisPaintDeviceSP tmpdev3 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");;
00443     KisPaintDeviceSP srcdev = m_dev;
00444 
00445     double xscale = m_xscale;
00446     double yscale = m_yscale;
00447     double xshear = m_xshear;
00448     double yshear = m_yshear;
00449     double rotation = m_rotation;
00450     Q_INT32 xtranslate = m_xtranslate;
00451     Q_INT32 ytranslate = m_ytranslate;
00452 
00453     if(rotation < 0.0)
00454         rotation = -fmod(-rotation, 2*M_PI) + 2*M_PI;
00455     else
00456         rotation = fmod(rotation, 2*M_PI);
00457     int rotQuadrant = int(rotation /(M_PI/2) + 0.5) & 3;
00458 
00459     double tmp;
00460     switch(rotQuadrant)
00461     {
00462         case 0:
00463             break;
00464         case 1:
00465             rotateRight90(srcdev, tmpdev1);
00466             srcdev = tmpdev1;
00467             rotation -= M_PI/2;
00468             tmp = xscale;
00469             xscale=yscale;
00470             yscale=tmp;
00471             break;
00472         case 2:
00473             rotate180(srcdev, tmpdev1);
00474             srcdev = tmpdev1;
00475             rotation -= M_PI;
00476             break;
00477         case 3:
00478             rotateLeft90(srcdev, tmpdev1);
00479             srcdev = tmpdev1;
00480             rotation += M_PI/2 + 2*M_PI;
00481             tmp = xscale;
00482             xscale = yscale;
00483             yscale = tmp;
00484             break;
00485         default:
00486             break;
00487     }
00488 
00489     yshear = sin(rotation);
00490     xshear = -tan(rotation/2);
00491     xtranslate -= int(xshear*ytranslate);
00492 
00493     m_progressTotalSteps = int(yscale * r.width() * r.height());
00494     m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width()*yshear));
00495 
00496     m_lastProgressReport=0;
00497 
00498     if ( m_cancelRequested) {
00499         emit notifyProgressDone();
00500         return false;
00501     }
00502 
00503     transformPass <KisHLineIteratorPixel>(srcdev, tmpdev2, xscale, yscale*xshear, 0, m_filter);
00504     if(m_dev->hasSelection())
00505         m_dev->selection()->clear();
00506 
00507     if ( m_cancelRequested) {
00508         emit notifyProgressDone();
00509         return false;
00510     }
00511 
00512     transformPass <KisVLineIteratorPixel>(tmpdev2, tmpdev3, yscale, yshear, ytranslate, m_filter);
00513     if(m_dev->hasSelection())
00514         m_dev->selection()->clear();
00515 
00516     if ( m_cancelRequested) {
00517         emit notifyProgressDone();
00518         return false;
00519     }
00520 
00521     transformPass <KisHLineIteratorPixel>(tmpdev3, m_dev, 1.0, xshear, xtranslate, m_filter);
00522     if (m_dev->parentLayer()) {
00523         m_dev->parentLayer()->setDirty();
00524     }
00525 
00526     //progress info
00527     emit notifyProgressDone();
00528     m_dev->emitSelectionChanged();
00529 
00530     return m_cancelRequested;
00531 }
KDE Home | KDE Accessibility Home | Description of Access Keys