| @@ -244,7 +244,7 @@ private: | |||||
| if (w != h || (w != 16 && w != 32 && w != 48 && w != 64)) | if (w != h || (w != 16 && w != 32 && w != 48 && w != 64)) | ||||
| { | { | ||||
| const int newSize = w >= 128 ? 128 : (w >= 64 ? 64 : (w >= 32 ? 32 : 16)); | 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); | Graphics g (newIm); | ||||
| g.drawImageWithin (image, 0, 0, newSize, newSize, | g.drawImageWithin (image, 0, 0, newSize, newSize, | ||||
| RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); | RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); | ||||
| @@ -274,7 +274,7 @@ Image ProjectExporter::getBestIconForSize (int size, bool returnNullIfNothingBig | |||||
| if (returnNullIfNothingBigEnough && im.getWidth() < size && im.getHeight() < size) | if (returnNullIfNothingBigEnough && im.getWidth() < size && im.getHeight() < size) | ||||
| return Image::null; | return Image::null; | ||||
| Image newIm (Image::ARGB, size, size, true, Image::SoftwareImage); | |||||
| Image newIm (Image::ARGB, size, size, true, SoftwareImageType()); | |||||
| Graphics g (newIm); | Graphics g (newIm); | ||||
| g.drawImageWithin (im, 0, 0, size, size, | g.drawImageWithin (im, 0, 0, size, size, | ||||
| RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); | RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); | ||||
| @@ -89,7 +89,8 @@ public: | |||||
| This takes into account the opacity of the pixel being overlaid, and blends | This takes into account the opacity of the pixel being overlaid, and blends | ||||
| it accordingly. | it accordingly. | ||||
| */ | */ | ||||
| forcedinline void blend (const PixelARGB& src) noexcept | |||||
| template <class Pixel> | |||||
| forcedinline void blend (const Pixel& src) noexcept | |||||
| { | { | ||||
| uint32 sargb = src.getARGB(); | uint32 sargb = src.getARGB(); | ||||
| const uint32 alpha = 0x100 - (sargb >> 24); | const uint32 alpha = 0x100 - (sargb >> 24); | ||||
| @@ -100,14 +101,6 @@ public: | |||||
| argb = sargb; | 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. | /** Blends another pixel onto this one. | ||||
| This takes into account the opacity of the pixel being overlaid, and blends | 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 | 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. */ | /** 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. */ | /** 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 }; | enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; | ||||
| #else | |||||
| #else | |||||
| enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; | enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; | ||||
| #endif | |||||
| #endif | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| struct Components | struct Components | ||||
| { | { | ||||
| #if JUCE_BIG_ENDIAN | |||||
| #if JUCE_BIG_ENDIAN | |||||
| uint8 a : 8, r : 8, g : 8, b : 8; | uint8 a : 8, r : 8, g : 8, b : 8; | ||||
| #else | |||||
| #else | |||||
| uint8 b, g, r, a; | uint8 b, g, r, a; | ||||
| #endif | |||||
| #endif | |||||
| } PACKED; | } PACKED; | ||||
| union | union | ||||
| @@ -328,7 +321,8 @@ public: | |||||
| This takes into account the opacity of the pixel being overlaid, and blends | This takes into account the opacity of the pixel being overlaid, and blends | ||||
| it accordingly. | it accordingly. | ||||
| */ | */ | ||||
| forcedinline void blend (const PixelARGB& src) noexcept | |||||
| template <class Pixel> | |||||
| forcedinline void blend (const Pixel& src) noexcept | |||||
| { | { | ||||
| uint32 sargb = src.getARGB(); | uint32 sargb = src.getARGB(); | ||||
| const uint32 alpha = 0x100 - (sargb >> 24); | const uint32 alpha = 0x100 - (sargb >> 24); | ||||
| @@ -346,8 +340,6 @@ public: | |||||
| set (src); | set (src); | ||||
| } | } | ||||
| forcedinline void blend (const PixelAlpha& src) noexcept; | |||||
| /** Blends another pixel onto this one, applying an extra multiplier to its opacity. | /** 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 | 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. */ | /** Multiplies the colour's alpha value with another one. */ | ||||
| forcedinline void multiplyAlpha (int) noexcept {} | 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. */ | /** Sets the pixel's colour from individual components. */ | ||||
| void setARGB (const uint8, const uint8 r_, const uint8 g_, const uint8 b_) noexcept | 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. */ | /** 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 }; | enum { indexR = 0, indexG = 1, indexB = 2 }; | ||||
| #else | |||||
| #else | |||||
| enum { indexR = 2, indexG = 1, indexB = 0 }; | enum { indexR = 2, indexG = 1, indexB = 0 }; | ||||
| #endif | |||||
| #endif | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_MAC | |||||
| #if JUCE_MAC | |||||
| uint8 r, g, b; | uint8 r, g, b; | ||||
| #else | |||||
| #else | |||||
| uint8 b, g, r; | uint8 b, g, r; | ||||
| #endif | |||||
| #endif | |||||
| } | } | ||||
| #ifndef DOXYGEN | #ifndef DOXYGEN | ||||
| @@ -520,7 +515,7 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void tween (const Pixel& src, const uint32 amount) noexcept | 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. | /** Copies another pixel colour over this one. | ||||
| @@ -558,18 +553,12 @@ public: | |||||
| } | } | ||||
| /** Premultiplies the pixel's RGB values by its alpha. */ | /** Premultiplies the pixel's RGB values by its alpha. */ | ||||
| forcedinline void premultiply() noexcept | |||||
| { | |||||
| } | |||||
| forcedinline void premultiply() noexcept {} | |||||
| /** Unpremultiplies the pixel's RGB values. */ | /** 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. */ | /** The indexes of the different components in the byte layout of this type of colour. */ | ||||
| @@ -584,26 +573,8 @@ private: | |||||
| #endif | #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 | #if JUCE_MSVC | ||||
| #pragma pack (pop) | |||||
| #pragma pack (pop) | |||||
| #endif | #endif | ||||
| #undef PACKED | #undef PACKED | ||||
| @@ -223,14 +223,13 @@ private: | |||||
| { | { | ||||
| fresh = false; | 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) | if (sp > stack) | ||||
| @@ -256,8 +255,8 @@ private: | |||||
| return -2; | return -2; | ||||
| uint8 buf [260]; | uint8 buf [260]; | ||||
| int n; | int n; | ||||
| while ((n = readDataBlock (buf)) > 0) | while ((n = readDataBlock (buf)) > 0) | ||||
| {} | {} | ||||
| @@ -290,8 +289,7 @@ private: | |||||
| table[1][code] = firstcode; | table[1][code] = firstcode; | ||||
| ++maxCode; | ++maxCode; | ||||
| if ((maxCode >= maxCodeSize) | |||||
| && (maxCodeSize < maxGifCode)) | |||||
| if (maxCode >= maxCodeSize && maxCodeSize < maxGifCode) | |||||
| { | { | ||||
| maxCodeSize <<= 1; | maxCodeSize <<= 1; | ||||
| ++codeSize; | ++codeSize; | ||||
| @@ -345,7 +343,6 @@ private: | |||||
| } | } | ||||
| currentBit += codeSize_; | currentBit += codeSize_; | ||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -389,9 +386,7 @@ private: | |||||
| while (ypos >= destData.height) | while (ypos >= destData.height) | ||||
| { | { | ||||
| ++pass; | |||||
| switch (pass) | |||||
| switch (++pass) | |||||
| { | { | ||||
| case 1: ypos = 4; yStep = 8; break; | case 1: ypos = 4; yStep = 8; break; | ||||
| case 2: ypos = 2; yStep = 4; break; | case 2: ypos = 2; yStep = 4; break; | ||||
| @@ -436,12 +431,12 @@ bool GIFImageFormat::canUnderstand (InputStream& in) | |||||
| Image GIFImageFormat::decodeImage (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); | return juce_loadWithCoreImage (in); | ||||
| #else | |||||
| #else | |||||
| const ScopedPointer <GIFLoader> loader (new GIFLoader (in)); | const ScopedPointer <GIFLoader> loader (new GIFLoader (in)); | ||||
| return loader->image; | return loader->image; | ||||
| #endif | |||||
| #endif | |||||
| } | } | ||||
| bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) | bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) | ||||
| @@ -26,32 +26,58 @@ | |||||
| BEGIN_JUCE_NAMESPACE | 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<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), dest.lineStride); | |||||
| return newImage; | |||||
| } | |||||
| //============================================================================== | |||||
| NativeImageType::NativeImageType() {} | |||||
| NativeImageType::~NativeImageType() {} | |||||
| int NativeImageType::getTypeID() const | |||||
| { | |||||
| return 1; | |||||
| } | |||||
| //============================================================================== | |||||
| class SoftwarePixelData : public ImagePixelData | |||||
| { | { | ||||
| public: | 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)), | 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() | LowLevelGraphicsContext* createLowLevelContext() | ||||
| @@ -59,48 +85,53 @@ public: | |||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | 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.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = format; | |||||
| bitmap.pixelFormat = pixelFormat; | |||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | 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)); | memcpy (s->imageData, imageData, (size_t) (lineStride * height)); | ||||
| return s; | return s; | ||||
| } | } | ||||
| ImageType* createType() const { return new SoftwareImageType(); } | |||||
| private: | private: | ||||
| HeapBlock<uint8> imageData; | HeapBlock<uint8> imageData; | ||||
| const int pixelStride, lineStride; | 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: | public: | ||||
| SubsectionSharedImage (Image::SharedImage* const image_, const Rectangle<int>& area_) | |||||
| : Image::SharedImage (image_->getPixelFormat(), area_.getWidth(), area_.getHeight()), | |||||
| SubsectionPixelData (ImagePixelData* const image_, const Rectangle<int>& area_) | |||||
| : ImagePixelData (image_->pixelFormat, area_.getWidth(), area_.getHeight()), | |||||
| image (image_), area (area_) | image (image_), area (area_) | ||||
| { | { | ||||
| } | } | ||||
| Image::ImageType getType() const | |||||
| { | |||||
| return Image::SoftwareImage; | |||||
| } | |||||
| LowLevelGraphicsContext* createLowLevelContext() | LowLevelGraphicsContext* createLowLevelContext() | ||||
| { | { | ||||
| LowLevelGraphicsContext* g = image->createLowLevelContext(); | LowLevelGraphicsContext* g = image->createLowLevelContext(); | ||||
| @@ -114,25 +145,29 @@ public: | |||||
| image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); | 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<ImageType> type (image->createType()); | |||||
| Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB)); | |||||
| { | { | ||||
| Graphics g (newImage); | 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: | private: | ||||
| const ReferenceCountedObjectPtr<Image::SharedImage> image; | |||||
| const ReferenceCountedObjectPtr<ImagePixelData> image; | |||||
| const Rectangle<int> area; | const Rectangle<int> area; | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionSharedImage); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData); | |||||
| }; | }; | ||||
| Image Image::getClippedImage (const Rectangle<int>& area) const | Image Image::getClippedImage (const Rectangle<int>& area) const | ||||
| @@ -144,7 +179,7 @@ Image Image::getClippedImage (const Rectangle<int>& area) const | |||||
| if (validArea.isEmpty()) | if (validArea.isEmpty()) | ||||
| return Image::null; | 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 (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::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 | #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | ||||
| Image::Image (Image&& other) noexcept | Image::Image (Image&& other) noexcept | ||||
| : image (static_cast <ReferenceCountedObjectPtr<SharedImage>&&> (other.image)) | |||||
| : image (static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (other.image)) | |||||
| { | { | ||||
| } | } | ||||
| Image& Image::operator= (Image&& other) noexcept | Image& Image::operator= (Image&& other) noexcept | ||||
| { | { | ||||
| image = static_cast <ReferenceCountedObjectPtr<SharedImage>&&> (other.image); | |||||
| image = static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (other.image); | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -196,6 +233,16 @@ Image::~Image() | |||||
| const Image Image::null; | 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<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 | LowLevelGraphicsContext* Image::createLowLevelContext() const | ||||
| { | { | ||||
| return image == nullptr ? nullptr : image->createLowLevelContext(); | 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)) | if (image == nullptr || (image->width == newWidth && image->height == newHeight)) | ||||
| return *this; | return *this; | ||||
| Image newImage (image->format, newWidth, newHeight, hasAlphaChannel(), image->getType()); | |||||
| const ScopedPointer<ImageType> type (image->createType()); | |||||
| Image newImage (type->create (image->pixelFormat, newWidth, newHeight, hasAlphaChannel())); | |||||
| Graphics g (newImage); | Graphics g (newImage); | ||||
| g.setImageResamplingQuality (quality); | g.setImageResamplingQuality (quality); | ||||
| @@ -223,11 +271,13 @@ Image Image::rescaled (const int newWidth, const int newHeight, const Graphics:: | |||||
| Image Image::convertedToFormat (PixelFormat newFormat) const | Image Image::convertedToFormat (PixelFormat newFormat) const | ||||
| { | { | ||||
| if (image == nullptr || newFormat == image->format) | |||||
| if (image == nullptr || newFormat == image->pixelFormat) | |||||
| return *this; | return *this; | ||||
| const int w = image->width, h = image->height; | const int w = image->width, h = image->height; | ||||
| Image newImage (newFormat, w, h, false, image->getType()); | |||||
| const ScopedPointer<ImageType> type (image->createType()); | |||||
| Image newImage (type->create (image->pixelFormat, w, h, false)); | |||||
| if (newFormat == SingleChannel) | 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 destData (newImage, 0, 0, w, h, BitmapData::writeOnly); | ||||
| const BitmapData srcData (*this, 0, 0, w, h); | 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) | 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; | default: jassertfalse; break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -357,51 +407,13 @@ void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& | |||||
| //============================================================================== | //============================================================================== | ||||
| void Image::clear (const Rectangle<int>& area, const Colour& colourToClearTo) | void Image::clear (const Rectangle<int>& area, const Colour& colourToClearTo) | ||||
| { | { | ||||
| const Rectangle<int> 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<LowLevelGraphicsContext> 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())) | 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 <class PixelType> | |||||
| struct PixelIterator | |||||
| { | { | ||||
| if (hasAlphaChannel()) | |||||
| template <class PixelOperation> | |||||
| 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 <class PixelOperation> | |||||
| 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<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; | |||||
| } | } | ||||
| } | } | ||||
| void Image::desaturate() | |||||
| struct AlphaMultiplyOp | |||||
| { | { | ||||
| if (isARGB() || isRGB()) | |||||
| AlphaMultiplyOp (float alpha_) noexcept : alpha (alpha_) {} | |||||
| const float alpha; | |||||
| template <class PixelType> | |||||
| 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 <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()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -594,7 +604,7 @@ void Image::moveImageSection (int dx, int dy, | |||||
| const int minX = jmin (dx, sx); | const int minX = jmin (dx, sx); | ||||
| const int minY = jmin (dy, sy); | 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)); | h = jmin (h, getHeight() - jmax (sy, dy)); | ||||
| if (w > 0 && h > 0) | if (w > 0 && h > 0) | ||||
| @@ -629,5 +639,4 @@ void Image::moveImageSection (int dx, int dy, | |||||
| } | } | ||||
| } | } | ||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -29,6 +29,9 @@ | |||||
| #include "../colour/juce_Colour.h" | #include "../colour/juce_Colour.h" | ||||
| #include "../contexts/juce_GraphicsContext.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. */ | SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ | ||||
| }; | }; | ||||
| /** | |||||
| */ | |||||
| enum ImageType | |||||
| { | |||||
| SoftwareImage = 0, | |||||
| NativeImage | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a null image. */ | /** Creates a null image. */ | ||||
| Image(); | Image(); | ||||
| /** Creates an image with a specified size and format. | /** 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 format the number of colour channels in the image | ||||
| @param imageWidth the desired width of the image, in pixels - this value must be | @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) | 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) | @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 | 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. | 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. | /** Creates a shared reference to another image. | ||||
| @@ -150,30 +157,30 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns the image's width (in pixels). */ | /** 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). */ | /** 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. | /** Returns a rectangle with the same size as this image. | ||||
| The rectangle's origin is always (0, 0). | The rectangle's origin is always (0, 0). | ||||
| */ | */ | ||||
| Rectangle<int> getBounds() const noexcept { return image == nullptr ? Rectangle<int>() : Rectangle<int> (image->width, image->height); } | |||||
| Rectangle<int> getBounds() const noexcept; | |||||
| /** Returns the image's pixel format. */ | /** 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. */ | /** 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. */ | /** 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. */ | /** 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. */ | /** 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. | /** Clears a section of the image with a given colour. | ||||
| @@ -236,7 +243,7 @@ public: | |||||
| @see setPixelAt, Image::BitmapData::getPixelColour | @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. | /** Sets the colour of one of the image's pixels. | ||||
| @@ -387,54 +394,117 @@ public: | |||||
| @see duplicateIfShared | @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<ImagePixelData> 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: | 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<SharedImage> 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; | |||||
| }; | }; | ||||
| @@ -158,18 +158,17 @@ DECLARE_JNI_CLASS (RadialGradientClass, "android/graphics/RadialGradient"); | |||||
| #undef JNI_CLASS_MEMBERS | #undef JNI_CLASS_MEMBERS | ||||
| //============================================================================== | //============================================================================== | ||||
| class AndroidImage : public Image::SharedImage | |||||
| class AndroidImage : public ImagePixelData | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | |||||
| AndroidImage (const int width_, const int height_, const bool clearImage) | 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)) | bitmap (createBitmap (width_, height_, false)) | ||||
| { | { | ||||
| } | } | ||||
| AndroidImage (const int width_, const int height_, const GlobalRef& bitmap_) | AndroidImage (const int width_, const int height_, const GlobalRef& bitmap_) | ||||
| : Image::SharedImage (Image::ARGB, width_, height_), | |||||
| : ImagePixelData (Image::ARGB, width_, height_), | |||||
| bitmap (bitmap_) | bitmap (bitmap_) | ||||
| { | { | ||||
| } | } | ||||
| @@ -180,7 +179,6 @@ public: | |||||
| bitmap.callVoidMethod (BitmapClass.recycle); | bitmap.callVoidMethod (BitmapClass.recycle); | ||||
| } | } | ||||
| Image::ImageType getType() const { return Image::NativeImage; } | |||||
| LowLevelGraphicsContext* createLowLevelContext(); | LowLevelGraphicsContext* createLowLevelContext(); | ||||
| void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode) | 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); | bm.dataReleaser = new CopyHandler (*this, bm, x, y, mode); | ||||
| } | } | ||||
| SharedImage* clone() | |||||
| ImagePixelData* clone() | |||||
| { | { | ||||
| JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
| jobject mode = env->GetStaticObjectField (BitmapConfig, BitmapConfig.ARGB_8888); | jobject mode = env->GetStaticObjectField (BitmapConfig, BitmapConfig.ARGB_8888); | ||||
| @@ -201,6 +199,8 @@ public: | |||||
| return new AndroidImage (width, height, newCopy); | return new AndroidImage (width, height, newCopy); | ||||
| } | } | ||||
| ImageType* createType() const { return new NativeImageType(); } | |||||
| static jobject createBitmap (int width, int height, bool asSingleChannel) | static jobject createBitmap (int width, int height, bool asSingleChannel) | ||||
| { | { | ||||
| JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
| @@ -282,14 +282,14 @@ private: | |||||
| }; | }; | ||||
| #endif | #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 USE_ANDROID_CANVAS | ||||
| if (format != Image::SingleChannel) | |||||
| if (pixelFormat != Image::SingleChannel) | |||||
| return new AndroidImage (width, height, clearImage); | return new AndroidImage (width, height, clearImage); | ||||
| #endif | #endif | ||||
| return createSoftwareImage (format, width, height, clearImage); | |||||
| return SoftwareImageType().create (format, width, height, clearImage); | |||||
| } | } | ||||
| #if USE_ANDROID_CANVAS | #if USE_ANDROID_CANVAS | ||||
| @@ -452,7 +452,7 @@ public: | |||||
| void drawImage (const Image& sourceImage, const AffineTransform& transform) | void drawImage (const Image& sourceImage, const AffineTransform& transform) | ||||
| { | { | ||||
| AndroidImage* androidImage = dynamic_cast <AndroidImage*> (sourceImage.getSharedImage()); | |||||
| AndroidImage* androidImage = dynamic_cast <AndroidImage*> (sourceImage.getPixelData()); | |||||
| if (androidImage != 0) | if (androidImage != 0) | ||||
| { | { | ||||
| @@ -27,14 +27,13 @@ | |||||
| #include "juce_mac_CoreGraphicsContext.h" | #include "juce_mac_CoreGraphicsContext.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| class CoreGraphicsImage : public Image::SharedImage | |||||
| class CoreGraphicsImage : public ImagePixelData | |||||
| { | { | ||||
| public: | 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; | lineStride = (pixelStride * jmax (1, width) + 3) & ~3; | ||||
| imageData.allocate (lineStride * jmax (1, height), clearImage); | imageData.allocate (lineStride * jmax (1, height), clearImage); | ||||
| @@ -43,7 +42,7 @@ public: | |||||
| : CGColorSpaceCreateDeviceRGB(); | : CGColorSpaceCreateDeviceRGB(); | ||||
| context = CGBitmapContextCreate (imageData, width, height, 8, lineStride, | context = CGBitmapContextCreate (imageData, width, height, 8, lineStride, | ||||
| colourSpace, getCGImageFlags (format_)); | |||||
| colourSpace, getCGImageFlags (format)); | |||||
| CGColorSpaceRelease (colourSpace); | CGColorSpaceRelease (colourSpace); | ||||
| } | } | ||||
| @@ -53,29 +52,30 @@ public: | |||||
| CGContextRelease (context); | CGContextRelease (context); | ||||
| } | } | ||||
| Image::ImageType getType() const { return Image::NativeImage; } | |||||
| LowLevelGraphicsContext* createLowLevelContext(); | 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.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = format; | |||||
| bitmap.pixelFormat = pixelFormat; | |||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | 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); | memcpy (im->imageData, imageData, lineStride * height); | ||||
| return im; | return im; | ||||
| } | } | ||||
| ImageType* createType() const { return new NativeImageType(); } | |||||
| //============================================================================== | //============================================================================== | ||||
| static CGImageRef createImage (const Image& juceImage, const bool forAlpha, | static CGImageRef createImage (const Image& juceImage, const bool forAlpha, | ||||
| CGColorSpaceRef colourSpace, const bool mustOutliveSource) | CGColorSpaceRef colourSpace, const bool mustOutliveSource) | ||||
| { | { | ||||
| const CoreGraphicsImage* nativeImage = dynamic_cast <const CoreGraphicsImage*> (juceImage.getSharedImage()); | |||||
| const CoreGraphicsImage* nativeImage = dynamic_cast <const CoreGraphicsImage*> (juceImage.getPixelData()); | |||||
| if (nativeImage != nullptr && (juceImage.getFormat() == Image::SingleChannel || ! forAlpha)) | if (nativeImage != nullptr && (juceImage.getFormat() == Image::SingleChannel || ! forAlpha)) | ||||
| { | { | ||||
| @@ -125,9 +125,9 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsImage); | 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 != kCGImageAlphaNoneSkipLast | ||||
| && alphaInfo != kCGImageAlphaNoneSkipFirst); | && 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<CoreGraphicsImage*> (image.getSharedImage()); | |||||
| CoreGraphicsImage* const cgImage = dynamic_cast<CoreGraphicsImage*> (image.getPixelData()); | |||||
| jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used. | 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); | 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) | CGContextRef juce_getImageContext (const Image& image) | ||||
| { | { | ||||
| CoreGraphicsImage* const cgi = dynamic_cast <CoreGraphicsImage*> (image.getSharedImage()); | |||||
| CoreGraphicsImage* const cgi = dynamic_cast <CoreGraphicsImage*> (image.getPixelData()); | |||||
| jassert (cgi != nullptr); | jassert (cgi != nullptr); | ||||
| return cgi != nullptr ? cgi->context : 0; | return cgi != nullptr ? cgi->context : 0; | ||||
| } | } | ||||
| @@ -1789,7 +1789,7 @@ void Component::paintComponent (Graphics& g) | |||||
| if (bufferedImage.isNull()) | if (bufferedImage.isNull()) | ||||
| { | { | ||||
| bufferedImage = Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, | bufferedImage = Image (flags.opaqueFlag ? Image::RGB : Image::ARGB, | ||||
| getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); | |||||
| getWidth(), getHeight(), ! flags.opaqueFlag); | |||||
| Graphics imG (bufferedImage); | Graphics imG (bufferedImage); | ||||
| paint (imG); | paint (imG); | ||||
| @@ -1893,7 +1893,7 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) | |||||
| if (effect != nullptr) | if (effect != nullptr) | ||||
| { | { | ||||
| Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, | Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, | ||||
| getWidth(), getHeight(), ! flags.opaqueFlag, Image::NativeImage); | |||||
| getWidth(), getHeight(), ! flags.opaqueFlag); | |||||
| { | { | ||||
| Graphics g2 (effectImage); | Graphics g2 (effectImage); | ||||
| paintComponentAndChildren (g2); | paintComponentAndChildren (g2); | ||||
| @@ -196,7 +196,7 @@ void DropShadower::updateShadows() | |||||
| if (bigIm.isNull()) | 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); | Graphics bigG (bigIm); | ||||
| bigG.setColour (Colours::black.withAlpha (alpha)); | 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, | void DropShadower::setShadowImage (const Image& src, const int num, const int w, const int h, | ||||
| const int sx, const int sy) | 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]); | Graphics g (shadowImageSections[num]); | ||||
| g.drawImage (src, 0, 0, w, h, sx, sy, w, h); | g.drawImage (src, 0, 0, w, h, sx, sy, w, h); | ||||
| @@ -498,11 +498,11 @@ private: | |||||
| bool usingAndroidGraphics, fullScreen; | bool usingAndroidGraphics, fullScreen; | ||||
| int sizeAllocated; | int sizeAllocated; | ||||
| class PreallocatedImage : public Image::SharedImage | |||||
| class PreallocatedImage : public ImagePixelData | |||||
| { | { | ||||
| public: | public: | ||||
| PreallocatedImage (const int width_, const int height_, jint* data_, bool hasAlpha_) | 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_) | if (hasAlpha_) | ||||
| zeromem (data_, width * height * sizeof (jint)); | 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)); } | LowLevelGraphicsContext* createLowLevelContext() { return new LowLevelGraphicsSoftwareRenderer (Image (this)); } | ||||
| void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode) | 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); | bm.data = (uint8*) (data + x + y * width); | ||||
| } | } | ||||
| SharedImage* clone() | |||||
| ImagePixelData* clone() | |||||
| { | { | ||||
| PreallocatedImage* s = new PreallocatedImage (width, height, 0, hasAlpha); | PreallocatedImage* s = new PreallocatedImage (width, height, 0, hasAlpha); | ||||
| s->allocatedData.malloc (sizeof (jint) * width * height); | s->allocatedData.malloc (sizeof (jint) * width * height); | ||||
| @@ -436,18 +436,18 @@ namespace Visuals | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class XBitmapImage : public Image::SharedImage | |||||
| class XBitmapImage : public ImagePixelData | |||||
| { | { | ||||
| public: | 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) | const bool clearImage, const int imageDepth_, Visual* visual) | ||||
| : Image::SharedImage (format_, w, h), | |||||
| : ImagePixelData (format, w, h), | |||||
| imageDepth (imageDepth_), | imageDepth (imageDepth_), | ||||
| gc (None) | 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); | lineStride = ((w * pixelStride + 3) & ~3); | ||||
| ScopedXLock xlock; | ScopedXLock xlock; | ||||
| @@ -499,7 +499,7 @@ public: | |||||
| if (! usingXShm) | if (! usingXShm) | ||||
| #endif | #endif | ||||
| { | { | ||||
| imageDataAllocated.allocate (lineStride * h, format_ == Image::ARGB && clearImage); | |||||
| imageDataAllocated.allocate (lineStride * h, format == Image::ARGB && clearImage); | |||||
| imageData = imageDataAllocated; | imageData = imageDataAllocated; | ||||
| xImage = (XImage*) ::calloc (1, sizeof (XImage)); | xImage = (XImage*) ::calloc (1, sizeof (XImage)); | ||||
| @@ -567,27 +567,27 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| Image::ImageType getType() const { return Image::NativeImage; } | |||||
| LowLevelGraphicsContext* createLowLevelContext() | LowLevelGraphicsContext* createLowLevelContext() | ||||
| { | { | ||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | 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.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = format; | |||||
| bitmap.pixelFormat = pixelFormat; | |||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| } | } | ||||
| SharedImage* clone() | |||||
| ImagePixelData* clone() | |||||
| { | { | ||||
| jassertfalse; | jassertfalse; | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| ImageType* createType() const { return new NativeImageType(); } | |||||
| void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy) | void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy) | ||||
| { | { | ||||
| ScopedXLock xlock; | ScopedXLock xlock; | ||||
| @@ -1774,7 +1774,7 @@ private: | |||||
| #endif | #endif | ||||
| const Rectangle<int>& r = *i.getRectangle(); | const Rectangle<int>& r = *i.getRectangle(); | ||||
| static_cast<XBitmapImage*> (image.getSharedImage()) | |||||
| static_cast<XBitmapImage*> (image.getPixelData()) | |||||
| ->blitToWindow (peer->windowH, | ->blitToWindow (peer->windowH, | ||||
| r.getX(), r.getY(), r.getWidth(), r.getHeight(), | r.getX(), r.getY(), r.getWidth(), r.getHeight(), | ||||
| r.getX() - totalArea.getX(), r.getY() - totalArea.getY()); | r.getX() - totalArea.getX(), r.getY() - totalArea.getY()); | ||||
| @@ -3008,9 +3008,9 @@ Image juce_createIconForFile (const File& file) | |||||
| return Image::null; | 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); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -328,7 +328,7 @@ Image juce_createIconForFile (const File& file) | |||||
| NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())]; | 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 saveGraphicsState]; | ||||
| [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (result) flipped: false]]; | [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (result) flipped: false]]; | ||||
| @@ -194,16 +194,16 @@ const int KeyPress::rewindKey = 0x30003; | |||||
| //============================================================================== | //============================================================================== | ||||
| class WindowsBitmapImage : public Image::SharedImage | |||||
| class WindowsBitmapImage : public ImagePixelData | |||||
| { | { | ||||
| public: | public: | ||||
| WindowsBitmapImage (const Image::PixelFormat format_, | |||||
| WindowsBitmapImage (const Image::PixelFormat format, | |||||
| const int w, const int h, const bool clearImage) | 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); | lineStride = -((w * pixelStride + 3) & ~3); | ||||
| zerostruct (bitmapInfo); | zerostruct (bitmapInfo); | ||||
| @@ -214,7 +214,7 @@ public: | |||||
| bitmapInfo.bV4CSType = 1; | bitmapInfo.bV4CSType = 1; | ||||
| bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8); | bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8); | ||||
| if (format_ == Image::ARGB) | |||||
| if (format == Image::ARGB) | |||||
| { | { | ||||
| bitmapInfo.bV4AlphaMask = 0xff000000; | bitmapInfo.bV4AlphaMask = 0xff000000; | ||||
| bitmapInfo.bV4RedMask = 0xff0000; | bitmapInfo.bV4RedMask = 0xff0000; | ||||
| @@ -238,7 +238,7 @@ public: | |||||
| previousBitmap = SelectObject (hdc, hBitmap); | previousBitmap = SelectObject (hdc, hBitmap); | ||||
| if (format_ == Image::ARGB && clearImage) | |||||
| if (format == Image::ARGB && clearImage) | |||||
| zeromem (bitmapData, (size_t) std::abs (h * lineStride)); | zeromem (bitmapData, (size_t) std::abs (h * lineStride)); | ||||
| imageData = bitmapData - (lineStride * (h - 1)); | imageData = bitmapData - (lineStride * (h - 1)); | ||||
| @@ -251,24 +251,24 @@ public: | |||||
| DeleteObject (hBitmap); | DeleteObject (hBitmap); | ||||
| } | } | ||||
| Image::ImageType getType() const { return Image::NativeImage; } | |||||
| ImageType* createType() const { return new NativeImageType(); } | |||||
| LowLevelGraphicsContext* createLowLevelContext() | LowLevelGraphicsContext* createLowLevelContext() | ||||
| { | { | ||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | 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.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = format; | |||||
| bitmap.pixelFormat = pixelFormat; | |||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | 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) | for (int i = 0; i < height; ++i) | ||||
| memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride); | memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride); | ||||
| @@ -1367,7 +1367,7 @@ private: | |||||
| } | } | ||||
| if (! dontRepaint) | if (! dontRepaint) | ||||
| static_cast <WindowsBitmapImage*> (offscreenImage.getSharedImage()) | |||||
| static_cast <WindowsBitmapImage*> (offscreenImage.getPixelData()) | |||||
| ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha); | ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha); | ||||
| } | } | ||||
| @@ -2851,9 +2851,9 @@ void Desktop::setMousePosition (const Point<int>& 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); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -882,7 +882,7 @@ Image ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) | |||||
| imageArea = imageArea.getIntersection (getLocalBounds()); | imageArea = imageArea.getIntersection (getLocalBounds()); | ||||
| imageX = imageArea.getX(); | imageX = imageArea.getX(); | ||||
| imageY = imageArea.getY(); | 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;) | for (i = getNumRowsOnScreen() + 2; --i >= 0;) | ||||
| { | { | ||||
| @@ -39,6 +39,10 @@ void OpenGLComponent::internalRepaint (int x, int y, int w, int h) | |||||
| Component::internalRepaint (x, y, w, h); | Component::internalRepaint (x, y, w, h); | ||||
| } | } | ||||
| void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>&) | |||||
| { | |||||
| } | |||||
| bool OpenGLHelpers::isContextActive() | bool OpenGLHelpers::isContextActive() | ||||
| { | { | ||||
| return false; | return false; | ||||
| @@ -350,8 +350,8 @@ void OpenGLFrameBuffer::setCurrentFrameBufferTarget (GLuint frameBufferID) | |||||
| GLuint OpenGLFrameBuffer::getCurrentFrameBufferTarget() | GLuint OpenGLFrameBuffer::getCurrentFrameBufferTarget() | ||||
| { | { | ||||
| GLint fb; | |||||
| glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fb); | |||||
| GLint fb; | |||||
| glGetIntegerv (GL_FRAMEBUFFER_BINDING_EXT, &fb); | |||||
| return (GLuint) fb; | return (GLuint) fb; | ||||
| } | } | ||||
| @@ -398,26 +398,22 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int> | |||||
| glDisable (GL_DEPTH_TEST); | glDisable (GL_DEPTH_TEST); | ||||
| glDisable (GL_BLEND); | glDisable (GL_BLEND); | ||||
| OpenGLTexture tex; | |||||
| tex.load (data, area.getWidth(), area.getHeight()); | |||||
| #if JUCE_OPENGL_ES | #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(); | 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); | 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()); | glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight()); | ||||
| glBindTexture (GL_TEXTURE_2D, 0); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| } | } | ||||
| #else | #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_MIN_FILTER, GL_LINEAR); | ||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_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); | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||
| @@ -985,11 +985,8 @@ public: | |||||
| { | { | ||||
| const Rectangle<int>& clipBounds = clip->getClipBounds(); | const Rectangle<int>& 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->transparencyLayerAlpha = opacity; | ||||
| s->cloneClipIfMultiplyReferenced(); | s->cloneClipIfMultiplyReferenced(); | ||||
| } | } | ||||
| @@ -703,11 +703,11 @@ OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) | |||||
| : width (image.getWidth()), | : width (image.getWidth()), | ||||
| height (image.getHeight()) | height (image.getHeight()) | ||||
| { | { | ||||
| OpenGLFrameBufferImage* glImage = dynamic_cast <OpenGLFrameBufferImage*> (image.getSharedImage()); | |||||
| OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image); | |||||
| if (glImage != nullptr) | |||||
| if (fb != nullptr) | |||||
| { | { | ||||
| textureID = glImage->frameBuffer.getTextureID(); | |||||
| textureID = fb->getTextureID(); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -25,44 +25,62 @@ | |||||
| BEGIN_JUCE_NAMESPACE | 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<Dummy, Writer>::initialise (frameBuffer, bitmapData, x, y); break; | |||||
| case Image::BitmapData::readOnly: DataReleaser<Reader, Dummy> ::initialise (frameBuffer, bitmapData, x, y); break; | |||||
| case Image::BitmapData::readWrite: DataReleaser<Reader, Writer>::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 | struct Dummy | ||||
| { | { | ||||
| Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} | Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} | ||||
| @@ -145,24 +163,35 @@ namespace OpenGLImageHelpers | |||||
| HeapBlock<PixelARGB> data; | HeapBlock<PixelARGB> data; | ||||
| WriterType writer; | 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<Dummy, Writer>::initialise (frameBuffer, bitmapData, x, y); break; | |||||
| case Image::BitmapData::readOnly: DataReleaser<Reader, Dummy> ::initialise (frameBuffer, bitmapData, x, y); break; | |||||
| case Image::BitmapData::readWrite: DataReleaser<Reader, Writer>::initialise (frameBuffer, bitmapData, x, y); break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| return im; | |||||
| } | |||||
| OpenGLFrameBuffer* OpenGLImageType::getFrameBufferFrom (const Image& image) | |||||
| { | |||||
| OpenGLFrameBufferImage* const glImage = dynamic_cast<OpenGLFrameBufferImage*> (image.getPixelData()); | |||||
| return glImage != nullptr ? &(glImage->frameBuffer) : nullptr; | |||||
| } | } | ||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -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. | framebuffer, allowing a JUCE Image object to wrap a framebuffer. | ||||
| By creating an Image from an instance of an OpenGLFrameBufferImage, | By creating an Image from an instance of an OpenGLFrameBufferImage, | ||||
| you can then use a Graphics object to draw into the framebuffer using normal | you can then use a Graphics object to draw into the framebuffer using normal | ||||
| JUCE 2D operations. | 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: | 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__ | #endif // __JUCE_OPENGLIMAGE_JUCEHEADER__ | ||||