void RefocusFilter::convolveImage(const Args& prm) { int progress; QList<int> vals = multithreadedSteps(prm.width); for (int y1 = 0; runningFlag() && (y1 < prm.height); ++y1) { QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &RefocusFilter::convolveImageMultithreaded, vals[j], vals[j+1], y1, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)y1 * 100.0) / prm.height); if (progress % 5 == 0) { postProgress(progress); } } }
void DistortionFXFilter::tileMultithreaded(const Args& prm) { int tx, ty, progress=0, oldProgress=0; for (int h = prm.start; runningFlag() && (h < prm.stop); h += prm.HSize) { for (int w = 0; runningFlag() && (w < (int)prm.orgImage->width()); w += prm.WSize) { d->lock2.lock(); tx = d->generator.number(-prm.Random / 2, prm.Random / 2); ty = d->generator.number(-prm.Random / 2, prm.Random / 2); d->lock2.unlock(); prm.destImage->bitBltImage(prm.orgImage, w, h, prm.WSize, prm.HSize, w + tx, h + ty); } // Update the progress bar in dialog. progress = (int)( ( (double)h * (100.0 / QThreadPool::globalInstance()->maxThreadCount()) ) / (prm.stop - prm.start)); if ((progress % 5 == 0) && (progress > oldProgress)) { d->lock.lock(); oldProgress = progress; d->globalProgress += 5; postProgress(d->globalProgress); d->lock.unlock(); } } }
/* Function to apply the waves effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Amplitude => Sinoidal maximum height. * Frequency => Frequency value. * FillSides => Like a boolean variable. * Direction => Vertical or horizontal flag. * * Theory => This is an amazing effect, very funny, and very simple to * understand. You just need understand how qSin and qCos works. */ void DistortionFXFilter::waves(DImg* orgImage, DImg* destImage, int Amplitude, int Frequency, bool FillSides, bool Direction) { if (Amplitude < 0) { Amplitude = 0; } if (Frequency < 0) { Frequency = 0; } Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Amplitude = Amplitude; prm.Frequency = Frequency; prm.FillSides = FillSides; if (Direction) // Horizontal { QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::wavesHorizontalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); } else { QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::wavesVerticalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); } }
/* Function to apply the circular waves effect backported from ImageProcesqSing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * X, Y => Position of circle center on the image. * Amplitude => Sinoidal maximum height * Frequency => Frequency value. * Phase => Phase value. * WavesType => If true the amplitude is proportional to radius. * Antialias => Smart bluring result. * * Theory => Similar to Waves effect, but here I apply a senoidal function * with the angle point. */ void DistortionFXFilter::circularWaves(DImg* orgImage, DImg* destImage, int X, int Y, double Amplitude, double Frequency, double Phase, bool WavesType, bool AntiAlias) { if (Amplitude < 0.0) { Amplitude = 0.0; } if (Frequency < 0.0) { Frequency = 0.0; } int progress; QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Phase = Phase; prm.Frequency = Frequency; prm.Amplitude = Amplitude; prm.WavesType = WavesType; prm.X = X; prm.Y = Y; prm.AntiAlias = AntiAlias; for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::circularWavesMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the Cilindrical effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Coeff => Cilindrical value. * Horizontal => Apply horizontally. * Vertical => Apply vertically. * Antialias => Smart blurring result. * * Theory => This is a great effect, similar to Spherize (Photoshop). * If you understand FishEye, you will understand Cilindrical * FishEye apply a logarithm function using a sphere radius, * Spherize use the same function but in a rectangular * environment. */ void DistortionFXFilter::cilindrical(DImg* orgImage, DImg* destImage, double Coeff, bool Horizontal, bool Vertical, bool AntiAlias) { if ((Coeff == 0.0) || (!(Horizontal || Vertical))) { return; } int progress; // initial copy memcpy(destImage->bits(), orgImage->bits(), orgImage->numBytes()); QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Coeff = Coeff; prm.Horizontal = Horizontal; prm.Vertical = Vertical; prm.AntiAlias = AntiAlias; // main loop for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::cilindricalMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
/* Function to apply the block waves effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Amplitude => Sinoidal maximum height * Frequency => Frequency value * Mode => The mode to be applied. * * Theory => This is an amazing effect, very funny when amplitude and * frequency are small values. */ void DistortionFXFilter::blockWaves(DImg* orgImage, DImg* destImage, int Amplitude, int Frequency, bool Mode) { if (Amplitude < 0) { Amplitude = 0; } if (Frequency < 0) { Frequency = 0; } int progress; QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.Mode = Mode; prm.Frequency = Frequency; prm.Amplitude = Amplitude; for (int w = 0; runningFlag() && (w < (int)orgImage->width()); ++w) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.w = w; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::blockWavesMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)w * 100.0) / orgImage->width()); if (progress % 5 == 0) { postProgress(progress); } } }
void DistortionFXFilter::wavesVerticalMultithreaded(const Args& prm) { int oldProgress=0, progress=0, ty; for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { ty = lround(prm.Amplitude * qSin((prm.Frequency * 2) * w * (M_PI / 180))); prm.destImage->bitBltImage(prm.orgImage, w, 0, 1, prm.orgImage->height(), w, ty); if (prm.FillSides) { prm.destImage->bitBltImage(prm.orgImage, w, prm.orgImage->height() - ty, 1, ty, w, 0); prm.destImage->bitBltImage(prm.orgImage, w, 0, 1, prm.orgImage->height() - (prm.orgImage->height() - 2 * prm.Amplitude + ty), w, prm.orgImage->height() + ty); } // Update the progress bar in dialog. progress = (int)( ( (double)w * (100.0 / QThreadPool::globalInstance()->maxThreadCount()) ) / (prm.stop - prm.start)); if ((progress % 5 == 0) && (progress > oldProgress)) { d->lock.lock(); oldProgress = progress; d->globalProgress += 5; postProgress(d->globalProgress); d->lock.unlock(); } } }
/** This method have been implemented following this report in bugzilla : https://bugs.kde.org/show_bug.cgi?id=148540 We use YCbCr color space to perform noise addition. Please follow this url for details about this color space : http://en.allexperts.com/e/y/yc/ycbcr.htm */ void FilmGrainFilter::filterImage() { if (d->settings.lumaIntensity <= 0 || d->settings.chromaBlueIntensity <= 0 || d->settings.chromaRedIntensity <= 0 || !d->settings.isDirty()) { m_destImage = m_orgImage; return; } d->div = m_orgImage.sixteenBit() ? 65535.0 : 255.0; d->leadLumaNoise = d->settings.lumaIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->leadChromaBlueNoise = d->settings.chromaBlueIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->leadChromaRedNoise = d->settings.chromaRedIntensity * (m_orgImage.sixteenBit() ? 256.0 : 1.0); d->generator.seed(1); // noise will always be the same QList<int> vals = multithreadedSteps(m_orgImage.width()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &FilmGrainFilter::filmgrainMultithreaded, vals[j], vals[j+1] )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); }
void DistortionFXFilter::wavesHorizontalMultithreaded(const Args& prm) { int oldProgress=0, progress=0, tx; for (int h = prm.start; runningFlag() && (h < prm.stop); ++h) { tx = lround(prm.Amplitude * qSin((prm.Frequency * 2) * h * (M_PI / 180))); prm.destImage->bitBltImage(prm.orgImage, 0, h, prm.orgImage->width(), 1, tx, h); if (prm.FillSides) { prm.destImage->bitBltImage(prm.orgImage, prm.orgImage->width() - tx, h, tx, 1, 0, h); prm.destImage->bitBltImage(prm.orgImage, 0, h, prm.orgImage->width() - (prm.orgImage->width() - 2 * prm.Amplitude + tx), 1, prm.orgImage->width() + tx, h); } // Update the progress bar in dialog. progress = (int)( ( (double)h * (100.0 / QThreadPool::globalInstance()->maxThreadCount()) ) / (prm.stop - prm.start)); if ((progress % 5 == 0) && (progress > oldProgress)) { d->lock.lock(); oldProgress = progress; d->globalProgress += 5; postProgress(d->globalProgress); d->lock.unlock(); } } }
void DistortionFXFilter::circularWavesMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); double nh, nw; double lfRadius, lfRadMax; double lfNewAmp = prm.Amplitude; double lfFreqAngle = prm.Frequency * ANGLE_RATIO; double phase = prm.Phase * ANGLE_RATIO; lfRadMax = qSqrt(Height * Height + Width * Width); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { nw = prm.X - w; nh = prm.Y - prm.h; lfRadius = qSqrt(nw * nw + nh * nh); if (prm.WavesType) { lfNewAmp = prm.Amplitude * lfRadius / lfRadMax; } nw = (double)w + lfNewAmp * qSin(lfFreqAngle * lfRadius + phase); nh = (double)prm.h + lfNewAmp * qCos(lfFreqAngle * lfRadius + phase); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } }
/* Function to apply the twirl effect backported from ImageProcesqSing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * dist => Distance value. * Antialias => Smart blurring result. * * Theory => Take spiral studies, you will understand better, I'm studying * hard on this effect, because it is not too fast. */ void DistortionFXFilter::twirl(DImg* orgImage, DImg* destImage, int dist, bool AntiAlias) { // if dist value is zero, we do nothing if (dist == 0) { return; } int progress; QList<int> vals = multithreadedSteps(orgImage->width()); QList <QFuture<void> > tasks; Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.dist = dist; prm.AntiAlias = AntiAlias; // main loop for (int h = 0; runningFlag() && (h < (int)orgImage->height()); ++h) { for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.h = h; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::twirlMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); // Update the progress bar in dialog. progress = (int)(((double)h * 100.0) / orgImage->height()); if (progress % 5 == 0) { postProgress(progress); } } }
bool CharcoalFilter::convolveImage(const unsigned int order, const double* kernel) { long kernelWidth = order; if ((kernelWidth % 2) == 0) { qCWarning(DIGIKAM_DIMG_LOG) << "Kernel width must be an odd number!"; return false; } long i; double normalize = 0.0; QScopedArrayPointer<double> normal_kernel(new double[kernelWidth * kernelWidth]); if (!normal_kernel) { qCWarning(DIGIKAM_DIMG_LOG) << "Unable to allocate memory!"; return false; } for (i = 0; i < (kernelWidth * kernelWidth); ++i) { normalize += kernel[i]; } if (fabs(normalize) <= Epsilon) { normalize = 1.0; } normalize = 1.0 / normalize; for (i = 0; i < (kernelWidth * kernelWidth); ++i) { normal_kernel[i] = normalize * kernel[i]; } // -------------------------------------------------------- QList<int> vals = multithreadedSteps(m_orgImage.height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { tasks.append(QtConcurrent::run(this, &CharcoalFilter::convolveImageMultithreaded, vals[j], vals[j+1], normal_kernel.data(), kernelWidth )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); return true; }
bool RawProcessingFilter::continueQuery() const { if (m_observer && !m_observer->continueQuery(0)) { return false; } return runningFlag(); }
void DistortionFXFilter::polarCoordinatesMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); int nHalfW = Width / 2; int nHalfH = Height / 2; double lfXScale = 1.0; double lfYScale = 1.0; double lfAngle, lfRadius, lfRadMax; double nh, nw, tw; if (Width > Height) { lfYScale = (double)Width / (double)Height; } else if (Height > Width) { lfXScale = (double)Height / (double)Width; } lfRadMax = (double)qMax(Height, Width) / 2.0; double th = lfYScale * (double)(prm.h - nHalfH); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { tw = lfXScale * (double)(w - nHalfW); if (prm.Type) { // now, we get the distance lfRadius = qSqrt(th * th + tw * tw); // we find the angle from the center lfAngle = qAtan2(tw, th); // now we find the exact position's x and y nh = lfRadius * (double) Height / lfRadMax; nw = lfAngle * (double) Width / (2 * M_PI); nw = (double)nHalfW + nw; } else { lfRadius = (double)(prm.h) * lfRadMax / (double)Height; lfAngle = (double)(w) * (2 * M_PI) / (double) Width; nw = (double)nHalfW - (lfRadius / lfXScale) * qSin(lfAngle); nh = (double)nHalfH - (lfRadius / lfYScale) * qCos(lfAngle); } setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } }
void SharpenFilter::convolveImageMultithreaded(const Args& prm) { double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0; double* k = 0; double red, green, blue, alpha; int mx, my, sx, sy, mcx, mcy; DColor color; for (uint x = prm.start ; runningFlag() && (x < prm.stop) ; ++x) { k = prm.normal_kernel; red = green = blue = alpha = 0; sy = prm.y - prm.halfKernelWidth; for (mcy = 0 ; runningFlag() && (mcy < prm.kernelWidth) ; ++mcy, ++sy) { my = sy < 0 ? 0 : sy > (int)m_destImage.height() - 1 ? m_destImage.height() - 1 : sy; sx = x + (-prm.halfKernelWidth); for (mcx = 0 ; runningFlag() && (mcx < prm.kernelWidth) ; ++mcx, ++sx) { mx = sx < 0 ? 0 : sx > (int)m_destImage.width() - 1 ? m_destImage.width() - 1 : sx; color = m_orgImage.getPixelColor(mx, my); red += (*k) * (color.red() * 257.0); green += (*k) * (color.green() * 257.0); blue += (*k) * (color.blue() * 257.0); alpha += (*k) * (color.alpha() * 257.0); ++k; } } red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red + 0.5; green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green + 0.5; blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue + 0.5; alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha + 0.5; m_destImage.setPixelColor(x, prm.y, DColor((int)(red / 257UL), (int)(green / 257UL), (int)(blue / 257UL), (int)(alpha / 257UL), m_destImage.sixteenBit())); } }
void NRFilter::thresholdingMultithreaded(const Args& prm) { for (uint i = prm.start; runningFlag() && (i < prm.stop); ++i) { if (prm.fimg[*prm.lpass][i] > 0.8) { *prm.thold = prm.threshold * prm.stdev[4]; } else if (prm.fimg[*prm.lpass][i] > 0.6) { *prm.thold = prm.threshold * prm.stdev[3]; } else if (prm.fimg[*prm.lpass][i] > 0.4) { *prm.thold = prm.threshold * prm.stdev[2]; } else if (prm.fimg[*prm.lpass][i] > 0.2) { *prm.thold = prm.threshold * prm.stdev[1]; } else { *prm.thold = prm.threshold * prm.stdev[0]; } if (prm.fimg[*prm.hpass][i] < -*prm.thold) { prm.fimg[*prm.hpass][i] += *prm.thold - *prm.thold * prm.softness; } else if (prm.fimg[*prm.hpass][i] > *prm.thold) { prm.fimg[*prm.hpass][i] -= *prm.thold - *prm.thold * prm.softness; } else { prm.fimg[*prm.hpass][i] *= prm.softness; } if (*prm.hpass) { prm.fimg[0][i] += prm.fimg[*prm.hpass][i]; } } }
/* Function to apply the tile effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * WSize => Tile Width * HSize => Tile Height * Random => Maximum random value * * Theory => Similar to Tile effect from Photoshop and very easy to * understand. We get a rectangular area uqSing WSize and HSize and * replace in a position with a random distance from the original * position. */ void DistortionFXFilter::tile(DImg* orgImage, DImg* destImage, int WSize, int HSize, int Random) { if (WSize < 1) { WSize = 1; } if (HSize < 1) { HSize = 1; } if (Random < 1) { Random = 1; } Args prm; prm.orgImage = orgImage; prm.destImage = destImage; prm.WSize = WSize; prm.HSize = HSize; prm.Random = Random; d->generator.seed(d->randomSeed); QList<int> vals = multithreadedSteps(orgImage->height()); QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; tasks.append(QtConcurrent::run(this, &DistortionFXFilter::tileMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); }
void DistortionFXFilter::blockWavesMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); int nw, nh; DColor color; int offset, offsetOther; int nHalfW = Width / 2; int nHalfH = Height / 2; for (int h = prm.start; runningFlag() && (h < prm.stop); ++h) { nw = nHalfW - prm.w; nh = nHalfH - h; if (prm.Mode) { nw = (int)(prm.w + prm.Amplitude * qSin(prm.Frequency * nw * (M_PI / 180))); nh = (int)(h + prm.Amplitude * qCos(prm.Frequency * nh * (M_PI / 180))); } else { nw = (int)(prm.w + prm.Amplitude * qSin(prm.Frequency * prm.w * (M_PI / 180))); nh = (int)(h + prm.Amplitude * qCos(prm.Frequency * h * (M_PI / 180))); } offset = getOffset(Width, prm.w, h, bytesDepth); offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth); // read color color.setColor(data + offsetOther, sixteenBit); // write color to destination color.setPixel(pResBits + offset); } }
void DistortionFXFilter::multipleCornersMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); double nh, nw; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfRadMax = qSqrt(Height * Height + Width * Width) / 2.0; double lfAngle, lfNewRadius, lfCurrentRadius; for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { // we find the distance from the center nh = nHalfH - prm.h; nw = nHalfW - w; // now, we get the distance lfCurrentRadius = qSqrt(nh * nh + nw * nw); // we find the angle from the center lfAngle = qAtan2(nh, nw) * (double)prm.Factor; // ok, we sum angle with accumuled to find a new angle lfNewRadius = lfCurrentRadius * lfCurrentRadius / lfRadMax; // now we find the exact position's x and y nw = (double)nHalfW - (qCos(lfAngle) * lfNewRadius); nh = (double)nHalfH - (qSin(lfAngle) * lfNewRadius); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } }
void NRFilter::calculteStdevMultithreaded(const Args& prm) { for (uint i = prm.start; runningFlag() && (i < prm.stop); ++i) { prm.fimg[*prm.hpass][i] -= prm.fimg[*prm.lpass][i]; if (prm.fimg[*prm.hpass][i] < *prm.thold && prm.fimg[*prm.hpass][i] > -*prm.thold) { if (prm.fimg[*prm.lpass][i] > 0.8) { prm.stdev[4] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i]; prm.samples[4]++; } else if (prm.fimg[*prm.lpass][i] > 0.6) { prm.stdev[3] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i]; prm.samples[3]++; } else if (prm.fimg[*prm.lpass][i] > 0.4) { prm.stdev[2] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i]; prm.samples[2]++; } else if (prm.fimg[*prm.lpass][i] > 0.2) { prm.stdev[1] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i]; prm.samples[1]++; } else { prm.stdev[0] += prm.fimg[*prm.hpass][i] * prm.fimg[*prm.hpass][i]; prm.samples[0]++; } } } }
void LoadSaveThread::run() { while (runningFlag()) { { QMutexLocker lock(threadMutex()); delete d->lastTask; d->lastTask = 0; delete m_currentTask; m_currentTask = 0; if (!m_todo.isEmpty()) { m_currentTask = m_todo.takeFirst(); if (m_notificationPolicy == NotificationPolicyTimeLimited) { // set timing values so that first event is sent only // after an initial time span. d->notificationTime = QTime::currentTime(); d->blockNotification = true; } } else { stop(lock); } } if (m_currentTask) { m_currentTask->execute(); } } }
void CharcoalFilter::convolveImageMultithreaded(uint start, uint stop, double* normal_kernel, double kernelWidth) { int mx, my, sx, sy, mcx, mcy, oldProgress=0, progress=0; double red, green, blue, alpha; double* k = 0; uint height = m_destImage.height(); uint width = m_destImage.width(); bool sixteenBit = m_destImage.sixteenBit(); uchar* ddata = m_destImage.bits(); int ddepth = m_destImage.bytesDepth(); uchar* sdata = m_orgImage.bits(); int sdepth = m_orgImage.bytesDepth(); double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0; for (uint y = start ; runningFlag() && (y < stop) ; ++y) { sy = y - (kernelWidth / 2); for (uint x = 0; runningFlag() && (x < width); ++x) { k = normal_kernel; red = green = blue = alpha = 0; sy = y - (kernelWidth / 2); for (mcy = 0; runningFlag() && (mcy < kernelWidth); ++mcy, ++sy) { my = sy < 0 ? 0 : sy > (int) height - 1 ? height - 1 : sy; sx = x + (-kernelWidth / 2); for (mcx = 0; runningFlag() && (mcx < kernelWidth); ++mcx, ++sx) { mx = sx < 0 ? 0 : sx > (int) width - 1 ? width - 1 : sx; DColor color(sdata + mx * sdepth + (width * my * sdepth), sixteenBit); red += (*k) * (color.red() * 257.0); green += (*k) * (color.green() * 257.0); blue += (*k) * (color.blue() * 257.0); alpha += (*k) * (color.alpha() * 257.0); ++k; } } red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red + 0.5; green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green + 0.5; blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue + 0.5; alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha + 0.5; DColor color((int)(red / 257UL), (int)(green / 257UL), (int)(blue / 257UL), (int)(alpha / 257UL), sixteenBit); color.setPixel((ddata + x * ddepth + (width * y * ddepth))); } progress = (int)( ( (double)y * (80.0 / QThreadPool::globalInstance()->maxThreadCount()) ) / (stop-start)); if ((progress % 5 == 0) && (progress > oldProgress)) { d->lock.lock(); oldProgress = progress; d->globalProgress += 5; postProgress(d->globalProgress); d->lock.unlock(); } } }
bool SharpenFilter::convolveImage(const unsigned int order, const double* const kernel) { uint y; int progress; long i; double normalize = 0.0; Args prm; prm.kernelWidth = order; prm.halfKernelWidth = prm.kernelWidth / 2;; if ((prm.kernelWidth % 2) == 0) { qCWarning(DIGIKAM_DIMG_LOG) << "Kernel width must be an odd number!"; return false; } QScopedArrayPointer<double> normal_kernel(new double[prm.kernelWidth * prm.kernelWidth]); if (normal_kernel.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << "Unable to allocate memory!"; return false; } for (i = 0 ; i < (prm.kernelWidth * prm.kernelWidth) ; ++i) { normalize += kernel[i]; } if (fabs(normalize) <= Epsilon) { normalize = 1.0; } normalize = 1.0 / normalize; for (i = 0 ; i < (prm.kernelWidth * prm.kernelWidth) ; ++i) { normal_kernel[i] = normalize * kernel[i]; } prm.normal_kernel = normal_kernel.data(); QList<int> vals = multithreadedSteps(m_destImage.width()); for (y = 0 ; runningFlag() && (y < m_destImage.height()) ; ++y) { QList <QFuture<void> > tasks; for (int j = 0 ; runningFlag() && (j < vals.count()-1) ; ++j) { prm.start = vals[j]; prm.stop = vals[j+1]; prm.y = y; tasks.append(QtConcurrent::run(this, &SharpenFilter::convolveImageMultithreaded, prm )); } foreach(QFuture<void> t, tasks) t.waitForFinished(); progress = (int)(((double)y * 100.0) / m_destImage.height()); if (progress % 5 == 0) { postProgress(progress); } } return true; }
void TextureFilter::filterImage() { // Texture tile. int w = m_orgImage.width(); int h = m_orgImage.height(); int bytesDepth = m_orgImage.bytesDepth(); bool sixteenBit = m_orgImage.sixteenBit(); kDebug() << "Texture File: " << m_texturePath; DImg texture(m_texturePath); if ( texture.isNull() ) { return; } DImg textureImg(w, h, m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); texture.convertToDepthOfImage(&textureImg); for (int x = 0 ; x < w ; x+=texture.width()) { for (int y = 0 ; y < h ; y+=texture.height()) { textureImg.bitBltImage(&texture, x, y); } } // Apply texture. uchar* data = m_orgImage.bits(); uchar* pTeData = textureImg.bits(); uchar* pOutBits = m_destImage.bits(); uint offset; DColor teData, transData, inData, outData; uchar* ptr, *dptr, *tptr; int progress; int blendGain; if (sixteenBit) { blendGain = (m_blendGain + 1) * 256 - 1; } else { blendGain = m_blendGain; } // Make textured transparent layout. for (int x = 0; runningFlag() && x < w; ++x) { for (int y = 0; runningFlag() && y < h; ++y) { offset = x * bytesDepth + (y * w * bytesDepth); ptr = data + offset; tptr = pTeData + offset; // Read color teData.setColor(tptr, sixteenBit); // in the old algorithm, this was //teData.channel.red = (teData.channel.red * (255 - m_blendGain) + // transData.channel.red * m_blendGain) >> 8; // but transdata was uninitialized, its components were apparently 0, // so I removed the part after the "+". if (sixteenBit) { teData.blendInvAlpha16(blendGain); } else { teData.blendInvAlpha8(blendGain); } // Overwrite RGB. teData.setPixel(tptr); } // Update progress bar in dialog. progress = (int) (((double) x * 50.0) / w); if (progress % 5 == 0) { postProgress(progress); } } // Merge layout and image using overlay method. for (int x = 0; runningFlag() && x < w; ++x) { for (int y = 0; runningFlag() && y < h; ++y) { offset = x * bytesDepth + (y * w * bytesDepth); ptr = data + offset; dptr = pOutBits + offset; tptr = pTeData + offset; inData.setColor(ptr, sixteenBit); outData.setColor(dptr, sixteenBit); teData.setColor(tptr, sixteenBit); if (sixteenBit) { outData.setRed(intMult16(inData.red(), inData.red() + intMult16(2 * teData.red(), 65535 - inData.red()))); outData.setGreen(intMult16(inData.green(), inData.green() + intMult16(2 * teData.green(), 65535 - inData.green()))); outData.setBlue(intMult16(inData.blue(), inData.blue() + intMult16(2 * teData.blue(), 65535 - inData.blue()))); } else { outData.setRed(intMult8(inData.red(), inData.red() + intMult8(2 * teData.red(), 255 - inData.red()))); outData.setGreen(intMult8(inData.green(), inData.green() + intMult8(2 * teData.green(), 255 - inData.green()))); outData.setBlue(intMult8(inData.blue(), inData.blue() + intMult8(2 * teData.blue(), 255 - inData.blue()))); } outData.setAlpha(inData.alpha()); outData.setPixel(dptr); } // Update progress bar in dialog. progress = (int) (50.0 + ((double) x * 50.0) / w); if (progress%5 == 0) { postProgress(progress); } } }
/** Performs an histogram equalization of the image. this method adjusts the brightness of colors across the active image so that the histogram for the value channel is as nearly as possible flat, that is, so that each possible brightness value appears at about the same number of pixels as each other value. Sometimes Equalize works wonderfully at enhancing the contrasts in an image. Other times it gives garbage. It is a very powerful operation, which can either work miracles on an image or destroy it.*/ void EqualizeFilter::equalizeImage() { struct double_packet high, low, intensity; struct double_packet* map; struct int_packet* equalize_map; register int i; int progress; if (m_orgImage.sixteenBit() != m_refImage.sixteenBit()) { kDebug() << "Ref. image and Org. has different bits depth"; return; } // Create an histogram of the reference image. ImageHistogram* histogram = new ImageHistogram(m_refImage.bits(), m_refImage.width(), m_refImage.height(), m_refImage.sixteenBit()); histogram->calculate(); // Memory allocation. map = new double_packet[histogram->getHistogramSegments()]; equalize_map = new int_packet[histogram->getHistogramSegments()]; if ( !histogram || !map || !equalize_map ) { delete histogram; if (map) { delete [] map; } if (equalize_map) { delete [] equalize_map; } kWarning() << ("Unable to allocate memory!"); return; } // Integrate the histogram to get the equalization map. memset(&intensity, 0, sizeof(struct double_packet)); memset(&high, 0, sizeof(struct double_packet)); memset(&low, 0, sizeof(struct double_packet)); for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i) { intensity.red += histogram->getValue(RedChannel, i); intensity.green += histogram->getValue(GreenChannel, i); intensity.blue += histogram->getValue(BlueChannel, i); intensity.alpha += histogram->getValue(AlphaChannel, i); map[i] = intensity; } // Stretch the histogram. low = map[0]; high = map[histogram->getHistogramSegments()-1]; memset(equalize_map, 0, histogram->getHistogramSegments()*sizeof(int_packet)); // TODO magic number 256 for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i) { if (high.red != low.red) equalize_map[i].red = (uint)(((256*histogram->getHistogramSegments() -1) * (map[i].red-low.red))/(high.red-low.red)); if (high.green != low.green) equalize_map[i].green = (uint)(((256*histogram->getHistogramSegments() -1) * (map[i].green-low.green))/(high.green-low.green)); if (high.blue != low.blue) equalize_map[i].blue = (uint)(((256*histogram->getHistogramSegments() -1) * (map[i].blue-low.blue))/(high.blue-low.blue)); if (high.alpha != low.alpha) equalize_map[i].alpha = (uint)(((256*histogram->getHistogramSegments() -1) * (map[i].alpha-low.alpha))/(high.alpha-low.alpha)); } delete histogram; delete [] map; uchar* data = m_orgImage.bits(); int w = m_orgImage.width(); int h = m_orgImage.height(); bool sixteenBit = m_orgImage.sixteenBit(); int size = w*h; // Apply results to image. // TODO magic number 257 if (!sixteenBit) // 8 bits image. { uchar red, green, blue, alpha; uchar* ptr = data; for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (equalize_map[red].red)/257; } if (low.green != high.green) { green = (equalize_map[green].green)/257; } if (low.blue != high.blue) { blue = (equalize_map[blue].blue)/257; } if (low.alpha != high.alpha) { alpha = (equalize_map[alpha].alpha)/257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; ptr += 4; progress = (int)(((double)i * 100.0) / size); if ( progress%5 == 0 ) { postProgress( progress ); } } } else // 16 bits image. { unsigned short red, green, blue, alpha; unsigned short* ptr = (unsigned short*)data; for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (equalize_map[red].red)/257; } if (low.green != high.green) { green = (equalize_map[green].green)/257; } if (low.blue != high.blue) { blue = (equalize_map[blue].blue)/257; } if (low.alpha != high.alpha) { alpha = (equalize_map[alpha].alpha)/257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; ptr += 4; progress = (int)(((double)i * 100.0) / size); if ( progress%5 == 0 ) { postProgress( progress ); } } } delete [] equalize_map; }
void DistortionFXFilter::twirlMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); DColor color; int offset; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfXScale = 1.0; double lfYScale = 1.0; double lfAngle, lfNewAngle, lfAngleSum, lfCurrentRadius; double tw, nh, nw; if (Width > Height) { lfYScale = (double)Width / (double)Height; } else if (Height > Width) { lfXScale = (double)Height / (double)Width; } // the angle step is dist divided by 10000 double lfAngleStep = prm.dist / 10000.0; // now, we get the minimum radius double lfRadMax = (double)qMax(Width, Height) / 2.0; double th = lfYScale * (double)(prm.h - nHalfH); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { tw = lfXScale * (double)(w - nHalfW); // now, we get the distance lfCurrentRadius = qSqrt(th * th + tw * tw); // if distance is less than maximum radius... if (lfCurrentRadius < lfRadMax) { // we find the angle from the center lfAngle = qAtan2(th, tw); // we get the accumuled angle lfAngleSum = lfAngleStep * (-1.0 * (lfCurrentRadius - lfRadMax)); // ok, we sum angle with accumuled to find a new angle lfNewAngle = lfAngle + lfAngleSum; // now we find the exact position's x and y nw = (double)nHalfW + qCos(lfNewAngle) * (lfCurrentRadius / lfXScale); nh = (double)nHalfH + qSin(lfNewAngle) * (lfCurrentRadius / lfYScale); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } else { // copy pixel offset = getOffset(Width, w, prm.h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(pResBits + offset); } } }
void CharcoalFilter::filterImage() { if (m_orgImage.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << "No image data available!"; return; } if (d->pencil <= 0.0) { m_destImage = m_orgImage; return; } // -- Applying Edge effect ----------------------------------------------- register long i = 0; int kernelWidth = getOptimalKernelWidth(d->pencil, d->smooth); if ((int)m_orgImage.width() < kernelWidth) { qCWarning(DIGIKAM_DIMG_LOG) << "Image is smaller than radius!"; return; } QScopedArrayPointer<double> kernel(new double[kernelWidth * kernelWidth]); if (kernel.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << "Unable to allocate memory!"; return; } for (i = 0 ; i < (kernelWidth * kernelWidth) ; ++i) { kernel[i] = (-1.0); } kernel[i / 2] = kernelWidth * kernelWidth - 1.0; convolveImage(kernelWidth, kernel.data()); // -- Applying Gaussian blur effect --------------------------------------- BlurFilter(this, m_destImage, m_destImage, 80, 85, (int)(d->smooth / 10.0)); if (!runningFlag()) { return; } // -- Applying stretch contrast color effect ------------------------------- StretchFilter stretch(&m_destImage, &m_destImage); stretch.startFilterDirectly(); m_destImage.putImageData(stretch.getTargetImage().bits()); postProgress(90); if (!runningFlag()) { return; } // -- Inverting image color ----------------------------------------------- InvertFilter invert(&m_destImage); invert.startFilterDirectly(); m_destImage.putImageData(invert.getTargetImage().bits()); postProgress(95); if (!runningFlag()) { return; } // -- Convert to neutral black & white ------------------------------------ MixerContainer settings; settings.bMonochrome = true; settings.blackRedGain = 0.3; settings.blackGreenGain = 0.59; settings.blackBlueGain = 0.11; MixerFilter mixer(&m_destImage, 0L, settings); mixer.startFilterDirectly(); m_destImage.putImageData(mixer.getTargetImage().bits()); postProgress(100); if (!runningFlag()) { return; } }
void DistortionFXFilter::cilindricalMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); double nh, nw; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfCoeffX = 1.0; double lfCoeffY = 1.0; double lfCoeffStep = prm.Coeff / 1000.0; if (prm.Horizontal) { lfCoeffX = (double)nHalfW / qLn(qFabs(lfCoeffStep) * nHalfW + 1.0); } if (prm.Vertical) { lfCoeffY = (double)nHalfH / qLn(qFabs(lfCoeffStep) * nHalfH + 1.0); } for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { // we find the distance from the center nh = qFabs((double)(prm.h - nHalfH)); nw = qFabs((double)(w - nHalfW)); if (prm.Horizontal) { if (prm.Coeff > 0.0) { nw = (qExp(nw / lfCoeffX) - 1.0) / lfCoeffStep; } else { nw = lfCoeffX * qLn(1.0 + (-1.0 * lfCoeffStep) * nw); } } if (prm.Vertical) { if (prm.Coeff > 0.0) { nh = (qExp(nh / lfCoeffY) - 1.0) / lfCoeffStep; } else { nh = lfCoeffY * qLn(1.0 + (-1.0 * lfCoeffStep) * nh); } } nw = (double)nHalfW + ((w >= nHalfW) ? nw : -nw); nh = (double)nHalfH + ((prm.h >= nHalfH) ? nh : -nh); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } }
void HSLFilter::applyHSL(DImg& image) { if (image.isNull()) { return; } bool sixteenBit = image.sixteenBit(); uint numberOfPixels = image.numPixels(); int progress; int hue, sat, lig; double vib = d->settings.vibrance; DColor color; if (sixteenBit) // 16 bits image. { unsigned short* data = (unsigned short*) image.bits(); for (uint i=0; runningFlag() && (i<numberOfPixels); ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL color.getHSL(&hue, &sat, &lig); // convert HSL to RGB color.setHSL(d->htransfer16[hue], vibranceBias(d->stransfer16[sat], hue, vib, sixteenBit), d->ltransfer16[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); if ( progress%5 == 0 ) { postProgress( progress ); } } } else // 8 bits image. { uchar* data = image.bits(); for (uint i=0; runningFlag() && (i<numberOfPixels); ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL color.getHSL(&hue, &sat, &lig); // convert HSL to RGB color.setHSL(d->htransfer[hue], vibranceBias(d->stransfer[sat],hue,vib,sixteenBit), d->ltransfer[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); if ( progress%5 == 0 ) { postProgress( progress ); } } } }
void DistortionFXFilter::fisheyeMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); double nh, nw, tw; DColor color; int offset; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfXScale = 1.0; double lfYScale = 1.0; double lfCoeffStep = prm.Coeff / 1000.0; double lfRadius, lfAngle; if (Width > Height) { lfYScale = (double)Width / (double)Height; } else if (Height > Width) { lfXScale = (double)Height / (double)Width; } double lfRadMax = (double)qMax(Height, Width) / 2.0; double lfCoeff = lfRadMax / qLn(qFabs(lfCoeffStep) * lfRadMax + 1.0); double th = lfYScale * (double)(prm.h - nHalfH); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { tw = lfXScale * (double)(w - nHalfW); // we find the distance from the center lfRadius = qSqrt(th * th + tw * tw); if (lfRadius < lfRadMax) { lfAngle = qAtan2(th, tw); if (prm.Coeff > 0.0) { lfRadius = (qExp(lfRadius / lfCoeff) - 1.0) / lfCoeffStep; } else { lfRadius = lfCoeff * qLn(1.0 + (-1.0 * lfCoeffStep) * lfRadius); } nw = (double)nHalfW + (lfRadius / lfXScale) * qCos(lfAngle); nh = (double)nHalfH + (lfRadius / lfYScale) * qSin(lfAngle); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } else { // copy pixel offset = getOffset(Width, w, prm.h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(pResBits + offset); } } }