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.
koffice/chalk/core/kis_scale_visitor.cc

280 lines
10 KiB

/*
* Copyright (c) 2004, 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* 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 <tqdatetime.h>
#include <kdebug.h>
#include <tdelocale.h>
#include "kis_paint_device.h"
#include "kis_scale_visitor.h"
#include "kis_filter_strategy.h"
void KisScaleWorker::run()
{
double fwidth = m_filterStrategy->support();
TQRect rect = m_dev -> exactBounds();
TQ_INT32 width = rect.width();
TQ_INT32 height = rect.height();
m_pixelSize=m_dev -> pixelSize();
// compute size of target image
if ( m_sx == 1.0F && m_sy == 1.0F ) {
return;
}
TQ_INT32 targetW = TQABS( tqRound( m_sx * width ) );
TQ_INT32 targetH = TQABS( tqRound( m_sy * height ) );
TQ_UINT8* newData = new TQ_UINT8[targetW * targetH * m_pixelSize ];
TQ_CHECK_PTR(newData);
double* weight = new double[ m_pixelSize ]; /* filter calculation variables */
TQ_UINT8* pel = new TQ_UINT8[ m_pixelSize ];
TQ_CHECK_PTR(pel);
TQ_UINT8 *pel2 = new TQ_UINT8[ m_pixelSize ];
TQ_CHECK_PTR(pel2);
bool* bPelDelta = new bool[ m_pixelSize ];
ContribList *contribX;
ContribList contribY;
const TQ_INT32 BLACK_PIXEL=0;
const TQ_INT32 WHITE_PIXEL=255;
// create intermediate row to hold vertical dst row zoom
TQ_UINT8 * tmp = new TQ_UINT8[ width * m_pixelSize ];
TQ_CHECK_PTR(tmp);
//create array of pointers to intermediate rows
TQ_UINT8 **tmpRows = new TQ_UINT8*[ height ];
//create array of pointers to intermediate rows that are actually used simultaneously and allocate memory for the rows
TQ_UINT8 **tmpRowsMem;
if(m_sy < 1.0)
{
tmpRowsMem = new TQ_UINT8*[ (int)(fwidth / m_sy * 2 + 1) ];
for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
{
tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ];
TQ_CHECK_PTR(tmpRowsMem[i]);
}
}
else
{
tmpRowsMem = new TQ_UINT8*[ (int)(fwidth * 2 + 1) ];
for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
{
tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ];
TQ_CHECK_PTR(tmpRowsMem[i]);
}
}
// build x weights
contribX = new ContribList[ targetW ];
for(int x = 0; x < targetW; x++)
{
calcContrib(&contribX[x], m_sx, fwidth, width, m_filterStrategy, x);
}
TQTime starttime = TQTime::currentTime ();
for(int y = 0; y < targetH; y++)
{
// build y weights
calcContrib(&contribY, m_sy, fwidth, height, m_filterStrategy, y);
//copy pixel data to temporary arrays
for(int srcpos = 0; srcpos < contribY.n; srcpos++)
{
if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height))
{
tmpRows[contribY.p[srcpos].m_pixel] = new TQ_UINT8[ width * m_pixelSize ];
//tmpRows[ contribY.p[srcpos].m_pixel ] = tmpRowsMem[ srcpos ];
m_dev ->readBytes(tmpRows[contribY.p[srcpos].m_pixel], 0, contribY.p[srcpos].m_pixel, width, 1);
}
}
/* Apply vert filter to make dst row in tmp. */
for(int x = 0; x < width; x++)
{
for(int channel = 0; channel < m_pixelSize; channel++){
weight[channel] = 0.0;
bPelDelta[channel] = FALSE;
pel[channel]=tmpRows[contribY.p[0].m_pixel][ x * m_pixelSize + channel ];
}
for(int srcpos = 0; srcpos < contribY.n; srcpos++)
{
if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)){
for(int channel = 0; channel < m_pixelSize; channel++)
{
pel2[channel]=tmpRows[contribY.p[srcpos].m_pixel][ x * m_pixelSize + channel ];
if(pel2[channel] != pel[channel]) bPelDelta[channel] = TRUE;
weight[channel] += pel2[channel] * contribY.p[srcpos].m_weight;
}
}
}
for(int channel = 0; channel < m_pixelSize; channel++){
weight[channel] = bPelDelta[channel] ? static_cast<int>(tqRound(weight[channel])) : pel[channel];
tmp[ x * m_pixelSize + channel ] = static_cast<TQ_UINT8>(CLAMP(weight[channel], BLACK_PIXEL, WHITE_PIXEL));
}
} /* next row in temp column */
delete[] contribY.p;
for(int x = 0; x < targetW; x++)
{
for(int channel = 0; channel < m_pixelSize; channel++){
weight[channel] = 0.0;
bPelDelta[channel] = FALSE;
pel[channel] = tmp[ contribX[x].p[0].m_pixel * m_pixelSize + channel ];
}
for(int srcpos = 0; srcpos < contribX[x].n; srcpos++)
{
for(int channel = 0; channel < m_pixelSize; channel++){
pel2[channel] = tmp[ contribX[x].p[srcpos].m_pixel * m_pixelSize + channel ];
if(pel2[channel] != pel[channel])
bPelDelta[channel] = TRUE;
weight[channel] += pel2[channel] * contribX[x].p[srcpos].m_weight;
}
}
for(int channel = 0; channel < m_pixelSize; channel++){
weight[channel] = bPelDelta[channel] ? static_cast<int>(tqRound(weight[channel])) : pel[channel];
int currentPos = (y*targetW+x) * m_pixelSize; // try to be at least a little efficient
if (weight[channel]<0)
newData[currentPos + channel] = 0;
else if (weight[channel]>255)
newData[currentPos + channel] = 255;
else
newData[currentPos + channel] = (uchar)weight[channel];
}
} /* next dst row */
} /* next dst column */
// XXX: I'm thinking that we should be able to cancel earlier, in the look.
if(!isCanceled()){
m_dev -> writeBytes( newData, 0, 0, targetW, targetH);
m_dev -> crop(0, 0, targetW, targetH);
}
/* free the memory allocated for horizontal filter weights */
for(int x = 0; x < targetW; x++)
delete[] contribX[x].p;
delete[] contribX;
delete[] newData;
delete[] pel;
delete[] pel2;
delete[] tmp;
delete[] weight;
delete[] bPelDelta;
if(m_sy < 1.0)
{
for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++)
{
delete[] tmpRowsMem[i];
}
}
else
{
for(int i = 0; i < (int)(fwidth * 2 + 1); i++)
{
delete[] tmpRowsMem[i];
}
}
TQTime stoptime = TQTime::currentTime ();
return;
}
int KisScaleWorker::calcContrib(ContribList *contrib, double scale, double fwidth, int srcwidth, KisFilterStrategy* filterStrategy, TQ_INT32 i)
{
//ContribList* contribX: receiver of contrib info
//double m_sx: horizontal zooming scale
//double fwidth: Filter sampling width
//int dstwidth: Target bitmap width
//int srcwidth: Source bitmap width
//double (*filterf)(double): Filter proc
//int i: Pixel column in source bitmap being processed
double width;
double fscale;
double center, begin, end;
double weight;
TQ_INT32 k, n;
if(scale < 1.0)
{
//Shrinking image
width = fwidth / scale;
fscale = 1.0 / scale;
contrib->n = 0;
contrib->p = new Contrib[ (int)(width * 2 + 1) ];
center = (double) i / scale;
begin = ceil(center - width);
end = floor(center + width);
for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
{
weight = center - (double) srcpos;
weight = filterStrategy->valueAt(weight / fscale) / fscale;
if(srcpos < 0)
n = -srcpos;
else if(srcpos >= srcwidth)
n = (srcwidth - srcpos) + srcwidth - 1;
else
n = srcpos;
k = contrib->n++;
contrib->p[k].m_pixel = n;
contrib->p[k].m_weight = weight;
}
}
else
{
// Expanding image
contrib->n = 0;
contrib->p = new Contrib[ (int)(fwidth * 2 + 1) ];
center = (double) i / scale;
begin = ceil(center - fwidth);
end = floor(center + fwidth);
for(int srcpos = (int)begin; srcpos <= end; ++srcpos)
{
weight = center - (double) srcpos;
weight = filterStrategy->valueAt(weight);
if(srcpos < 0) {
n = -srcpos;
} else if(srcpos >= srcwidth) {
n = (srcwidth - srcpos) + srcwidth - 1;
} else {
n = srcpos;
}
k = contrib->n++;
contrib->p[k].m_pixel = n;
contrib->p[k].m_weight = weight;
}
}
return 0;
} /* calc_x_contrib */