-1

I am trying to draw overlapping transparent circles to a 2d numpy array:

img = np.zeros((256, 256), dtype=np.uint8)
surface = cairo.ImageSurface.create_for_data(
    img, cairo.FORMAT_A8, 256, 256
)
ctx = cairo.Context(surface)

ctx.arc(128, 128, 22, 0, 2 * math.pi)
ctx.set_source_rgba(1, 1, 1, 1)
ctx.fill()

ctx.arc(128, 102, 22, 0, 2 * math.pi)
ctx.set_source_rgba(0, 0, 0, 0.5)
ctx.fill()

The output should look like this: enter image description here

Instead, it looks like this:

enter image description here .

Why is this happening? All I want is to draw transparent grayscale circles overlayed on top of each other, but it doesn't seem to detect color, and it only uses alpha value.

Ahmed Khalf
  • 136
  • 1
  • 9
  • What happens when you use cairo.FORMAT_ARGB32 instead of cairo.FORMAT_A8 when creating the surface? – hetepeperfan May 23 '20 at 09:12
  • 1
    Also ctx.set_source_rgba() uses all floats in the range [0, 1], not 256. – hetepeperfan May 23 '20 at 09:15
  • @hetepeperfan Oops I meant 1, 1, 1. Edited the question. Also if I put argb32 and dtype = uint32. I get 2 white circles that arent transparent and the other circle isn't even black! https://imgur.com/a/Mwz8lwo – Ahmed Khalf May 23 '20 at 09:27
  • After further inspection, the black circle seems to have value 2.15e+0.9. And the white circle value 4.29e+09. The white circle seems to be drawn on top. As there is no overlapping values. – Ahmed Khalf May 23 '20 at 09:35

1 Answers1

0
img = np.zeros((256, 256), dtype=np.uint8)
surface = cairo.ImageSurface.create_for_data(
    img, cairo.FORMAT_A8, 256, 256
)
ctx = cairo.Context(surface)

At this point, the surface is completely transparent, which is saved as a completely black PNG.

ctx.arc(128, 128, 22, 0, 2 * math.pi)
ctx.set_source_rgba(1, 1, 1, 1)
ctx.fill()

Now you fill a circle with "completely opaque" (the color component is ignored). This ends up as a white circle

ctx.arc(128, 102, 22, 0, 2 * math.pi)
ctx.set_source_rgba(0, 0, 0, 0.5)
ctx.fill()

Here you draw a second circle with "semi transparent". This ends up as black. The part where the other circle was already drawn is "semi transparent" over "completely opaque", which ends up as "completely opaque", so white when saved as a PNG.

The output should look like this:

So, you do not want the second circle to be drawn outside of the first circle. For this, you need ctx.clip().

Here is some Lua code...

local cairo = require("lgi").cairo
local surface = cairo.ImageSurface.create(cairo.Format.A8, 256, 256)
local ctx = cairo.Context(surface)

ctx:arc(128, 128, 22, 0, 2 * math.pi)
ctx:set_source_rgba(1, 1, 1, 1)
-- Fill the circle, but do not clear the current path
ctx:fill_preserve()
-- Use the circle as a clip
ctx:clip()

ctx:arc(128, 102, 22, 0, 2 * math.pi)
ctx:set_source_rgba(0, 0, 0, 0.5)
-- Replace the default operator OVER with SOURCE:
-- Instead of drawing "ontop", just copy the source to the target
ctx:set_operator(cairo.Operator.SOURCE)
ctx:fill()

surface:write_to_png("out.png")

...that produces this output (I used the same coordinates that your code uses, no idea how you end up output images with X/Y flipped):

output of the program

Uli Schlachter
  • 9,337
  • 1
  • 23
  • 39
  • Thank you, but how would one handle x amount of circles drawn this way? This way seems to only work with 2 circles. – Ahmed Khalf May 24 '20 at 08:35
  • Well... what is the result that you want for your `x` circles? I am not quite sure what you are aiming for nor do I understand why you would expect your code to "just work". Why would drawing the second circle "do nothing" outside of the first circle? – Uli Schlachter May 24 '20 at 15:08
  • That's because I want to draw with both grayscale color and transparency. A semi-opaque black circle on a black background should not be visible but on a white circle it should be visible. I want to overlap many transparent circles on top of each other, and all those transparent circles should have color, like black white or gray. – Ahmed Khalf May 24 '20 at 18:23
  • An `A8` surface only has an alpha channel. Colors make no difference for the drawing there. If you want color and transparency, you need an `ARGB32` surface. The black/gray/white that you see is just a representation of the alpha channel as something that can be saved to a file. – Uli Schlachter May 25 '20 at 12:13