Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cairo backend: Fix alpha render of collections #12774

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

Merged
merged 1 commit into from
Nov 28, 2018

Conversation

joh
Copy link
Contributor

@joh joh commented Nov 8, 2018

Fix for #12773

PR Summary

RendererCairo.draw_path_collection() had two issues related to alpha channels.

First, the call to gc.set_alpha in line 310 would also inadvertently set gc._forced_alpha = True. This is fixed by moving the line which updates the gc from gc_vars below the loop.

Second, the code has an optimization where artists with identical properties would be grouped and then rendered in one go. This strategy doesn't work if the artists overlap and have alpha value < 1.0 in which case alpha blending is necessary. So the second fix disables the grouping optimization if any of the artists have alpha < 1.0 or the whole collection has alpha < 1.0.

PR Checklist

  • Has Pytest style unit tests
    Not yet, but I'll try to write one if necessary.
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@tacaswell tacaswell added this to the v3.1 milestone Nov 8, 2018
@joh
Copy link
Contributor Author

joh commented Nov 8, 2018

I tried adapting test_patches.py::test_patch_alpha_coloring to use the cairo backend, and the result reveals there are more issues with RendererCairo.draw_path_collection. With my fix for alpha above, this is the result:

patch_alpha_coloring

So the alpha channel is correct, but the line style is wrong. The result looks correct if I remove RendererCairo.draw_path_collection() altogether to fall back on RendererBase.draw_path_collection().

@anntzer
Copy link
Contributor

anntzer commented Nov 8, 2018

The proposed fix looks reasonable.
Not sure what's happening with linestyles but I guess you can also exclude grouping in presence of them?

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

Had a look at the linestyles issue, and it seems to be unrelated to the grouped draw. It's incorrect also if I disable grouped draw, but correct if I remove RendererCairo.draw_path_collection() altogether.

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

No progress on the linestyle issue, maybe @anntzer can help?

@anntzer
Copy link
Contributor

anntzer commented Nov 9, 2018

Looks like

diff --git i/lib/matplotlib/backends/backend_cairo.py w/lib/matplotlib/backends/backend_cairo.py
index ce68d198f..3afd5be32 100644
--- i/lib/matplotlib/backends/backend_cairo.py
+++ w/lib/matplotlib/backends/backend_cairo.py
@@ -307,10 +307,13 @@ class RendererCairo(RendererBase):
             for k, v in gc_vars.items():
                 if k == "_linestyle":  # Deprecated, no effect.
                     continue
-                try:
-                    getattr(gc, "set" + k)(v)
-                except (AttributeError, TypeError) as e:
-                    pass
+                elif k == "_dashes":
+                    gc.set_dashes(*v)  # Needs to be unpacked.
+                else:
+                    try:
+                        getattr(gc, "set" + k)(v)
+                    except (AttributeError, TypeError) as e:
+                        pass
             gc.ctx.new_path()
             paths, transforms = zip(*grouped_draw)
             grouped_draw.clear()
@@ -326,7 +329,8 @@ class RendererCairo(RendererBase):
             transform = (Affine2D(transform.get_matrix())
                          .translate(xo, yo - self.height).scale(1, -1))
             # rgb_fc could be a ndarray, for which equality is elementwise.
-            new_key = vars(gc0), tuple(rgb_fc) if rgb_fc is not None else None
+            new_key = (vars(gc0).copy(), tuple(rgb_fc) if rgb_fc is not None
+                       else None)
             if new_key == reuse_key:
                 grouped_draw.append((path, transform))
             else:

should fix the dashes issue. Can you confirm?

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

Great, can confirm that @anntzer's patch indeed fixes the issue. @anntzer will you commit your fix to master first, then I can rebase my alpha fix on top of it.

@anntzer
Copy link
Contributor

anntzer commented Nov 9, 2018

Can you just integrate it in your pr?

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

Sure, I can do that

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

Do you want me to add the test as well? Currently there seems to be no tests for the cairo backend...

@anntzer
Copy link
Contributor

anntzer commented Nov 9, 2018

There's also no machinery for that, but sure, if you can figure it out how to make it work, go for it!
I'd suggest using some adaptation of the check_figures_equal decorator (plotting the same thing two ways using the cairo backend, e.g. here drawing with a PathCollection or just two separate patches), to avoid adding more baseline images to the repo (as you likely won't be able to share the agg baselines due to differences in rasterization).

@joh
Copy link
Contributor Author

joh commented Nov 9, 2018

Sure, a drawback of using check_figures_equal is of course that we assume the reference figure is correct.

@timhoffm
Copy link
Member

timhoffm commented Nov 9, 2018

You could e.g. compare an ellipse collection with two single ellipses. While in theory both may be wrong. On the one hand, it's reasonable to check that the two code paths yield the same result. Also it's quite unlikely that a future code change with simultaneously change both. So the test would actually be quite good.

@joh
Copy link
Contributor Author

joh commented Nov 14, 2018

So I've added the test, which revealed yet another bug with the grouped draw (set_foreground() wasn't called for _rgb). After fixing this, the test passes, but only because it uses alpha blending and disables the grouped draw.

Now if I modify the test and sett fill color alpha to 1.0, thus enabling grouped drawing, the draw order is messed up somehow:

test_patch_alpha_coloring

edgecolor=(0, 0, 1, 0.75))
ax.add_collection(col)

plt.savefig('patch_alpha_coloring_test.png')
Copy link
Contributor

@anntzer anntzer Nov 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to save the image in the test, you can check them using tools/triage_tests.py.

@anntzer
Copy link
Contributor

anntzer commented Nov 15, 2018

Looking at this again I'm not surprised by the failure: this is because the pathcollection drawer concatenates all paths and fills them in one go, then draw the edges in one go (so the entire blue edge is drawn after the entire red fill). I overlooked the overlapping-patch problem in #8787, sorry about that.
Unfortunately I don't think there's a really good fix other than deleting draw_path_collection and going back to the slow, one-at-a-time approach.
Remilestoning to 3.0.x as this fixes a regression.

@anntzer anntzer modified the milestones: v3.1, v3.0.3 Nov 15, 2018
@joh
Copy link
Contributor Author

joh commented Nov 15, 2018

Ah ok, should I change the patch then to remove draw_path_collection?

@anntzer
Copy link
Contributor

anntzer commented Nov 15, 2018

At least I can't think of anything better...

@anntzer
Copy link
Contributor

anntzer commented Nov 19, 2018

The style checker is unhappy: https://travis-ci.org/matplotlib/matplotlib/jobs/456934939#L2255.

The OSX test failure should be covered by #12835.

Remove RendererCairo.draw_path_collection as it made some wrong
assumptions in the grouped draw optimization.

Add test for the cairo backend to check rendering of path collections.

Fixes matplotlib#12773
@timhoffm timhoffm merged commit 8272e7b into matplotlib:master Nov 28, 2018
@timhoffm
Copy link
Member

Thanks and congratulations to your first contribution to matplotlib! Hope to see you back some time.

@lumberbot-app
Copy link

lumberbot-app bot commented Nov 28, 2018

Owee, I'm MrMeeseeks, Look at me.

There seem to be a conflict, please backport manually. Here are approximate instructions:

  1. Checkout backport branch and update it.
$ git checkout v3.0.x
$ git pull
  1. Cherry pick the first parent branch of the this PR on top of the older branch:
$ git cherry-pick -m1 8272e7b7870d3c85824dbbbde499ff385d268790
  1. You will likely have some merge/cherry-pick conflict here, fix them and commit:
$ git commit -am 'Backport PR #12774: Cairo backend: Fix alpha render of collections'
  1. Push to a named branch :
git push YOURFORK v3.0.x:auto-backport-of-pr-12774-on-v3.0.x
  1. Create a PR against branch v3.0.x, I would have named this PR:

"Backport PR #12774 on branch v3.0.x"

And apply the correct labels and milestones.

Congratulation you did some good work ! Hopefully your backport PR will be tested by the continuous integration and merged soon!

If these instruction are inaccurate, feel free to suggest an improvement.

timhoffm pushed a commit to timhoffm/matplotlib that referenced this pull request Nov 29, 2018
jklymak added a commit that referenced this pull request Nov 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants