|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file is part of the JUCE library.
|
|
|
|
Copyright (c) 2015 - ROLI 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<int>& 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<int> 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|