/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2013 - Raw Material Software Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE 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. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ ImageConvolutionKernel::ImageConvolutionKernel (const int size_) : values ((size_t) (size_ * size_)), size (size_) { clear(); } ImageConvolutionKernel::~ImageConvolutionKernel() { } //============================================================================== float ImageConvolutionKernel::getKernelValue (const int x, const int y) const noexcept { if (isPositiveAndBelow (x, size) && isPositiveAndBelow (y, size)) return values [x + y * size]; jassertfalse; return 0; } void ImageConvolutionKernel::setKernelValue (const int x, const int y, const float value) noexcept { if (isPositiveAndBelow (x, size) && isPositiveAndBelow (y, size)) { values [x + y * size] = value; } else { jassertfalse; } } void ImageConvolutionKernel::clear() { for (int i = size * size; --i >= 0;) values[i] = 0; } void ImageConvolutionKernel::setOverallSum (const float desiredTotalSum) { double currentTotal = 0.0; for (int i = size * size; --i >= 0;) currentTotal += values[i]; rescaleAllValues ((float) (desiredTotalSum / currentTotal)); } void ImageConvolutionKernel::rescaleAllValues (const float multiplier) { for (int i = size * size; --i >= 0;) values[i] *= multiplier; } //============================================================================== void ImageConvolutionKernel::createGaussianBlur (const float radius) { const double radiusFactor = -1.0 / (radius * radius * 2); const int centre = size >> 1; for (int y = size; --y >= 0;) { for (int x = size; --x >= 0;) { const int cx = x - centre; const int cy = y - centre; values [x + y * size] = (float) exp (radiusFactor * (cx * cx + cy * cy)); } } setOverallSum (1.0f); } //============================================================================== void ImageConvolutionKernel::applyToImage (Image& destImage, const Image& sourceImage, const Rectangle& destinationArea) const { if (sourceImage == destImage) { destImage.duplicateIfShared(); } else { if (sourceImage.getWidth() != destImage.getWidth() || sourceImage.getHeight() != destImage.getHeight() || sourceImage.getFormat() != destImage.getFormat()) { jassertfalse; return; } } const Rectangle area (destinationArea.getIntersection (destImage.getBounds())); if (area.isEmpty()) return; const int right = area.getRight(); const int bottom = area.getBottom(); const Image::BitmapData destData (destImage, area.getX(), area.getY(), area.getWidth(), area.getHeight(), Image::BitmapData::writeOnly); uint8* line = destData.data; const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly); if (destData.pixelStride == 4) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; float c2 = 0; float c3 = 0; float c4 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; c2 += kernelMult * *src++; c3 += kernelMult * *src++; c4 += kernelMult * *src++; } else { src += 4; } ++sx; } } } *dest++ = (uint8) jmin (0xff, roundToInt (c1)); *dest++ = (uint8) jmin (0xff, roundToInt (c2)); *dest++ = (uint8) jmin (0xff, roundToInt (c3)); *dest++ = (uint8) jmin (0xff, roundToInt (c4)); } } } else if (destData.pixelStride == 3) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; float c2 = 0; float c3 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; c2 += kernelMult * *src++; c3 += kernelMult * *src++; } else { src += 3; } ++sx; } } } *dest++ = (uint8) roundToInt (c1); *dest++ = (uint8) roundToInt (c2); *dest++ = (uint8) roundToInt (c3); } } } else if (destData.pixelStride == 1) { for (int y = area.getY(); y < bottom; ++y) { uint8* dest = line; line += destData.lineStride; for (int x = area.getX(); x < right; ++x) { float c1 = 0; for (int yy = 0; yy < size; ++yy) { const int sy = y + yy - (size >> 1); if (sy >= srcData.height) break; if (sy >= 0) { int sx = x - (size >> 1); const uint8* src = srcData.getPixelPointer (sx, sy); for (int xx = 0; xx < size; ++xx) { if (sx >= srcData.width) break; if (sx >= 0) { const float kernelMult = values [xx + yy * size]; c1 += kernelMult * *src++; } else { src += 3; } ++sx; } } } *dest++ = (uint8) roundToInt (c1); } } } }