git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
698
deps/juce/modules/juce_graphics/images/juce_Image.cpp
vendored
Normal file
698
deps/juce/modules/juce_graphics/images/juce_Image.cpp
vendored
Normal file
@ -0,0 +1,698 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ImagePixelData::ImagePixelData (Image::PixelFormat format, int w, int h)
|
||||
: pixelFormat (format), width (w), height (h)
|
||||
{
|
||||
jassert (format == Image::RGB || format == Image::ARGB || format == Image::SingleChannel);
|
||||
jassert (w > 0 && h > 0); // It's illegal to create a zero-sized image!
|
||||
}
|
||||
|
||||
ImagePixelData::~ImagePixelData()
|
||||
{
|
||||
listeners.call ([this] (Listener& l) { l.imageDataBeingDeleted (this); });
|
||||
}
|
||||
|
||||
void ImagePixelData::sendDataChangeMessage()
|
||||
{
|
||||
listeners.call ([this] (Listener& l) { l.imageDataChanged (this); });
|
||||
}
|
||||
|
||||
int ImagePixelData::getSharedCount() const noexcept
|
||||
{
|
||||
return getReferenceCount();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ImageType::ImageType() {}
|
||||
ImageType::~ImageType() {}
|
||||
|
||||
Image ImageType::convert (const Image& source) const
|
||||
{
|
||||
if (source.isNull() || getTypeID() == source.getPixelData()->createType()->getTypeID())
|
||||
return source;
|
||||
|
||||
const Image::BitmapData src (source, Image::BitmapData::readOnly);
|
||||
|
||||
Image newImage (create (src.pixelFormat, src.width, src.height, false));
|
||||
Image::BitmapData dest (newImage, Image::BitmapData::writeOnly);
|
||||
|
||||
if (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat)
|
||||
{
|
||||
for (int y = 0; y < dest.height; ++y)
|
||||
memcpy (dest.getLinePointer (y), src.getLinePointer (y), (size_t) dest.lineStride);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = 0; y < dest.height; ++y)
|
||||
for (int x = 0; x < dest.width; ++x)
|
||||
dest.setPixelColour (x, y, src.getPixelColour (x, y));
|
||||
}
|
||||
|
||||
return newImage;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SoftwarePixelData : public ImagePixelData
|
||||
{
|
||||
public:
|
||||
SoftwarePixelData (Image::PixelFormat formatToUse, int w, int h, bool clearImage)
|
||||
: ImagePixelData (formatToUse, w, h),
|
||||
pixelStride (formatToUse == Image::RGB ? 3 : ((formatToUse == Image::ARGB) ? 4 : 1)),
|
||||
lineStride ((pixelStride * jmax (1, w) + 3) & ~3)
|
||||
{
|
||||
imageData.allocate ((size_t) lineStride * (size_t) jmax (1, h), clearImage);
|
||||
}
|
||||
|
||||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
|
||||
{
|
||||
sendDataChangeMessage();
|
||||
return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (*this));
|
||||
}
|
||||
|
||||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
|
||||
{
|
||||
bitmap.data = imageData + (size_t) x * (size_t) pixelStride + (size_t) y * (size_t) lineStride;
|
||||
bitmap.pixelFormat = pixelFormat;
|
||||
bitmap.lineStride = lineStride;
|
||||
bitmap.pixelStride = pixelStride;
|
||||
|
||||
if (mode != Image::BitmapData::readOnly)
|
||||
sendDataChangeMessage();
|
||||
}
|
||||
|
||||
ImagePixelData::Ptr clone() override
|
||||
{
|
||||
auto s = new SoftwarePixelData (pixelFormat, width, height, false);
|
||||
memcpy (s->imageData, imageData, (size_t) lineStride * (size_t) height);
|
||||
return *s;
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageType> createType() const override { return std::make_unique<SoftwareImageType>(); }
|
||||
|
||||
private:
|
||||
HeapBlock<uint8> imageData;
|
||||
const int pixelStride, lineStride;
|
||||
|
||||
JUCE_LEAK_DETECTOR (SoftwarePixelData)
|
||||
};
|
||||
|
||||
SoftwareImageType::SoftwareImageType() {}
|
||||
SoftwareImageType::~SoftwareImageType() {}
|
||||
|
||||
ImagePixelData::Ptr SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
|
||||
{
|
||||
return *new SoftwarePixelData (format, width, height, clearImage);
|
||||
}
|
||||
|
||||
int SoftwareImageType::getTypeID() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
NativeImageType::NativeImageType() {}
|
||||
NativeImageType::~NativeImageType() {}
|
||||
|
||||
int NativeImageType::getTypeID() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
|
||||
ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
|
||||
{
|
||||
return new SoftwarePixelData (format, width, height, clearImage);
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class SubsectionPixelData : public ImagePixelData
|
||||
{
|
||||
public:
|
||||
SubsectionPixelData (ImagePixelData::Ptr source, Rectangle<int> r)
|
||||
: ImagePixelData (source->pixelFormat, r.getWidth(), r.getHeight()),
|
||||
sourceImage (std::move (source)), area (r)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
|
||||
{
|
||||
auto g = sourceImage->createLowLevelContext();
|
||||
g->clipToRectangle (area);
|
||||
g->setOrigin (area.getPosition());
|
||||
return g;
|
||||
}
|
||||
|
||||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
|
||||
{
|
||||
sourceImage->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
|
||||
|
||||
if (mode != Image::BitmapData::readOnly)
|
||||
sendDataChangeMessage();
|
||||
}
|
||||
|
||||
ImagePixelData::Ptr clone() override
|
||||
{
|
||||
jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting)
|
||||
auto type = createType();
|
||||
|
||||
Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB));
|
||||
|
||||
{
|
||||
Graphics g (newImage);
|
||||
g.drawImageAt (Image (*this), 0, 0);
|
||||
}
|
||||
|
||||
return *newImage.getPixelData();
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageType> createType() const override { return sourceImage->createType(); }
|
||||
|
||||
/* as we always hold a reference to image, don't double count */
|
||||
int getSharedCount() const noexcept override { return getReferenceCount() + sourceImage->getSharedCount() - 1; }
|
||||
|
||||
private:
|
||||
friend class Image;
|
||||
const ImagePixelData::Ptr sourceImage;
|
||||
const Rectangle<int> area;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData)
|
||||
};
|
||||
|
||||
Image Image::getClippedImage (const Rectangle<int>& area) const
|
||||
{
|
||||
if (area.contains (getBounds()))
|
||||
return *this;
|
||||
|
||||
auto validArea = area.getIntersection (getBounds());
|
||||
|
||||
if (validArea.isEmpty())
|
||||
return {};
|
||||
|
||||
return Image (*new SubsectionPixelData (image, validArea));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Image::Image() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image (ReferenceCountedObjectPtr<ImagePixelData> instance) noexcept
|
||||
: image (std::move (instance))
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image (PixelFormat format, int width, int height, bool clearImage)
|
||||
: image (NativeImageType().create (format, width, height, clearImage))
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image (PixelFormat format, int width, int height, bool clearImage, const ImageType& type)
|
||||
: image (type.create (format, width, height, clearImage))
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image (const Image& other) noexcept
|
||||
: image (other.image)
|
||||
{
|
||||
}
|
||||
|
||||
Image& Image::operator= (const Image& other)
|
||||
{
|
||||
image = other.image;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Image::Image (Image&& other) noexcept
|
||||
: image (std::move (other.image))
|
||||
{
|
||||
}
|
||||
|
||||
Image& Image::operator= (Image&& other) noexcept
|
||||
{
|
||||
image = std::move (other.image);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
}
|
||||
|
||||
int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getSharedCount(); }
|
||||
int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; }
|
||||
int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; }
|
||||
Rectangle<int> Image::getBounds() const noexcept { return image == nullptr ? Rectangle<int>() : Rectangle<int> (image->width, image->height); }
|
||||
Image::PixelFormat Image::getFormat() const noexcept { return image == nullptr ? UnknownFormat : image->pixelFormat; }
|
||||
bool Image::isARGB() const noexcept { return getFormat() == ARGB; }
|
||||
bool Image::isRGB() const noexcept { return getFormat() == RGB; }
|
||||
bool Image::isSingleChannel() const noexcept { return getFormat() == SingleChannel; }
|
||||
bool Image::hasAlphaChannel() const noexcept { return getFormat() != RGB; }
|
||||
|
||||
std::unique_ptr<LowLevelGraphicsContext> Image::createLowLevelContext() const
|
||||
{
|
||||
if (image != nullptr)
|
||||
return image->createLowLevelContext();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Image::duplicateIfShared()
|
||||
{
|
||||
if (getReferenceCount() > 1)
|
||||
image = image->clone();
|
||||
}
|
||||
|
||||
Image Image::createCopy() const
|
||||
{
|
||||
if (image != nullptr)
|
||||
return Image (image->clone());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Image Image::rescaled (int newWidth, int newHeight, Graphics::ResamplingQuality quality) const
|
||||
{
|
||||
if (image == nullptr || (image->width == newWidth && image->height == newHeight))
|
||||
return *this;
|
||||
|
||||
auto type = image->createType();
|
||||
Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel()));
|
||||
|
||||
Graphics g (newImage);
|
||||
g.setImageResamplingQuality (quality);
|
||||
g.drawImageTransformed (*this, AffineTransform::scale ((float) newWidth / (float) image->width,
|
||||
(float) newHeight / (float) image->height), false);
|
||||
return newImage;
|
||||
}
|
||||
|
||||
Image Image::convertedToFormat (PixelFormat newFormat) const
|
||||
{
|
||||
if (image == nullptr || newFormat == image->pixelFormat)
|
||||
return *this;
|
||||
|
||||
auto w = image->width, h = image->height;
|
||||
|
||||
auto type = image->createType();
|
||||
Image newImage (type->create (newFormat, w, h, false));
|
||||
|
||||
if (newFormat == SingleChannel)
|
||||
{
|
||||
if (! hasAlphaChannel())
|
||||
{
|
||||
newImage.clear (getBounds(), Colours::black);
|
||||
}
|
||||
else
|
||||
{
|
||||
const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly);
|
||||
const BitmapData srcData (*this, 0, 0, w, h);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
auto src = reinterpret_cast<const PixelARGB*> (srcData.getLinePointer (y));
|
||||
auto dst = destData.getLinePointer (y);
|
||||
|
||||
for (int x = 0; x < w; ++x)
|
||||
dst[x] = src[x].getAlpha();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (image->pixelFormat == SingleChannel && newFormat == Image::ARGB)
|
||||
{
|
||||
const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly);
|
||||
const BitmapData srcData (*this, 0, 0, w, h);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
auto src = reinterpret_cast<const PixelAlpha*> (srcData.getLinePointer (y));
|
||||
auto dst = reinterpret_cast<PixelARGB*> (destData.getLinePointer (y));
|
||||
|
||||
for (int x = 0; x < w; ++x)
|
||||
dst[x].set (src[x]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasAlphaChannel())
|
||||
newImage.clear (getBounds());
|
||||
|
||||
Graphics g (newImage);
|
||||
g.drawImageAt (*this, 0, 0);
|
||||
}
|
||||
|
||||
return newImage;
|
||||
}
|
||||
|
||||
NamedValueSet* Image::getProperties() const
|
||||
{
|
||||
return image == nullptr ? nullptr : &(image->userData);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Image::BitmapData::BitmapData (Image& im, int x, int y, int w, int h, BitmapData::ReadWriteMode mode)
|
||||
: width (w), height (h)
|
||||
{
|
||||
// The BitmapData class must be given a valid image, and a valid rectangle within it!
|
||||
jassert (im.image != nullptr);
|
||||
jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight());
|
||||
|
||||
im.image->initialiseBitmapData (*this, x, y, mode);
|
||||
jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
|
||||
}
|
||||
|
||||
Image::BitmapData::BitmapData (const Image& im, int x, int y, int w, int h)
|
||||
: width (w), height (h)
|
||||
{
|
||||
// The BitmapData class must be given a valid image, and a valid rectangle within it!
|
||||
jassert (im.image != nullptr);
|
||||
jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight());
|
||||
|
||||
im.image->initialiseBitmapData (*this, x, y, readOnly);
|
||||
jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
|
||||
}
|
||||
|
||||
Image::BitmapData::BitmapData (const Image& im, BitmapData::ReadWriteMode mode)
|
||||
: width (im.getWidth()),
|
||||
height (im.getHeight())
|
||||
{
|
||||
// The BitmapData class must be given a valid image!
|
||||
jassert (im.image != nullptr);
|
||||
|
||||
im.image->initialiseBitmapData (*this, 0, 0, mode);
|
||||
jassert (data != nullptr && pixelStride > 0 && lineStride != 0);
|
||||
}
|
||||
|
||||
Image::BitmapData::~BitmapData()
|
||||
{
|
||||
}
|
||||
|
||||
Colour Image::BitmapData::getPixelColour (int x, int y) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
|
||||
|
||||
auto pixel = getPixelPointer (x, y);
|
||||
|
||||
switch (pixelFormat)
|
||||
{
|
||||
case Image::ARGB: return Colour ( ((const PixelARGB*) pixel)->getUnpremultiplied());
|
||||
case Image::RGB: return Colour (*((const PixelRGB*) pixel));
|
||||
case Image::SingleChannel: return Colour (*((const PixelAlpha*) pixel));
|
||||
case Image::UnknownFormat:
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Image::BitmapData::setPixelColour (int x, int y, Colour colour) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
|
||||
|
||||
auto pixel = getPixelPointer (x, y);
|
||||
auto col = colour.getPixelARGB();
|
||||
|
||||
switch (pixelFormat)
|
||||
{
|
||||
case Image::ARGB: ((PixelARGB*) pixel)->set (col); break;
|
||||
case Image::RGB: ((PixelRGB*) pixel)->set (col); break;
|
||||
case Image::SingleChannel: ((PixelAlpha*) pixel)->set (col); break;
|
||||
case Image::UnknownFormat:
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Image::clear (const Rectangle<int>& area, Colour colourToClearTo)
|
||||
{
|
||||
if (image != nullptr)
|
||||
{
|
||||
auto g = image->createLowLevelContext();
|
||||
g->setFill (colourToClearTo);
|
||||
g->fillRect (area, true);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Colour Image::getPixelAt (int x, int y) const
|
||||
{
|
||||
if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
|
||||
{
|
||||
const BitmapData srcData (*this, x, y, 1, 1);
|
||||
return srcData.getPixelColour (0, 0);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Image::setPixelAt (int x, int y, Colour colour)
|
||||
{
|
||||
if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
|
||||
{
|
||||
const BitmapData destData (*this, x, y, 1, 1, BitmapData::writeOnly);
|
||||
destData.setPixelColour (0, 0, colour);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::multiplyAlphaAt (int x, int y, float multiplier)
|
||||
{
|
||||
if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())
|
||||
&& hasAlphaChannel())
|
||||
{
|
||||
const BitmapData destData (*this, x, y, 1, 1, BitmapData::readWrite);
|
||||
|
||||
if (isARGB())
|
||||
reinterpret_cast<PixelARGB*> (destData.data)->multiplyAlpha (multiplier);
|
||||
else
|
||||
*(destData.data) = (uint8) (*(destData.data) * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
template <class PixelType>
|
||||
struct PixelIterator
|
||||
{
|
||||
template <class PixelOperation>
|
||||
static void iterate (const Image::BitmapData& data, const PixelOperation& pixelOp)
|
||||
{
|
||||
for (int y = 0; y < data.height; ++y)
|
||||
{
|
||||
auto p = data.getLinePointer (y);
|
||||
|
||||
for (int x = 0; x < data.width; ++x)
|
||||
{
|
||||
pixelOp (*reinterpret_cast<PixelType*> (p));
|
||||
p += data.pixelStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class PixelOperation>
|
||||
static void performPixelOp (const Image::BitmapData& data, const PixelOperation& pixelOp)
|
||||
{
|
||||
switch (data.pixelFormat)
|
||||
{
|
||||
case Image::ARGB: PixelIterator<PixelARGB> ::iterate (data, pixelOp); break;
|
||||
case Image::RGB: PixelIterator<PixelRGB> ::iterate (data, pixelOp); break;
|
||||
case Image::SingleChannel: PixelIterator<PixelAlpha>::iterate (data, pixelOp); break;
|
||||
case Image::UnknownFormat:
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
}
|
||||
|
||||
struct AlphaMultiplyOp
|
||||
{
|
||||
float alpha;
|
||||
|
||||
template <class PixelType>
|
||||
void operator() (PixelType& pixel) const
|
||||
{
|
||||
pixel.multiplyAlpha (alpha);
|
||||
}
|
||||
};
|
||||
|
||||
void Image::multiplyAllAlphas (float amountToMultiplyBy)
|
||||
{
|
||||
jassert (hasAlphaChannel());
|
||||
|
||||
const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
|
||||
performPixelOp (destData, AlphaMultiplyOp { amountToMultiplyBy });
|
||||
}
|
||||
|
||||
struct DesaturateOp
|
||||
{
|
||||
template <class PixelType>
|
||||
void operator() (PixelType& pixel) const
|
||||
{
|
||||
pixel.desaturate();
|
||||
}
|
||||
};
|
||||
|
||||
void Image::desaturate()
|
||||
{
|
||||
if (isARGB() || isRGB())
|
||||
{
|
||||
const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite);
|
||||
performPixelOp (destData, DesaturateOp());
|
||||
}
|
||||
}
|
||||
|
||||
void Image::createSolidAreaMask (RectangleList<int>& result, float alphaThreshold) const
|
||||
{
|
||||
if (hasAlphaChannel())
|
||||
{
|
||||
auto threshold = (uint8) jlimit (0, 255, roundToInt (alphaThreshold * 255.0f));
|
||||
SparseSet<int> pixelsOnRow;
|
||||
|
||||
const BitmapData srcData (*this, 0, 0, getWidth(), getHeight());
|
||||
|
||||
for (int y = 0; y < srcData.height; ++y)
|
||||
{
|
||||
pixelsOnRow.clear();
|
||||
auto lineData = srcData.getLinePointer (y);
|
||||
|
||||
if (isARGB())
|
||||
{
|
||||
for (int x = 0; x < srcData.width; ++x)
|
||||
{
|
||||
if (reinterpret_cast<const PixelARGB*> (lineData)->getAlpha() >= threshold)
|
||||
pixelsOnRow.addRange (Range<int> (x, x + 1));
|
||||
|
||||
lineData += srcData.pixelStride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int x = 0; x < srcData.width; ++x)
|
||||
{
|
||||
if (*lineData >= threshold)
|
||||
pixelsOnRow.addRange (Range<int> (x, x + 1));
|
||||
|
||||
lineData += srcData.pixelStride;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < pixelsOnRow.getNumRanges(); ++i)
|
||||
{
|
||||
auto range = pixelsOnRow.getRange (i);
|
||||
result.add (Rectangle<int> (range.getStart(), y, range.getLength(), 1));
|
||||
}
|
||||
|
||||
result.consolidate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.add (0, 0, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void Image::moveImageSection (int dx, int dy,
|
||||
int sx, int sy,
|
||||
int w, int h)
|
||||
{
|
||||
if (dx < 0)
|
||||
{
|
||||
w += dx;
|
||||
sx -= dx;
|
||||
dx = 0;
|
||||
}
|
||||
|
||||
if (dy < 0)
|
||||
{
|
||||
h += dy;
|
||||
sy -= dy;
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
if (sx < 0)
|
||||
{
|
||||
w += sx;
|
||||
dx -= sx;
|
||||
sx = 0;
|
||||
}
|
||||
|
||||
if (sy < 0)
|
||||
{
|
||||
h += sy;
|
||||
dy -= sy;
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
const int minX = jmin (dx, sx);
|
||||
const int minY = jmin (dy, sy);
|
||||
|
||||
w = jmin (w, getWidth() - jmax (sx, dx));
|
||||
h = jmin (h, getHeight() - jmax (sy, dy));
|
||||
|
||||
if (w > 0 && h > 0)
|
||||
{
|
||||
auto maxX = jmax (dx, sx) + w;
|
||||
auto maxY = jmax (dy, sy) + h;
|
||||
|
||||
const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, BitmapData::readWrite);
|
||||
|
||||
auto dst = destData.getPixelPointer (dx - minX, dy - minY);
|
||||
auto src = destData.getPixelPointer (sx - minX, sy - minY);
|
||||
|
||||
auto lineSize = (size_t) destData.pixelStride * (size_t) w;
|
||||
|
||||
if (dy > sy)
|
||||
{
|
||||
while (--h >= 0)
|
||||
{
|
||||
const int offset = h * destData.lineStride;
|
||||
memmove (dst + offset, src + offset, lineSize);
|
||||
}
|
||||
}
|
||||
else if (dst != src)
|
||||
{
|
||||
while (--h >= 0)
|
||||
{
|
||||
memmove (dst, src, lineSize);
|
||||
dst += destData.lineStride;
|
||||
src += destData.lineStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
|
||||
|
||||
const Image Image::null;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
556
deps/juce/modules/juce_graphics/images/juce_Image.h
vendored
Normal file
556
deps/juce/modules/juce_graphics/images/juce_Image.h
vendored
Normal file
@ -0,0 +1,556 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class ImageType;
|
||||
class ImagePixelData;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a fixed-size bitmap.
|
||||
|
||||
The image is stored in either 24-bit RGB or 32-bit premultiplied-ARGB format.
|
||||
|
||||
To draw into an image, create a Graphics object for it.
|
||||
e.g. @code
|
||||
|
||||
// create a transparent 500x500 image..
|
||||
Image myImage (Image::RGB, 500, 500, true);
|
||||
|
||||
Graphics g (myImage);
|
||||
g.setColour (Colours::red);
|
||||
g.fillEllipse (20, 20, 300, 200); // draws a red ellipse in our image.
|
||||
@endcode
|
||||
|
||||
Other useful ways to create an image are with the ImageCache class, or the
|
||||
ImageFileFormat, which provides a way to load common image files.
|
||||
|
||||
@see Graphics, ImageFileFormat, ImageCache, ImageConvolutionKernel
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API Image final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
enum PixelFormat
|
||||
{
|
||||
UnknownFormat,
|
||||
RGB, /**<< each pixel is a 3-byte packed RGB colour value. For byte order, see the PixelRGB class. */
|
||||
ARGB, /**<< each pixel is a 4-byte ARGB premultiplied colour value. For byte order, see the PixelARGB class. */
|
||||
SingleChannel /**<< each pixel is a 1-byte alpha channel value. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a null image. */
|
||||
Image() noexcept;
|
||||
|
||||
/** Creates an image with a specified size and format.
|
||||
|
||||
The image's internal type will be of the NativeImageType class - to specify a
|
||||
different type, use the other constructor, which takes an ImageType to use.
|
||||
|
||||
@param format the preferred pixel format. Note that this is only a *hint* which
|
||||
is passed to the ImageType class - different ImageTypes may not support
|
||||
all formats, so may substitute e.g. ARGB for RGB.
|
||||
@param imageWidth the desired width of the image, in pixels - this value must be
|
||||
greater than zero (otherwise a width of 1 will be used)
|
||||
@param imageHeight the desired width of the image, in pixels - this value must be
|
||||
greater than zero (otherwise a height of 1 will be used)
|
||||
@param clearImage if true, the image will initially be cleared to black (if it's RGB)
|
||||
or transparent black (if it's ARGB). If false, the image may contain
|
||||
junk initially, so you need to make sure you overwrite it thoroughly.
|
||||
*/
|
||||
Image (PixelFormat format, int imageWidth, int imageHeight, bool clearImage);
|
||||
|
||||
/** Creates an image with a specified size and format.
|
||||
|
||||
@param format the preferred pixel format. Note that this is only a *hint* which
|
||||
is passed to the ImageType class - different ImageTypes may not support
|
||||
all formats, so may substitute e.g. ARGB for RGB.
|
||||
@param imageWidth the desired width of the image, in pixels - this value must be
|
||||
greater than zero (otherwise a width of 1 will be used)
|
||||
@param imageHeight the desired width of the image, in pixels - this value must be
|
||||
greater than zero (otherwise a height of 1 will be used)
|
||||
@param clearImage if true, the image will initially be cleared to black (if it's RGB)
|
||||
or transparent black (if it's ARGB). If false, the image may contain
|
||||
junk initially, so you need to make sure you overwrite it thoroughly.
|
||||
@param type the type of image - this lets you specify the internal format that will
|
||||
be used to allocate and manage the image data.
|
||||
*/
|
||||
Image (PixelFormat format, int imageWidth, int imageHeight, bool clearImage, const ImageType& type);
|
||||
|
||||
/** Creates a shared reference to another image.
|
||||
|
||||
This won't create a duplicate of the image - when Image objects are copied, they simply
|
||||
point to the same shared image data. To make sure that an Image object has its own unique,
|
||||
unshared internal data, call duplicateIfShared().
|
||||
*/
|
||||
Image (const Image&) noexcept;
|
||||
|
||||
/** Makes this image refer to the same underlying image as another object.
|
||||
|
||||
This won't create a duplicate of the image - when Image objects are copied, they simply
|
||||
point to the same shared image data. To make sure that an Image object has its own unique,
|
||||
unshared internal data, call duplicateIfShared().
|
||||
*/
|
||||
Image& operator= (const Image&);
|
||||
|
||||
/** Move constructor */
|
||||
Image (Image&&) noexcept;
|
||||
|
||||
/** Move assignment operator */
|
||||
Image& operator= (Image&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Image();
|
||||
|
||||
/** Returns true if the two images are referring to the same internal, shared image. */
|
||||
bool operator== (const Image& other) const noexcept { return image == other.image; }
|
||||
|
||||
/** Returns true if the two images are not referring to the same internal, shared image. */
|
||||
bool operator!= (const Image& other) const noexcept { return image != other.image; }
|
||||
|
||||
/** Returns true if this image isn't null.
|
||||
If you create an Image with the default constructor, it has no size or content, and is null
|
||||
until you reassign it to an Image which contains some actual data.
|
||||
The isNull() method is the opposite of isValid().
|
||||
@see isNull
|
||||
*/
|
||||
inline bool isValid() const noexcept { return image != nullptr; }
|
||||
|
||||
/** Returns true if this image is not valid.
|
||||
If you create an Image with the default constructor, it has no size or content, and is null
|
||||
until you reassign it to an Image which contains some actual data.
|
||||
The isNull() method is the opposite of isValid().
|
||||
@see isValid
|
||||
*/
|
||||
inline bool isNull() const noexcept { return image == nullptr; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the image's width (in pixels). */
|
||||
int getWidth() const noexcept;
|
||||
|
||||
/** Returns the image's height (in pixels). */
|
||||
int getHeight() const noexcept;
|
||||
|
||||
/** Returns a rectangle with the same size as this image.
|
||||
The rectangle's origin is always (0, 0).
|
||||
*/
|
||||
Rectangle<int> getBounds() const noexcept;
|
||||
|
||||
/** Returns the image's pixel format. */
|
||||
PixelFormat getFormat() const noexcept;
|
||||
|
||||
/** True if the image's format is ARGB. */
|
||||
bool isARGB() const noexcept;
|
||||
|
||||
/** True if the image's format is RGB. */
|
||||
bool isRGB() const noexcept;
|
||||
|
||||
/** True if the image's format is a single-channel alpha map. */
|
||||
bool isSingleChannel() const noexcept;
|
||||
|
||||
/** True if the image contains an alpha-channel. */
|
||||
bool hasAlphaChannel() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears a section of the image with a given colour.
|
||||
|
||||
This won't do any alpha-blending - it just sets all pixels in the image to
|
||||
the given colour (which may be non-opaque if the image has an alpha channel).
|
||||
*/
|
||||
void clear (const Rectangle<int>& area, Colour colourToClearTo = Colour (0x00000000));
|
||||
|
||||
/** Returns a rescaled version of this image.
|
||||
|
||||
A new image is returned which is a copy of this one, rescaled to the given size.
|
||||
|
||||
Note that if the new size is identical to the existing image, this will just return
|
||||
a reference to the original image, and won't actually create a duplicate.
|
||||
*/
|
||||
Image rescaled (int newWidth, int newHeight,
|
||||
Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const;
|
||||
|
||||
/** Creates a copy of this image.
|
||||
Note that it's usually more efficient to use duplicateIfShared(), because it may not be necessary
|
||||
to copy an image if nothing else is using it.
|
||||
@see getReferenceCount
|
||||
*/
|
||||
Image createCopy() const;
|
||||
|
||||
/** Returns a version of this image with a different image format.
|
||||
|
||||
A new image is returned which has been converted to the specified format.
|
||||
|
||||
Note that if the new format is no different to the current one, this will just return
|
||||
a reference to the original image, and won't actually create a copy.
|
||||
*/
|
||||
Image convertedToFormat (PixelFormat newFormat) const;
|
||||
|
||||
/** Makes sure that no other Image objects share the same underlying data as this one.
|
||||
|
||||
If no other Image objects refer to the same shared data as this one, this method has no
|
||||
effect. But if there are other references to the data, this will create a new copy of
|
||||
the data internally.
|
||||
|
||||
Call this if you want to draw onto the image, but want to make sure that this doesn't
|
||||
affect any other code that may be sharing the same data.
|
||||
|
||||
@see getReferenceCount
|
||||
*/
|
||||
void duplicateIfShared();
|
||||
|
||||
/** Returns an image which refers to a subsection of this image.
|
||||
|
||||
This will not make a copy of the original - the new image will keep a reference to it, so that
|
||||
if the original image is changed, the contents of the subsection will also change. Likewise if you
|
||||
draw into the subimage, you'll also be drawing onto that area of the original image. Note that if
|
||||
you use operator= to make the original Image object refer to something else, the subsection image
|
||||
won't pick up this change, it'll remain pointing at the original.
|
||||
|
||||
The area passed-in will be clipped to the bounds of this image, so this may return a smaller
|
||||
image than the area you asked for, or even a null image if the area was out-of-bounds.
|
||||
*/
|
||||
Image getClippedImage (const Rectangle<int>& area) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour of one of the pixels in the image.
|
||||
|
||||
If the coordinates given are beyond the image's boundaries, this will
|
||||
return Colours::transparentBlack.
|
||||
|
||||
@see setPixelAt, Image::BitmapData::getPixelColour
|
||||
*/
|
||||
Colour getPixelAt (int x, int y) const;
|
||||
|
||||
/** Sets the colour of one of the image's pixels.
|
||||
|
||||
If the coordinates are beyond the image's boundaries, then nothing will happen.
|
||||
|
||||
Note that this won't do any alpha-blending, it'll just replace the existing pixel
|
||||
with the given one. The colour's opacity will be ignored if this image doesn't have
|
||||
an alpha-channel.
|
||||
|
||||
@see getPixelAt, Image::BitmapData::setPixelColour
|
||||
*/
|
||||
void setPixelAt (int x, int y, Colour colour);
|
||||
|
||||
/** Changes the opacity of a pixel.
|
||||
|
||||
This only has an effect if the image has an alpha channel and if the
|
||||
given coordinates are inside the image's boundary.
|
||||
|
||||
The multiplier must be in the range 0 to 1.0, and the current alpha
|
||||
at the given coordinates will be multiplied by this value.
|
||||
|
||||
@see setPixelAt
|
||||
*/
|
||||
void multiplyAlphaAt (int x, int y, float multiplier);
|
||||
|
||||
/** Changes the overall opacity of the image.
|
||||
|
||||
This will multiply the alpha value of each pixel in the image by the given
|
||||
amount (limiting the resulting alpha values between 0 and 255). This allows
|
||||
you to make an image more or less transparent.
|
||||
|
||||
If the image doesn't have an alpha channel, this won't have any effect.
|
||||
*/
|
||||
void multiplyAllAlphas (float amountToMultiplyBy);
|
||||
|
||||
/** Changes all the colours to be shades of grey, based on their current luminosity.
|
||||
*/
|
||||
void desaturate();
|
||||
|
||||
//==============================================================================
|
||||
/** Retrieves a section of an image as raw pixel data, so it can be read or written to.
|
||||
|
||||
You should only use this class as a last resort - messing about with the internals of
|
||||
an image is only recommended for people who really know what they're doing!
|
||||
|
||||
A BitmapData object should be used as a temporary, stack-based object. Don't keep one
|
||||
hanging around while the image is being used elsewhere.
|
||||
|
||||
Depending on the way the image class is implemented, this may create a temporary buffer
|
||||
which is copied back to the image when the object is deleted, or it may just get a pointer
|
||||
directly into the image's raw data.
|
||||
|
||||
You can use the stride and data values in this class directly, but don't alter them!
|
||||
The actual format of the pixel data depends on the image's format - see Image::getFormat(),
|
||||
and the PixelRGB, PixelARGB and PixelAlpha classes for more info.
|
||||
*/
|
||||
class JUCE_API BitmapData final
|
||||
{
|
||||
public:
|
||||
enum ReadWriteMode
|
||||
{
|
||||
readOnly,
|
||||
writeOnly,
|
||||
readWrite
|
||||
};
|
||||
|
||||
BitmapData (Image& image, int x, int y, int w, int h, ReadWriteMode mode);
|
||||
BitmapData (const Image& image, int x, int y, int w, int h);
|
||||
BitmapData (const Image& image, ReadWriteMode mode);
|
||||
~BitmapData();
|
||||
|
||||
/** Returns a pointer to the start of a line in the image.
|
||||
The coordinate you provide here isn't checked, so it's the caller's responsibility to make
|
||||
sure it's not out-of-range.
|
||||
*/
|
||||
inline uint8* getLinePointer (int y) const noexcept { return data + (size_t) y * (size_t) lineStride; }
|
||||
|
||||
/** Returns a pointer to a pixel in the image.
|
||||
The coordinates you give here are not checked, so it's the caller's responsibility to make sure they're
|
||||
not out-of-range.
|
||||
*/
|
||||
inline uint8* getPixelPointer (int x, int y) const noexcept { return data + (size_t) y * (size_t) lineStride + (size_t) x * (size_t) pixelStride; }
|
||||
|
||||
/** Returns the colour of a given pixel.
|
||||
For performance reasons, this won't do any bounds-checking on the coordinates, so it's the caller's
|
||||
responsibility to make sure they're within the image's size.
|
||||
*/
|
||||
Colour getPixelColour (int x, int y) const noexcept;
|
||||
|
||||
/** Sets the colour of a given pixel.
|
||||
For performance reasons, this won't do any bounds-checking on the coordinates, so it's the caller's
|
||||
responsibility to make sure they're within the image's size.
|
||||
*/
|
||||
void setPixelColour (int x, int y, Colour colour) const noexcept;
|
||||
|
||||
/** Returns the size of the bitmap. */
|
||||
Rectangle<int> getBounds() const noexcept { return Rectangle<int> (width, height); }
|
||||
|
||||
uint8* data; /**< The raw pixel data, packed according to the image's pixel format. */
|
||||
PixelFormat pixelFormat; /**< The format of the data. */
|
||||
int lineStride; /**< The number of bytes between each line. */
|
||||
int pixelStride; /**< The number of bytes between each pixel. */
|
||||
int width, height;
|
||||
|
||||
//==============================================================================
|
||||
/** Used internally by custom image types to manage pixel data lifetime. */
|
||||
class BitmapDataReleaser
|
||||
{
|
||||
protected:
|
||||
BitmapDataReleaser() = default;
|
||||
public:
|
||||
virtual ~BitmapDataReleaser() = default;
|
||||
};
|
||||
|
||||
std::unique_ptr<BitmapDataReleaser> dataReleaser;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (BitmapData)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Copies a section of the image to somewhere else within itself. */
|
||||
void moveImageSection (int destX, int destY,
|
||||
int sourceX, int sourceY,
|
||||
int width, int height);
|
||||
|
||||
/** Creates a RectangleList containing rectangles for all non-transparent pixels
|
||||
of the image.
|
||||
|
||||
@param result the list that will have the area added to it
|
||||
@param alphaThreshold for a semi-transparent image, any pixels whose alpha is
|
||||
above this level will be considered opaque
|
||||
*/
|
||||
void createSolidAreaMask (RectangleList<int>& result, float alphaThreshold) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a NamedValueSet that is attached to the image and which can be used for
|
||||
associating custom values with it.
|
||||
|
||||
If this is a null image, this will return a null pointer.
|
||||
*/
|
||||
NamedValueSet* getProperties() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a context suitable for drawing onto this image.
|
||||
Don't call this method directly! It's used internally by the Graphics class.
|
||||
*/
|
||||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() const;
|
||||
|
||||
/** Returns the number of Image objects which are currently referring to the same internal
|
||||
shared image data.
|
||||
|
||||
@see duplicateIfShared
|
||||
*/
|
||||
int getReferenceCount() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
ImagePixelData* getPixelData() const noexcept { return image.get(); }
|
||||
|
||||
/** @internal */
|
||||
explicit Image (ReferenceCountedObjectPtr<ImagePixelData>) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN)
|
||||
/* A null Image object that can be used when you need to return an invalid image. */
|
||||
[[deprecated ("If you need a default-constructed var, just use Image() or {}.")]]
|
||||
static const Image null;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ReferenceCountedObjectPtr<ImagePixelData> image;
|
||||
|
||||
JUCE_LEAK_DETECTOR (Image)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This is a base class for holding image data in implementation-specific ways.
|
||||
|
||||
You may never need to use this class directly - it's used internally
|
||||
by the Image class to store the actual image data. To access pixel data directly,
|
||||
you should use Image::BitmapData rather than this class.
|
||||
|
||||
ImagePixelData objects are created indirectly, by subclasses of ImageType.
|
||||
@see Image, ImageType
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API ImagePixelData : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
ImagePixelData (Image::PixelFormat, int width, int height);
|
||||
~ImagePixelData() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<ImagePixelData>;
|
||||
|
||||
/** Creates a context that will draw into this image. */
|
||||
virtual std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() = 0;
|
||||
/** Creates a copy of this image. */
|
||||
virtual Ptr clone() = 0;
|
||||
/** Creates an instance of the type of this image. */
|
||||
virtual std::unique_ptr<ImageType> createType() const = 0;
|
||||
/** Initialises a BitmapData object. */
|
||||
virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0;
|
||||
/** Returns the number of Image objects which are currently referring to the same internal
|
||||
shared image data. This is different to the reference count as an instance of ImagePixelData
|
||||
can internally depend on another ImagePixelData via it's member variables. */
|
||||
virtual int getSharedCount() const noexcept;
|
||||
|
||||
|
||||
/** The pixel format of the image data. */
|
||||
const Image::PixelFormat pixelFormat;
|
||||
const int width, height;
|
||||
|
||||
/** User-defined settings that are attached to this image.
|
||||
@see Image::getProperties().
|
||||
*/
|
||||
NamedValueSet userData;
|
||||
|
||||
//==============================================================================
|
||||
/** Used to receive callbacks for image data changes */
|
||||
struct Listener
|
||||
{
|
||||
virtual ~Listener() = default;
|
||||
|
||||
virtual void imageDataChanged (ImagePixelData*) = 0;
|
||||
virtual void imageDataBeingDeleted (ImagePixelData*) = 0;
|
||||
};
|
||||
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
void sendDataChangeMessage();
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This base class is for handlers that control a type of image manipulation format,
|
||||
e.g. an in-memory bitmap, an OpenGL image, CoreGraphics image, etc.
|
||||
|
||||
@see SoftwareImageType, NativeImageType, OpenGLImageType
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API ImageType
|
||||
{
|
||||
public:
|
||||
ImageType();
|
||||
virtual ~ImageType();
|
||||
|
||||
/** Creates a new image of this type, and the specified parameters. */
|
||||
virtual ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool shouldClearImage) const = 0;
|
||||
|
||||
/** Must return a unique number to identify this type. */
|
||||
virtual int getTypeID() const = 0;
|
||||
|
||||
/** Returns an image which is a copy of the source image, but using this type of storage mechanism.
|
||||
For example, to make sure that an image is stored in-memory, you could use:
|
||||
@code myImage = SoftwareImageType().convert (myImage); @endcode
|
||||
*/
|
||||
virtual Image convert (const Image& source) const;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An image storage type which holds the pixels in-memory as a simple block of values.
|
||||
@see ImageType, NativeImageType
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API SoftwareImageType : public ImageType
|
||||
{
|
||||
public:
|
||||
SoftwareImageType();
|
||||
~SoftwareImageType() override;
|
||||
|
||||
ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override;
|
||||
int getTypeID() const override;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An image storage type which holds the pixels using whatever is the default storage
|
||||
format on the current platform.
|
||||
@see ImageType, SoftwareImageType
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API NativeImageType : public ImageType
|
||||
{
|
||||
public:
|
||||
NativeImageType();
|
||||
~NativeImageType() override;
|
||||
|
||||
ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override;
|
||||
int getTypeID() const override;
|
||||
};
|
||||
|
||||
} // namespace juce
|
169
deps/juce/modules/juce_graphics/images/juce_ImageCache.cpp
vendored
Normal file
169
deps/juce/modules/juce_graphics/images/juce_ImageCache.cpp
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
struct ImageCache::Pimpl : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
Pimpl() {}
|
||||
~Pimpl() override { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (ImageCache::Pimpl)
|
||||
|
||||
Image getFromHashCode (const int64 hashCode) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (auto& item : images)
|
||||
{
|
||||
if (item.hashCode == hashCode)
|
||||
{
|
||||
item.lastUseTime = Time::getApproximateMillisecondCounter();
|
||||
return item.image;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void addImageToCache (const Image& image, const int64 hashCode)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
if (! isTimerRunning())
|
||||
startTimer (2000);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
images.add ({ image, hashCode, Time::getApproximateMillisecondCounter() });
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
auto now = Time::getApproximateMillisecondCounter();
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = images.size(); --i >= 0;)
|
||||
{
|
||||
auto& item = images.getReference(i);
|
||||
|
||||
if (item.image.getReferenceCount() <= 1)
|
||||
{
|
||||
if (now > item.lastUseTime + cacheTimeout || now < item.lastUseTime - 1000)
|
||||
images.remove (i);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.lastUseTime = now; // multiply-referenced, so this image is still in use.
|
||||
}
|
||||
}
|
||||
|
||||
if (images.isEmpty())
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
void releaseUnusedImages()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = images.size(); --i >= 0;)
|
||||
if (images.getReference(i).image.getReferenceCount() <= 1)
|
||||
images.remove (i);
|
||||
}
|
||||
|
||||
struct Item
|
||||
{
|
||||
Image image;
|
||||
int64 hashCode;
|
||||
uint32 lastUseTime;
|
||||
};
|
||||
|
||||
Array<Item> images;
|
||||
CriticalSection lock;
|
||||
unsigned int cacheTimeout = 5000;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (ImageCache::Pimpl)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Image ImageCache::getFromHashCode (const int64 hashCode)
|
||||
{
|
||||
if (Pimpl::getInstanceWithoutCreating() != nullptr)
|
||||
return Pimpl::getInstanceWithoutCreating()->getFromHashCode (hashCode);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ImageCache::addImageToCache (const Image& image, const int64 hashCode)
|
||||
{
|
||||
Pimpl::getInstance()->addImageToCache (image, hashCode);
|
||||
}
|
||||
|
||||
Image ImageCache::getFromFile (const File& file)
|
||||
{
|
||||
auto hashCode = file.hashCode64();
|
||||
auto image = getFromHashCode (hashCode);
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
image = ImageFileFormat::loadFrom (file);
|
||||
addImageToCache (image, hashCode);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
Image ImageCache::getFromMemory (const void* imageData, const int dataSize)
|
||||
{
|
||||
auto hashCode = (int64) (pointer_sized_int) imageData;
|
||||
auto image = getFromHashCode (hashCode);
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
image = ImageFileFormat::loadFrom (imageData, (size_t) dataSize);
|
||||
addImageToCache (image, hashCode);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void ImageCache::setCacheTimeout (const int millisecs)
|
||||
{
|
||||
jassert (millisecs >= 0);
|
||||
Pimpl::getInstance()->cacheTimeout = (unsigned int) millisecs;
|
||||
}
|
||||
|
||||
void ImageCache::releaseUnusedImages()
|
||||
{
|
||||
Pimpl::getInstance()->releaseUnusedImages();
|
||||
}
|
||||
|
||||
} // namespace juce
|
127
deps/juce/modules/juce_graphics/images/juce_ImageCache.h
vendored
Normal file
127
deps/juce/modules/juce_graphics/images/juce_ImageCache.h
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A global cache of images that have been loaded from files or memory.
|
||||
|
||||
If you're loading an image and may need to use the image in more than one
|
||||
place, this is used to allow the same image to be shared rather than loading
|
||||
multiple copies into memory.
|
||||
|
||||
Another advantage is that after images are released, they will be kept in
|
||||
memory for a few seconds before it is actually deleted, so if you're repeatedly
|
||||
loading/deleting the same image, it'll reduce the chances of having to reload it
|
||||
each time.
|
||||
|
||||
@see Image, ImageFileFormat
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API ImageCache
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Loads an image from a file, (or just returns the image if it's already cached).
|
||||
|
||||
If the cache already contains an image that was loaded from this file,
|
||||
that image will be returned. Otherwise, this method will try to load the
|
||||
file, add it to the cache, and return it.
|
||||
|
||||
Remember that the image returned is shared, so drawing into it might
|
||||
affect other things that are using it! If you want to draw on it, first
|
||||
call Image::duplicateIfShared()
|
||||
|
||||
@param file the file to try to load
|
||||
@returns the image, or null if it there was an error loading it
|
||||
@see getFromMemory, getFromCache, ImageFileFormat::loadFrom
|
||||
*/
|
||||
static Image getFromFile (const File& file);
|
||||
|
||||
/** Loads an image from an in-memory image file, (or just returns the image if it's already cached).
|
||||
|
||||
If the cache already contains an image that was loaded from this block of memory,
|
||||
that image will be returned. Otherwise, this method will try to load the
|
||||
file, add it to the cache, and return it.
|
||||
|
||||
Remember that the image returned is shared, so drawing into it might
|
||||
affect other things that are using it! If you want to draw on it, first
|
||||
call Image::duplicateIfShared()
|
||||
|
||||
@param imageData the block of memory containing the image data
|
||||
@param dataSize the data size in bytes
|
||||
@returns the image, or an invalid image if it there was an error loading it
|
||||
@see getFromMemory, getFromCache, ImageFileFormat::loadFrom
|
||||
*/
|
||||
static Image getFromMemory (const void* imageData, int dataSize);
|
||||
|
||||
//==============================================================================
|
||||
/** Checks the cache for an image with a particular hashcode.
|
||||
|
||||
If there's an image in the cache with this hashcode, it will be returned,
|
||||
otherwise it will return an invalid image.
|
||||
|
||||
@param hashCode the hash code that was associated with the image by addImageToCache()
|
||||
@see addImageToCache
|
||||
*/
|
||||
static Image getFromHashCode (int64 hashCode);
|
||||
|
||||
/** Adds an image to the cache with a user-defined hash-code.
|
||||
|
||||
The image passed-in will be referenced (not copied) by the cache, so it's probably
|
||||
a good idea not to draw into it after adding it, otherwise this will affect all
|
||||
instances of it that may be in use.
|
||||
|
||||
@param image the image to add
|
||||
@param hashCode the hash-code to associate with it
|
||||
@see getFromHashCode
|
||||
*/
|
||||
static void addImageToCache (const Image& image, int64 hashCode);
|
||||
|
||||
/** Changes the amount of time before an unused image will be removed from the cache.
|
||||
By default this is about 5 seconds.
|
||||
*/
|
||||
static void setCacheTimeout (int millisecs);
|
||||
|
||||
/** Releases any images in the cache that aren't being referenced by active
|
||||
Image objects.
|
||||
*/
|
||||
static void releaseUnusedImages();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Pimpl;
|
||||
friend struct Pimpl;
|
||||
|
||||
ImageCache();
|
||||
~ImageCache();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ImageCache)
|
||||
};
|
||||
|
||||
} // namespace juce
|
296
deps/juce/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp
vendored
Normal file
296
deps/juce/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
112
deps/juce/modules/juce_graphics/images/juce_ImageConvolutionKernel.h
vendored
Normal file
112
deps/juce/modules/juce_graphics/images/juce_ImageConvolutionKernel.h
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a filter kernel to use in convoluting an image.
|
||||
|
||||
@see Image::applyConvolution
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API ImageConvolutionKernel
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty convolution kernel.
|
||||
|
||||
@param size the length of each dimension of the kernel, so e.g. if the size
|
||||
is 5, it will create a 5x5 kernel
|
||||
*/
|
||||
ImageConvolutionKernel (int size);
|
||||
|
||||
/** Destructor. */
|
||||
~ImageConvolutionKernel();
|
||||
|
||||
//==============================================================================
|
||||
/** Resets all values in the kernel to zero. */
|
||||
void clear();
|
||||
|
||||
/** Returns one of the kernel values. */
|
||||
float getKernelValue (int x, int y) const noexcept;
|
||||
|
||||
/** Sets the value of a specific cell in the kernel.
|
||||
|
||||
The x and y parameters must be in the range 0 < x < getKernelSize().
|
||||
|
||||
@see setOverallSum
|
||||
*/
|
||||
void setKernelValue (int x, int y, float value) noexcept;
|
||||
|
||||
/** Rescales all values in the kernel to make the total add up to a fixed value.
|
||||
|
||||
This will multiply all values in the kernel by (desiredTotalSum / currentTotalSum).
|
||||
*/
|
||||
void setOverallSum (float desiredTotalSum);
|
||||
|
||||
/** Multiplies all values in the kernel by a value. */
|
||||
void rescaleAllValues (float multiplier);
|
||||
|
||||
/** Initialises the kernel for a gaussian blur.
|
||||
|
||||
@param blurRadius this may be larger or smaller than the kernel's actual
|
||||
size but this will obviously be wasteful or clip at the
|
||||
edges. Ideally the kernel should be just larger than
|
||||
(blurRadius * 2).
|
||||
*/
|
||||
void createGaussianBlur (float blurRadius);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the size of the kernel.
|
||||
|
||||
E.g. if it's a 3x3 kernel, this returns 3.
|
||||
*/
|
||||
int getKernelSize() const { return size; }
|
||||
|
||||
//==============================================================================
|
||||
/** Applies the kernel to an image.
|
||||
|
||||
@param destImage the image that will receive the resultant convoluted pixels.
|
||||
@param sourceImage the source image to read from - this can be the same image as
|
||||
the destination, but if different, it must be exactly the same
|
||||
size and format.
|
||||
@param destinationArea the region of the image to apply the filter to
|
||||
*/
|
||||
void applyToImage (Image& destImage,
|
||||
const Image& sourceImage,
|
||||
const Rectangle<int>& destinationArea) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
HeapBlock<float> values;
|
||||
const int size;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageConvolutionKernel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
111
deps/juce/modules/juce_graphics/images/juce_ImageFileFormat.cpp
vendored
Normal file
111
deps/juce/modules/juce_graphics/images/juce_ImageFileFormat.cpp
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
struct DefaultImageFormats
|
||||
{
|
||||
static ImageFileFormat** get()
|
||||
{
|
||||
static DefaultImageFormats formats;
|
||||
return formats.formats;
|
||||
}
|
||||
|
||||
private:
|
||||
DefaultImageFormats() noexcept
|
||||
{
|
||||
formats[0] = &png;
|
||||
formats[1] = &jpg;
|
||||
formats[2] = &gif;
|
||||
formats[3] = nullptr;
|
||||
}
|
||||
|
||||
PNGImageFormat png;
|
||||
JPEGImageFormat jpg;
|
||||
GIFImageFormat gif;
|
||||
|
||||
ImageFileFormat* formats[4];
|
||||
};
|
||||
|
||||
ImageFileFormat* ImageFileFormat::findImageFormatForStream (InputStream& input)
|
||||
{
|
||||
const int64 streamPos = input.getPosition();
|
||||
|
||||
for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i)
|
||||
{
|
||||
const bool found = (*i)->canUnderstand (input);
|
||||
input.setPosition (streamPos);
|
||||
|
||||
if (found)
|
||||
return *i;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImageFileFormat* ImageFileFormat::findImageFormatForFileExtension (const File& file)
|
||||
{
|
||||
for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i)
|
||||
if ((*i)->usesFileExtension (file))
|
||||
return *i;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Image ImageFileFormat::loadFrom (InputStream& input)
|
||||
{
|
||||
if (ImageFileFormat* format = findImageFormatForStream (input))
|
||||
return format->decodeImage (input);
|
||||
|
||||
return Image();
|
||||
}
|
||||
|
||||
Image ImageFileFormat::loadFrom (const File& file)
|
||||
{
|
||||
FileInputStream stream (file);
|
||||
|
||||
if (stream.openedOk())
|
||||
{
|
||||
BufferedInputStream b (stream, 8192);
|
||||
return loadFrom (b);
|
||||
}
|
||||
|
||||
return Image();
|
||||
}
|
||||
|
||||
Image ImageFileFormat::loadFrom (const void* rawData, const size_t numBytes)
|
||||
{
|
||||
if (rawData != nullptr && numBytes > 4)
|
||||
{
|
||||
MemoryInputStream stream (rawData, numBytes, false);
|
||||
return loadFrom (stream);
|
||||
}
|
||||
|
||||
return Image();
|
||||
}
|
||||
|
||||
} // namespace juce
|
223
deps/juce/modules/juce_graphics/images/juce_ImageFileFormat.h
vendored
Normal file
223
deps/juce/modules/juce_graphics/images/juce_ImageFileFormat.h
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base-class for codecs that can read and write image file formats such
|
||||
as PNG, JPEG, etc.
|
||||
|
||||
This class also contains static methods to make it easy to load images
|
||||
from files, streams or from memory.
|
||||
|
||||
@see Image, ImageCache
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API ImageFileFormat
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates an ImageFormat. */
|
||||
ImageFileFormat() = default;
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ImageFileFormat() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a description of this file format.
|
||||
|
||||
E.g. "JPEG", "PNG"
|
||||
*/
|
||||
virtual String getFormatName() = 0;
|
||||
|
||||
/** Returns true if the given stream seems to contain data that this format understands.
|
||||
|
||||
The format class should only read the first few bytes of the stream and sniff
|
||||
for header bytes that it understands.
|
||||
|
||||
Note that this will advance the stream and leave it in a new position, so if you're
|
||||
planning on re-using it, you may want to rewind it after calling this method.
|
||||
|
||||
@see decodeImage
|
||||
*/
|
||||
virtual bool canUnderstand (InputStream& input) = 0;
|
||||
|
||||
/** Returns true if this format uses the file extension of the given file. */
|
||||
virtual bool usesFileExtension (const File& possibleFile) = 0;
|
||||
|
||||
/** Tries to decode and return an image from the given stream.
|
||||
|
||||
This will be called for an image format after calling its canUnderStand() method
|
||||
to see if it can handle the stream.
|
||||
|
||||
@param input the stream to read the data from. The stream will be positioned
|
||||
at the start of the image data (but this may not necessarily
|
||||
be position 0)
|
||||
@returns the image that was decoded, or an invalid image if it fails.
|
||||
@see loadFrom
|
||||
*/
|
||||
virtual Image decodeImage (InputStream& input) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Attempts to write an image to a stream.
|
||||
|
||||
To specify extra information like encoding quality, there will be appropriate parameters
|
||||
in the subclasses of the specific file types.
|
||||
|
||||
@returns true if it nothing went wrong.
|
||||
*/
|
||||
virtual bool writeImageToStream (const Image& sourceImage,
|
||||
OutputStream& destStream) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries the built-in formats to see if it can find one to read this stream.
|
||||
There are currently built-in decoders for PNG, JPEG and GIF formats.
|
||||
The object that is returned should not be deleted by the caller.
|
||||
@see canUnderstand, decodeImage, loadFrom
|
||||
*/
|
||||
static ImageFileFormat* findImageFormatForStream (InputStream& input);
|
||||
|
||||
/** Looks for a format that can handle the given file extension.
|
||||
There are currently built-in formats for PNG, JPEG and GIF formats.
|
||||
The object that is returned should not be deleted by the caller.
|
||||
*/
|
||||
static ImageFileFormat* findImageFormatForFileExtension (const File& file);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to load an image from a stream.
|
||||
|
||||
This will use the findImageFormatForStream() method to locate a suitable
|
||||
codec, and use that to load the image.
|
||||
|
||||
@returns the image that was decoded, or an invalid image if it fails.
|
||||
*/
|
||||
static Image loadFrom (InputStream& input);
|
||||
|
||||
/** Tries to load an image from a file.
|
||||
|
||||
This will use the findImageFormatForStream() method to locate a suitable
|
||||
codec, and use that to load the image.
|
||||
|
||||
@returns the image that was decoded, or an invalid image if it fails.
|
||||
*/
|
||||
static Image loadFrom (const File& file);
|
||||
|
||||
/** Tries to load an image from a block of raw image data.
|
||||
|
||||
This will use the findImageFormatForStream() method to locate a suitable
|
||||
codec, and use that to load the image.
|
||||
|
||||
@returns the image that was decoded, or an invalid image if it fails.
|
||||
*/
|
||||
static Image loadFrom (const void* rawData,
|
||||
size_t numBytesOfData);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A subclass of ImageFileFormat for reading and writing PNG files.
|
||||
|
||||
@see ImageFileFormat, JPEGImageFormat
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API PNGImageFormat : public ImageFileFormat
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
PNGImageFormat();
|
||||
~PNGImageFormat() override;
|
||||
|
||||
//==============================================================================
|
||||
String getFormatName() override;
|
||||
bool usesFileExtension (const File&) override;
|
||||
bool canUnderstand (InputStream&) override;
|
||||
Image decodeImage (InputStream&) override;
|
||||
bool writeImageToStream (const Image&, OutputStream&) override;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A subclass of ImageFileFormat for reading and writing JPEG files.
|
||||
|
||||
@see ImageFileFormat, PNGImageFormat
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API JPEGImageFormat : public ImageFileFormat
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
JPEGImageFormat();
|
||||
~JPEGImageFormat() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies the quality to be used when writing a JPEG file.
|
||||
|
||||
@param newQuality a value 0 to 1.0, where 0 is low quality, 1.0 is best, or
|
||||
any negative value is "default" quality
|
||||
*/
|
||||
void setQuality (float newQuality);
|
||||
|
||||
//==============================================================================
|
||||
String getFormatName() override;
|
||||
bool usesFileExtension (const File&) override;
|
||||
bool canUnderstand (InputStream&) override;
|
||||
Image decodeImage (InputStream&) override;
|
||||
bool writeImageToStream (const Image&, OutputStream&) override;
|
||||
|
||||
private:
|
||||
float quality;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A subclass of ImageFileFormat for reading GIF files.
|
||||
|
||||
@see ImageFileFormat, PNGImageFormat, JPEGImageFormat
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class JUCE_API GIFImageFormat : public ImageFileFormat
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
GIFImageFormat();
|
||||
~GIFImageFormat() override;
|
||||
|
||||
//==============================================================================
|
||||
String getFormatName() override;
|
||||
bool usesFileExtension (const File&) override;
|
||||
bool canUnderstand (InputStream&) override;
|
||||
Image decodeImage (InputStream&) override;
|
||||
bool writeImageToStream (const Image&, OutputStream&) override;
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user