| @@ -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); | |||
| @@ -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); | |||
| @@ -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 <class Pixel> | |||
| 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 <class Pixel> | |||
| 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 <class Pixel> | |||
| 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 | |||
| @@ -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 <GIFLoader> loader (new GIFLoader (in)); | |||
| return loader->image; | |||
| #endif | |||
| #endif | |||
| } | |||
| bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/) | |||
| @@ -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<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: | |||
| 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<uint8> 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<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::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<ImageType> type (image->createType()); | |||
| Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB)); | |||
| { | |||
| Graphics g (newImage); | |||
| g.drawImageAt (Image (this), 0, 0); | |||
| 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::SharedImage> image; | |||
| const ReferenceCountedObjectPtr<ImagePixelData> image; | |||
| 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 | |||
| @@ -144,7 +179,7 @@ Image Image::getClippedImage (const Rectangle<int>& 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 <ReferenceCountedObjectPtr<SharedImage>&&> (other.image)) | |||
| : image (static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (other.image)) | |||
| { | |||
| } | |||
| Image& Image::operator= (Image&& other) noexcept | |||
| { | |||
| image = static_cast <ReferenceCountedObjectPtr<SharedImage>&&> (other.image); | |||
| image = static_cast <ReferenceCountedObjectPtr<ImagePixelData>&&> (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<int> Image::getBounds() const noexcept { return image == nullptr ? Rectangle<int>() : Rectangle<int> (image->width, image->height); } | |||
| Image::PixelFormat Image::getFormat() const noexcept { return image == nullptr ? UnknownFormat : image->pixelFormat; } | |||
| bool Image::isARGB() const noexcept { return getFormat() == ARGB; } | |||
| bool Image::isRGB() const noexcept { return getFormat() == RGB; } | |||
| bool Image::isSingleChannel() const noexcept { return getFormat() == SingleChannel; } | |||
| bool Image::hasAlphaChannel() const noexcept { return getFormat() != RGB; } | |||
| LowLevelGraphicsContext* Image::createLowLevelContext() const | |||
| { | |||
| return image == nullptr ? nullptr : image->createLowLevelContext(); | |||
| @@ -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<ImageType> 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<ImageType> 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<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())) | |||
| { | |||
| @@ -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 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 | |||
| @@ -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<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. */ | |||
| 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<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: | |||
| //============================================================================== | |||
| 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 | |||
| //============================================================================== | |||
| 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 <AndroidImage*> (sourceImage.getSharedImage()); | |||
| AndroidImage* androidImage = dynamic_cast <AndroidImage*> (sourceImage.getPixelData()); | |||
| if (androidImage != 0) | |||
| { | |||
| @@ -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 <const CoreGraphicsImage*> (juceImage.getSharedImage()); | |||
| const CoreGraphicsImage* nativeImage = dynamic_cast <const CoreGraphicsImage*> (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<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. | |||
| 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 <CoreGraphicsImage*> (image.getSharedImage()); | |||
| CoreGraphicsImage* const cgi = dynamic_cast <CoreGraphicsImage*> (image.getPixelData()); | |||
| jassert (cgi != nullptr); | |||
| return cgi != nullptr ? cgi->context : 0; | |||
| } | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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<int>& r = *i.getRectangle(); | |||
| static_cast<XBitmapImage*> (image.getSharedImage()) | |||
| static_cast<XBitmapImage*> (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); | |||
| } | |||
| //============================================================================== | |||
| @@ -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]]; | |||
| @@ -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 <WindowsBitmapImage*> (offscreenImage.getSharedImage()) | |||
| static_cast <WindowsBitmapImage*> (offscreenImage.getPixelData()) | |||
| ->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()); | |||
| 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;) | |||
| { | |||
| @@ -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<int>&) | |||
| { | |||
| } | |||
| bool OpenGLHelpers::isContextActive() | |||
| { | |||
| return false; | |||
| @@ -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<int> | |||
| 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); | |||
| @@ -985,11 +985,8 @@ public: | |||
| { | |||
| 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->cloneClipIfMultiplyReferenced(); | |||
| } | |||
| @@ -703,11 +703,11 @@ OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) | |||
| : width (image.getWidth()), | |||
| 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 | |||
| { | |||
| @@ -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<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 | |||
| { | |||
| Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} | |||
| @@ -145,24 +163,35 @@ namespace OpenGLImageHelpers | |||
| HeapBlock<PixelARGB> 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<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 | |||
| @@ -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__ | |||