diff --git a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h index 527c60d2ef..bb0154e654 100644 --- a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h +++ b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h @@ -244,7 +244,7 @@ private: if (w != h || (w != 16 && w != 32 && w != 48 && w != 64)) { const int newSize = w >= 128 ? 128 : (w >= 64 ? 64 : (w >= 32 ? 32 : 16)); - Image newIm (Image::ARGB, newSize, newSize, true, Image::SoftwareImage); + Image newIm (Image::ARGB, newSize, newSize, true, SoftwareImageType()); Graphics g (newIm); g.drawImageWithin (image, 0, 0, newSize, newSize, RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); diff --git a/extras/Introjucer/Source/Project Saving/jucer_ProjectExporter.cpp b/extras/Introjucer/Source/Project Saving/jucer_ProjectExporter.cpp index bd6d5d12bb..65d31ff5b8 100644 --- a/extras/Introjucer/Source/Project Saving/jucer_ProjectExporter.cpp +++ b/extras/Introjucer/Source/Project Saving/jucer_ProjectExporter.cpp @@ -274,7 +274,7 @@ Image ProjectExporter::getBestIconForSize (int size, bool returnNullIfNothingBig if (returnNullIfNothingBigEnough && im.getWidth() < size && im.getHeight() < size) return Image::null; - Image newIm (Image::ARGB, size, size, true, Image::SoftwareImage); + Image newIm (Image::ARGB, size, size, true, SoftwareImageType()); Graphics g (newIm); g.drawImageWithin (im, 0, 0, size, size, RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); diff --git a/modules/juce_graphics/colour/juce_PixelFormats.h b/modules/juce_graphics/colour/juce_PixelFormats.h index 2326789a48..80c9a7d019 100644 --- a/modules/juce_graphics/colour/juce_PixelFormats.h +++ b/modules/juce_graphics/colour/juce_PixelFormats.h @@ -89,7 +89,8 @@ public: This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ - forcedinline void blend (const PixelARGB& src) noexcept + template + forcedinline void blend (const Pixel& src) noexcept { uint32 sargb = src.getARGB(); const uint32 alpha = 0x100 - (sargb >> 24); @@ -100,14 +101,6 @@ public: argb = sargb; } - /** Blends another pixel onto this one. - - This takes into account the opacity of the pixel being overlaid, and blends - it accordingly. - */ - forcedinline void blend (const PixelAlpha& src) noexcept; - - /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -183,7 +176,7 @@ public: forcedinline void multiplyAlpha (const float multiplier) noexcept { - multiplyAlpha ((int) (multiplier * 256.0f)); + multiplyAlpha ((int) (multiplier * 255.0f)); } /** Sets the pixel's colour from individual components. */ @@ -257,21 +250,21 @@ public: //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ - #if JUCE_BIG_ENDIAN + #if JUCE_BIG_ENDIAN enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; - #else + #else enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; - #endif + #endif private: //============================================================================== struct Components { - #if JUCE_BIG_ENDIAN + #if JUCE_BIG_ENDIAN uint8 a : 8, r : 8, g : 8, b : 8; - #else + #else uint8 b, g, r, a; - #endif + #endif } PACKED; union @@ -328,7 +321,8 @@ public: This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ - forcedinline void blend (const PixelARGB& src) noexcept + template + forcedinline void blend (const Pixel& src) noexcept { uint32 sargb = src.getARGB(); const uint32 alpha = 0x100 - (sargb >> 24); @@ -346,8 +340,6 @@ public: set (src); } - forcedinline void blend (const PixelAlpha& src) noexcept; - /** Blends another pixel onto this one, applying an extra multiplier to its opacity. The opacity of the pixel being overlaid is scaled by the extraAlpha factor before @@ -408,6 +400,9 @@ public: /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int) noexcept {} + /** Multiplies the colour's alpha value with another one. */ + forcedinline void multiplyAlpha (float) noexcept {} + /** Sets the pixel's colour from individual components. */ void setARGB (const uint8, const uint8 r_, const uint8 g_, const uint8 b_) noexcept { @@ -429,19 +424,19 @@ public: //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ - #if JUCE_MAC + #if JUCE_MAC enum { indexR = 0, indexG = 1, indexB = 2 }; - #else + #else enum { indexR = 2, indexG = 1, indexB = 0 }; - #endif + #endif private: //============================================================================== -#if JUCE_MAC + #if JUCE_MAC uint8 r, g, b; -#else + #else uint8 b, g, r; -#endif + #endif } #ifndef DOXYGEN @@ -520,7 +515,7 @@ public: template forcedinline void tween (const Pixel& src, const uint32 amount) noexcept { - a += ((src,getAlpha() - a) * amount) >> 8; + a += ((src.getAlpha() - a) * amount) >> 8; } /** Copies another pixel colour over this one. @@ -558,18 +553,12 @@ public: } /** Premultiplies the pixel's RGB values by its alpha. */ - forcedinline void premultiply() noexcept - { - } + forcedinline void premultiply() noexcept {} /** Unpremultiplies the pixel's RGB values. */ - forcedinline void unpremultiply() noexcept - { - } + forcedinline void unpremultiply() noexcept {} - forcedinline void desaturate() noexcept - { - } + forcedinline void desaturate() noexcept {} //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ @@ -584,26 +573,8 @@ private: #endif ; -forcedinline void PixelRGB::blend (const PixelAlpha& src) noexcept -{ - blend (PixelARGB (src.getARGB())); -} - - -forcedinline void PixelARGB::blend (const PixelAlpha& src) noexcept -{ - uint32 sargb = src.getARGB(); - const uint32 alpha = 0x100 - (sargb >> 24); - - sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); - sargb += 0xff00ff00 & (getAG() * alpha); - - argb = sargb; -} - - #if JUCE_MSVC - #pragma pack (pop) + #pragma pack (pop) #endif #undef PACKED diff --git a/modules/juce_graphics/image_formats/juce_GIFLoader.cpp b/modules/juce_graphics/image_formats/juce_GIFLoader.cpp index 64906d843b..50cbfc84a4 100644 --- a/modules/juce_graphics/image_formats/juce_GIFLoader.cpp +++ b/modules/juce_graphics/image_formats/juce_GIFLoader.cpp @@ -223,14 +223,13 @@ private: { fresh = false; - do + for (;;) { - firstcode = oldcode - = getCode (codeSize, false); - } - while (firstcode == clearCode); + firstcode = oldcode = getCode (codeSize, false); - return firstcode; + if (firstcode != clearCode) + return firstcode; + } } if (sp > stack) @@ -256,8 +255,8 @@ private: return -2; uint8 buf [260]; - int n; + while ((n = readDataBlock (buf)) > 0) {} @@ -290,8 +289,7 @@ private: table[1][code] = firstcode; ++maxCode; - if ((maxCode >= maxCodeSize) - && (maxCodeSize < maxGifCode)) + if (maxCode >= maxCodeSize && maxCodeSize < maxGifCode) { maxCodeSize <<= 1; ++codeSize; @@ -345,7 +343,6 @@ private: } currentBit += codeSize_; - return result; } @@ -389,9 +386,7 @@ private: while (ypos >= destData.height) { - ++pass; - - switch (pass) + switch (++pass) { case 1: ypos = 4; yStep = 8; break; case 2: ypos = 2; yStep = 4; break; @@ -436,12 +431,12 @@ bool GIFImageFormat::canUnderstand (InputStream& in) Image GIFImageFormat::decodeImage (InputStream& in) { - #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER + #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER return juce_loadWithCoreImage (in); - #else + #else const ScopedPointer loader (new GIFLoader (in)); return loader->image; - #endif + #endif } bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index e873713093..c4d6b800be 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -26,32 +26,58 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -Image::SharedImage::SharedImage (const PixelFormat format_, const int width_, const int height_) - : format (format_), width (width_), height (height_) +ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, const int h) + : pixelFormat (format), width (w), height (h) { - jassert (format_ == RGB || format_ == ARGB || format_ == SingleChannel); - jassert (width > 0 && height > 0); // It's illegal to create a zero-sized image! + jassert (format == Image::RGB || format == Image::ARGB || format == Image::SingleChannel); + jassert (w > 0 && h > 0); // It's illegal to create a zero-sized image! } -Image::SharedImage::~SharedImage() +ImagePixelData::~ImagePixelData() { } //============================================================================== -class SoftwareSharedImage : public Image::SharedImage +ImageType::ImageType() {} +ImageType::~ImageType() {} + +Image ImageType::convert (const Image& source) const +{ + if (source.isNull() || getTypeID() == (ScopedPointer (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), dest.lineStride); + + return newImage; +} + +//============================================================================== +NativeImageType::NativeImageType() {} +NativeImageType::~NativeImageType() {} + +int NativeImageType::getTypeID() const +{ + return 1; +} + +//============================================================================== +class SoftwarePixelData : public ImagePixelData { public: - SoftwareSharedImage (const Image::PixelFormat format_, const int width_, const int height_, const bool clearImage) - : Image::SharedImage (format_, width_, height_), + 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, width_) + 3) & ~3) + lineStride ((pixelStride * jmax (1, w) + 3) & ~3) { - imageData.allocate ((size_t) (lineStride * jmax (1, height_)), clearImage); - } - - Image::ImageType getType() const - { - return Image::SoftwareImage; + imageData.allocate ((size_t) (lineStride * jmax (1, h)), clearImage); } LowLevelGraphicsContext* createLowLevelContext() @@ -59,48 +85,53 @@ public: return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) { bitmap.data = imageData + x * pixelStride + y * lineStride; - bitmap.pixelFormat = format; + bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; } - Image::SharedImage* clone() + ImagePixelData* clone() { - SoftwareSharedImage* s = new SoftwareSharedImage (format, width, height, false); + SoftwarePixelData* s = new SoftwarePixelData (pixelFormat, width, height, false); memcpy (s->imageData, imageData, (size_t) (lineStride * height)); return s; } + ImageType* createType() const { return new SoftwareImageType(); } + private: HeapBlock imageData; const int pixelStride, lineStride; - JUCE_LEAK_DETECTOR (SoftwareSharedImage); + JUCE_LEAK_DETECTOR (SoftwarePixelData); }; -Image::SharedImage* Image::SharedImage::createSoftwareImage (Image::PixelFormat format, int width, int height, bool clearImage) +SoftwareImageType::SoftwareImageType() {} +SoftwareImageType::~SoftwareImageType() {} + +ImagePixelData* SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { - return new SoftwareSharedImage (format, width, height, clearImage); + return new SoftwarePixelData (format, width, height, clearImage); +} + +int SoftwareImageType::getTypeID() const +{ + return 2; } //============================================================================== -class SubsectionSharedImage : public Image::SharedImage +class SubsectionPixelData : public ImagePixelData { public: - SubsectionSharedImage (Image::SharedImage* const image_, const Rectangle& area_) - : Image::SharedImage (image_->getPixelFormat(), area_.getWidth(), area_.getHeight()), + SubsectionPixelData (ImagePixelData* const image_, const Rectangle& area_) + : ImagePixelData (image_->pixelFormat, area_.getWidth(), area_.getHeight()), image (image_), area (area_) { } - Image::ImageType getType() const - { - return Image::SoftwareImage; - } - LowLevelGraphicsContext* createLowLevelContext() { LowLevelGraphicsContext* g = image->createLowLevelContext(); @@ -114,25 +145,29 @@ public: image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); } - Image::SharedImage* clone() + ImagePixelData* clone() { - Image newImage (format, area.getWidth(), area.getHeight(), - format != Image::RGB, image->getType()); + jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting) + const ScopedPointer type (image->createType()); + + Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB)); { Graphics g (newImage); - g.drawImageAt (Image (this), 0, 0); + g.drawImageAt (Image (this), -area.getX(), -area.getY()); } - newImage.getSharedImage()->incReferenceCount(); - return newImage.getSharedImage(); + newImage.getPixelData()->incReferenceCount(); + return newImage.getPixelData(); } + ImageType* createType() const { return image->createType(); } + private: - const ReferenceCountedObjectPtr image; + const ReferenceCountedObjectPtr image; const Rectangle area; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionSharedImage); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData); }; Image Image::getClippedImage (const Rectangle& area) const @@ -144,7 +179,7 @@ Image Image::getClippedImage (const Rectangle& area) const if (validArea.isEmpty()) return Image::null; - return Image (new SubsectionSharedImage (image, validArea)); + return Image (new SubsectionPixelData (image, validArea)); } @@ -153,21 +188,23 @@ Image::Image() { } -Image::Image (SharedImage* const instance) +Image::Image (ImagePixelData* const instance) : image (instance) { } -Image::Image (const PixelFormat format, - const int width, const int height, - const bool clearImage, const ImageType type) - : image (type == Image::NativeImage ? SharedImage::createNativeImage (format, width, height, clearImage) - : new SoftwareSharedImage (format, width, height, clearImage)) +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) - : image (other.image) + : image (other.image) { } @@ -179,13 +216,13 @@ Image& Image::operator= (const Image& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Image::Image (Image&& other) noexcept - : image (static_cast &&> (other.image)) + : image (static_cast &&> (other.image)) { } Image& Image::operator= (Image&& other) noexcept { - image = static_cast &&> (other.image); + image = static_cast &&> (other.image); return *this; } #endif @@ -196,6 +233,16 @@ Image::~Image() const Image Image::null; +int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getReferenceCount(); } +int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; } +int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; } +Rectangle Image::getBounds() const noexcept { return image == nullptr ? Rectangle() : Rectangle (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(); @@ -212,7 +259,8 @@ Image Image::rescaled (const int newWidth, const int newHeight, const Graphics:: if (image == nullptr || (image->width == newWidth && image->height == newHeight)) return *this; - Image newImage (image->format, newWidth, newHeight, hasAlphaChannel(), image->getType()); + const ScopedPointer type (image->createType()); + Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel())); Graphics g (newImage); g.setImageResamplingQuality (quality); @@ -223,11 +271,13 @@ Image Image::rescaled (const int newWidth, const int newHeight, const Graphics:: Image Image::convertedToFormat (PixelFormat newFormat) const { - if (image == nullptr || newFormat == image->format) + if (image == nullptr || newFormat == image->pixelFormat) return *this; const int w = image->width, h = image->height; - Image newImage (newFormat, w, h, false, image->getType()); + + const ScopedPointer type (image->createType()); + Image newImage (type->create (image->pixelFormat, w, h, false)); if (newFormat == SingleChannel) { @@ -250,7 +300,7 @@ Image Image::convertedToFormat (PixelFormat newFormat) const } } } - else if (image->format == SingleChannel && newFormat == Image::ARGB) + 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); @@ -347,9 +397,9 @@ void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& switch (pixelFormat) { - case Image::ARGB: ((PixelARGB*) pixel)->set (col); break; - case Image::RGB: ((PixelRGB*) pixel)->set (col); break; - case Image::SingleChannel: *pixel = col.getAlpha(); break; + 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; } } @@ -357,51 +407,13 @@ void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& //============================================================================== void Image::clear (const Rectangle& area, const Colour& colourToClearTo) { - const Rectangle clipped (area.getIntersection (getBounds())); - - if (! clipped.isEmpty()) - { - const PixelARGB col (colourToClearTo.getPixelARGB()); - - const BitmapData destData (*this, clipped.getX(), clipped.getY(), clipped.getWidth(), clipped.getHeight(), BitmapData::writeOnly); - uint8* dest = destData.data; - int dh = clipped.getHeight(); - - while (--dh >= 0) - { - uint8* line = dest; - dest += destData.lineStride; - - if (isARGB()) - { - for (int x = clipped.getWidth(); --x >= 0;) - { - ((PixelARGB*) line)->set (col); - line += destData.pixelStride; - } - } - else if (isRGB()) - { - for (int x = clipped.getWidth(); --x >= 0;) - { - ((PixelRGB*) line)->set (col); - line += destData.pixelStride; - } - } - else - { - for (int x = clipped.getWidth(); --x >= 0;) - { - *line = col.getAlpha(); - line += destData.pixelStride; - } - } - } - } + const ScopedPointer g (image->createLowLevelContext()); + g->setFill (colourToClearTo); + g->fillRect (area, true); } //============================================================================== -const Colour Image::getPixelAt (const int x, const int y) const +Colour Image::getPixelAt (const int x, const int y) const { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { @@ -435,77 +447,75 @@ void Image::multiplyAlphaAt (const int x, const int y, const float multiplier) } } -void Image::multiplyAllAlphas (const float amountToMultiplyBy) +template +struct PixelIterator { - if (hasAlphaChannel()) + template + static void iterate (const Image::BitmapData& data, const PixelOperation& pixelOp) { - const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); - - if (isARGB()) + for (int y = 0; y < data.height; ++y) { - for (int y = 0; y < destData.height; ++y) - { - uint8* p = destData.getLinePointer (y); + uint8* p = data.getLinePointer (y); - for (int x = 0; x < destData.width; ++x) - { - ((PixelARGB*) p)->multiplyAlpha (amountToMultiplyBy); - p += destData.pixelStride; - } - } - } - else - { - for (int y = 0; y < destData.height; ++y) + for (int x = 0; x < data.width; ++x) { - uint8* p = destData.getLinePointer (y); - - for (int x = 0; x < destData.width; ++x) - { - *p = (uint8) (*p * amountToMultiplyBy); - p += destData.pixelStride; - } + pixelOp (*(PixelType*) p); + p += data.pixelStride; } } } - else +}; + +template +static void performPixelOp (const Image::BitmapData& data, const PixelOperation& pixelOp) +{ + switch (data.pixelFormat) { - jassertfalse; // can't do this without an alpha-channel! + case Image::ARGB: PixelIterator ::iterate (data, pixelOp); break; + case Image::RGB: PixelIterator ::iterate (data, pixelOp); break; + case Image::SingleChannel: PixelIterator::iterate (data, pixelOp); break; + default: jassertfalse; break; } } -void Image::desaturate() +struct AlphaMultiplyOp { - if (isARGB() || isRGB()) + AlphaMultiplyOp (float alpha_) noexcept : alpha (alpha_) {} + + const float alpha; + + template + void operator() (PixelType& pixel) const { - const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); + pixel.multiplyAlpha (alpha); + } - if (isARGB()) - { - for (int y = 0; y < destData.height; ++y) - { - uint8* p = destData.getLinePointer (y); + JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp); +}; - for (int x = 0; x < destData.width; ++x) - { - ((PixelARGB*) p)->desaturate(); - p += destData.pixelStride; - } - } - } - else - { - for (int y = 0; y < destData.height; ++y) - { - uint8* p = destData.getLinePointer (y); +void Image::multiplyAllAlphas (const float amountToMultiplyBy) +{ + jassert (hasAlphaChannel()); - for (int x = 0; x < destData.width; ++x) - { - ((PixelRGB*) p)->desaturate(); - p += destData.pixelStride; - } - } - } + const BitmapData destData (*this, 0, 0, getWidth(), getHeight(), BitmapData::readWrite); + performPixelOp (destData, AlphaMultiplyOp (amountToMultiplyBy)); +} + +struct DesaturateOp +{ + template + 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()); } } @@ -594,7 +604,7 @@ void Image::moveImageSection (int dx, int dy, const int minX = jmin (dx, sx); const int minY = jmin (dy, sy); - w = jmin (w, getWidth() - jmax (sx, dx)); + w = jmin (w, getWidth() - jmax (sx, dx)); h = jmin (h, getHeight() - jmax (sy, dy)); if (w > 0 && h > 0) @@ -629,5 +639,4 @@ void Image::moveImageSection (int dx, int dy, } } - END_JUCE_NAMESPACE diff --git a/modules/juce_graphics/images/juce_Image.h b/modules/juce_graphics/images/juce_Image.h index fae09943c1..ca97f8c95e 100644 --- a/modules/juce_graphics/images/juce_Image.h +++ b/modules/juce_graphics/images/juce_Image.h @@ -29,6 +29,9 @@ #include "../colour/juce_Colour.h" #include "../contexts/juce_GraphicsContext.h" +class ImageType; +class ImagePixelData; + //============================================================================== /** @@ -66,20 +69,15 @@ public: SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ }; - /** - */ - enum ImageType - { - SoftwareImage = 0, - NativeImage - }; - //============================================================================== /** Creates a null image. */ Image(); /** 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 number of colour channels in the image @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) @@ -88,14 +86,23 @@ public: @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 whether you want a purely - memory-based image, or one that may be managed by the OS if possible. */ - Image (PixelFormat format, - int imageWidth, - int imageHeight, - bool clearImage, - ImageType type = NativeImage); + Image (PixelFormat format, int imageWidth, int imageHeight, bool clearImage); + + /** Creates an image with a specified size and format. + + @param format the number of colour channels in the image + @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. @@ -150,30 +157,30 @@ public: //============================================================================== /** Returns the image's width (in pixels). */ - int getWidth() const noexcept { return image == nullptr ? 0 : image->width; } + int getWidth() const noexcept; /** Returns the image's height (in pixels). */ - int getHeight() const noexcept { return image == nullptr ? 0 : image->height; } + int getHeight() const noexcept; /** Returns a rectangle with the same size as this image. The rectangle's origin is always (0, 0). */ - Rectangle getBounds() const noexcept { return image == nullptr ? Rectangle() : Rectangle (image->width, image->height); } + Rectangle getBounds() const noexcept; /** Returns the image's pixel format. */ - PixelFormat getFormat() const noexcept { return image == nullptr ? UnknownFormat : image->format; } + PixelFormat getFormat() const noexcept; /** True if the image's format is ARGB. */ - bool isARGB() const noexcept { return getFormat() == ARGB; } + bool isARGB() const noexcept; /** True if the image's format is RGB. */ - bool isRGB() const noexcept { return getFormat() == RGB; } + bool isRGB() const noexcept; /** True if the image's format is a single-channel alpha map. */ - bool isSingleChannel() const noexcept { return getFormat() == SingleChannel; } + bool isSingleChannel() const noexcept; /** True if the image contains an alpha-channel. */ - bool hasAlphaChannel() const noexcept { return getFormat() != RGB; } + bool hasAlphaChannel() const noexcept; //============================================================================== /** Clears a section of the image with a given colour. @@ -236,7 +243,7 @@ public: @see setPixelAt, Image::BitmapData::getPixelColour */ - const Colour getPixelAt (int x, int y) const; + Colour getPixelAt (int x, int y) const; /** Sets the colour of one of the image's pixels. @@ -387,54 +394,117 @@ public: @see duplicateIfShared */ - int getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getReferenceCount(); } + int getReferenceCount() const noexcept; //============================================================================== - /** This is a base class for task-specific types of image. + /** @internal */ + ImagePixelData* getPixelData() const noexcept { return image; } - Don't use this class directly! It's used internally by the Image class. - */ - class SharedImage : public ReferenceCountedObject - { - public: - SharedImage (PixelFormat format, int width, int height); - ~SharedImage(); + /** @internal */ + explicit Image (ImagePixelData*); - virtual LowLevelGraphicsContext* createLowLevelContext() = 0; - virtual SharedImage* clone() = 0; - virtual ImageType getType() const = 0; - virtual void initialiseBitmapData (BitmapData& bitmapData, int x, int y, BitmapData::ReadWriteMode mode) = 0; +private: + //============================================================================== + ReferenceCountedObjectPtr image; - static SharedImage* createNativeImage (PixelFormat format, int width, int height, bool clearImage); - static SharedImage* createSoftwareImage (PixelFormat format, int width, int height, bool clearImage); + JUCE_LEAK_DETECTOR (Image); +}; - PixelFormat getPixelFormat() const noexcept { return format; } - int getWidth() const noexcept { return width; } - int getHeight() const noexcept { return height; } - protected: - friend class Image; - friend class BitmapData; - const PixelFormat format; - const int width, height; - NamedValueSet userData; +//============================================================================== +/** + This is a base class for holding image data in implementation-specific ways. - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedImage); - }; + 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. - /** @internal */ - SharedImage* getSharedImage() const noexcept { return image; } - /** @internal */ - explicit Image (SharedImage* instance); + ImagePixelData objects are created indirectly, by subclasses of ImageType. + @see Image, ImageType +*/ +class JUCE_API ImagePixelData : public ReferenceCountedObject +{ +public: + ImagePixelData (Image::PixelFormat, int width, int height); + ~ImagePixelData(); + + /** Creates a context that will draw into this image. */ + virtual LowLevelGraphicsContext* createLowLevelContext() = 0; + /** Creates a copy of this image. */ + virtual ImagePixelData* clone() = 0; + /** Creates an instance of the type of this image. */ + virtual ImageType* createType() const = 0; + /** Initialises a BitmapData object. */ + virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0; + + /** 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; private: - //============================================================================== - friend class SharedImage; - friend class BitmapData; + 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 +*/ +class JUCE_API ImageType +{ +public: + ImageType(); + virtual ~ImageType(); - ReferenceCountedObjectPtr image; + /** Creates a new image of this type, and the specified parameters. */ + virtual ImagePixelData* create (Image::PixelFormat format, int width, int height, bool shouldClearImage) const = 0; - JUCE_LEAK_DETECTOR (Image); + /** 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 +*/ +class JUCE_API SoftwareImageType : public ImageType +{ +public: + SoftwareImageType(); + ~SoftwareImageType(); + + ImagePixelData* create (Image::PixelFormat, int width, int height, bool clearImage) const; + int getTypeID() const; +}; + +//============================================================================== +/** + An image storage type which holds the pixels using whatever is the default storage + format on the current platform. + @see ImageType, SoftwareImageType +*/ +class JUCE_API NativeImageType : public ImageType +{ +public: + NativeImageType(); + ~NativeImageType(); + + ImagePixelData* create (Image::PixelFormat, int width, int height, bool clearImage) const; + int getTypeID() const; }; diff --git a/modules/juce_graphics/native/juce_android_GraphicsContext.cpp b/modules/juce_graphics/native/juce_android_GraphicsContext.cpp index f96f168c6c..f70cc233b0 100644 --- a/modules/juce_graphics/native/juce_android_GraphicsContext.cpp +++ b/modules/juce_graphics/native/juce_android_GraphicsContext.cpp @@ -158,18 +158,17 @@ DECLARE_JNI_CLASS (RadialGradientClass, "android/graphics/RadialGradient"); #undef JNI_CLASS_MEMBERS //============================================================================== -class AndroidImage : public Image::SharedImage +class AndroidImage : public ImagePixelData { public: - //============================================================================== AndroidImage (const int width_, const int height_, const bool clearImage) - : Image::SharedImage (Image::ARGB, width_, height_), + : ImagePixelData (Image::ARGB, width_, height_), bitmap (createBitmap (width_, height_, false)) { } AndroidImage (const int width_, const int height_, const GlobalRef& bitmap_) - : Image::SharedImage (Image::ARGB, width_, height_), + : ImagePixelData (Image::ARGB, width_, height_), bitmap (bitmap_) { } @@ -180,7 +179,6 @@ public: bitmap.callVoidMethod (BitmapClass.recycle); } - Image::ImageType getType() const { return Image::NativeImage; } LowLevelGraphicsContext* createLowLevelContext(); void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode) @@ -191,7 +189,7 @@ public: bm.dataReleaser = new CopyHandler (*this, bm, x, y, mode); } - SharedImage* clone() + ImagePixelData* clone() { JNIEnv* env = getEnv(); jobject mode = env->GetStaticObjectField (BitmapConfig, BitmapConfig.ARGB_8888); @@ -201,6 +199,8 @@ public: return new AndroidImage (width, height, newCopy); } + ImageType* createType() const { return new NativeImageType(); } + static jobject createBitmap (int width, int height, bool asSingleChannel) { JNIEnv* env = getEnv(); @@ -282,14 +282,14 @@ private: }; #endif -Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage) +ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { #if USE_ANDROID_CANVAS - if (format != Image::SingleChannel) + if (pixelFormat != Image::SingleChannel) return new AndroidImage (width, height, clearImage); #endif - return createSoftwareImage (format, width, height, clearImage); + return SoftwareImageType().create (format, width, height, clearImage); } #if USE_ANDROID_CANVAS @@ -452,7 +452,7 @@ public: void drawImage (const Image& sourceImage, const AffineTransform& transform) { - AndroidImage* androidImage = dynamic_cast (sourceImage.getSharedImage()); + AndroidImage* androidImage = dynamic_cast (sourceImage.getPixelData()); if (androidImage != 0) { diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index e6a7ecea5e..49e43c33b2 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -27,14 +27,13 @@ #include "juce_mac_CoreGraphicsContext.h" //============================================================================== -class CoreGraphicsImage : public Image::SharedImage +class CoreGraphicsImage : public ImagePixelData { public: - //============================================================================== - CoreGraphicsImage (const Image::PixelFormat format_, const int width_, const int height_, const bool clearImage) - : Image::SharedImage (format_, width_, height_) + CoreGraphicsImage (const Image::PixelFormat format, const int width_, const int height_, const bool clearImage) + : ImagePixelData (format, width_, height_) { - pixelStride = format_ == Image::RGB ? 3 : ((format_ == Image::ARGB) ? 4 : 1); + pixelStride = format == Image::RGB ? 3 : ((format == Image::ARGB) ? 4 : 1); lineStride = (pixelStride * jmax (1, width) + 3) & ~3; imageData.allocate (lineStride * jmax (1, height), clearImage); @@ -43,7 +42,7 @@ public: : CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate (imageData, width, height, 8, lineStride, - colourSpace, getCGImageFlags (format_)); + colourSpace, getCGImageFlags (format)); CGColorSpaceRelease (colourSpace); } @@ -53,29 +52,30 @@ public: CGContextRelease (context); } - Image::ImageType getType() const { return Image::NativeImage; } LowLevelGraphicsContext* createLowLevelContext(); - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) { bitmap.data = imageData + x * pixelStride + y * lineStride; - bitmap.pixelFormat = format; + bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; } - SharedImage* clone() + ImagePixelData* clone() { - CoreGraphicsImage* im = new CoreGraphicsImage (format, width, height, false); + CoreGraphicsImage* im = new CoreGraphicsImage (pixelFormat, width, height, false); memcpy (im->imageData, imageData, lineStride * height); return im; } + ImageType* createType() const { return new NativeImageType(); } + //============================================================================== static CGImageRef createImage (const Image& juceImage, const bool forAlpha, CGColorSpaceRef colourSpace, const bool mustOutliveSource) { - const CoreGraphicsImage* nativeImage = dynamic_cast (juceImage.getSharedImage()); + const CoreGraphicsImage* nativeImage = dynamic_cast (juceImage.getPixelData()); if (nativeImage != nullptr && (juceImage.getFormat() == Image::SingleChannel || ! forAlpha)) { @@ -125,9 +125,9 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsImage); }; -Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage) +ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { - return new CoreGraphicsImage (format == RGB ? ARGB : format, width, height, clearImage); + return new CoreGraphicsImage (format == Image::RGB ? Image::ARGB : format, width, height, clearImage); } //============================================================================== @@ -818,11 +818,12 @@ Image juce_loadWithCoreImage (InputStream& input) && alphaInfo != kCGImageAlphaNoneSkipLast && alphaInfo != kCGImageAlphaNoneSkipFirst); - Image image (Image::ARGB, // (CoreImage doesn't work with 24-bit images) - (int) CGImageGetWidth (loadedImage), (int) CGImageGetHeight (loadedImage), - hasAlphaChan, Image::NativeImage); + Image image (NativeImageType().create (Image::ARGB, // (CoreImage doesn't work with 24-bit images) + (int) CGImageGetWidth (loadedImage), + (int) CGImageGetHeight (loadedImage), + hasAlphaChan)); - CoreGraphicsImage* const cgImage = dynamic_cast (image.getSharedImage()); + CoreGraphicsImage* const cgImage = dynamic_cast (image.getPixelData()); jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used. CGContextDrawImage (cgImage->context, CGRectMake (0, 0, image.getWidth(), image.getHeight()), loadedImage); @@ -863,7 +864,7 @@ CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, const bool forA CGContextRef juce_getImageContext (const Image& image) { - CoreGraphicsImage* const cgi = dynamic_cast (image.getSharedImage()); + CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData()); jassert (cgi != nullptr); return cgi != nullptr ? cgi->context : 0; } diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index f12728cbdf..70b86630f2 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -1789,7 +1789,7 @@ void Component::paintComponent (Graphics& g) if (bufferedImage.isNull()) { bufferedImage = Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); + getWidth(), getHeight(), ! flags.opaqueFlag); Graphics imG (bufferedImage); paint (imG); @@ -1893,7 +1893,7 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) if (effect != nullptr) { Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); + getWidth(), getHeight(), ! flags.opaqueFlag); { Graphics g2 (effectImage); paintComponentAndChildren (g2); diff --git a/modules/juce_gui_basics/misc/juce_DropShadower.cpp b/modules/juce_gui_basics/misc/juce_DropShadower.cpp index 072c829c35..6cc54897e5 100644 --- a/modules/juce_gui_basics/misc/juce_DropShadower.cpp +++ b/modules/juce_gui_basics/misc/juce_DropShadower.cpp @@ -196,7 +196,7 @@ void DropShadower::updateShadows() if (bigIm.isNull()) { - bigIm = Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true, Image::NativeImage); + bigIm = Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true); Graphics bigG (bigIm); bigG.setColour (Colours::black.withAlpha (alpha)); @@ -280,7 +280,7 @@ void DropShadower::updateShadows() void DropShadower::setShadowImage (const Image& src, const int num, const int w, const int h, const int sx, const int sy) { - shadowImageSections[num] = Image (Image::ARGB, w, h, true, Image::NativeImage); + shadowImageSections[num] = Image (Image::ARGB, w, h, true); Graphics g (shadowImageSections[num]); g.drawImage (src, 0, 0, w, h, sx, sy, w, h); diff --git a/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/modules/juce_gui_basics/native/juce_android_Windowing.cpp index 648e2fc85b..d3488aea4e 100644 --- a/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -498,11 +498,11 @@ private: bool usingAndroidGraphics, fullScreen; int sizeAllocated; - class PreallocatedImage : public Image::SharedImage + class PreallocatedImage : public ImagePixelData { public: PreallocatedImage (const int width_, const int height_, jint* data_, bool hasAlpha_) - : Image::SharedImage (Image::ARGB, width_, height_), data (data_), hasAlpha (hasAlpha_) + : ImagePixelData (Image::ARGB, width_, height_), data (data_), hasAlpha (hasAlpha_) { if (hasAlpha_) zeromem (data_, width * height * sizeof (jint)); @@ -522,7 +522,7 @@ private: } } - Image::ImageType getType() const { return Image::SoftwareImage; } + ImageType* createType() const { return new SoftwareImageType(); } LowLevelGraphicsContext* createLowLevelContext() { return new LowLevelGraphicsSoftwareRenderer (Image (this)); } void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode) @@ -533,7 +533,7 @@ private: bm.data = (uint8*) (data + x + y * width); } - SharedImage* clone() + ImagePixelData* clone() { PreallocatedImage* s = new PreallocatedImage (width, height, 0, hasAlpha); s->allocatedData.malloc (sizeof (jint) * width * height); diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index afd4485c66..acc682c695 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -436,18 +436,18 @@ namespace Visuals } //============================================================================== -class XBitmapImage : public Image::SharedImage +class XBitmapImage : public ImagePixelData { public: - XBitmapImage (const Image::PixelFormat format_, const int w, const int h, + XBitmapImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage, const int imageDepth_, Visual* visual) - : Image::SharedImage (format_, w, h), + : ImagePixelData (format, w, h), imageDepth (imageDepth_), gc (None) { - jassert (format_ == Image::RGB || format_ == Image::ARGB); + jassert (format == Image::RGB || format == Image::ARGB); - pixelStride = (format_ == Image::RGB) ? 3 : 4; + pixelStride = (format == Image::RGB) ? 3 : 4; lineStride = ((w * pixelStride + 3) & ~3); ScopedXLock xlock; @@ -499,7 +499,7 @@ public: if (! usingXShm) #endif { - imageDataAllocated.allocate (lineStride * h, format_ == Image::ARGB && clearImage); + imageDataAllocated.allocate (lineStride * h, format == Image::ARGB && clearImage); imageData = imageDataAllocated; xImage = (XImage*) ::calloc (1, sizeof (XImage)); @@ -567,27 +567,27 @@ public: } } - Image::ImageType getType() const { return Image::NativeImage; } - LowLevelGraphicsContext* createLowLevelContext() { return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) { bitmap.data = imageData + x * pixelStride + y * lineStride; - bitmap.pixelFormat = format; + bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; } - SharedImage* clone() + ImagePixelData* clone() { jassertfalse; return nullptr; } + ImageType* createType() const { return new NativeImageType(); } + void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy) { ScopedXLock xlock; @@ -1774,7 +1774,7 @@ private: #endif const Rectangle& r = *i.getRectangle(); - static_cast (image.getSharedImage()) + static_cast (image.getPixelData()) ->blitToWindow (peer->windowH, r.getX(), r.getY(), r.getWidth(), r.getHeight(), r.getX() - totalArea.getX(), r.getY() - totalArea.getY()); @@ -3008,9 +3008,9 @@ Image juce_createIconForFile (const File& file) return Image::null; } -Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage) +ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { - return createSoftwareImage (format, width, height, clearImage); + return SoftwareImageType().create (format, width, height, clearImage); } //============================================================================== diff --git a/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/modules/juce_gui_basics/native/juce_mac_Windowing.mm index bb0e5f12fd..1f8d93cd5d 100644 --- a/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -328,7 +328,7 @@ Image juce_createIconForFile (const File& file) NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())]; - Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true, Image::NativeImage); + Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true); [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (result) flipped: false]]; diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 77f06dd7e4..89ff279e82 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -194,16 +194,16 @@ const int KeyPress::rewindKey = 0x30003; //============================================================================== -class WindowsBitmapImage : public Image::SharedImage +class WindowsBitmapImage : public ImagePixelData { public: - WindowsBitmapImage (const Image::PixelFormat format_, + WindowsBitmapImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage) - : Image::SharedImage (format_, w, h) + : ImagePixelData (format, w, h) { - jassert (format_ == Image::RGB || format_ == Image::ARGB); + jassert (format == Image::RGB || format == Image::ARGB); - pixelStride = (format_ == Image::RGB) ? 3 : 4; + pixelStride = (format == Image::RGB) ? 3 : 4; lineStride = -((w * pixelStride + 3) & ~3); zerostruct (bitmapInfo); @@ -214,7 +214,7 @@ public: bitmapInfo.bV4CSType = 1; bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8); - if (format_ == Image::ARGB) + if (format == Image::ARGB) { bitmapInfo.bV4AlphaMask = 0xff000000; bitmapInfo.bV4RedMask = 0xff0000; @@ -238,7 +238,7 @@ public: previousBitmap = SelectObject (hdc, hBitmap); - if (format_ == Image::ARGB && clearImage) + if (format == Image::ARGB && clearImage) zeromem (bitmapData, (size_t) std::abs (h * lineStride)); imageData = bitmapData - (lineStride * (h - 1)); @@ -251,24 +251,24 @@ public: DeleteObject (hBitmap); } - Image::ImageType getType() const { return Image::NativeImage; } + ImageType* createType() const { return new NativeImageType(); } LowLevelGraphicsContext* createLowLevelContext() { return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) { bitmap.data = imageData + x * pixelStride + y * lineStride; - bitmap.pixelFormat = format; + bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; } - Image::SharedImage* clone() + ImagePixelData* clone() { - WindowsBitmapImage* im = new WindowsBitmapImage (format, width, height, false); + WindowsBitmapImage* im = new WindowsBitmapImage (pixelFormat, width, height, false); for (int i = 0; i < height; ++i) memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride); @@ -1367,7 +1367,7 @@ private: } if (! dontRepaint) - static_cast (offscreenImage.getSharedImage()) + static_cast (offscreenImage.getPixelData()) ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha); } @@ -2851,9 +2851,9 @@ void Desktop::setMousePosition (const Point& newPosition) } //============================================================================== -Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage) +ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { - return createSoftwareImage (format, width, height, clearImage); + return SoftwareImageType().create (format, width, height, clearImage); } //============================================================================== diff --git a/modules/juce_gui_basics/widgets/juce_ListBox.cpp b/modules/juce_gui_basics/widgets/juce_ListBox.cpp index 4d421fb262..393853308b 100644 --- a/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -882,7 +882,7 @@ Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) imageArea = imageArea.getIntersection (getLocalBounds()); imageX = imageArea.getX(); imageY = imageArea.getY(); - Image snapshot (Image::ARGB, imageArea.getWidth(), imageArea.getHeight(), true, Image::NativeImage); + Image snapshot (Image::ARGB, imageArea.getWidth(), imageArea.getHeight(), true); for (i = getNumRowsOnScreen() + 2; --i >= 0;) { diff --git a/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp b/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp index 1312fe88b4..990292cad1 100644 --- a/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp +++ b/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp @@ -39,6 +39,10 @@ void OpenGLComponent::internalRepaint (int x, int y, int w, int h) Component::internalRepaint (x, y, w, h); } +void OpenGLComponent::updateEmbeddedPosition (const Rectangle&) +{ +} + bool OpenGLHelpers::isContextActive() { return false; diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 779881f069..d89d2ef880 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -350,8 +350,8 @@ void OpenGLFrameBuffer::setCurrentFrameBufferTarget (GLuint frameBufferID) GLuint OpenGLFrameBuffer::getCurrentFrameBufferTarget() { - GLint fb; - glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fb); + GLint fb; + glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fb); return (GLuint) fb; } @@ -398,26 +398,22 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle glDisable (GL_DEPTH_TEST); glDisable (GL_BLEND); + OpenGLTexture tex; + tex.load (data, area.getWidth(), area.getHeight()); + #if JUCE_OPENGL_ES { - glEnable (GL_TEXTURE_2D); - glColor4f (1.0f, 1.0f, 1.0f, 1.0f); - - OpenGLTexture tex; - tex.load (data, area.getWidth(), area.getHeight()); tex.bind(); - const GLint cropRect[4] = { 0, 0, area.getWidth(), area.getHeight() }; + const GLint cropRect[4] = { 0, tex.getHeight() - area.getHeight(), area.getWidth(), area.getHeight() }; glTexParameteriv (GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect); - + glEnable (GL_TEXTURE_2D); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight()); glBindTexture (GL_TEXTURE_2D, 0); } #else { - OpenGLTexture tex; - tex.load (data, area.getWidth(), area.getHeight()); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 0e36e5b685..51a29d8226 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -985,11 +985,8 @@ public: { const Rectangle& clipBounds = clip->getClipBounds(); - OpenGLFrameBufferImage* fbi = new OpenGLFrameBufferImage (clipBounds.getWidth(), clipBounds.getHeight()); - fbi->frameBuffer.clear (Colours::transparentBlack); - - s->transparencyLayer = Image (fbi); - s->target = OpenGLTarget (fbi->frameBuffer, clipBounds.getPosition()); + s->transparencyLayer = Image (OpenGLImageType().create (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true)); + s->target = OpenGLTarget (*OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); s->transparencyLayerAlpha = opacity; s->cloneClipIfMultiplyReferenced(); } diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index 6e6757960b..ad96e484b2 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -703,11 +703,11 @@ OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) : width (image.getWidth()), height (image.getHeight()) { - OpenGLFrameBufferImage* glImage = dynamic_cast (image.getSharedImage()); + OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image); - if (glImage != nullptr) + if (fb != nullptr) { - textureID = glImage->frameBuffer.getTextureID(); + textureID = fb->getTextureID(); } else { diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp index e90ac5e6c5..b49e18a4d2 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp @@ -25,44 +25,62 @@ BEGIN_JUCE_NAMESPACE -OpenGLFrameBufferImage::OpenGLFrameBufferImage (int width, int height) - : Image::SharedImage (Image::ARGB, width, height), - pixelStride (4), - lineStride (width * pixelStride) + +//============================================================================== +class OpenGLFrameBufferImage : public ImagePixelData { - frameBuffer.initialise (width, height); - frameBuffer.clear (Colours::transparentBlack); -} +public: + OpenGLFrameBufferImage (int width, int height) + : ImagePixelData (Image::ARGB, width, height), + pixelStride (4), + lineStride (width * pixelStride) + { + frameBuffer.initialise (width, height); + frameBuffer.clear (Colours::transparentBlack); + } -OpenGLFrameBufferImage::~OpenGLFrameBufferImage() {} + LowLevelGraphicsContext* createLowLevelContext() + { + return new OpenGLRenderer (frameBuffer); + } -LowLevelGraphicsContext* OpenGLFrameBufferImage::createLowLevelContext() -{ - return new OpenGLRenderer (frameBuffer); -} + ImageType* createType() const { return new OpenGLImageType(); } -Image::SharedImage* OpenGLFrameBufferImage::clone() -{ - OpenGLFrameBufferImage* im = new OpenGLFrameBufferImage (getWidth(), getHeight()); - im->incReferenceCount(); + ImagePixelData* clone() + { + OpenGLFrameBufferImage* im = new OpenGLFrameBufferImage (width, height); + im->incReferenceCount(); + { + Image newImage (im); + Graphics g (newImage); + g.drawImageAt (Image (this), 0, 0, false); + } + + im->resetReferenceCount(); + return im; + } + + void initialiseBitmapData (Image::BitmapData& bitmapData, int x, int y, Image::BitmapData::ReadWriteMode mode) { - Image newImage (im); - Graphics g (newImage); - g.drawImageAt (Image (this), 0, 0, false); + bitmapData.pixelFormat = pixelFormat; + bitmapData.lineStride = lineStride; + bitmapData.pixelStride = pixelStride; + + switch (mode) + { + case Image::BitmapData::writeOnly: DataReleaser::initialise (frameBuffer, bitmapData, x, y); break; + case Image::BitmapData::readOnly: DataReleaser ::initialise (frameBuffer, bitmapData, x, y); break; + case Image::BitmapData::readWrite: DataReleaser::initialise (frameBuffer, bitmapData, x, y); break; + default: jassertfalse; break; + } } - im->resetReferenceCount(); - return im; -} + OpenGLFrameBuffer frameBuffer; -Image::ImageType OpenGLFrameBufferImage::getType() const -{ - return Image::NativeImage; -} +private: + int pixelStride, lineStride; -namespace OpenGLImageHelpers -{ struct Dummy { Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} @@ -145,24 +163,35 @@ namespace OpenGLImageHelpers HeapBlock data; WriterType writer; }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLFrameBufferImage); +}; + + +//============================================================================== +OpenGLImageType::OpenGLImageType() {} +OpenGLImageType::~OpenGLImageType() {} + +int OpenGLImageType::getTypeID() const +{ + return 3; } -void OpenGLFrameBufferImage::initialiseBitmapData (Image::BitmapData& bitmapData, int x, int y, - Image::BitmapData::ReadWriteMode mode) +ImagePixelData* OpenGLImageType::create (Image::PixelFormat, int width, int height, bool shouldClearImage) const { - using namespace OpenGLImageHelpers; + OpenGLFrameBufferImage* im = new OpenGLFrameBufferImage (width, height); - bitmapData.pixelFormat = format; - bitmapData.lineStride = lineStride; - bitmapData.pixelStride = pixelStride; + if (shouldClearImage) + im->frameBuffer.clear (Colours::transparentBlack); - switch (mode) - { - case Image::BitmapData::writeOnly: DataReleaser::initialise (frameBuffer, bitmapData, x, y); break; - case Image::BitmapData::readOnly: DataReleaser ::initialise (frameBuffer, bitmapData, x, y); break; - case Image::BitmapData::readWrite: DataReleaser::initialise (frameBuffer, bitmapData, x, y); break; - default: jassertfalse; break; - } + return im; +} + +OpenGLFrameBuffer* OpenGLImageType::getFrameBufferFrom (const Image& image) +{ + OpenGLFrameBufferImage* const glImage = dynamic_cast (image.getPixelData()); + + return glImage != nullptr ? &(glImage->frameBuffer) : nullptr; } END_JUCE_NAMESPACE diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.h b/modules/juce_opengl/opengl/juce_OpenGLImage.h index 0796d2e523..5832fcd74a 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.h +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.h @@ -29,42 +29,25 @@ //============================================================================== /** - A type of Image::SharedImage that stores its image data in an OpenGL + A type of ImagePixelData that stores its image data in an OpenGL framebuffer, allowing a JUCE Image object to wrap a framebuffer. By creating an Image from an instance of an OpenGLFrameBufferImage, you can then use a Graphics object to draw into the framebuffer using normal JUCE 2D operations. - @see Image, Image::SharedImage, OpenGLFrameBuffer + @see Image, ImageType, ImagePixelData, OpenGLFrameBuffer */ -class JUCE_API OpenGLFrameBufferImage : public Image::SharedImage +class JUCE_API OpenGLImageType : public ImageType { public: - OpenGLFrameBufferImage (int width, int height); + OpenGLImageType(); + ~OpenGLImageType(); - /** Destructor. */ - ~OpenGLFrameBufferImage(); + ImagePixelData* create (Image::PixelFormat, int width, int height, bool shouldClearImage) const; + int getTypeID() const; - /** The underlying framebuffer. - Although this is exposed to allow access to use it as a texture, etc, be - careful not to change its size while the image is using it. - */ - OpenGLFrameBuffer frameBuffer; - - /** @internal */ - LowLevelGraphicsContext* createLowLevelContext(); - /** @internal */ - SharedImage* clone(); - /** @internal */ - Image::ImageType getType() const; - /** @internal */ - void initialiseBitmapData (Image::BitmapData&, int, int, Image::BitmapData::ReadWriteMode); - -private: - int pixelStride, lineStride; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLFrameBufferImage); + static OpenGLFrameBuffer* getFrameBufferFrom (const Image&); }; #endif // __JUCE_OPENGLIMAGE_JUCEHEADER__