close
Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Add support for blending operations to CGContext#2222

Merged
DHowett-MSFT merged 8 commits into
microsoft:developfrom
DHowett-MSFT:201703-blending
Mar 15, 2017
Merged

Add support for blending operations to CGContext#2222
DHowett-MSFT merged 8 commits into
microsoft:developfrom
DHowett-MSFT:201703-blending

Conversation

@DHowett-MSFT
Copy link
Copy Markdown

This pull request adds support for blending to CGContext. There are a number of caveats.

  • kCGBlendModeClear is not supported.
  • The Source In, Destination In, Source Out, Destination Atop, and Source Copy blend modes cannot be used properly without transparency layers. Their use per-primitive will result in unusual artifacts.
  • 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:

|                                    before             after   delta
| CoreText__CTLineDrawComplete       742.51            716.11   -3.5546% faster |
| CoreText__CTLineDrawGroup         292,909           307,828    5.0934% slower |
| CoreText__CTLineDrawSingle         233.43            280.05   19.9746% slower |

times in μs.

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.

Copy link
Copy Markdown
Author

@DHowett-MSFT DHowett-MSFT left a 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.

Comment thread 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.
Copy link
Copy Markdown

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;
Copy link
Copy Markdown

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;

Copy link
Copy Markdown
Contributor

@aballway aballway left a 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 :shipit:

deviceContext->Clear({ 0, 0, 0, 0 }); // Clear the original target to transparent black.
RETURN_IF_FAILED(inputBitmap.As(&copiedImage));
}
return __super::Stage(context, deviceContext);
Copy link
Copy Markdown
Contributor

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

// 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);
Copy link
Copy Markdown
Contributor

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?

Comment thread Frameworks/CoreGraphics/CGContext.mm Outdated
// PROPERTIES
// D2D1_BLEND_PROP_MODE: blend mode
ComPtr<ID2D1Effect> blendEffect;
FAIL_FAST_IF_FAILED(deviceContext->CreateEffect(CLSID_D2D1Blend, &blendEffect));
Copy link
Copy Markdown
Contributor

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?

@DHowett-MSFT
Copy link
Copy Markdown
Author

Ping!

Copy link
Copy Markdown
Contributor

@aballway aballway left a 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

@DHowett-MSFT
Copy link
Copy Markdown
Author

Ping 2: The Pingening!

@DHowett-MSFT
Copy link
Copy Markdown
Author

Ping 3: Ping Harder

@ms-jihua @msft-Jeyaram

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,
Copy link
Copy Markdown
Contributor

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

@DHowett-MSFT
Copy link
Copy Markdown
Author

@ms-jihua thank you 💯

I'm going to diagram this specific implementation, as well as how the command list blending operations work.

@DHowett-MSFT DHowett-MSFT merged commit 13999bd into microsoft:develop Mar 15, 2017
@DHowett-MSFT DHowett-MSFT deleted the 201703-blending branch March 15, 2017 23:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CGD2D] Draw primitives with Porter-Duff blend modes

7 participants