krita

kis_scale_visitor.cc

00001 /*
00002  *  Copyright (c) 2004, 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 #include <qdatetime.h>
00019 
00020 #include <kdebug.h>
00021 #include <klocale.h>
00022 
00023 #include "kis_paint_device.h"
00024 #include "kis_scale_visitor.h"
00025 #include "kis_filter_strategy.h"
00026 
00027 
00028 void KisScaleWorker::run()
00029 {
00030     double fwidth = m_filterStrategy->support();
00031 
00032     QRect rect = m_dev -> exactBounds();
00033     Q_INT32 width = rect.width();
00034     Q_INT32 height =  rect.height();
00035     m_pixelSize=m_dev -> pixelSize();
00036 
00037     // compute size of target image
00038     if ( m_sx == 1.0F && m_sy == 1.0F ) {
00039         return;
00040     }
00041     Q_INT32 targetW = QABS( qRound( m_sx * width ) );
00042     Q_INT32 targetH = QABS( qRound( m_sy * height ) );
00043 
00044     Q_UINT8* newData = new Q_UINT8[targetW * targetH * m_pixelSize ];
00045     Q_CHECK_PTR(newData);
00046 
00047     double* weight = new double[ m_pixelSize ];    /* filter calculation variables */
00048 
00049     Q_UINT8* pel = new Q_UINT8[ m_pixelSize ];
00050     Q_CHECK_PTR(pel);
00051 
00052     Q_UINT8 *pel2 = new Q_UINT8[ m_pixelSize ];
00053     Q_CHECK_PTR(pel2);
00054 
00055     bool* bPelDelta = new bool[ m_pixelSize ];
00056     ContribList    *contribX;
00057     ContribList    contribY;
00058     const Q_INT32 BLACK_PIXEL=0;
00059     const Q_INT32 WHITE_PIXEL=255;
00060 
00061 
00062     // create intermediate row to hold vertical dst row zoom
00063     Q_UINT8 * tmp = new Q_UINT8[ width * m_pixelSize ];
00064     Q_CHECK_PTR(tmp);
00065 
00066     //create array of pointers to intermediate rows
00067     Q_UINT8 **tmpRows = new Q_UINT8*[ height ];
00068 
00069     //create array of pointers to intermediate rows that are actually used simultaneously and allocate memory for the rows
00070     Q_UINT8 **tmpRowsMem;
00071     if(m_sy < 1.0)
00072     {
00073         tmpRowsMem = new Q_UINT8*[ (int)(fwidth / m_sy * 2 + 1) ];
00074         for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
00075         {
00076              tmpRowsMem[i] = new Q_UINT8[ width * m_pixelSize ];
00077              Q_CHECK_PTR(tmpRowsMem[i]);
00078         }
00079     }
00080     else
00081     {
00082         tmpRowsMem = new Q_UINT8*[ (int)(fwidth * 2 + 1) ];
00083         for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
00084         {
00085             tmpRowsMem[i] = new Q_UINT8[ width * m_pixelSize ];
00086             Q_CHECK_PTR(tmpRowsMem[i]);
00087         }
00088     }
00089 
00090     // build x weights
00091     contribX = new ContribList[ targetW ];
00092     for(int x = 0; x < targetW; x++)
00093     {
00094         calcContrib(&contribX[x], m_sx, fwidth, width, m_filterStrategy, x);
00095     }
00096 
00097     QTime starttime = QTime::currentTime ();
00098 
00099     for(int y = 0; y < targetH; y++)
00100     {
00101         // build y weights
00102         calcContrib(&contribY, m_sy, fwidth, height, m_filterStrategy, y);
00103 
00104         //copy pixel data to temporary arrays
00105         for(int srcpos = 0; srcpos < contribY.n; srcpos++)
00106         {
00107             if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height))
00108             {
00109                 tmpRows[contribY.p[srcpos].m_pixel] = new Q_UINT8[ width * m_pixelSize ];
00110                 //tmpRows[ contribY.p[srcpos].m_pixel ] = tmpRowsMem[ srcpos ];
00111                 m_dev ->readBytes(tmpRows[contribY.p[srcpos].m_pixel], 0, contribY.p[srcpos].m_pixel, width, 1);
00112             }
00113         }
00114 
00115         /* Apply vert filter to make dst row in tmp. */
00116         for(int x = 0; x < width; x++)
00117         {
00118             for(int channel = 0; channel < m_pixelSize; channel++){
00119                 weight[channel] = 0.0;
00120                 bPelDelta[channel] = FALSE;
00121                 pel[channel]=tmpRows[contribY.p[0].m_pixel][ x * m_pixelSize + channel ];
00122             }
00123             for(int srcpos = 0; srcpos < contribY.n; srcpos++)
00124             {
00125                 if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)){
00126                     for(int channel = 0; channel < m_pixelSize; channel++)
00127                     {
00128                         pel2[channel]=tmpRows[contribY.p[srcpos].m_pixel][ x * m_pixelSize + channel ];
00129                         if(pel2[channel] != pel[channel]) bPelDelta[channel] = TRUE;
00130                             weight[channel] += pel2[channel] * contribY.p[srcpos].m_weight;
00131                     }
00132                 }
00133             }
00134 
00135             for(int channel = 0; channel < m_pixelSize; channel++){
00136                 weight[channel] = bPelDelta[channel] ? static_cast<int>(qRound(weight[channel])) : pel[channel];
00137                         tmp[ x * m_pixelSize + channel ] = static_cast<Q_UINT8>(CLAMP(weight[channel], BLACK_PIXEL, WHITE_PIXEL));
00138             }
00139         } /* next row in temp column */
00140         delete[] contribY.p;
00141 
00142         for(int x = 0; x < targetW; x++)
00143         {
00144             for(int channel = 0; channel < m_pixelSize; channel++){
00145                 weight[channel] = 0.0;
00146                 bPelDelta[channel] = FALSE;
00147                 pel[channel] = tmp[ contribX[x].p[0].m_pixel * m_pixelSize + channel ];
00148             }
00149             for(int srcpos = 0; srcpos < contribX[x].n; srcpos++)
00150             {
00151                 for(int channel = 0; channel < m_pixelSize; channel++){
00152                     pel2[channel] = tmp[ contribX[x].p[srcpos].m_pixel * m_pixelSize + channel ];
00153                     if(pel2[channel] != pel[channel])
00154                         bPelDelta[channel] = TRUE;
00155                     weight[channel] += pel2[channel] * contribX[x].p[srcpos].m_weight;
00156                 }
00157             }
00158             for(int channel = 0; channel < m_pixelSize; channel++){
00159                 weight[channel] = bPelDelta[channel] ? static_cast<int>(qRound(weight[channel])) : pel[channel];
00160                 int currentPos = (y*targetW+x) * m_pixelSize; // try to be at least a little efficient
00161                 if (weight[channel]<0)
00162                     newData[currentPos + channel] = 0;
00163                 else if (weight[channel]>255)
00164                     newData[currentPos + channel] = 255;
00165                 else
00166                     newData[currentPos + channel] = (uchar)weight[channel];
00167              }
00168         } /* next dst row */
00169     } /* next dst column */
00170 
00171     // XXX: I'm thinking that we should be able to cancel earlier, in the look.
00172     if(!isCanceled()){
00173         m_dev -> writeBytes( newData, 0, 0, targetW, targetH);
00174         m_dev -> crop(0, 0, targetW, targetH);
00175     }
00176 
00177     /* free the memory allocated for horizontal filter weights */
00178     for(int x = 0; x < targetW; x++)
00179         delete[] contribX[x].p;
00180     delete[] contribX;
00181 
00182     delete[] newData;
00183     delete[] pel;
00184     delete[] pel2;
00185     delete[] tmp;
00186     delete[] weight;
00187     delete[] bPelDelta;
00188 
00189     if(m_sy < 1.0)
00190     {
00191         for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
00192         {
00193             delete[] tmpRowsMem[i];
00194         }
00195     }
00196     else
00197     {
00198         for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
00199         {
00200             delete[] tmpRowsMem[i];
00201         }
00202     }
00203 
00204     QTime stoptime = QTime::currentTime ();
00205     return;
00206 }
00207 
00208 int KisScaleWorker::calcContrib(ContribList *contrib, double scale, double fwidth, int srcwidth, KisFilterStrategy* filterStrategy, Q_INT32 i)
00209 {
00210         //ContribList* contribX: receiver of contrib info
00211         //double m_sx: horizontal zooming scale
00212         //double fwidth: Filter sampling width
00213         //int dstwidth: Target bitmap width
00214         //int srcwidth: Source bitmap width
00215         //double (*filterf)(double): Filter proc
00216         //int i: Pixel column in source bitmap being processed
00217 
00218         double width;
00219         double fscale;
00220         double center, begin, end;
00221         double weight;
00222         Q_INT32 k, n;
00223 
00224         if(scale < 1.0)
00225         {
00226                 //Shrinking image
00227                 width = fwidth / scale;
00228                 fscale = 1.0 / scale;
00229 
00230                 contrib->n = 0;
00231                 contrib->p = new Contrib[ (int)(width * 2 + 1) ];
00232 
00233                 center = (double) i / scale;
00234                 begin = ceil(center - width);
00235                 end = floor(center + width);
00236                 for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
00237                 {
00238                         weight = center - (double) srcpos;
00239                         weight = filterStrategy->valueAt(weight / fscale) / fscale;
00240                         if(srcpos < 0)
00241                                 n = -srcpos;
00242                         else if(srcpos >= srcwidth)
00243                                 n = (srcwidth - srcpos) + srcwidth - 1;
00244                         else
00245                                 n = srcpos;
00246 
00247                         k = contrib->n++;
00248                         contrib->p[k].m_pixel = n;
00249                         contrib->p[k].m_weight = weight;
00250                 }
00251         }
00252         else
00253         {
00254                 // Expanding image
00255                 contrib->n = 0;
00256                 contrib->p = new Contrib[ (int)(fwidth * 2 + 1) ];
00257 
00258                 center = (double) i / scale;
00259                 begin = ceil(center - fwidth);
00260                 end = floor(center + fwidth);
00261 
00262                 for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
00263                 {
00264                         weight = center - (double) srcpos;
00265                         weight = filterStrategy->valueAt(weight);
00266                         if(srcpos < 0) {
00267                                 n = -srcpos;
00268                         } else if(srcpos >= srcwidth) {
00269                                 n = (srcwidth - srcpos) + srcwidth - 1;
00270                         } else {
00271                                 n = srcpos;
00272                         }
00273                         k = contrib->n++;
00274                         contrib->p[k].m_pixel = n;
00275                         contrib->p[k].m_weight = weight;
00276                 }
00277         }
00278         return 0;
00279 } /* calc_x_contrib */
KDE Home | KDE Accessibility Home | Description of Access Keys