|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2013 - Raw Material Software Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, const 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 (&Listener::imageDataBeingDeleted, this);
- }
-
- void ImagePixelData::sendDataChangeMessage()
- {
- listeners.call (&Listener::imageDataChanged, this);
- }
-
- int ImagePixelData::getSharedCount() const noexcept
- {
- return getReferenceCount();
- }
-
- //==============================================================================
- ImageType::ImageType() {}
- ImageType::~ImageType() {}
-
- Image ImageType::convert (const Image& source) const
- {
- if (source.isNull() || getTypeID() == (ScopedPointer<ImageType> (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);
-
- jassert (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);
-
- return newImage;
- }
-
- //==============================================================================
- class SoftwarePixelData : public ImagePixelData
- {
- public:
- SoftwarePixelData (const Image::PixelFormat format_, const int w, const int h, const bool clearImage)
- : ImagePixelData (format_, w, h),
- pixelStride (format_ == Image::RGB ? 3 : ((format_ == Image::ARGB) ? 4 : 1)),
- lineStride ((pixelStride * jmax (1, w) + 3) & ~3)
- {
- imageData.allocate ((size_t) (lineStride * jmax (1, h)), clearImage);
- }
-
- LowLevelGraphicsContext* createLowLevelContext() override
- {
- sendDataChangeMessage();
- return new LowLevelGraphicsSoftwareRenderer (Image (this));
- }
-
- void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
- {
- bitmap.data = imageData + x * pixelStride + y * lineStride;
- bitmap.pixelFormat = pixelFormat;
- bitmap.lineStride = lineStride;
- bitmap.pixelStride = pixelStride;
-
- if (mode != Image::BitmapData::readOnly)
- sendDataChangeMessage();
- }
-
- ImagePixelData* clone() override
- {
- SoftwarePixelData* s = new SoftwarePixelData (pixelFormat, width, height, false);
- memcpy (s->imageData, imageData, (size_t) (lineStride * height));
- return s;
- }
-
- ImageType* createType() const override { return new 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
- 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* const im, const Rectangle<int>& r)
- : ImagePixelData (im->pixelFormat, r.getWidth(), r.getHeight()),
- image (im), area (r)
- {
- }
-
- LowLevelGraphicsContext* createLowLevelContext() override
- {
- LowLevelGraphicsContext* g = image->createLowLevelContext();
- g->clipToRectangle (area);
- g->setOrigin (area.getPosition());
- return g;
- }
-
- void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
- {
- image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
-
- if (mode != Image::BitmapData::readOnly)
- sendDataChangeMessage();
- }
-
- ImagePixelData* clone() override
- {
- jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting)
- const ScopedPointer<ImageType> type (image->createType());
-
- Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB));
-
- {
- Graphics g (newImage);
- g.drawImageAt (Image (this), 0, 0);
- }
-
- newImage.getPixelData()->incReferenceCount();
- return newImage.getPixelData();
- }
-
- ImageType* createType() const override { return image->createType(); }
-
- /* as we always hold a reference to image, don't double count */
- int getSharedCount() const noexcept override { return getReferenceCount() + image->getSharedCount() - 1; }
-
- private:
- friend class Image;
- const ImagePixelData::Ptr image;
- 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;
-
- const Rectangle<int> validArea (area.getIntersection (getBounds()));
- return Image (validArea.isEmpty() ? nullptr : new SubsectionPixelData (image, validArea));
- }
-
-
- //==============================================================================
- Image::Image() noexcept
- {
- }
-
- Image::Image (ImagePixelData* const instance) noexcept
- : image (instance)
- {
- }
-
- Image::Image (const PixelFormat format, int width, int height, bool clearImage)
- : image (NativeImageType().create (format, width, height, clearImage))
- {
- }
-
- Image::Image (const 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;
- }
-
- #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
- Image::Image (Image&& other) noexcept
- : image (static_cast <ImagePixelData::Ptr&&> (other.image))
- {
- }
-
- Image& Image::operator= (Image&& other) noexcept
- {
- image = static_cast <ImagePixelData::Ptr&&> (other.image);
- return *this;
- }
- #endif
-
- Image::~Image()
- {
- }
-
- const Image Image::null;
-
- 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; }
-
- LowLevelGraphicsContext* Image::createLowLevelContext() const
- {
- return image == nullptr ? nullptr : image->createLowLevelContext();
- }
-
- void Image::duplicateIfShared()
- {
- if (getReferenceCount() > 1)
- image = image->clone();
- }
-
- Image Image::createCopy() const
- {
- if (image != nullptr)
- return Image (image->clone());
-
- return Image();
- }
-
- Image Image::rescaled (const int newWidth, const int newHeight, const Graphics::ResamplingQuality quality) const
- {
- if (image == nullptr || (image->width == newWidth && image->height == newHeight))
- return *this;
-
- const ScopedPointer<ImageType> type (image->createType());
- Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel()));
-
- Graphics g (newImage);
- g.setImageResamplingQuality (quality);
- g.drawImageTransformed (*this, AffineTransform::scale (newWidth / (float) image->width,
- newHeight / (float) image->height), false);
- return newImage;
- }
-
- Image Image::convertedToFormat (PixelFormat newFormat) const
- {
- if (image == nullptr || newFormat == image->pixelFormat)
- return *this;
-
- const int w = image->width, h = image->height;
-
- const ScopedPointer<ImageType> 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)
- {
- const PixelARGB* const src = (const PixelARGB*) srcData.getLinePointer (y);
- uint8* const 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)
- {
- const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y);
- PixelARGB* const dst = (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, const int x, const int y, const int w, const 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, const int x, const int y, const int w, const 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 (const int x, const int y) const noexcept
- {
- jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
-
- const uint8* const 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));
- default: jassertfalse; break;
- }
-
- return Colour();
- }
-
- void Image::BitmapData::setPixelColour (const int x, const int y, Colour colour) const noexcept
- {
- jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height));
-
- uint8* const pixel = getPixelPointer (x, y);
- const PixelARGB 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;
- default: jassertfalse; break;
- }
- }
-
- //==============================================================================
- void Image::clear (const Rectangle<int>& area, Colour colourToClearTo)
- {
- const ScopedPointer<LowLevelGraphicsContext> g (image->createLowLevelContext());
- g->setFill (colourToClearTo);
- g->fillRect (area, true);
- }
-
- //==============================================================================
- Colour Image::getPixelAt (const int x, const int y) const
- {
- if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight()))
- {
- const BitmapData srcData (*this, x, y, 1, 1);
- return srcData.getPixelColour (0, 0);
- }
-
- return Colour();
- }
-
- void Image::setPixelAt (const int x, const 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 (const int x, const int y, const float multiplier)
- {
- if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())
- && hasAlphaChannel())
- {
- const BitmapData destData (*this, x, y, 1, 1, BitmapData::readWrite);
-
- if (isARGB())
- ((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)
- {
- uint8* p = data.getLinePointer (y);
-
- for (int x = 0; x < data.width; ++x)
- {
- pixelOp (*(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;
- default: jassertfalse; break;
- }
- }
-
- struct AlphaMultiplyOp
- {
- AlphaMultiplyOp (float alpha_) noexcept : alpha (alpha_) {}
-
- const float alpha;
-
- template <class PixelType>
- void operator() (PixelType& pixel) const
- {
- pixel.multiplyAlpha (alpha);
- }
-
- JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp)
- };
-
- void Image::multiplyAllAlphas (const 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, const float alphaThreshold) const
- {
- if (hasAlphaChannel())
- {
- const uint8 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();
- const uint8* lineData = srcData.getLinePointer (y);
-
- if (isARGB())
- {
- for (int x = 0; x < srcData.width; ++x)
- {
- if (((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)
- {
- const Range<int> 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)
- {
- const int maxX = jmax (dx, sx) + w;
- const int maxY = jmax (dy, sy) + h;
-
- const BitmapData destData (*this, minX, minY, maxX - minX, maxY - minY, BitmapData::readWrite);
-
- uint8* dst = destData.getPixelPointer (dx - minX, dy - minY);
- const uint8* src = destData.getPixelPointer (sx - minX, sy - minY);
-
- const size_t lineSize = (size_t) (destData.pixelStride * 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;
- }
- }
- }
- }
|