-
Notifications
You must be signed in to change notification settings - Fork 806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for blending operations to CGContext #2222
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests to consider adding:
- Composition into a transparency layer.
- Composition of an image.
- Composition with an image.
Frameworks/CoreGraphics/CGContext.mm
Outdated
// We cannot fulfill this request. | ||
UNIMPLEMENTED_WITH_MSG("Unsupported operator blend mode %4.04x", mode); | ||
} else if (mode == kCGBlendModePlusDarker) { | ||
// No UNIMPLEMENTED here: we will proceed but with an unusual output. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No UNIMPLEMENTED [](start = 11, length = 16)
nit: no, not? not sure what it means.
} | ||
|
||
auto& state = context->CurrentGState(); | ||
state.blendMode = mode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: context->CurrentGState().blendMode = mode;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some comments I want to make before I forget, still trying to think of any better ways of doing this before giving a
deviceContext->Clear({ 0, 0, 0, 0 }); // Clear the original target to transparent black. | ||
RETURN_IF_FAILED(inputBitmap.As(&copiedImage)); | ||
} | ||
return __super::Stage(context, deviceContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would prefer explicit _CRenderOpCommandListBase::Stage
Frameworks/CoreGraphics/CGContext.mm
Outdated
// It would be ideal to store each operation on the local stack, but that is infeasible because | ||
// of scoping concerns. | ||
std::vector<std::unique_ptr<IRenderOperation>> operations; | ||
operations.reserve(16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: magic number. Why 16?
Frameworks/CoreGraphics/CGContext.mm
Outdated
// PROPERTIES | ||
// D2D1_BLEND_PROP_MODE: blend mode | ||
ComPtr<ID2D1Effect> blendEffect; | ||
FAIL_FAST_IF_FAILED(deviceContext->CreateEffect(CLSID_D2D1Blend, &blendEffect)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why fail fast here when this returns HRESULT?
Ping! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 unable to find more elegant solution
Ping 2: The Pingening! |
Ping 3: Ping Harder |
deviceContext->SetTransform(__CGAffineTransformToD2D_F(transform)); | ||
auto revertTransform = wil::ScopeExit([this]() { this->deviceContext->SetTransform(D2D1::IdentityMatrix()); }); | ||
RETURN_IF_FAILED(std::forward<Lambda>(drawLambda)(this, deviceContext.Get())); | ||
// Stage and scaffold all rendering operations; this will create layers, command lists, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i really really really like this abstraction geez
@ms-jihua thank you 💯 I'm going to diagram this specific implementation, as well as how the command list blending operations work. |
This pull request adds support for blending to CGContext. There are a number of caveats.
kCGBlendModeClear
is not supported.kCGBlendModePlusDarker
is not supported (and was not supported in the original Cairo implementation).Some blend modes (the ones without Source/Destination) in the name require a full buffer read-back and compose operation. This is unavoidable. 😦
This change unfortunately does include a rewrite of the CGD2D rendering stack. Instead of adding yet another nesting level of command lists and layers, I switched it to a scaffold/commit stack. Depending on various conditions, operations will be queued up, started in order, and committed in reverse order. A great many of those operations involve creating command lists and layers and blending them or otherwise composing them.
There is a not-insignificant performance penalty in certain cases:
While 19% seems like a lot, it's only 47μs/operation; an application running at 60fps requires its rendering operations complete in ~16000μs. It's not significant, but it is a cost taken per operation.
I believe there is opportunity for improvement; specifically, I had to compromise on allocations to achieve the desired effects of polymorphism in the new render operation stack.
Please examine this very closely.
Fixes #1389.