paulxstretch/deps/juce/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp

297 lines
9.0 KiB
C++
Raw Normal View History

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ImageConvolutionKernel::ImageConvolutionKernel (int sizeToUse)
: values ((size_t) (sizeToUse * sizeToUse)),
size (sizeToUse)
{
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;)
{
auto cx = x - centre;
auto cy = y - centre;
values [x + y * size] = (float) std::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;
}
}
auto area = destinationArea.getIntersection (destImage.getBounds());
if (area.isEmpty())
return;
auto right = area.getRight();
auto 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);
}
}
}
}
} // namespace juce