-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fully save all GraphicsContext variables for MacOS backend #4202
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
Conversation
…GraphicsContextMac class
attn @mdehoon |
This seems like overkill to me. At what point exactly does the graphics context get out of sync? |
The graphics context gets out of sync due to the call to gc.get_rgb() in _draw_text_as_path in PathEffectRenderer. |
This bug also appears with the gtkcairo backend. |
I counted the usage of gc.get_rgb() in matplotlib overall. Other than in the function _draw_text_as_path in PathEffectRenderer, I only found it in backend-specific code. In general, calling gc.get_rgb() is not safe; depending on the backend it may or may not work. |
@mdehoon Why does |
@tacaswell Because it simply returns self._rgb, self being GraphicsContextBase, and self._rgb is not saved on the stack. |
Could the stack based context managers be modified to over ride |
Yes of course, but that would be a huge mistake. |
Why would that be a mistake? |
Because it is an unnecessary complication of the code. As I wrote above, get_rgb is barely used anywhere in matplotlib, except in backend-specific code, which means that matplotlib can work perfectly fine without get_rgb. |
It is not clear to me why #4532, which seems to be working around the fact that How about over-riding If [edited to fix horrible typo] |
I assume you mean get_rgb? |
Yes, sorry just noticed that mistake. |
No no no, that would suggest that backends are supposed to support get_rgb. Just remove get_rgb from the matplotlib codebase altogether (except of course in backend-specific code if it is needed). |
According to git-blame On Wed, Jun 17, 2015 at 10:10 PM mdehoon notifications@github.com wrote:
|
Just move get_rgb to the backends that need it, and let other backends implement set_foreground in the way that is most appropriate for them. Since get_rgb is used in backend-specific code only, this is safe. Also user code should not be using get_rgb (though deprecating first before removing it is of course safer). As a related point, we should also consider to let set_foreground accept rgb and rgba only, and not generic matplotlib color values. Most of matplotlib is already calling set_foreground with rgb or rgba, and I don't see a reason why set_foreground should allow for generic color values. |
Converting all of the internal color storage RBGA tuples is a long term goal, but that will be a huge job. After thinking about this for a bit, I greatly prefer this PR over #4532 as it address what I see as the real bug; that If we want to have a discussion about changing the so 👍 on merging this from me, but I would like some more eyes on this. |
I agree with @tacaswell that this PR makes sense given the long-established GraphicsContextBase API; I don't see any downside to it other than a little bit of overhead, regardless of any later changes to low-level color handling and general backend APIs. |
Sorry guys, but this PR is unacceptable to me. |
@mdehoon We have to come to a better understanding of how to proceed, recognizing that it might take several steps. I don't yet understand your strong objection to the present PR as a first step. In particular, you have not answered @tacaswell's question above: "If get_rgb is broken how are things like https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_macosx.py#L203 working?" Doesn't the present PR fix a problem that could otherwise occur with that line? In other words, isn't the macosx backend relying on the behavior that this PR will produce, which is consistent with the long-standing API? |
@efiring Sure. All my scientific work is done with matplotlib (with the MacOSX backend on Mac and with other backends on Linux), so I really hope to arrive at a solution that is best for matplotlib in the long run. gc1.set_foreground('red')
gc2 = renderer.new_gc()
gc2.set_foreground('blue')
gc1.set_alpha(0.5) # gc1 will think it's blue I am not aware of any cases where this kind of pattern appears in matplotlib, so this doesn't manifest itself as a bug. So it works in practice, but I agree that fundamentally it is a bug, which I would like to see fixed.
Yes. However, matplotlib suffers from API bloat and is in places unnecessarily complex. Accepting this PR would further add to the complexity of the code. And the more complexity we add, the harder it will get to disentangle it later. Let's reduce some of the complexity of matplotlib first, making it more reliable, less prone to bugs, faster, and more maintainable. |
@mdehoon Thank you for the explanation. I think everyone agrees with you that "matplotlib suffers from API bloat and is in places unnecessarily complex", so the question is just about how to improve the situation. |
@efiring I am OK with abandoning PR #4532 . I can explain in more detail why the current PR is unacceptable, but I think it is easier to see after we deal with gc.set_foreground. I don't insist that this should happen at the next point release, but if the long-term goal is to convert to rgba tuples for internal color storage, then we will have to deal with it at some point. |
@tacaswell No there isn't. But I wouldn't worry too much about it. This is not a major bug and few matplotlib users will run into it. |
To get started, how exactly is graphics context . set_foreground defined? |
@mdehoon, mpl evolves gradually, with a long history of trying to minimize breakage of user code. The handling of transparency has been a difficult part of that transition, as the importance and usage of transparency have increased over mpl's lifetime. I think JDH started mpl largely based on the gtk drawing model as it was a dozen years ago. The backend code has to do its best to match the mid-level Artists to the actual drawing models of the various backends, including ps, which doesn't support alpha at all. A number of years ago we were running into big problems with alpha, and the sort of ambiguity you refer to. The partial solution was to keep track, in the GC, of whether the overall alpha should override the A in an RGBA value. That's what Regarding your first question: it turns out that "foreground" is just the color for whatever is being drawn with the gc. Yes, it turns out that it might be better to have separate color attributes for fill and for stroke, but that's not the way this thing evolved. Until a major overhaul is done, we have to get along as well as possible with what we have. So the situation with respect to the present PR is that a bug has turned up, specific to the macosx backend, and specifically because that backend does not adhere to the long-established (if imperfect) model in |
Guys, let me try and explain more detail why I don’t want to accept this patch:
The options now are to either (A) update the MacOSX backend to support get_rgb and to comply with the long-standing API, or (B) remove get_rgb from the API because there is no need for it. The proposed patch ensures that get_rgb returns the correct value. However, note that the MacOSX backend does not use get_rgb for drawing, but relies on gc.set_foreground only. So this does not provide a general solution; there will be cases where the tkagg and the MacOSX backend will behave differently even after accepting this patch. While appreciating @cimarronm ’s efforts, this patch implements an API that shouldn’t exist in the first place. Accepting this patch now will mean having to remove it later. For this particular bug, the solution is simple: Don't use get_rgb. No other part of matplotlib needs it, so I presume that we can implement PathEffectRenderer without using get_rgb. My patch was perhaps a bit radical; there are alternative solutions to solve this bug that are less invasive. |
@mdehoon, thank you for the very clear and explicit explanation. |
While testing this with the #4609 problem, I encountered an exception. I was not able to reproduce it on master, so it looks like it is triggered by this PR, after closing the test figure window based on #4609 and manipulating another figure. Unfortunately I didn't capture the traceback and I don't have time now to regenerate it. I think it was related to a reference to a non-existent GC from the original plot, though. |
Punted as this is not the right solution. @mdehoon For this use case I think the It might also be worth deleting the default attributes on the gc. |
Thanks for working this out @cimarronm. Given all the other issues with the macosx backend, I think we're leaning toward just using the Agg renderer which would fix this and a host of other things. Obsoleted by #6178, which uses Agg for rendering the OSX backend and thus works around the bug in a different way. (Fixed by #6178). |
Modifies the save/restore graphics context of the MacOS backend to fully save all GraphicsContext variables to a stack for the
GraphicsContextMac
class. Currently, the graphics context is saved to the stack in the underlying_macosx.so
backend but the variables associated with the context from theGraphicsContextBase
are not saved/restored so the correspondingGraphicsContext
class is not in sync with the actual graphic context. This saves these variables to a stack so they are in sync.This fixes the issue seen in #4024 (PR #4201 must also be applied to fully fix #4024). Below is the example from issue #4024 before and after.
Before
After