/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-05-25 * Description : Blur FX threaded image filter. * * Copyright 2005-2007 by Gilles Caulier * Copyright 2006-2007 by Marcel Wiesweg * * Original Blur algorithms copyrighted 2004 by * Pieter Z. Voloshyn . * * 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, 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. * * ============================================================ */ // Represents 1 #define ANGLE_RATIO 0.017453292519943295769236907685 // C++ includes. #include #include #include // TQt includes. #include // Local includes. #include "dimg.h" #include "dimggaussianblur.h" #include "blurfx.h" namespace DigikamBlurFXImagesPlugin { BlurFX::BlurFX(Digikam::DImg *orgImage, TQObject *parent, int blurFXType, int distance, int level) : Digikam::DImgThreadedFilter(orgImage, parent, "BlurFX") { m_blurFXType = blurFXType; m_distance = distance; m_level = level; initFilter(); } void BlurFX::filterImage(void) { int w = m_orgImage.width(); int h = m_orgImage.height(); switch (m_blurFXType) { case ZoomBlur: zoomBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance); break; case RadialBlur: radialBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance); break; case FarBlur: farBlur(&m_orgImage, &m_destImage, m_distance); break; case MotionBlur: motionBlur(&m_orgImage, &m_destImage, m_distance, (double)m_level); break; case SoftenerBlur: softenerBlur(&m_orgImage, &m_destImage); break; case ShakeBlur: shakeBlur(&m_orgImage, &m_destImage, m_distance); break; case FocusBlur: focusBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance, m_level*10); break; case SmartBlur: smartBlur(&m_orgImage, &m_destImage, m_distance, m_level); break; case FrostGlass: frostGlass(&m_orgImage, &m_destImage, m_distance); break; case Mosaic: mosaic(&m_orgImage, &m_destImage, m_distance, m_distance); break; } } /* Function to apply the ZoomBlur effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * X, Y => Center of zoom in the image * Distance => Distance value * pArea => Preview area. * * Theory => Here we have a effect similar to RadialBlur mode Zoom from * Photoshop. The theory is very similar to RadialBlur, but has one * difference. Instead we use pixels with the same radius and * near angles, we take pixels with the same angle but near radius * This radius is always from the center to out of the image, we * calc a proportional radius from the center. */ void BlurFX::zoomBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, int Distance, TQRect pArea) { if (Distance <= 1) return; int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); // We working on full image. int xMin = 0; int xMax = Width; int yMin = 0; int yMax = Height; // If we working in preview mode, else we using the preview area. if ( pArea.isValid() ) { xMin = pArea.x(); xMax = pArea.x() + pArea.width(); yMin = pArea.y(); yMax = pArea.y() + pArea.height(); } int h, w, nh, nw, r; int sumR, sumG, sumB, nCount; double lfRadius, lfNewRadius, lfRadMax, lfAngle; Digikam::DColor color; int offset; lfRadMax = sqrt (Height * Height + Width * Width); // number of added pixels nCount = 0; // we have reached the main loop for (h = yMin; !m_cancel && (h < yMax); h++) { for (w = xMin; !m_cancel && (w < xMax); w++) { // ...we enter this loop to sum the bits // we initialize the variables sumR = sumG = sumB = nCount = 0; nw = X - w; nh = Y - h; lfRadius = sqrt (nw * nw + nh * nh); lfAngle = atan2 ((double)nh, (double)nw); lfNewRadius = (lfRadius * Distance) / lfRadMax; for (r = 0; !m_cancel && (r <= lfNewRadius); r++) { // we need to calc the positions nw = (int)(X - (lfRadius - r) * cos (lfAngle)); nh = (int)(Y - (lfRadius - r) * sin (lfAngle)); if (IsInside(Width, Height, nw, nh)) { // read color offset = GetOffset(Width, nw, nh, bytesDepth); color.setColor(data + offset, sixteenBit); // we sum the bits sumR += color.red(); sumG += color.green(); sumB += color.blue(); nCount++; } } if (nCount == 0) nCount = 1; // calculate pointer offset = GetOffset(Width, w, h, bytesDepth); // read color to preserve alpha color.setColor(data + offset, sixteenBit); // now, we have to calc the arithmetic average color.setRed (sumR / nCount); color.setGreen(sumG / nCount); color.setBlue (sumB / nCount); // write color to destination color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (((double)(h - yMin) * 100.0) / (yMax - yMin)); if (progress%5 == 0) postProgress(progress); } } /* Function to apply the radialBlur effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * X, Y => Center of radial in the image * Distance => Distance value * pArea => Preview area. * * Theory => Similar to RadialBlur from Photoshop, its an amazing effect * Very easy to understand but a little hard to implement. * We have all the image and find the center pixel. Now, we analize * all the pixels and calc the radius from the center and find the * angle. After this, we sum this pixel with others with the same * radius, but different angles. Here I'm using degrees angles. */ void BlurFX::radialBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, int Distance, TQRect pArea) { if (Distance <= 1) return; int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); // We working on full image. int xMin = 0; int xMax = Width; int yMin = 0; int yMax = Height; // If we working in preview mode, else we using the preview area. if ( pArea.isValid() ) { xMin = pArea.x(); xMax = pArea.x() + pArea.width(); yMin = pArea.y(); yMax = pArea.y() + pArea.height(); } int sumR, sumG, sumB, nw, nh; double Radius, Angle, AngleRad; Digikam::DColor color; int offset; double *nMultArray = new double[Distance * 2 + 1]; for (int i = -Distance; i <= Distance; i++) nMultArray[i + Distance] = i * ANGLE_RATIO; // number of added pixels int nCount = 0; // we have reached the main loop for (int h = yMin; !m_cancel && (h < yMax); h++) { for (int w = xMin; !m_cancel && (w < xMax); w++) { // ...we enter this loop to sum the bits // we initialize the variables sumR = sumG = sumB = nCount = 0; nw = X - w; nh = Y - h; Radius = sqrt (nw * nw + nh * nh); AngleRad = atan2 ((double)nh, (double)nw); for (int a = -Distance; !m_cancel && (a <= Distance); a++) { Angle = AngleRad + nMultArray[a + Distance]; // we need to calc the positions nw = (int)(X - Radius * cos (Angle)); nh = (int)(Y - Radius * sin (Angle)); if (IsInside(Width, Height, nw, nh)) { // read color offset = GetOffset(Width, nw, nh, bytesDepth); color.setColor(data + offset, sixteenBit); // we sum the bits sumR += color.red(); sumG += color.green(); sumB += color.blue(); nCount++; } } if (nCount == 0) nCount = 1; // calculate pointer offset = GetOffset(Width, w, h, bytesDepth); // read color to preserve alpha color.setColor(data + offset, sixteenBit); // now, we have to calc the arithmetic average color.setRed (sumR / nCount); color.setGreen(sumG / nCount); color.setBlue (sumB / nCount); // write color to destination color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (((double)(h - yMin) * 100.0) / (yMax - yMin)); if (progress%5 == 0) postProgress(progress); } delete [] nMultArray; } /* Function to apply the focusBlur effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * BlurRadius => Radius of blurred image. * BlendRadius => Radius of blending effect. * bInversed => If true, invert focus effect. * pArea => Preview area. * */ void BlurFX::focusBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, int BlurRadius, int BlendRadius, bool bInversed, TQRect pArea) { int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); // We working on full image. int xMin = 0; int xMax = Width; int yMin = 0; int yMax = Height; // If we working in preview mode, else we using the preview area. if ( pArea.isValid() ) { xMin = pArea.x(); xMax = pArea.x() + pArea.width(); yMin = pArea.y(); yMax = pArea.y() + pArea.height(); } if (pArea.isValid()) { //UNTESTED (unused) // We do not have access to the loop of the Gaussian blur, // so we have to cut the image that we run the effect on. int xMinBlur = xMin - BlurRadius; int xMaxBlur = xMax + BlurRadius; int yMinBlur = yMin - BlurRadius; int yMaxBlur = yMax + BlurRadius; Digikam::DImg areaImage = orgImage->copy(xMinBlur, yMaxBlur, xMaxBlur - xMinBlur, yMaxBlur - yMinBlur); Digikam::DImgGaussianBlur(this, *orgImage, *destImage, 10, 75, BlurRadius); // I am unsure about differences of 1 pixel destImage->bitBltImage(&areaImage, xMinBlur, yMinBlur); destImage->bitBltImage(orgImage, 0, 0, Width, yMinBlur, 0, 0); destImage->bitBltImage(orgImage, 0, yMinBlur, xMinBlur, yMaxBlur - yMinBlur, 0, yMinBlur); destImage->bitBltImage(orgImage, xMaxBlur + 1, yMinBlur, Width - xMaxBlur - 1, yMaxBlur - yMinBlur, yMaxBlur, yMinBlur); destImage->bitBltImage(orgImage, 0, yMaxBlur + 1, Width, Height - yMaxBlur - 1, 0, yMaxBlur); postProgress(80); } else { // copy bits for blurring memcpy(pResBits, data, orgImage->numBytes()); // Gaussian blur using the BlurRadius parameter. Digikam::DImgGaussianBlur(this, *orgImage, *destImage, 10, 80, BlurRadius); } // Blending results. int nBlendFactor; double lfRadius; int offset; Digikam::DColor colorOrgImage, colorBlurredImage; int alpha; uchar *ptr; // get composer for default blending Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffNone); int nh = 0, nw = 0; for (int h = yMin; !m_cancel && (h < yMax); h++) { nh = Y - h; for (int w = xMin; !m_cancel && (w < xMax); w++) { nw = X - w; lfRadius = sqrt (nh * nh + nw * nw); if (sixteenBit) nBlendFactor = LimitValues16 ((int)(65535.0 * lfRadius / (double)BlendRadius)); else nBlendFactor = LimitValues8 ((int)(255.0 * lfRadius / (double)BlendRadius)); // Read color values offset = GetOffset(Width, w, h, bytesDepth); ptr = pResBits + offset; colorOrgImage.setColor(data + offset, sixteenBit); colorBlurredImage.setColor(ptr, sixteenBit); // Preserve alpha alpha = colorOrgImage.alpha(); // In normal mode, the image is focused in the middle // and less focused towards the border. // In inversed mode, the image is more focused towards the edge // and less focused in the middle. // This is achieved by swapping src and dest while blending. if (bInversed) { // set blending alpha value as src alpha. Original value is stored above. colorOrgImage.setAlpha(nBlendFactor); // compose colors, writing to dest - colorBlurredImage composer->compose(colorBlurredImage, colorOrgImage); // restore alpha colorBlurredImage.setAlpha(alpha); // write color to destination colorBlurredImage.setPixel(ptr); } else { // set blending alpha value as src alpha. Original value is stored above. colorBlurredImage.setAlpha(nBlendFactor); // compose colors, writing to dest - colorOrgImage composer->compose(colorOrgImage, colorBlurredImage); // restore alpha colorOrgImage.setAlpha(alpha); // write color to destination colorOrgImage.setPixel(ptr); } } // Update the progress bar in dialog. progress = (int) (80.0 + ((double)(h - yMin) * 20.0) / (yMax - yMin)); if (progress%5 == 0) postProgress(progress); } delete composer; } /* Function to apply the farBlur effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Distance => Distance value * * Theory => This is an interesting effect, the blur is applied in that * way: (the value "1" means pixel to be used in a blur calc, ok?) * e.g. With distance = 2 * |1|1|1|1|1| * |1|0|0|0|1| * |1|0|C|0|1| * |1|0|0|0|1| * |1|1|1|1|1| * We sum all the pixels with value = 1 and apply at the pixel with* * the position "C". */ void BlurFX::farBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance) { if (Distance < 1) return; // we need to create our kernel // e.g. distance = 3, so kernel={3 1 1 2 1 1 3} int *nKern = new int[Distance * 2 + 1]; for (int i = 0; i < Distance * 2 + 1; i++) { // the first element is 3 if (i == 0) nKern[i] = 2; // the center element is 2 else if (i == Distance) nKern[i] = 3; // the last element is 3 else if (i == Distance * 2) nKern[i] = 3; // all other elements will be 1 else nKern[i] = 1; } // now, we apply a convolution with kernel MakeConvolution(orgImage, destImage, Distance, nKern); // we must delete to free memory delete [] nKern; } /* Function to apply the SmartBlur effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Radius => blur matrix radius. * Strenght => Color strenght. * * Theory => Similar to SmartBlur from Photoshop, this function has the * same engine as Blur function, but, in a matrix with n * dimentions, we take only colors that pass by sensibility filter * The result is a clean image, not totally blurred, but a image * with correction between pixels. */ void BlurFX::smartBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Strength) { if (Radius <= 0) return; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); int progress; int sumR, sumG, sumB, nCount, w, h, a; int StrengthRange = Strength; if (sixteenBit) StrengthRange = (StrengthRange + 1) * 256 - 1; Digikam::DColor color, radiusColor, radiusColorBlur; int offset, loopOffset; uchar* pBlur = new uchar[orgImage->numBytes()]; // We need to copy our bits to blur bits memcpy (pBlur, data, orgImage->numBytes()); // we have reached the main loop for (h = 0; !m_cancel && (h < Height); h++) { for (w = 0; !m_cancel && (w < Width); w++) { // we initialize the variables sumR = sumG = sumB = nCount = 0; // read color offset = GetOffset(Width, w, h, bytesDepth); color.setColor(data + offset, sixteenBit); // ...we enter this loop to sum the bits for (a = -Radius; !m_cancel && (a <= Radius); a++) { // verify if is inside the rect if (IsInside( Width, Height, w + a, h)) { // read color loopOffset = GetOffset(Width, w+a, h, bytesDepth); radiusColor.setColor(data + loopOffset, sixteenBit); // now, we have to check if is inside the sensibility filter if (IsColorInsideTheRange (color.red(), color.green(), color.blue(), radiusColor.red(), radiusColor.green(), radiusColor.blue(), StrengthRange)) { // finally we sum the bits sumR += radiusColor.red(); sumG += radiusColor.green(); sumB += radiusColor.blue(); } else { // finally we sum the bits sumR += color.red(); sumG += color.green(); sumB += color.blue(); } // increment counter nCount++; } } // now, we have to calc the arithmetic average color.setRed (sumR / nCount); color.setGreen(sumG / nCount); color.setBlue (sumB / nCount); // write color to destination color.setPixel(pBlur + offset); } // Update the progress bar in dialog. progress = (int) (((double)h * 50.0) / Height); if (progress%5 == 0) postProgress(progress); } // we have reached the second part of main loop for (w = 0; !m_cancel && (w < Width); w++) { for (h = 0;!m_cancel && ( h < Height); h++) { // we initialize the variables sumR = sumG = sumB = nCount = 0; // read color offset = GetOffset(Width, w, h, bytesDepth); color.setColor(data + offset, sixteenBit); // ...we enter this loop to sum the bits for (a = -Radius; !m_cancel && (a <= Radius); a++) { // verify if is inside the rect if (IsInside( Width, Height, w, h + a)) { // read color loopOffset = GetOffset(Width, w, h+a, bytesDepth); radiusColor.setColor(data + loopOffset, sixteenBit); // now, we have to check if is inside the sensibility filter if (IsColorInsideTheRange (color.red(), color.green(), color.blue(), radiusColor.red(), radiusColor.green(), radiusColor.blue(), StrengthRange)) { radiusColorBlur.setColor(pBlur + loopOffset, sixteenBit); // finally we sum the bits sumR += radiusColorBlur.red(); sumG += radiusColorBlur.green(); sumB += radiusColorBlur.blue(); } else { // finally we sum the bits sumR += color.red(); sumG += color.green(); sumB += color.blue(); } // increment counter nCount++; } } // now, we have to calc the arithmetic average color.setRed (sumR / nCount); color.setGreen(sumG / nCount); color.setBlue (sumB / nCount); // write color to destination color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (50.0 + ((double)w * 50.0) / Width); if (progress%5 == 0) postProgress(progress); } // now, we must free memory delete [] pBlur; } /* Function to apply the motionBlur effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Distance => Distance value * Angle => Angle direction (degrees) * * Theory => Similar to MotionBlur from Photoshop, the engine is very * simple to undertand, we take a pixel (duh!), with the angle we * will taking near pixels. After this we blur (add and do a * division). */ void BlurFX::motionBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance, double Angle) { if (Distance == 0) return; int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); Digikam::DColor color; int offset; // we try to avoid division by 0 (zero) if (Angle == 0.0) Angle = 360.0; int sumR, sumG, sumB, nCount, nw, nh; double nAngX, nAngY; // we initialize cos and sin for a best performance nAngX = cos ((2.0 * M_PI) / (360.0 / Angle)); nAngY = sin ((2.0 * M_PI) / (360.0 / Angle)); // total of bits to be taken is given by this formula nCount = Distance * 2 + 1; // we will alloc size and calc the possible results int *lpXArray = new int[nCount]; int *lpYArray = new int[nCount]; for (int i = 0; i < nCount; i++) { lpXArray[i] = lround( (double)(i - Distance) * nAngX); lpYArray[i] = lround( (double)(i - Distance) * nAngY); } // we have reached the main loop for (int h = 0; !m_cancel && (h < Height); h++) { for (int w = 0; !m_cancel && (w < Width); w++) { // we initialize the variables sumR = sumG = sumB = 0; // ...we enter this loop to sum the bits for (int a = -Distance; !m_cancel && (a <= Distance); a++) { // we need to calc the positions nw = w + lpXArray[a + Distance]; nh = h + lpYArray[a + Distance]; offset = GetOffsetAdjusted(Width, Height, nw, nh, bytesDepth); color.setColor(data + offset, sixteenBit); // we sum the bits sumR += color.red(); sumG += color.green(); sumB += color.blue(); } if (nCount == 0) nCount = 1; // calculate pointer offset = GetOffset(Width, w, h, bytesDepth); // read color to preserve alpha color.setColor(data + offset, sixteenBit); // now, we have to calc the arithmetic average color.setRed (sumR / nCount); color.setGreen(sumG / nCount); color.setBlue (sumB / nCount); // write color to destination color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (((double)h * 100.0) / Height); if (progress%5 == 0) postProgress(progress); } delete [] lpXArray; delete [] lpYArray; } /* Function to apply the softenerBlur effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * * Theory => An interesting blur-like function. In dark tones we apply a * blur with 3x3 dimentions, in light tones, we apply a blur with * 5x5 dimentions. Easy, hun? */ void BlurFX::softenerBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage) { int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); int SomaR = 0, SomaG = 0, SomaB = 0; int Gray; Digikam::DColor color, colorSoma; int offset, offsetSoma; int grayLimit = sixteenBit ? 32767 : 127; for (int h = 0; !m_cancel && (h < Height); h++) { for (int w = 0; !m_cancel && (w < Width); w++) { SomaR = SomaG = SomaB = 0; offset = GetOffset(Width, w, h, bytesDepth); color.setColor(data + offset, sixteenBit); Gray = (color.red() + color.green() + color.blue()) / 3; if (Gray > grayLimit) { // 7x7 for (int a = -3; !m_cancel && (a <= 3); a++) { for (int b = -3; !m_cancel && (b <= 3); b++) { if ((h + a < 0) || (w + b < 0)) offsetSoma = offset; else offsetSoma = GetOffset(Width, (w + Lim_Max (w, b, Width)), (h + Lim_Max (h, a, Height)), bytesDepth); colorSoma.setColor(data + offsetSoma, sixteenBit); SomaR += colorSoma.red(); SomaG += colorSoma.green(); SomaB += colorSoma.blue(); } } // 7*7 = 49 color.setRed (SomaR / 49); color.setGreen(SomaG / 49); color.setBlue (SomaB / 49); color.setPixel(pResBits + offset); } else { // 3x3 for (int a = -1; !m_cancel && (a <= 1); a++) { for (int b = -1; !m_cancel && (b <= 1); b++) { if ((h + a < 0) || (w + b < 0)) offsetSoma = offset; else offsetSoma = GetOffset(Width, (w + Lim_Max (w, b, Width)), (h + Lim_Max (h, a, Height)), bytesDepth); colorSoma.setColor(data + offsetSoma, sixteenBit); SomaR += colorSoma.red(); SomaG += colorSoma.green(); SomaB += colorSoma.blue(); } } // 3*3 = 9 color.setRed (SomaR / 9); color.setGreen(SomaG / 9); color.setBlue (SomaB / 9); color.setPixel(pResBits + offset); } } // Update the progress bar in dialog. progress = (int) (((double)h * 100.0) / Height); if (progress%5 == 0) postProgress(progress); } } /* Function to apply the shake blur effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Distance => Distance between layers (from origin) * * Theory => Similar to Fragment effect from Photoshop. We create 4 layers * each one has the same distance from the origin, but have * different positions (top, button, left and right), with these 4 * layers, we join all the pixels. */ void BlurFX::shakeBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance) { int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); Digikam::DColor color, colorLayer, color1, color2, color3, color4; int offset, offsetLayer; int numBytes = orgImage->numBytes(); uchar* Layer1 = new uchar[numBytes]; uchar* Layer2 = new uchar[numBytes]; uchar* Layer3 = new uchar[numBytes]; uchar* Layer4 = new uchar[numBytes]; int h, w, nw, nh; for (h = 0; !m_cancel && (h < Height); h++) { for (w = 0; !m_cancel && (w < Width); w++) { offsetLayer = GetOffset(Width, w, h, bytesDepth); nh = (h + Distance >= Height) ? Height - 1 : h + Distance; offset = GetOffset(Width, w, nh, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(Layer1 + offsetLayer); nh = (h - Distance < 0) ? 0 : h - Distance; offset = GetOffset(Width, w, nh, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(Layer2 + offsetLayer); nw = (w + Distance >= Width) ? Width - 1 : w + Distance; offset = GetOffset(Width, nw, h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(Layer3 + offsetLayer); nw = (w - Distance < 0) ? 0 : w - Distance; offset = GetOffset(Width, nw, h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(Layer4 + offsetLayer); } // Update the progress bar in dialog. progress = (int) (((double)h * 50.0) / Height); if (progress%5 == 0) postProgress(progress); } for (int h = 0; !m_cancel && (h < Height); h++) { for (int w = 0; !m_cancel && (w < Width); w++) { offset = GetOffset(Width, w, h, bytesDepth); // read original data to preserve alpha color.setColor(data + offset, sixteenBit); // read colors from all four layers color1.setColor(Layer1 + offset, sixteenBit); color2.setColor(Layer2 + offset, sixteenBit); color3.setColor(Layer3 + offset, sixteenBit); color4.setColor(Layer4 + offset, sixteenBit); // set color components of resulting color color.setRed ( (color1.red() + color2.red() + color3.red() + color4.red()) / 4 ); color.setGreen( (color1.green() + color2.green() + color3.green() + color4.green()) / 4 ); color.setBlue ( (color1.blue() + color2.blue() + color3.blue() + color4.blue()) / 4 ); color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (50.0 + ((double)h * 50.0) / Height); if (progress%5 == 0) postProgress(progress); } delete [] Layer1; delete [] Layer2; delete [] Layer3; delete [] Layer4; } /* Function to apply the frostGlass effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Frost => Frost value * * Theory => Similar to Diffuse effect, but the random byte is defined * in a matrix. Diffuse uses a random diagonal byte. */ void BlurFX::frostGlass(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Frost) { int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); Frost = (Frost < 1) ? 1 : (Frost > 10) ? 10 : Frost; int h, w; Digikam::DColor color; int offset; // Randomize. TQDateTime dt = TQDateTime::currentDateTime(); TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); uint seed = dt.secsTo(Y2000); int range = sixteenBit ? 65535 : 255; // it is a huge optimizsation to allocate these here once uchar *IntensityCount = new uchar[range + 1]; uint *AverageColorR = new uint[range + 1]; uint *AverageColorG = new uint[range + 1]; uint *AverageColorB = new uint[range + 1]; for (h = 0; !m_cancel && (h < Height); h++) { for (w = 0; !m_cancel && (w < Width); w++) { offset = GetOffset(Width, w, h, bytesDepth); // read color to preserve alpha color.setColor(data + offset, sixteenBit); // get random color from surrounding of w|h color = RandomColor (data, Width, Height, sixteenBit, bytesDepth, w, h, Frost, color.alpha(), &seed, range, IntensityCount, AverageColorR, AverageColorG, AverageColorB); // write color to destination color.setPixel(pResBits + offset); } // Update the progress bar in dialog. progress = (int) (((double)h * 100.0) / Height); if (progress%5 == 0) postProgress(progress); } delete [] IntensityCount; delete [] AverageColorR; delete [] AverageColorG; delete [] AverageColorB; } /* Function to apply the mosaic effect backported from ImageProcessing version 2 * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Size => Size of mosaic . * * Theory => Ok, you can find some mosaic effects on PSC, but this one * has a great feature, if you see a mosaic in other code you will * see that the corner pixel doesn't change. The explanation is * simple, the color of the mosaic is the same as the first pixel * get. Here, the color of the mosaic is the same as the mosaic * center pixel. * Now the function scan the rows from the top (like photoshop). */ void BlurFX::mosaic(Digikam::DImg *orgImage, Digikam::DImg *destImage, int SizeW, int SizeH) { int progress; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pResBits = destImage->bits(); // we need to check for valid values if (SizeW < 1) SizeW = 1; if (SizeH < 1) SizeH = 1; if ((SizeW == 1) && (SizeH == 1)) return; Digikam::DColor color; int offsetCenter, offset; // this loop will never look for transparent colors for (int h = 0; !m_cancel && (h < Height); h += SizeH) { for (int w = 0; !m_cancel && (w < Width); w += SizeW) { // we have to find the center pixel for mosaic's rectangle offsetCenter = GetOffsetAdjusted(Width, Height, w + (SizeW / 2), h + (SizeH / 2), bytesDepth); color.setColor(data + offsetCenter, sixteenBit); // now, we fill the mosaic's rectangle with the center pixel color for (int subw = w; !m_cancel && (subw <= w + SizeW); subw++) { for (int subh = h; !m_cancel && (subh <= h + SizeH); subh++) { // if is inside... if (IsInside(Width, Height, subw, subh)) { // set color offset = GetOffset(Width, subw, subh, bytesDepth); color.setPixel(pResBits + offset); } } } } // Update the progress bar in dialog. progress = (int) (((double)h * 100.0) / Height); if (progress%5 == 0) postProgress(progress); } } /* Function to get a color in a matriz with a determined size * * Bits => Bits array * Width => Image width * Height => Image height * X => Position horizontal * Y => Position vertical * Radius => The radius of the matrix to be created * * Theory => This function takes from a distinct matrix a random color */ Digikam::DColor BlurFX::RandomColor(uchar *Bits, int Width, int Height, bool sixteenBit, int bytesDepth, int X, int Y, int Radius, int alpha, uint *randomSeed, int range, uchar *IntensityCount, uint *AverageColorR, uint *AverageColorG, uint *AverageColorB) { Digikam::DColor color; int offset; int w, h, counter = 0; int I; // For 16 bit we have a problem here because this takes 255 times longer, // and the algorithm is really slow for 16 bit, but I think this cannot be avoided. memset(IntensityCount, 0, range ); memset(AverageColorR, 0, range ); memset(AverageColorG, 0, range ); memset(AverageColorB, 0, range ); for (w = X - Radius; !m_cancel && (w <= X + Radius); w++) { for (h = Y - Radius; !m_cancel && (h <= Y + Radius); h++) { if ((w >= 0) && (w < Width) && (h >= 0) && (h < Height)) { offset = GetOffset(Width, w, h, bytesDepth); color.setColor(Bits + offset, sixteenBit); I = GetIntensity (color.red(), color.green(), color.blue()); IntensityCount[I]++; counter++; if (IntensityCount[I] == 1) { AverageColorR[I] = color.red(); AverageColorG[I] = color.green(); AverageColorB[I] = color.blue(); } else { AverageColorR[I] += color.red(); AverageColorG[I] += color.green(); AverageColorB[I] += color.blue(); } } } } // check for m_cancel here before entering the do loop (will crash with SIGFPE otherwise) if (m_cancel) return Digikam::DColor(0, 0, 0, 0, sixteenBit); int RandNumber, count, Index, ErrorCount = 0; int J; do { RandNumber = abs( (int)((rand_r(randomSeed) + 1) * ((double)counter / (1 + (double) RAND_MAX))) ); count = 0; Index = 0; do { count += IntensityCount[Index]; Index++; } while (count < RandNumber && !m_cancel); J = Index - 1; ErrorCount++; } while ((IntensityCount[J] == 0) && (ErrorCount <= counter) && !m_cancel); if (m_cancel) return Digikam::DColor(0, 0, 0, 0, sixteenBit); color.setSixteenBit(sixteenBit); color.setAlpha(alpha); if (ErrorCount >= counter) { color.setRed (AverageColorR[J] / counter); color.setGreen(AverageColorG[J] / counter); color.setBlue (AverageColorB[J] / counter); } else { color.setRed (AverageColorR[J] / IntensityCount[J]); color.setGreen(AverageColorG[J] / IntensityCount[J]); color.setBlue (AverageColorB[J] / IntensityCount[J]); } return color; } /* Function to simple convolve a unique pixel with a determined radius * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * Radius => kernel radius, e.g. rad=1, so array will be 3X3 * Kernel => kernel array to apply. * * Theory => I've worked hard here, but I think this is a very smart * way to convolve an array, its very hard to explain how I reach * this, but the trick here its to store the sum used by the * previous pixel, so we sum with the other pixels that wasn't get */ void BlurFX::MakeConvolution (Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Kernel[]) { if (Radius <= 0) return; int Width = orgImage->width(); int Height = orgImage->height(); uchar* data = orgImage->bits(); bool sixteenBit = orgImage->sixteenBit(); int bytesDepth = orgImage->bytesDepth(); uchar* pOutBits = destImage->bits(); int progress; int n, h, w; int nSumR, nSumG, nSumB, nCount; int nKernelWidth = Radius * 2 + 1; int range = sixteenBit ? 65536 : 256; Digikam::DColor color; int offset; uchar* pBlur = new uchar[orgImage->numBytes()]; // We need to copy our bits to blur bits memcpy (pBlur, data, orgImage->numBytes()); // We need to alloc a 2d array to help us to store the values int** arrMult = Alloc2DArray (nKernelWidth, range); for (int i = 0; i < nKernelWidth; i++) for (int j = 0; j < range; j++) arrMult[i][j] = j * Kernel[i]; // Now, we enter in the main loop for (h = 0; !m_cancel && (h < Height); h++) { for (w = 0; !m_cancel && (w < Width); w++) { // initialize the variables nSumR = nSumG = nSumB = nCount = 0; // first of all, we need to blur the horizontal lines for (n = -Radius; !m_cancel && (n <= Radius); n++) { // if is inside... if (IsInside (Width, Height, w + n, h)) { // read color from orgImage offset = GetOffset(Width, w+n, h, bytesDepth); color.setColor(data + offset, sixteenBit); // finally, we sum the pixels using a method similar to assigntables nSumR += arrMult[n + Radius][color.red()]; nSumG += arrMult[n + Radius][color.green()]; nSumB += arrMult[n + Radius][color.blue()]; // we need to add the kernel value to the counter nCount += Kernel[n + Radius]; } } if (nCount == 0) nCount = 1; // calculate pointer offset = GetOffset(Width, w, h, bytesDepth); // read color from orgImage to preserve alpha color.setColor(data + offset, sixteenBit); // now, we have to calc the arithmetic average if (sixteenBit) { color.setRed (LimitValues16(nSumR / nCount)); color.setGreen(LimitValues16(nSumG / nCount)); color.setBlue (LimitValues16(nSumB / nCount)); } else { color.setRed (LimitValues8(nSumR / nCount)); color.setGreen(LimitValues8(nSumG / nCount)); color.setBlue (LimitValues8(nSumB / nCount)); } // write color to blur bits color.setPixel(pBlur + offset); } // Update the progress bar in dialog. progress = (int) (((double)h * 50.0) / Height); if (progress%5 == 0) postProgress(progress); } // We enter in the second main loop for (w = 0; !m_cancel && (w < Width); w++) { for (h = 0; !m_cancel && (h < Height); h++) { // initialize the variables nSumR = nSumG = nSumB = nCount = 0; // first of all, we need to blur the vertical lines for (n = -Radius; !m_cancel && (n <= Radius); n++) { // if is inside... if (IsInside(Width, Height, w, h + n)) { // read color from blur bits offset = GetOffset(Width, w, h+n, bytesDepth); color.setColor(pBlur + offset, sixteenBit); // finally, we sum the pixels using a method similar to assigntables nSumR += arrMult[n + Radius][color.red()]; nSumG += arrMult[n + Radius][color.green()]; nSumB += arrMult[n + Radius][color.blue()]; // we need to add the kernel value to the counter nCount += Kernel[n + Radius]; } } if (nCount == 0) nCount = 1; // calculate pointer offset = GetOffset(Width, w, h, bytesDepth); // read color from orgImage to preserve alpha color.setColor(data + offset, sixteenBit); // now, we have to calc the arithmetic average if (sixteenBit) { color.setRed (LimitValues16(nSumR / nCount)); color.setGreen(LimitValues16(nSumG / nCount)); color.setBlue (LimitValues16(nSumB / nCount)); } else { color.setRed (LimitValues8(nSumR / nCount)); color.setGreen(LimitValues8(nSumG / nCount)); color.setBlue (LimitValues8(nSumB / nCount)); } // write color to destination color.setPixel(pOutBits + offset); } // Update the progress bar in dialog. progress = (int) (50.0 + ((double)w * 50.0) / Width); if (progress%5 == 0) postProgress(progress); } // now, we must free memory Free2DArray (arrMult, nKernelWidth); delete [] pBlur; } } // NameSpace DigikamBlurFXImagesPlugin