mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
Image: Add new backup-extensions interface to support images with no main-memory backup
This commit is contained in:
parent
2fbb72d960
commit
55e5e2082c
7 changed files with 265 additions and 30 deletions
|
|
@ -49,6 +49,8 @@ void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const
|
|||
return;
|
||||
|
||||
auto blurred = srcImage.convertedToFormat (Image::SingleChannel);
|
||||
blurred.setBackupEnabled (false);
|
||||
|
||||
blurred.getPixelData()->applySingleChannelBoxBlurEffect (radius);
|
||||
|
||||
g.setColour (colour);
|
||||
|
|
@ -66,6 +68,7 @@ void DropShadow::drawForPath (Graphics& g, const Path& path) const
|
|||
if (area.getWidth() > 2 && area.getHeight() > 2)
|
||||
{
|
||||
Image pathImage { Image::SingleChannel, area.getWidth(), area.getHeight(), true };
|
||||
pathImage.setBackupEnabled (false);
|
||||
|
||||
{
|
||||
Graphics g2 (pathImage);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ void GlowEffect::setGlowProperties (float newRadius, Colour newColour, Point<int
|
|||
void GlowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha)
|
||||
{
|
||||
auto blurred = image.createCopy();
|
||||
blurred.getPixelData()->applyGaussianBlurEffect (radius * scaleFactor);
|
||||
blurred.setBackupEnabled (false);
|
||||
|
||||
if (auto ptr = blurred.getPixelData())
|
||||
ptr->applyGaussianBlurEffect (radius * scaleFactor);
|
||||
|
||||
g.setColour (colour.withMultipliedAlpha (alpha));
|
||||
g.drawImageAt (blurred, offset.x, offset.y, true);
|
||||
|
|
|
|||
|
|
@ -747,6 +747,20 @@ bool Image::BitmapData::convertFrom (const BitmapData& source)
|
|||
return BitmapDataDetail::convert (source, *this);
|
||||
}
|
||||
|
||||
bool Image::setBackupEnabled (bool enabled)
|
||||
{
|
||||
if (auto ptr = image)
|
||||
{
|
||||
if (auto* ext = ptr->getBackupExtensions())
|
||||
{
|
||||
ext->setBackupEnabled (enabled);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Image::clear (const Rectangle<int>& area, Colour colourToClearTo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -298,6 +298,15 @@ public:
|
|||
*/
|
||||
void desaturate();
|
||||
|
||||
/** This is a shorthand for dereferencing the internal ImagePixelData's BackupExtensions
|
||||
and calling setBackupEnabled() if the extensions exist.
|
||||
|
||||
@returns true if the extensions exist and the backup flag was updated, or false otherwise
|
||||
|
||||
@see ImagePixelDataBackupExtensions::setBackupEnabled()
|
||||
*/
|
||||
bool setBackupEnabled (bool);
|
||||
|
||||
//==============================================================================
|
||||
/** Retrieves a section of an image as raw pixel data, so it can be read or written to.
|
||||
|
||||
|
|
@ -446,6 +455,105 @@ private:
|
|||
JUCE_LEAK_DETECTOR (Image)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The methods on this interface allow clients of ImagePixelData to query and control
|
||||
the automatic-backup process from graphics memory to main memory, if this mechanism is
|
||||
relevant and supported.
|
||||
|
||||
Some image types (Direct2D, OpenGL) are backed by textures that live in graphics memory.
|
||||
Such textures are quick to display, but they will be lost if the graphics device goes away.
|
||||
|
||||
Normally, a backup of the texture will be kept in main memory, so that the image can still
|
||||
be used even if any graphics device goes away. While this has the benefit that programs are
|
||||
automatically resilient to graphics devices going away, it also incurs some performance
|
||||
overhead, because the texture content must be copied back to main memory after each
|
||||
modification.
|
||||
|
||||
For performance-sensitive applications it can be beneficial to disable the automatic sync
|
||||
behaviour, and to sync manually instead, which can be achieved using the methods of this type.
|
||||
|
||||
The following table shows how to interpret the results of this type's member functions.
|
||||
|
||||
needsBackup() | canBackup() | meaning
|
||||
--------------------------------------------------------------------------------------------
|
||||
true | true | the main-memory copy of the image is outdated, but there's an
|
||||
| | up-to-date copy in graphics memory
|
||||
| |
|
||||
true | false | although main memory is out-of-date, the most recent copy of
|
||||
| | the image in graphics memory has been lost
|
||||
| |
|
||||
false | true | main memory is up-to-date with graphics memory; graphics
|
||||
| | memory is still available;
|
||||
| |
|
||||
false | false | main memory has an up-to-date copy of the image, but the most
|
||||
| | recent copy of the image in graphics memory has been lost
|
||||
|
||||
@tags{Graphics}
|
||||
*/
|
||||
class ImagePixelDataBackupExtensions
|
||||
{
|
||||
public:
|
||||
/** The automatic image backup mechanism can be disabled by passing false to this function, or
|
||||
enabled by passing true.
|
||||
|
||||
If you disable automatic backup for a particular image, make sure you test that your
|
||||
software behaves as expected when graphics devices are disconnected. One easy way to test
|
||||
this on Windows is to use your program over a remote desktop session, and to end and
|
||||
re-start the session while the image is being displayed.
|
||||
|
||||
The most common scenario where this flag is useful is when drawing single-use images.
|
||||
e.g. for a drop shadow or other effect, the following series of steps might be carried
|
||||
out on each paint call:
|
||||
- Create a path that matches the shadowed component's outline
|
||||
- Draw the path into a temporary image
|
||||
- Blur the temporary image
|
||||
- Draw the temporary image into some other context
|
||||
- (destroy the temporary image)
|
||||
|
||||
In this case, where the image is created, modified, used, and destroyed in quick succession,
|
||||
there's no need to keep a resilient backup of the image around, so it's reasonable to call
|
||||
setBackupEnabled(false) after constructing the image.
|
||||
|
||||
@see isBackupEnabled(), backupNow()
|
||||
*/
|
||||
virtual void setBackupEnabled (bool) = 0;
|
||||
|
||||
/** @see setBackupEnabled(), backupNow() */
|
||||
virtual bool isBackupEnabled() const = 0;
|
||||
|
||||
/** This function will attempt to make the image resilient to graphics device disconnection by
|
||||
copying from graphics memory to main memory.
|
||||
|
||||
By default, backups happen automatically, so there's no need to call this function unless
|
||||
auto-backup has been disabled on this image.
|
||||
|
||||
Flushing may fail if the graphics device goes away before its memory can be read.
|
||||
If needsBackup() returns false, then backupNow() will always return true without doing any
|
||||
work.
|
||||
|
||||
@returns true if the main-memory copy of the image is up-to-date, or false otherwise.
|
||||
|
||||
@see setBackupEnabled(), isBackupEnabled()
|
||||
*/
|
||||
virtual bool backupNow() = 0;
|
||||
|
||||
/** Returns true if the main-memory copy of the image is out-of-date, false if it's up-to-date.
|
||||
|
||||
@see canBackup()
|
||||
*/
|
||||
virtual bool needsBackup() const = 0;
|
||||
|
||||
/** Returns if there is an up-to-date copy of this image in graphics memory, or false otherwise.
|
||||
|
||||
@see needsBackup()
|
||||
*/
|
||||
virtual bool canBackup() const = 0;
|
||||
|
||||
protected:
|
||||
// Not intended for virtual destruction
|
||||
~ImagePixelDataBackupExtensions() = default;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
|
@ -463,6 +571,8 @@ private:
|
|||
class JUCE_API ImagePixelData : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
using BackupExtensions = ImagePixelDataBackupExtensions;
|
||||
|
||||
ImagePixelData (Image::PixelFormat, int width, int height);
|
||||
~ImagePixelData() override;
|
||||
|
||||
|
|
@ -474,6 +584,13 @@ public:
|
|||
virtual Ptr clone() = 0;
|
||||
/** Creates an instance of the type of this image. */
|
||||
virtual std::unique_ptr<ImageType> createType() const = 0;
|
||||
|
||||
/** Returns a raw pointer to an instance of ImagePixelDataBackupExtensions if this ImagePixelData
|
||||
provides this extension, or nullptr otherwise.
|
||||
*/
|
||||
virtual BackupExtensions* getBackupExtensions() { return nullptr; }
|
||||
virtual const BackupExtensions* getBackupExtensions() const { return nullptr; }
|
||||
|
||||
/** Initialises a BitmapData object. */
|
||||
virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0;
|
||||
/** Returns the number of Image objects which are currently referring to the same internal
|
||||
|
|
|
|||
|
|
@ -199,9 +199,9 @@ static bool readFromDirect2DBitmap (ComSmartPtr<ID2D1DeviceContext1> context,
|
|||
target->pixelFormat,
|
||||
(int) size.width,
|
||||
(int) size.height } };
|
||||
Image::BitmapData srcBitmap { srcImage, Image::BitmapData::readOnly };
|
||||
Image::BitmapData dstBitmap { Image { target }, Image::BitmapData::writeOnly };
|
||||
BitmapDataDetail::convert (srcBitmap, dstBitmap);
|
||||
|
||||
Image::BitmapData dstData { Image { target }, Image::BitmapData::writeOnly };
|
||||
dstData.convertFrom ({ srcImage, Image::BitmapData::readOnly });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -225,9 +225,12 @@ static ImagePixelData::Ptr readFromDirect2DBitmap (ComSmartPtr<ID2D1DeviceContex
|
|||
return result;
|
||||
}
|
||||
|
||||
Direct2DPixelDataPages::Direct2DPixelDataPages (ComSmartPtr<ID2D1Bitmap1> bitmap,
|
||||
//==============================================================================
|
||||
Direct2DPixelDataPages::Direct2DPixelDataPages (ImagePixelDataBackupExtensions* parent,
|
||||
ComSmartPtr<ID2D1Bitmap1> bitmap,
|
||||
ImagePixelData::Ptr image)
|
||||
: backingData (image),
|
||||
: parentBackupExtensions (parent),
|
||||
backingData (image),
|
||||
pages { Page { bitmap, {} } },
|
||||
upToDate (true)
|
||||
{
|
||||
|
|
@ -235,10 +238,12 @@ Direct2DPixelDataPages::Direct2DPixelDataPages (ComSmartPtr<ID2D1Bitmap1> bitmap
|
|||
jassert (image->createType()->getTypeID() == SoftwareImageType{}.getTypeID());
|
||||
}
|
||||
|
||||
Direct2DPixelDataPages::Direct2DPixelDataPages (ComSmartPtr<ID2D1Device1> device,
|
||||
Direct2DPixelDataPages::Direct2DPixelDataPages (ImagePixelDataBackupExtensions* parent,
|
||||
ComSmartPtr<ID2D1Device1> device,
|
||||
ImagePixelData::Ptr image,
|
||||
State initialState)
|
||||
: backingData (image),
|
||||
: parentBackupExtensions (parent),
|
||||
backingData (image),
|
||||
pages (makePages (device, backingData, initialState == State::cleared)),
|
||||
upToDate (initialState != State::unsuitableToRead)
|
||||
{
|
||||
|
|
@ -246,11 +251,24 @@ Direct2DPixelDataPages::Direct2DPixelDataPages (ComSmartPtr<ID2D1Device1> device
|
|||
jassert (image->createType()->getTypeID() == SoftwareImageType{}.getTypeID());
|
||||
}
|
||||
|
||||
auto Direct2DPixelDataPages::getPagesWithoutSync() const -> Span<const Page>
|
||||
{
|
||||
// Accessing page data which is out-of-date!
|
||||
jassert (upToDate);
|
||||
return pages;
|
||||
}
|
||||
|
||||
auto Direct2DPixelDataPages::getPages() -> Span<const Page>
|
||||
{
|
||||
if (std::exchange (upToDate, true))
|
||||
const ScopeGuard scope { [this] { upToDate = true; } };
|
||||
|
||||
if (upToDate)
|
||||
return pages;
|
||||
|
||||
// We need to make sure that the parent image is up-to-date, otherwise we'll end up
|
||||
// fetching outdated image data.
|
||||
parentBackupExtensions->backupNow();
|
||||
|
||||
auto sourceToUse = backingData->pixelFormat == Image::RGB
|
||||
? Image { backingData }.convertedToFormat (Image::ARGB)
|
||||
: Image { backingData };
|
||||
|
|
@ -287,7 +305,7 @@ Direct2DPixelData::Direct2DPixelData (ComSmartPtr<ID2D1Device1> device,
|
|||
ComSmartPtr<ID2D1Bitmap1> page)
|
||||
: Direct2DPixelData (readFromDirect2DBitmap (Direct2DDeviceContext::create (device), page), State::drawn)
|
||||
{
|
||||
pagesForDevice.emplace (device, Direct2DPixelDataPages { page, backingData });
|
||||
pagesForDevice.emplace (device, Direct2DPixelDataPages { this, page, backingData });
|
||||
}
|
||||
|
||||
Direct2DPixelData::Direct2DPixelData (Image::PixelFormat formatToUse, int w, int h, bool clearIn)
|
||||
|
|
@ -310,6 +328,10 @@ bool Direct2DPixelData::createPersistentBackup (ComSmartPtr<ID2D1Device1> device
|
|||
return false;
|
||||
}
|
||||
|
||||
// If the backup is not outdated, then it must be up-to-date
|
||||
if (state != State::outdated)
|
||||
return true;
|
||||
|
||||
const auto iter = deviceHint != nullptr
|
||||
? pagesForDevice.find (deviceHint)
|
||||
: std::find_if (pagesForDevice.begin(),
|
||||
|
|
@ -334,13 +356,15 @@ bool Direct2DPixelData::createPersistentBackup (ComSmartPtr<ID2D1Device1> device
|
|||
return false;
|
||||
}
|
||||
|
||||
const auto result = readFromDirect2DBitmap (context, pages.getPages().front().bitmap, backingData);
|
||||
state = State::drawn;
|
||||
const auto result = readFromDirect2DBitmap (context, pages.getPagesWithoutSync().front().bitmap, backingData);
|
||||
state = result ? State::drawn : State::outdated;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Direct2DPixelData::getIteratorForDevice (ComSmartPtr<ID2D1Device1> device)
|
||||
{
|
||||
mostRecentDevice = device;
|
||||
|
||||
if (device == nullptr)
|
||||
return pagesForDevice.end();
|
||||
|
||||
|
|
@ -377,6 +401,11 @@ auto Direct2DPixelData::getIteratorForDevice (ComSmartPtr<ID2D1Device1> device)
|
|||
case State::drawing:
|
||||
jassertfalse;
|
||||
return Pages::State::unsuitableToRead;
|
||||
|
||||
// If this is hit, the pages will need to be synced through main memory before they are
|
||||
// suitable for reading.
|
||||
case State::outdated:
|
||||
return Pages::State::unsuitableToRead;
|
||||
}
|
||||
|
||||
// Unhandled switch case?
|
||||
|
|
@ -384,7 +413,7 @@ auto Direct2DPixelData::getIteratorForDevice (ComSmartPtr<ID2D1Device1> device)
|
|||
return Pages::State::unsuitableToRead;
|
||||
}();
|
||||
|
||||
const auto pair = pagesForDevice.emplace (device, Pages { device, backingData, initialState });
|
||||
const auto pair = pagesForDevice.emplace (device, Pages { this, device, backingData, initialState });
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
|
|
@ -408,7 +437,10 @@ struct Direct2DPixelData::Context : public Direct2DImageContext
|
|||
|
||||
endFrame();
|
||||
|
||||
self->createPersistentBackup (D2DUtilities::getDeviceForContext (getDeviceContext()));
|
||||
self->state = State::outdated;
|
||||
|
||||
if (self->sync)
|
||||
self->createPersistentBackup (D2DUtilities::getDeviceForContext (getDeviceContext()));
|
||||
}
|
||||
|
||||
Ptr self;
|
||||
|
|
@ -422,12 +454,18 @@ auto Direct2DPixelData::createNativeContext() -> std::unique_ptr<Context>
|
|||
|
||||
sendDataChangeMessage();
|
||||
|
||||
const auto adapter = directX->adapters.getDefaultAdapter();
|
||||
const auto device = std::invoke ([this]() -> ComSmartPtr<ID2D1Device1>
|
||||
{
|
||||
if (mostRecentDevice != nullptr)
|
||||
return mostRecentDevice;
|
||||
|
||||
if (adapter == nullptr)
|
||||
return nullptr;
|
||||
const auto adapter = directX->adapters.getDefaultAdapter();
|
||||
|
||||
const auto device = adapter->direct2DDevice;
|
||||
if (adapter == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return adapter->direct2DDevice;
|
||||
});
|
||||
|
||||
if (device == nullptr)
|
||||
return nullptr;
|
||||
|
|
@ -526,11 +564,17 @@ void Direct2DPixelData::initialiseBitmapData (Image::BitmapData& bitmap,
|
|||
int y,
|
||||
Image::BitmapData::ReadWriteMode mode)
|
||||
{
|
||||
// If this is hit, there's already another BitmapData or Graphics context active on this
|
||||
// image. Only one BitmapData or Graphics context may be active on an Image at a time.
|
||||
jassert (state != State::drawing);
|
||||
|
||||
// If we're about to read from the image, and the main-memory copy of the image is outdated,
|
||||
// then we must force a backup so that we can return up-to-date data
|
||||
if (mode != Image::BitmapData::writeOnly && state == State::outdated)
|
||||
createPersistentBackup (nullptr);
|
||||
|
||||
backingData->initialiseBitmapData (bitmap, x, y, mode);
|
||||
|
||||
// If we're only reading, then we can assume that the bitmap data was flushed to the software
|
||||
// image directly after it was last modified by d2d, so we can just use the BitmapData
|
||||
// initialised by the backing data.
|
||||
// If we're writing, then we'll need to update our textures next time we try to use them, so
|
||||
// mark them as outdated.
|
||||
if (mode == Image::BitmapData::readOnly)
|
||||
|
|
@ -538,12 +582,9 @@ void Direct2DPixelData::initialiseBitmapData (Image::BitmapData& bitmap,
|
|||
|
||||
struct Releaser : public Image::BitmapData::BitmapDataReleaser
|
||||
{
|
||||
Releaser (std::unique_ptr<BitmapDataReleaser> wrappedIn, Direct2DPixelData::Ptr selfIn)
|
||||
Releaser (std::unique_ptr<BitmapDataReleaser> wrappedIn, Ptr selfIn)
|
||||
: wrapped (std::move (wrappedIn)), self (std::move (selfIn))
|
||||
{
|
||||
// If this is hit, there's already another BitmapData or Graphics context active on this
|
||||
// image. Only one BitmapData or Graphics context may be active on an Image at a time.
|
||||
jassert (self->state != State::drawing);
|
||||
self->state = State::drawing;
|
||||
}
|
||||
|
||||
|
|
@ -556,7 +597,7 @@ void Direct2DPixelData::initialiseBitmapData (Image::BitmapData& bitmap,
|
|||
}
|
||||
|
||||
std::unique_ptr<BitmapDataReleaser> wrapped;
|
||||
Direct2DPixelData::Ptr self;
|
||||
Ptr self;
|
||||
};
|
||||
|
||||
bitmap.dataReleaser = std::make_unique<Releaser> (std::move (bitmap.dataReleaser), this);
|
||||
|
|
@ -714,6 +755,34 @@ auto Direct2DPixelData::getPagesForDevice (ComSmartPtr<ID2D1Device1> device) ->
|
|||
return getIteratorForDevice (device)->second.getPages();
|
||||
}
|
||||
|
||||
void Direct2DPixelData::setBackupEnabled (bool x)
|
||||
{
|
||||
sync = x;
|
||||
}
|
||||
|
||||
bool Direct2DPixelData::isBackupEnabled() const
|
||||
{
|
||||
return sync;
|
||||
}
|
||||
|
||||
bool Direct2DPixelData::backupNow()
|
||||
{
|
||||
return createPersistentBackup (nullptr);
|
||||
}
|
||||
|
||||
bool Direct2DPixelData::needsBackup() const
|
||||
{
|
||||
return state == State::outdated;
|
||||
}
|
||||
|
||||
bool Direct2DPixelData::canBackup() const
|
||||
{
|
||||
return std::any_of (pagesForDevice.begin(), pagesForDevice.end(), [] (const auto& pair)
|
||||
{
|
||||
return pair.second.isUpToDate();
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public:
|
|||
/* Creates a single page containing the provided bitmap and main-memory storage, marking the
|
||||
hardware data as up-to-date.
|
||||
*/
|
||||
Direct2DPixelDataPages (ComSmartPtr<ID2D1Bitmap1>, ImagePixelData::Ptr);
|
||||
Direct2DPixelDataPages (ImagePixelDataBackupExtensions*, ComSmartPtr<ID2D1Bitmap1>, ImagePixelData::Ptr);
|
||||
|
||||
/* Allocates hardware storage for the provided software bitmap.
|
||||
Depending on the initial state, will:
|
||||
|
|
@ -77,7 +77,7 @@ public:
|
|||
- mark the GPU images as up-to-date, or
|
||||
- clear the GPU images, then mark them as up-to-date
|
||||
*/
|
||||
Direct2DPixelDataPages (ComSmartPtr<ID2D1Device1>, ImagePixelData::Ptr, State);
|
||||
Direct2DPixelDataPages (ImagePixelDataBackupExtensions*, ComSmartPtr<ID2D1Device1>, ImagePixelData::Ptr, State);
|
||||
|
||||
/* Returns all pages included in this set.
|
||||
This will be called before reading from the pages (e.g. when drawing them).
|
||||
|
|
@ -86,6 +86,9 @@ public:
|
|||
*/
|
||||
Span<const Page> getPages();
|
||||
|
||||
/** Returns all pages without first syncing from main memory. */
|
||||
Span<const Page> getPagesWithoutSync() const;
|
||||
|
||||
/* Marks this set as needing to be updated from the software image.
|
||||
We don't actually do the copy until the next time that we need to read the hardware pages.
|
||||
This is to avoid redundant copies in the common case that pages are only drawn on a single
|
||||
|
|
@ -102,6 +105,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
ImagePixelDataBackupExtensions* parentBackupExtensions = nullptr;
|
||||
ImagePixelData::Ptr backingData;
|
||||
std::vector<Direct2DPixelDataPage> pages;
|
||||
bool upToDate = false;
|
||||
|
|
@ -126,7 +130,8 @@ private:
|
|||
render target are marked as outdated.
|
||||
*/
|
||||
class Direct2DPixelData : public ImagePixelData,
|
||||
private DxgiAdapterListener
|
||||
private DxgiAdapterListener,
|
||||
private ImagePixelDataBackupExtensions
|
||||
{
|
||||
public:
|
||||
using Ptr = ReferenceCountedObjectPtr<Direct2DPixelData>;
|
||||
|
|
@ -196,6 +201,9 @@ public:
|
|||
return ! pages.empty() ? pages.front().bitmap : nullptr;
|
||||
}
|
||||
|
||||
BackupExtensions* getBackupExtensions() override { return this; }
|
||||
const BackupExtensions* getBackupExtensions() const override { return this; }
|
||||
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
|
|
@ -203,6 +211,7 @@ private:
|
|||
initiallyCleared,
|
||||
drawing,
|
||||
drawn,
|
||||
outdated,
|
||||
};
|
||||
|
||||
Direct2DPixelData (ImagePixelData::Ptr, State);
|
||||
|
|
@ -232,17 +241,28 @@ private:
|
|||
template <typename Fn>
|
||||
bool applyEffectInArea (Rectangle<int>, Fn&&);
|
||||
|
||||
void setBackupEnabled (bool) override;
|
||||
bool isBackupEnabled() const override;
|
||||
bool backupNow() override;
|
||||
bool needsBackup() const override;
|
||||
bool canBackup() const override;
|
||||
|
||||
void adapterCreated (DxgiAdapter::Ptr) override {}
|
||||
void adapterRemoved (DxgiAdapter::Ptr adapter) override
|
||||
{
|
||||
if (adapter != nullptr)
|
||||
pagesForDevice.erase (adapter->direct2DDevice);
|
||||
|
||||
if (mostRecentDevice == adapter->direct2DDevice)
|
||||
mostRecentDevice = nullptr;
|
||||
}
|
||||
|
||||
SharedResourcePointer<DirectX> directX;
|
||||
ImagePixelData::Ptr backingData;
|
||||
ComSmartPtr<ID2D1Device1> mostRecentDevice;
|
||||
std::map<ComSmartPtr<ID2D1Device1>, Pages> pagesForDevice;
|
||||
State state;
|
||||
bool sync = true;
|
||||
|
||||
JUCE_LEAK_DETECTOR (Direct2DPixelData)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -55,10 +55,18 @@ struct StandardCachedComponentImage : public CachedComponentImage
|
|||
jmax (1, imageBounds.getWidth()),
|
||||
jmax (1, imageBounds.getHeight()),
|
||||
! owner.isOpaque());
|
||||
|
||||
image.setBackupEnabled (false);
|
||||
validArea.clear();
|
||||
}
|
||||
|
||||
// If the cached image is outdated but cannot be backed-up, this indicates that the graphics
|
||||
// device holding the most recent copy of the cached image has gone away. Therefore, we've
|
||||
// effectively lost the contents of the cache, and we must repaint the entire component.
|
||||
if (auto ptr = image.getPixelData())
|
||||
if (auto* extensions = ptr->getBackupExtensions())
|
||||
if (extensions->needsBackup() && ! extensions->canBackup())
|
||||
validArea.clear();
|
||||
|
||||
if (! validArea.containsRectangle (compBounds))
|
||||
{
|
||||
Graphics imG (image);
|
||||
|
|
@ -81,6 +89,7 @@ struct StandardCachedComponentImage : public CachedComponentImage
|
|||
|
||||
validArea = compBounds;
|
||||
|
||||
// TODO(reuk) test dragging/sharing between multiple graphics devices
|
||||
g.setColour (Colours::black.withAlpha (owner.getAlpha()));
|
||||
g.drawImageTransformed (image, AffineTransform::scale ((float) compBounds.getWidth() / (float) imageBounds.getWidth(),
|
||||
(float) compBounds.getHeight() / (float) imageBounds.getHeight()), false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue