mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Direct2D: Add support for bitmaps spanning multiple texture pages
This commit is contained in:
parent
470ada4454
commit
589d9940ed
7 changed files with 873 additions and 456 deletions
|
|
@ -80,6 +80,9 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
Rectangle<int> getSubsection() const { return area; }
|
||||
ImagePixelData::Ptr getSourcePixelData() const { return sourceImage; }
|
||||
|
||||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
|
||||
{
|
||||
auto g = sourceImage->createLowLevelContext();
|
||||
|
|
|
|||
|
|
@ -345,9 +345,9 @@ public:
|
|||
const auto d2d1Bitmap = [&]
|
||||
{
|
||||
if (auto direct2DPixelData = dynamic_cast<Direct2DPixelData*> (fillType.image.getPixelData()))
|
||||
if (auto bitmap = direct2DPixelData->getAdapterD2D1Bitmap())
|
||||
if (bitmap->GetPixelFormat().format == DXGI_FORMAT_B8G8R8A8_UNORM)
|
||||
return bitmap;
|
||||
if (const auto page = direct2DPixelData->getFirstPageForContext (context))
|
||||
if (page->GetPixelFormat().format == DXGI_FORMAT_B8G8R8A8_UNORM)
|
||||
return page;
|
||||
|
||||
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime);
|
||||
|
||||
|
|
@ -1162,11 +1162,20 @@ void Direct2DGraphicsContext::clipToImageAlpha (const Image& sourceImage, const
|
|||
|
||||
if (auto deviceContext = getPimpl()->getDeviceContext())
|
||||
{
|
||||
const auto maxDim = (int) deviceContext->GetMaximumBitmapSize();
|
||||
|
||||
if (sourceImage.getWidth() > maxDim || sourceImage.getHeight() > maxDim)
|
||||
{
|
||||
// The Direct2D renderer doesn't currently support clipping to very large images
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a Direct2D image already?
|
||||
ComSmartPtr<ID2D1Bitmap> d2d1Bitmap;
|
||||
|
||||
if (auto direct2DPixelData = dynamic_cast<Direct2DPixelData*> (sourceImage.getPixelData()))
|
||||
d2d1Bitmap = direct2DPixelData->getAdapterD2D1Bitmap();
|
||||
d2d1Bitmap = direct2DPixelData->getFirstPageForContext (deviceContext);
|
||||
|
||||
if (! d2d1Bitmap)
|
||||
{
|
||||
|
|
@ -1430,78 +1439,108 @@ void Direct2DGraphicsContext::strokePath (const Path& p, const PathStrokeType& s
|
|||
deviceContext->DrawGeometry (geometry, brush, strokeType.getStrokeThickness(), strokeStyle);
|
||||
}
|
||||
|
||||
void Direct2DGraphicsContext::drawImage (const Image& image, const AffineTransform& transform)
|
||||
void Direct2DGraphicsContext::drawImage (const Image& imageIn, const AffineTransform& transform)
|
||||
{
|
||||
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, drawImageTime)
|
||||
|
||||
JUCE_SCOPED_TRACE_EVENT_FRAME (etw::drawImage, etw::direct2dKeyword, getFrameId());
|
||||
|
||||
if (image.isNull())
|
||||
if (imageIn.isNull())
|
||||
return;
|
||||
|
||||
applyPendingClipList();
|
||||
|
||||
if (auto deviceContext = getPimpl()->getDeviceContext())
|
||||
{
|
||||
// Is this a Direct2D image already with the correct format?
|
||||
ComSmartPtr<ID2D1Bitmap1> d2d1Bitmap;
|
||||
auto image = NativeImageType{}.convert (imageIn);
|
||||
Direct2DPixelData* nativeBitmap = nullptr;
|
||||
Rectangle<int> imageClipArea;
|
||||
|
||||
if (auto direct2DPixelData = dynamic_cast<Direct2DPixelData*> (image.getPixelData()))
|
||||
const auto imageTransform = currentState->currentTransform.getTransformWith (transform);
|
||||
|
||||
if (auto* subsectionPixelData = dynamic_cast<SubsectionPixelData*> (image.getPixelData()))
|
||||
{
|
||||
d2d1Bitmap = direct2DPixelData->getAdapterD2D1Bitmap();
|
||||
if (auto direct2DPixelData = dynamic_cast<Direct2DPixelData*> (subsectionPixelData->getSourcePixelData().get()))
|
||||
{
|
||||
nativeBitmap = direct2DPixelData;
|
||||
imageClipArea = subsectionPixelData->getSubsection();
|
||||
}
|
||||
}
|
||||
else if (auto direct2DPixelData = dynamic_cast<Direct2DPixelData*> (image.getPixelData()))
|
||||
{
|
||||
nativeBitmap = direct2DPixelData;
|
||||
imageClipArea = { direct2DPixelData->width, direct2DPixelData->height };
|
||||
}
|
||||
|
||||
if (! d2d1Bitmap || d2d1Bitmap->GetPixelFormat().format != DXGI_FORMAT_B8G8R8A8_UNORM)
|
||||
else
|
||||
{
|
||||
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime);
|
||||
|
||||
d2d1Bitmap = Direct2DBitmap::toBitmap (image, deviceContext, Image::ARGB);
|
||||
imageClipArea = image.getBounds();
|
||||
// This shouldn't happen, we converted the image to a native type already
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
if (d2d1Bitmap)
|
||||
if (! nativeBitmap)
|
||||
{
|
||||
auto sourceRectF = D2DUtilities::toRECT_F (imageClipArea);
|
||||
|
||||
auto imageTransform = currentState->currentTransform.getTransformWith (transform);
|
||||
|
||||
if (imageTransform.isOnlyTranslation())
|
||||
{
|
||||
auto destinationRect = D2DUtilities::toRECT_F (imageClipArea.toFloat() + Point<float> { imageTransform.getTranslationX(), imageTransform.getTranslationY() });
|
||||
|
||||
deviceContext->DrawBitmap (d2d1Bitmap,
|
||||
&destinationRect,
|
||||
currentState->fillType.getOpacity(),
|
||||
currentState->interpolationMode,
|
||||
&sourceRectF,
|
||||
{});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (D2DHelpers::isTransformAxisAligned (imageTransform))
|
||||
{
|
||||
auto destinationRect = D2DUtilities::toRECT_F (imageClipArea.toFloat().transformedBy (imageTransform));
|
||||
|
||||
deviceContext->DrawBitmap (d2d1Bitmap,
|
||||
&destinationRect,
|
||||
currentState->fillType.getOpacity(),
|
||||
currentState->interpolationMode,
|
||||
&sourceRectF,
|
||||
{});
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedTransform scopedTransform { *getPimpl(), currentState, transform };
|
||||
deviceContext->DrawBitmap (d2d1Bitmap,
|
||||
nullptr,
|
||||
currentState->fillType.getOpacity(),
|
||||
currentState->interpolationMode,
|
||||
&sourceRectF,
|
||||
{});
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
auto drawTiles = [&] (const auto& pixelData, auto&& getRect)
|
||||
{
|
||||
for (const auto& page : pixelData->getPagesForContext (deviceContext))
|
||||
{
|
||||
const auto pageBounds = page.getBounds();
|
||||
const auto intersection = pageBounds.toFloat().getIntersection (imageClipArea.toFloat());
|
||||
|
||||
if (intersection.isEmpty())
|
||||
continue;
|
||||
|
||||
const auto src = intersection - pageBounds.getPosition().toFloat();
|
||||
const auto dst = getRect (intersection - imageClipArea.getPosition().toFloat());
|
||||
const auto [srcConverted, dstConverted] = std::tuple (D2DUtilities::toRECT_F (src),
|
||||
D2DUtilities::toRECT_F (dst));
|
||||
|
||||
if (nativeBitmap->pixelFormat == Image::SingleChannel)
|
||||
{
|
||||
const auto lastColour = currentState->colourBrush->GetColor();
|
||||
const auto lastMode = deviceContext->GetAntialiasMode();
|
||||
|
||||
currentState->colourBrush->SetColor (D2D1::ColorF (1.0f, 1.0f, 1.0f, currentState->fillType.getOpacity()));
|
||||
deviceContext->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
deviceContext->FillOpacityMask (page.bitmap,
|
||||
currentState->colourBrush,
|
||||
dstConverted,
|
||||
srcConverted);
|
||||
|
||||
deviceContext->SetAntialiasMode (lastMode);
|
||||
currentState->colourBrush->SetColor (lastColour);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceContext->DrawBitmap (page.bitmap,
|
||||
dstConverted,
|
||||
currentState->fillType.getOpacity(),
|
||||
currentState->interpolationMode,
|
||||
srcConverted,
|
||||
{});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (imageTransform.isOnlyTranslation() || D2DHelpers::isTransformAxisAligned (imageTransform))
|
||||
{
|
||||
drawTiles (nativeBitmap, [&] (auto intersection)
|
||||
{
|
||||
return intersection.transformedBy (imageTransform);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedTransform scopedTransform { *getPimpl(), currentState, transform };
|
||||
|
||||
drawTiles (nativeBitmap, [] (auto intersection)
|
||||
{
|
||||
return intersection;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1787,4 +1826,102 @@ Direct2DGraphicsContext::ScopedTransform::~ScopedTransform()
|
|||
pimpl.resetDeviceContextTransform();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class Direct2DGraphicsContextTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
Direct2DGraphicsContextTests() : UnitTest ("Direct2D Graphics Context", UnitTestCategories::graphics) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
const auto imageWidth = 1 << 15;
|
||||
const auto imageHeight = 128;
|
||||
Image largeImageSoftware { Image::RGB, imageWidth, imageHeight, false, SoftwareImageType{} };
|
||||
|
||||
{
|
||||
Graphics g { largeImageSoftware };
|
||||
g.setGradientFill ({ Colours::red, 0, 0, Colours::cyan, (float) largeImageSoftware.getWidth(), 0, false });
|
||||
g.fillAll();
|
||||
}
|
||||
|
||||
constexpr auto targetDim = 512;
|
||||
|
||||
const auto largeImageNative = NativeImageType{}.convert (largeImageSoftware);
|
||||
const auto subsection = largeImageNative.getClippedImage (largeImageNative.getBounds().withSizeKeepingCentre (1 << 14, 64));
|
||||
|
||||
beginTest ("Render large images");
|
||||
{
|
||||
for (const auto& imageToDraw : { largeImageNative, subsection })
|
||||
{
|
||||
const AffineTransform transformsToTest[]
|
||||
{
|
||||
{},
|
||||
AffineTransform::translation ((float) targetDim - (float) imageToDraw.getWidth(), 0),
|
||||
AffineTransform::translation (0, (float) targetDim - (float) imageToDraw.getHeight()),
|
||||
AffineTransform::scale ((float) targetDim / imageWidth),
|
||||
AffineTransform::scale ((float) targetDim / imageWidth)
|
||||
.followedBy (AffineTransform::translation (32, 64)),
|
||||
AffineTransform::scale (1.1f),
|
||||
AffineTransform::scale ((float) targetDim / imageWidth,
|
||||
(float) targetDim / imageHeight),
|
||||
AffineTransform::rotation (MathConstants<float>::pi * 0.25f),
|
||||
AffineTransform::rotation (MathConstants<float>::pi * 0.25f, imageWidth * 0.5f, 0)
|
||||
.followedBy (AffineTransform::translation (-imageWidth * 0.5f, 0)),
|
||||
};
|
||||
|
||||
for (const auto& transform : transformsToTest)
|
||||
{
|
||||
Image targetNative { Image::RGB, targetDim, targetDim, true, NativeImageType{} };
|
||||
Image targetSoftware { Image::RGB, targetDim, targetDim, true, SoftwareImageType{} };
|
||||
|
||||
for (auto& image : { &targetNative, &targetSoftware })
|
||||
{
|
||||
Graphics g { *image };
|
||||
g.drawImageTransformed (imageToDraw, transform);
|
||||
}
|
||||
|
||||
compareImages (targetNative, targetSoftware);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void compareImages (const Image& a, const Image& b)
|
||||
{
|
||||
expect (a.getBounds() == b.getBounds());
|
||||
|
||||
const Image::BitmapData bitmapA { a, Image::BitmapData::readOnly };
|
||||
const Image::BitmapData bitmapB { b, Image::BitmapData::readOnly };
|
||||
|
||||
int64_t accumulatedError{};
|
||||
int64_t numSamples{};
|
||||
|
||||
for (auto y = 0; y < a.getHeight(); y += 16)
|
||||
{
|
||||
for (auto x = 0; x < a.getWidth(); x += 16)
|
||||
{
|
||||
const auto expected = bitmapA.getPixelColour (x, y);
|
||||
const auto actual = bitmapB.getPixelColour (x, y);
|
||||
|
||||
for (auto& fn : { &Colour::getRed, &Colour::getGreen, &Colour::getBlue, &Colour::getAlpha })
|
||||
{
|
||||
accumulatedError += ((int64_t) (actual.*fn)() - (int64_t) (expected.*fn)());
|
||||
++numSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto averageError = (double) accumulatedError / (double) numSamples;
|
||||
expect (std::abs (averageError) < 1.0);
|
||||
}
|
||||
};
|
||||
|
||||
static Direct2DGraphicsContextTests direct2DGraphicsContextTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -649,9 +649,7 @@ public:
|
|||
if (const auto hr = snapshot->CopyFromBitmap (&p, swap.buffer, &sourceRect); FAILED (hr))
|
||||
return {};
|
||||
|
||||
const Image result { Direct2DPixelData::fromDirect2DBitmap (directX->adapters.getAdapterForHwnd (hwnd),
|
||||
context,
|
||||
snapshot) };
|
||||
const Image result { new Direct2DPixelData { context, snapshot } };
|
||||
|
||||
swap.chain->Present (0, DXGI_PRESENT_DO_NOT_WAIT);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -35,60 +35,176 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
/* A single bitmap that represents a subsection of a virtual bitmap. */
|
||||
struct Direct2DPixelDataPage
|
||||
{
|
||||
/* The bounds of the stored bitmap inside the virtual bitmap. */
|
||||
Rectangle<int> getBounds() const;
|
||||
|
||||
/* The stored subsection bitmap. */
|
||||
ComSmartPtr<ID2D1Bitmap1> bitmap;
|
||||
|
||||
/* The top-left position of this virtual bitmap inside the virtual bitmap. */
|
||||
Point<int> topLeft;
|
||||
};
|
||||
|
||||
/* A set of pages that together represent a full virtual bitmap.
|
||||
All pages in the set always share the same resource context.
|
||||
Additionally, stores a reference to a software-backed bitmap, the content of which will
|
||||
be copied to the pages when necessary in order to ensure that the software- and hardware-backed
|
||||
bitmaps match.
|
||||
*/
|
||||
class Direct2DPixelDataPages
|
||||
{
|
||||
public:
|
||||
using Page = Direct2DPixelDataPage;
|
||||
|
||||
enum class State
|
||||
{
|
||||
unsuitableToRead, // Image data is outdated
|
||||
suitableToRead, // Image data is up-to-date with the backing data
|
||||
cleared, // Implies suitableToRead
|
||||
};
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Allocates hardware storage for the provided software bitmap.
|
||||
Depending on the initial state, will:
|
||||
- mark the GPU images as needing to be copied from main memory before they are next accessed, or
|
||||
- mark the GPU images as up-to-date, or
|
||||
- clear the GPU images, then mark them as up-to-date
|
||||
*/
|
||||
Direct2DPixelDataPages (ComSmartPtr<ID2D1DeviceContext1>, ImagePixelData::Ptr, State);
|
||||
|
||||
/* Returns all pages included in this set.
|
||||
This will be called before reading from the pages (e.g. when drawing them).
|
||||
Therefore, this function will check whether the hardware data is out-of-date and
|
||||
copy from the software image if necessary before returning.
|
||||
*/
|
||||
Span<const Page> getPages();
|
||||
|
||||
/* 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
|
||||
device at a time.
|
||||
*/
|
||||
void markOutdated()
|
||||
{
|
||||
upToDate = false;
|
||||
}
|
||||
|
||||
private:
|
||||
ImagePixelData::Ptr backingData;
|
||||
std::vector<Direct2DPixelDataPage> pages;
|
||||
bool upToDate = false;
|
||||
};
|
||||
|
||||
/* Pixel data type providing accelerated access to cached Direct2D textures.
|
||||
|
||||
Direct2D bitmaps are device-dependent resources, but frequently a computer will
|
||||
have multiple devices, e.g. if there are several GPUs available which is common for laptops.
|
||||
In order to support a fast image type that can be drawn by any one of the available devices,
|
||||
we store a software bitmap which acts as the source-of-truth, and cache per-device hardware
|
||||
bitmaps alongside it. The caching mechanism tries to minimise the amount of redundant work.
|
||||
|
||||
When attempting to access hardware bitmaps, we first check the cache to see whether we've
|
||||
previously allocated bitmaps for the requested device, and only create bitmaps if none already
|
||||
exist.
|
||||
|
||||
We only copy from the software backup to hardware memory immediately before accessing the
|
||||
bitmaps for a particular device, and then only if that hardware bitmap is outdated. All
|
||||
hardware bitmaps are marked as outdated when a writeable BitmapData is created for the current
|
||||
PixelData. When creating a low-level graphics context, all hardware bitmaps other than the
|
||||
render target are marked as outdated.
|
||||
*/
|
||||
class Direct2DPixelData : public ImagePixelData,
|
||||
private DxgiAdapterListener
|
||||
{
|
||||
public:
|
||||
using Ptr = ReferenceCountedObjectPtr<Direct2DPixelData>;
|
||||
using Page = Direct2DPixelDataPage;
|
||||
using Pages = Direct2DPixelDataPages;
|
||||
using State = Pages::State;
|
||||
|
||||
static Ptr make (Image::PixelFormat formatToUse,
|
||||
int w,
|
||||
int h,
|
||||
bool clearImageIn,
|
||||
DxgiAdapter::Ptr adapterIn);
|
||||
/* Creates image storage, taking ownership of the provided bitmap.
|
||||
This will immediately copy the content of the image to the software backup, so that the
|
||||
image can still be drawn if original device goes away.
|
||||
*/
|
||||
Direct2DPixelData (ComSmartPtr<ID2D1DeviceContext1>, ComSmartPtr<ID2D1Bitmap1>);
|
||||
|
||||
static Ptr fromDirect2DBitmap (DxgiAdapter::Ptr,
|
||||
ComSmartPtr<ID2D1DeviceContext1>,
|
||||
ComSmartPtr<ID2D1Bitmap1>);
|
||||
/* Creates software image storage of the requested size. */
|
||||
Direct2DPixelData (Image::PixelFormat, int, int, bool);
|
||||
|
||||
~Direct2DPixelData() override;
|
||||
|
||||
/* Creates new software image storage with content matching the content of this image.
|
||||
Does not copy any hardware resources.
|
||||
*/
|
||||
ImagePixelData::Ptr clone() override
|
||||
{
|
||||
return new Direct2DPixelData (backingData->clone(), State::suitableToRead);
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageType> createType() const override
|
||||
{
|
||||
return std::make_unique<NativeImageType>();
|
||||
}
|
||||
|
||||
/* Creates a graphics context that will use the default device to draw into hardware bitmaps
|
||||
for that device. When the context is destroyed, the rendered hardware bitmap will be copied
|
||||
back to software storage.
|
||||
|
||||
This PixelData may hold device resources for devices other than the default device. In that
|
||||
case, the other device resources will be marked as outdated, to ensure that they are updated
|
||||
from the software backup before they are next accessed.
|
||||
*/
|
||||
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override;
|
||||
|
||||
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override;
|
||||
/* Provides access to the software image storage.
|
||||
|
||||
ImagePixelData::Ptr clone() override;
|
||||
If the bitmap data provides write access, then all device resources will be marked as
|
||||
outdated, to ensure that they are updated from the software backup before they are next
|
||||
accessed.
|
||||
*/
|
||||
void initialiseBitmapData (Image::BitmapData&, int, int, Image::BitmapData::ReadWriteMode) override;
|
||||
|
||||
void applyGaussianBlurEffect (float radius, Image& result) override;
|
||||
void applySingleChannelBoxBlurEffect (int radius, Image& result) override;
|
||||
|
||||
std::unique_ptr<ImageType> createType() const override;
|
||||
/* This returns image data that is suitable for use when drawing with the provided context.
|
||||
This image data should be treated as a read-only view - making modifications directly
|
||||
through the Direct2D API will have unpredictable results.
|
||||
If you want to render into this image using D2D, call createLowLevelContext.
|
||||
*/
|
||||
Span<const Page> getPagesForContext (ComSmartPtr<ID2D1DeviceContext1>);
|
||||
|
||||
DxgiAdapter::Ptr getAdapter() const { return adapter; }
|
||||
ComSmartPtr<ID2D1Bitmap1> getAdapterD2D1Bitmap();
|
||||
|
||||
void flushToSoftwareBackup();
|
||||
/* Utility function that just returns a pointer to the bitmap for the first page returned from
|
||||
getPagesForContext.
|
||||
*/
|
||||
ComSmartPtr<ID2D1Bitmap1> getFirstPageForContext (ComSmartPtr<ID2D1DeviceContext1> context)
|
||||
{
|
||||
const auto pages = getPagesForContext (context);
|
||||
return ! pages.empty() ? pages.front().bitmap : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Direct2DPixelData (Image::PixelFormat, int, int, bool, DxgiAdapter::Ptr);
|
||||
Direct2DPixelData (ImagePixelData::Ptr, State);
|
||||
auto getIteratorForContext (ComSmartPtr<ID2D1DeviceContext1>);
|
||||
|
||||
int getPixelStride() const { return pixelFormat == Image::SingleChannel ? 1 : 4; }
|
||||
int getLineStride() const { return (getPixelStride() * jmax (1, width) + 3) & ~3; }
|
||||
|
||||
void adapterCreated (DxgiAdapter::Ptr) override;
|
||||
void adapterRemoved (DxgiAdapter::Ptr) override;
|
||||
|
||||
void initBitmapDataReadOnly (Image::BitmapData&, int, int);
|
||||
|
||||
ComSmartPtr<ID2D1Bitmap1> createAdapterBitmap() const;
|
||||
void createDeviceResources();
|
||||
void adapterCreated (DxgiAdapter::Ptr) override {}
|
||||
void adapterRemoved (DxgiAdapter::Ptr adapter) override
|
||||
{
|
||||
if (adapter != nullptr)
|
||||
pagesForDevice.erase (adapter->direct2DDevice);
|
||||
}
|
||||
|
||||
SharedResourcePointer<DirectX> directX;
|
||||
const bool clearImage;
|
||||
Image backup;
|
||||
DxgiAdapter::Ptr adapter;
|
||||
ComSmartPtr<ID2D1DeviceContext1> context;
|
||||
ComSmartPtr<ID2D1Bitmap1> nativeBitmap;
|
||||
ImagePixelData::Ptr backingData;
|
||||
std::map<ComSmartPtr<ID2D1Device1>, Pages> pagesForDevice;
|
||||
State state;
|
||||
|
||||
JUCE_LEAK_DETECTOR (Direct2DPixelData)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -322,6 +322,8 @@ struct D2DUtilities
|
|||
static Point<int> toPoint (POINT p) noexcept { return { p.x, p.y }; }
|
||||
static POINT toPOINT (Point<int> p) noexcept { return { p.x, p.y }; }
|
||||
|
||||
static D2D1_POINT_2U toPOINT_2U (Point<int> p) { return D2D1::Point2U ((UINT32) p.x, (UINT32) p.y); }
|
||||
|
||||
static D2D1_COLOR_F toCOLOR_F (Colour c)
|
||||
{
|
||||
return { c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha() };
|
||||
|
|
@ -331,21 +333,23 @@ struct D2DUtilities
|
|||
{
|
||||
return { transform.mat00, transform.mat10, transform.mat01, transform.mat11, transform.mat02, transform.mat12 };
|
||||
}
|
||||
|
||||
static Rectangle<int> rectFromSize (D2D1_SIZE_U s)
|
||||
{
|
||||
return { (int) s.width, (int) s.height };
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct Direct2DDeviceContext
|
||||
{
|
||||
static ComSmartPtr<ID2D1DeviceContext1> create (DxgiAdapter::Ptr adapter)
|
||||
static ComSmartPtr<ID2D1DeviceContext1> create (ComSmartPtr<ID2D1Device1> device)
|
||||
{
|
||||
if (adapter == nullptr)
|
||||
return {};
|
||||
|
||||
ComSmartPtr<ID2D1DeviceContext1> result;
|
||||
|
||||
if (const auto hr = adapter->direct2DDevice->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
|
||||
result.resetAndGetPointerAddress());
|
||||
FAILED (hr))
|
||||
if (const auto hr = device->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
|
||||
result.resetAndGetPointerAddress());
|
||||
FAILED (hr))
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
|
|
@ -358,6 +362,11 @@ struct Direct2DDeviceContext
|
|||
return result;
|
||||
}
|
||||
|
||||
static ComSmartPtr<ID2D1DeviceContext1> create (DxgiAdapter::Ptr adapter)
|
||||
{
|
||||
return adapter != nullptr ? create (adapter->direct2DDevice) : nullptr;
|
||||
}
|
||||
|
||||
Direct2DDeviceContext() = delete;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5313,7 +5313,7 @@ private:
|
|||
|
||||
Image getImage() const
|
||||
{
|
||||
return Image { Direct2DPixelData::fromDirect2DBitmap (adapter, deviceContext, bitmap) };
|
||||
return Image { new Direct2DPixelData { deviceContext, bitmap } };
|
||||
}
|
||||
|
||||
ComSmartPtr<ID2D1Bitmap1> getBitmap() const
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue