-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Supporting different alphas for face and edge colours #1954
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
Changes from all commits
ae5c2f1
1e7399f
1aefc52
6cc8aaa
a7f7cc7
cd18645
71e9ad9
ba1f673
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -362,8 +362,6 @@ def _iter_collection(self, gc, master_transform, all_transforms, | |
gc0 = self.new_gc() | ||
gc0.copy_properties(gc) | ||
|
||
original_alpha = gc.get_alpha() | ||
|
||
if Nfacecolors == 0: | ||
rgbFace = None | ||
|
||
|
@@ -387,7 +385,6 @@ def _iter_collection(self, gc, master_transform, all_transforms, | |
yo = -(yp - yo) | ||
if not (np.isfinite(xo) and np.isfinite(yo)): | ||
continue | ||
gc0.set_alpha(original_alpha) | ||
if Nfacecolors: | ||
rgbFace = facecolors[i % Nfacecolors] | ||
if Nedgecolors: | ||
|
@@ -400,16 +397,12 @@ def _iter_collection(self, gc, master_transform, all_transforms, | |
if fg[3] == 0.0: | ||
gc0.set_linewidth(0) | ||
else: | ||
gc0.set_alpha(gc0.get_alpha() * fg[3]) | ||
gc0.set_foreground(fg[:3]) | ||
gc0.set_foreground(fg) | ||
else: | ||
gc0.set_foreground(fg) | ||
if rgbFace is not None and len(rgbFace) == 4: | ||
if rgbFace[3] == 0: | ||
rgbFace = None | ||
else: | ||
gc0.set_alpha(gc0.get_alpha() * rgbFace[3]) | ||
rgbFace = rgbFace[:3] | ||
gc0.set_antialiased(antialiaseds[i % Naa]) | ||
if Nurls: | ||
gc0.set_url(urls[i % Nurls]) | ||
|
@@ -562,7 +555,7 @@ def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath): | |
|
||
path, transform = self._get_text_path_transform( | ||
x, y, s, prop, angle, ismath) | ||
color = gc.get_rgb()[:3] | ||
color = gc.get_rgb() | ||
|
||
gc.set_linewidth(0.0) | ||
self.draw_path(gc, path, transform, rgbFace=color) | ||
1E79
|
@@ -702,7 +695,8 @@ def __init__(self): | |
self._joinstyle = 'round' | ||
self._linestyle = 'solid' | ||
self._linewidth = 1 | ||
self._rgb = (0.0, 0.0, 0.0) | ||
self._rgb = (0.0, 0.0, 0.0, 1.0) | ||
self._orig_color = (0.0, 0.0, 0.0, 1.0) | ||
self._hatch = None | ||
self._url = None | ||
self._gi F438 d = None | ||
|
@@ -711,6 +705,7 @@ def __init__(self): | |
def copy_properties(self, gc): | ||
'Copy properties from gc to self' | ||
self._alpha = gc._alpha | ||
self._forced_alpha = gc._forced_alpha | ||
self._antialiased = gc._antialiased | ||
self._capstyle = gc._capstyle | ||
self._cliprect = gc._cliprect | ||
|
@@ -720,6 +715,7 @@ def copy_properties(self, gc): | |
self._linestyle = gc._linestyle | ||
self._linewidth = gc._linewidth | ||
self._rgb = gc._rgb | ||
self._orig_color = gc._orig_color | ||
self._hatch = gc._hatch | ||
self._url = gc._url | ||
self._gid = gc._gid | ||
|
@@ -781,6 +777,13 @@ def get_dashes(self): | |
""" | ||
return self._dashes | ||
|
||
def get_forced_alpha(self): | ||
""" | ||
Return whether the value given by get_alpha() should be used to | ||
override any other alpha-channel values. | ||
""" | ||
return self._forced_alpha | ||
|
||
def get_joinstyle(self): | ||
""" | ||
Return the line join style as one of ('miter', 'round', 'bevel') | ||
|
@@ -833,14 +836,19 @@ def get_snap(self): | |
|
||
def set_alpha(self, alpha): | ||
""" | ||
Set the alpha value used for blending - not supported on | ||
all backends | ||
Set the alpha value used for blending - not supported on all backends. | ||
If ``alpha=None`` (the default), the alpha components of the | ||
foreground and fill colors will be used to set their respective | ||
transparencies (where applicable); otherwise, ``alpha`` will override | ||
them. | ||
""" | ||
if alpha is not None: | ||
self._alpha = alpha | ||
self._forced_alpha = True | ||
else: | ||
self._alpha = 1.0 | ||
self._forced_alpha = False | ||
self.set_foreground(self._orig_color) | ||
|
||
def set_antialiased(self, b): | ||
""" | ||
|
@@ -890,30 +898,28 @@ def set_dashes(self, dash_offset, dash_list): | |
""" | ||
self._dashes = dash_offset, dash_list | ||
|
||
def set_foreground(self, fg, isRGB=False): | ||
def set_foreground(self, fg, isRGBA=False): | ||
""" | ||
Set the foreground color. fg can be a MATLAB format string, a | ||
html hex color string, an rgb or rgba unit tuple, or a float between 0 | ||
and 1. In the latter case, grayscale is used. | ||
|
||
If you know fg is rgb or rgba, set ``isRGB=True`` for | ||
efficiency. | ||
If you know fg is rgba, set ``isRGBA=True`` for efficiency. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is subtle, but I think it will break the non forced alpha case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Should calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This doesn't break the non-forced alpha case because the backends themselves, where appropriate, now pull the alpha value straight out of |
||
""" | ||
if isRGB: | ||
self._orig_color = fg | ||
if self._forced_alpha: | ||
self._rgb = colors.colorConverter.to_rgba(fg, self._alpha) | ||
elif isRGBA: | ||
self._rgb = fg | ||
else: | ||
self._rgb = colors.colorConverter.to_rgba(fg) | ||
if len(self._rgb) == 4 and not self._forced_alpha: | ||
self.set_alpha(self._rgb[3]) | ||
# Use set_alpha method here so that subclasses will | ||
# be calling their own version, which may set their | ||
# own attributes. | ||
|
||
def set_graylevel(self, frac): | ||
""" | ||
Set the foreground color to be a gray level with *frac* | ||
""" | ||
self._rgb = (frac, frac, frac) | ||
self._orig_color = frac | ||
self._rgb = (frac, frac, frac, self._alpha) | ||
|
||
def set_joinstyle(self, js): | ||
""" | ||
|
@@ -942,7 +948,7 @@ def set_linestyle(self, style): | |
'dotted' : (0, (1.0, 3.0)), | ||
""" | ||
|
||
if style in self.dashd.keys(): | ||
if style in self.dashd: | ||
offset, dashes = self.dashd[style] | ||
elif isinstance(style, tuple): | ||
offset, dashes = style | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,13 +110,13 @@ def set_width_height(self, width, height): | |
# font transform? | ||
|
||
|
||
def _fill_and_stroke (self, ctx, fill_c, alpha): | ||
def _fill_and_stroke (self, ctx, fill_c, alpha, alpha_overrides): | ||
if fill_c is not None: | ||
ctx.save() | ||
if len(fill_c) == 3: | ||
if len(fill_c) == 3 or alpha_overrides: | ||
ctx.set_source_rgba (fill_c[0], fill_c[1], fill_c[2], alpha) | ||
else: | ||
ctx.set_source_rgba (fill_c[0], fill_c[1], fill_c[2], alpha*fill_c[3]) | ||
ctx.set_source_rgba (fill_c[0], fill_c[1], fill_c[2], fill_c[3]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ouch. We don't have cairo tests to check this either (we need to think about testing the cairo backend more). Nice spot. |
||
ctx.fill_preserve() | ||
ctx.restore() | ||
ctx.stroke() | ||
|
@@ -150,7 +150,7 @@ def draw_path(self, gc, path, transform, rgbFace=None): | |
ctx.new_path() | ||
self.convert_path(ctx, path, transform) | ||
|
||
self._fill_and_stroke(ctx, rgbFace, gc.get_alpha()) | ||
self._fill_and_stroke(ctx, rgbFace, gc.get_alpha(), gc.get_forced_alpha()) | ||
|
||
def draw_image(self, gc, x, y, im): | ||
# bbox - not currently used | ||
|
@@ -316,7 +316,10 @@ def set_alpha(self, alpha): | |
GraphicsContextBase.set_alpha(self, alpha) | ||
_alpha = self.get_alpha() | ||
rgb = self._rgb | ||
self.ctx.set_source_rgba (rgb[0], rgb[1], rgb[2], _alpha) | ||
if self.get_forced_alpha(): | ||
self.ctx.set_source_rgba (rgb[0], rgb[1], rgb[2], _alpha) | ||
else: | ||
self.ctx.set_source_rgba (rgb[0], rgb[1], rgb[2], rgb[3]) | ||
|
||
|
||
#def set_antialiased(self, b): | ||
|
@@ -359,8 +362,8 @@ def set_dashes(self, offset, dashes): | |
self.renderer.points_to_pixels (np.asarray(dashes)), offset) | ||
|
||
|
||
def set_foreground(self, fg, isRGB=None): | ||
GraphicsContextBase.set_foreground(self, fg, isRGB) | ||
def set_foreground(self, fg, isRGBA=None): | ||
GraphicsContextBase.set_foreground(self, fg, isRGBA) | ||
if len(self._rgb) == 3: | ||
self.ctx.set_source_rgb(*self._rgb) | ||
else: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1064,7 +1064,7 @@ def alphaState(self, alpha): | |
self.nextAlphaState += 1 | ||
self.alphaStates[alpha] = \ | ||
(name, { 'Type': Name('ExtGState'), | ||
'CA': alpha, 'ca': alpha }) | ||
'CA': alpha[0], 'ca': alpha[1] }) | ||
return name | ||
|
||
def hatchPattern(self, hatch_style): | ||
|
@@ -1443,11 +1443,21 @@ def check_gc(self, gc, fillcolor=None): | |
orig_fill = gc._fillcolor | ||
gc._fillcolor = fillcolor | ||
|
||
orig_alphas = gc._effective_alphas | ||
|
||
if gc._forced_alpha: | ||
gc._effective_alphas = (gc._alpha, gc._alpha) | ||
elif fillcolor is None or len(fillcolor) < 4: | ||
gc._effective_alphas = (gc._rgb[3], 1.0) | ||
else: | ||
gc._effective_alphas = (gc._rgb[3], fillcolor[3]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your effective alphas approach here is, IMHO, something that could be done in the GC object. The GC could then just draw an RGBA of fill and an RGBA of line color _and the backend wouldn't need to know anything about forced_alpha. Do you know of a reason why that wouldn't be a viable approach? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess what I'm suggesting is that we updated the necessary signatures (to draw_path for example) such that we pass through an RGBA rather than an RGB and expect the backend to figure out the final face RGBA color. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That actually is how The issue is there are instances where the GC is applying an alpha property to things that have neither fill or draw colours -- e.g., images -- which is done by setting an alpha on the backend's underlying context. (The Mac OS X backend comes to mind as an example.) Supporting that means:
I believe for |
||
|
||
delta = self.gc.delta(gc) | ||
if delta: self.file.output(*delta) | ||
|
||
# Restore gc to avoid unwanted side effects | ||
gc._fillcolor = orig_fill | ||
gc._effective_alphas = orig_alphas | ||
|
||
def tex_font_mapping(self, texfont): | ||
if self.tex_font_map is None: | ||
|
@@ -2004,6 +2014,7 @@ class GraphicsContextPdf(GraphicsContextBase): | |
def __init__(self, file): | ||
GraphicsContextBase.__init__(self) | ||
self._fillcolor = (0.0, 0.0, 0.0) | ||
self._effective_alphas = (1.0, 1.0) | ||
self.file = file | ||
self.parent = None | ||
|
||
|
@@ -2072,8 +2083,8 @@ def dash_cmd(self, dashes): | |
offset = 0 | ||
return [list(dash), offset, Op.setdash] | ||
|
||
def alpha_cmd(self, alpha): | ||
name = self.file.alphaState(alpha) | ||
def alpha_cmd(self, alpha, forced, effective_alphas): | ||
name = self.file.alphaState(effective_alphas) | ||
return [name, Op.setgstate] | ||
|
||
def hatch_cmd(self, hatch): | ||
|
@@ -2138,7 +2149,7 @@ def clip_cmd(self, cliprect, clippath): | |
|
||
commands = ( | ||
(('_cliprect', '_clippath'), clip_cmd), # must come first since may pop | ||
(('_alpha',), alpha_cmd), | ||
(('_alpha', '_forced_alpha', '_effective_alphas'), alpha_cmd), | ||
(('_capstyle',), capstyle_cmd), | ||
(('_fillcolor',), fillcolor_cmd), | ||
(('_joinstyle',), joinstyle_cmd), | ||
|
@@ -2183,6 +2194,7 @@ def copy_properties(self, other): | |
""" | ||
GraphicsContextBase.copy_properties(self, other) | ||
self._fillcolor = other._fillcolor | ||
self._effective_alphas = other._effective_alphas | ||
|
||
def finalize(self): | ||
""" | ||
|
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.
This is SVG only, right?