diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp index 733e079ef0..c2ead0ec73 100644 --- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp @@ -39,6 +39,23 @@ namespace juce JUCE_IMPLEMENT_SINGLETON (Direct2DMetricsHub) #endif +struct ScopedBlendCopy +{ + explicit ScopedBlendCopy (ComSmartPtr c) + : ctx (c) + { + ctx->SetPrimitiveBlend (D2D1_PRIMITIVE_BLEND_COPY); + } + + ~ScopedBlendCopy() + { + ctx->SetPrimitiveBlend (blend); + } + + ComSmartPtr ctx; + D2D1_PRIMITIVE_BLEND blend = ctx->GetPrimitiveBlend(); +}; + class PushedLayers { public: @@ -142,10 +159,100 @@ public: return pushedLayers.empty(); } + void fillGeometryWithNoLayersActive (ComSmartPtr ctx, + ComSmartPtr geo, + ComSmartPtr brush) + { + ComSmartPtr factory; + ctx->GetFactory (factory.resetAndGetPointerAddress()); + + const auto hasGeoLayer = std::any_of (pushedLayers.begin(), + pushedLayers.end(), + [] (const auto& x) { return std::holds_alternative (x.var); }); + + const auto intersection = [&]() -> ComSmartPtr + { + if (! hasGeoLayer) + return {}; + + const auto contextSize = ctx->GetPixelSize(); + + ComSmartPtr rect; + factory->CreateRectangleGeometry (D2D1::RectF (0.0f, + 0.0f, + (float) contextSize.width, + (float) contextSize.height), + rect.resetAndGetPointerAddress()); + + ComSmartPtr clip = rect; + + for (const auto& layer : pushedLayers) + { + ScopedGeometryWithSink scope { factory, D2D1_FILL_MODE_WINDING }; + + if (auto* l = std::get_if (&layer.var)) + { + clip->CombineWithGeometry (l->geometry, + D2D1_COMBINE_MODE_INTERSECT, + l->params.maskTransform, + scope.sink); + } + else if (auto* r = std::get_if> (&layer.var)) + { + ComSmartPtr temporaryRect; + factory->CreateRectangleGeometry (D2DUtilities::toRECT_F (*r), + temporaryRect.resetAndGetPointerAddress()); + clip->CombineWithGeometry (temporaryRect, + D2D1_COMBINE_MODE_INTERSECT, + D2D1::Matrix3x2F::Identity(), + scope.sink); + } + + clip = scope.geometry; + } + + return clip; + }(); + + const auto clipWithGeo = [&]() -> ComSmartPtr + { + if (intersection == nullptr) + return geo; + + ScopedGeometryWithSink scope { factory, D2D1_FILL_MODE_WINDING }; + intersection->CombineWithGeometry (geo, + D2D1_COMBINE_MODE_INTERSECT, + D2D1::Matrix3x2F::Identity(), + scope.sink); + return scope.geometry; + }(); + + if (intersection != nullptr) + { + std::for_each (pushedLayers.rbegin(), + pushedLayers.rend(), + [&] (const auto& layer) { layer.pop (ctx); }); + } + + { + const ScopedBlendCopy scope { ctx }; + ctx->FillGeometry (clipWithGeo, brush); + } + + if (intersection != nullptr) + { + pushedLayers.clear(); + + auto newLayer = D2D1::LayerParameters1(); + newLayer.geometricMask = intersection; + push (ctx, newLayer); + } + } + private: struct OwningLayer { - explicit OwningLayer (D2D1_LAYER_PARAMETERS1 p) : params (p) {} + explicit OwningLayer (const D2D1_LAYER_PARAMETERS1& p) : params (p) {} D2D1_LAYER_PARAMETERS1 params; ComSmartPtr geometry = params.geometricMask != nullptr ? addComSmartPtrOwner (params.geometricMask) : nullptr; @@ -1276,17 +1383,30 @@ void Direct2DGraphicsContext::fillRect (const Rectangle& r, bool replaceExi return; if (replaceExistingContents) - clipToRectangle (r); - - const auto clearColour = currentState->fillType.colour; - - auto fill = [replaceExistingContents, clearColour] (Rectangle rect, - ComSmartPtr deviceContext, - ComSmartPtr brush) { - if (replaceExistingContents) - deviceContext->Clear (D2DUtilities::toCOLOR_F (clearColour)); - else if (brush != nullptr) + applyPendingClipList(); + + const auto asRectF = D2DUtilities::toRECT_F (getPimpl()->getFrameSize().toFloat()); + ComSmartPtr rectGeometry; + getPimpl()->getDirect2DFactory()->CreateRectangleGeometry (asRectF, + rectGeometry.resetAndGetPointerAddress()); + + const auto matrix = D2DUtilities::transformToMatrix (currentState->currentTransform.getTransform()); + ComSmartPtr geo; + getPimpl()->getDirect2DFactory()->CreateTransformedGeometry (rectGeometry, + matrix, + geo.resetAndGetPointerAddress()); + + const auto brush = currentState->fillType.isInvisible() ? currentState->currentBrush : currentState->getBrush(); + currentState->layers.fillGeometryWithNoLayersActive (getPimpl()->getDeviceContext(), geo, brush); + return; + } + + const auto fill = [] (Rectangle rect, + ComSmartPtr deviceContext, + ComSmartPtr brush) + { + if (brush != nullptr) deviceContext->FillRectangle (D2DUtilities::toRECT_F (rect), brush); };