Add support for blending operations to CGContext#2222
Conversation
| // 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.
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.
nit: context->CurrentGState().blendMode = mode;
aballway
left a comment
There was a problem hiding this comment.
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.
nit: would prefer explicit _CRenderOpCommandListBase::Stage
| // 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.
nit: magic number. Why 16?
| // 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.
why fail fast here when this returns HRESULT?
|
Ping! |
aballway
left a comment
There was a problem hiding this comment.
👍 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.
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.
kCGBlendModeClearis not supported.kCGBlendModePlusDarkeris 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.