-
Notifications
You must be signed in to change notification settings - Fork 48
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
Brush/Pen transparency weirdness #16
Comments
Are you outputting PNG images and that's what you are looking at? Are you showing the image object inside a GUI? What GUI framework? Which version of python? Given that the python 3 support is much less tested than the python 2 version of the library I'd be curious if switching from 3 to 2 changes your results. Also what OS? |
Thanks for the quick response! This is running on Python 2.7 on macOS (10.11), and is being rendered to a PyOpenGL context that's created with PySDL2. It's a runtime environment for cognitive psychology experiments, and all shape drawing is done with aggdraw as a back-end (my pictures are just screenshots). The relevant drawing code is a bit complicated since there's a "shape object" class and a bunch of subclasses with their own drawing methods, but here's the simplified version: # create drawing context
self.canvas = Image.new("RGBA", self.dimensions, (0, 0, 0, 0))
self.surface = Draw(self.canvas)
self.surface.setantialias(True)
# create fill
if len(color)==3: # fill colour is given in in object init
color += [255]
self.fill = Brush(tuple(color[:3]), color[3])
# draw shape
self.surface.ellipse([x1, y1, x2, y2], self.stroke, self.fill) # x1, y1, etc. are generated based on given shape dimensions
self.surface.flush() # flush aggdraw drawings to Pillow Image context
self.rendered = numpy.asarray(self.canvas) # render Pillow image to numpy array for PyOpenGL I'll have to check and see if it still behaves the same if I'm drawing directly to an aggdraw-created context without the Pillow "canvas". For the time being, a workaround that half-fixes it is to create the draw context image with the fill colour of the shape I'm drawing (e.g. |
Is there a reason you have your background fill with an alpha of 0? I get similar results to you in my own tests, but if I set the alpha of the Image to |
Here is my example code:
I've swapped the image fill from black to white and 0 to 255 alpha and I'm kind of wondering if the color of a transparent shape over a transparent background is a little undefined. |
Or what if you use |
Also FYI I get the same results on python 2 and 3 with the version of aggdraw on master (no current changes from last PyPI release). |
I get this weird affect without using PIL directly and just using aggdraw's |
The background fill has an alpha of 0 because the generated images are being used as textures by OpenGL, which handles transparency itself (i.e. all shapes and text in my screenshots above are rendered as textures and are then drawn to the display buffer). In order for overlapping textures to be able to blend, they need to be RGBA. Using a background with full opacity results in everything having a square of the background fill colour around it: If I use Maybe AGG isn't set up to handle transparency properly when drawing to a surface with an alpha channel, so it just uses the same logic as if it's drawing to one with full opacity (i.e blending the brush colour with the background colour)? On a surface with 0 opacity I'd expect it to draw with the unaltered given Brush/Pen colour but with the opacity given (with results like I got with my hacky numpy workaround), but I'm not sure how the logic would work for something being drawn on a surface with, say, half-transparency. |
There's one fork of aggdraw with the backend updated to AGG 2.4 I've come across, I'll test it out and see if it works. Unfortunately it also has different line join properties (rounded corners by default) which make it not really work as a drop-in replacement for existing projects. EDIT: Nope, using the 2.4-based aggdraw doesn't fix it either. |
@a-hurst This should hopefully be fixed in the current master branch and v1.3.1 release I just made. If you get a chance let me know how it goes. |
@a-hurst I just noticed this is still open. Have you tested this lately? Does this work? |
@a-hurst sorry, I realize it's been a while since you provided a potential fix and I never tested it. In my own code I used the workaround I mentioned above of setting the canvas RGB values to the RGB values of for the shape drawn on it to make everything look right, so since it was working fine that way I'd forgotten about this. I just tested this again with 1.3.8, and unfortunately it looks like the bug is still present. Here's a screenshot of a paradigm where the Brush for the black shapes is set to 25% transparency ([0, 0, 0, 64]). Here's what it looks like when the Image.new canvas is created with a background colour of (0, 0, 0, 0): Here's the exact same thing, except the Image.new canvas is created with a background colour of (255, 255, 255, 0): I should really come up with a minimum reproducible example for this, but the above at least illustrates the issue. |
Ok thanks. I reread this whole issue and now re-remember what is going on. This is very likely a bug in agg underneath. Even worse is that you said the agg 2.4 fork of this repository didn't fix your issue. That is really our only solution right now. Maybe if we can get this down to some low-level agg calls we can figure this out. Note: I want to do something similar to this with our satpy library where we draw a ton of lines on an image with a transparent background and then burn the lines on to another image later (or cache the lines on disk). I'm not sure we need transparent lines so we may not run in to the same issue. |
While trying to dig up info on the Brush size regression w/ the move to 2.4 (i.e. the larger asterisk in #61), I accidentally stumbled upon this, which I think finally explains this bug: https://sourceforge.net/p/agg/discussion/118993/thread/859d8954/ Basically, the expected type of alpha handling here only works if the destination layer format is pixfmt_rgba32_pre, whereas aggdraw's just using regular pixfmt_rgba32 for RGBA shapes. I'll have to look at the docs a bit more to understand how it differs (and whether it would be easy to substitute for pixfmt_rgba32 in aggdraw's case), but at least now we know why this is happening! |
Wow! Great find. Any idea what has to happen in the code to make this work? Side note: What are we going to do about the website being gone? |
For various reasons, I've been using a pretty hacky method for applying transparency to aggdraw-drawn textures to surfaces in the Python package I maintain. This consisted of slicing off the 'A' value of any RGBA colours, creating a Brush and/or Pen with full opacity, and then applying the transparency value manually to the 'A' value for each pixel in the image using numpy. Last week, however, I figured out a way to fix the underlying problem that necessitated this workaround and decided to remove it and start using the regular Brush/Pen opacity values.
Much to my surprise, however, this produced dramatically different (and wrong) results. See the following two images:
In both images, the semi-transparent circle over the '6' is supposed to have a fill value of (192, 192, 192, 128). The first is done with the old numpy transparency hack, the second with an aggdraw Brush. As you can see, the aggdraw one is considerably darker than it should be.
Curious, I tried playing around with the Pillow image context aggdraw is drawing to, and sure enough, the fill colour of the fully transparent background affects the resulting colour of the aggdraw-drawn circle. For example, when setting opacity with aggdraw, drawing on a background with (255, 255, 255, 0) fill results in an actual fill (printing out the the pixel values to terminal) of (223, 223, 223, 127), whereas drawing on a background of (0, 0, 0, 0) fill results in an actual fill of (95, 95, 95, 127). When using the Numpy hack, it results in a background of (192, 192, 192, 128) regardless of what colour the transparent background fill has.
I'm guessing what's happening here is that aggdraw is trying to make up the difference between the background colour and the Brush colour as if it were an RGB surface without alpha, resulting in double-opacity. Is this something that's a quick fix in aggdraw, or is it a problem with the underling AGG library itself? I'd try my hand at a fix, but I have a lot on my plate programming-wise right now and I'm not too experienced in C.
Thanks in advance!
The text was updated successfully, but these errors were encountered: