This using aggdraw with PIL Image.

So I am trying to generate some shapes using bezier curves. but the issue is removing inner parts of drawing. If I use an inner and outer path it just fills both, if I separate them and use a transparent brush for the inner it doesn't do anything.

Say if you want to paint a donut, how do you remove the hole?

It seems impossible that there is no way to erase pixel with aggdraw, so.... maybe I missing something.

Question one: Can you paint with a transparent brush where by the pixels are turned to transparent?

Question two: How do you erase pixels, polygons, and paths?. Sure I can use a mask but I have to create the mask, so back to the same problem.

Lastly, there are some images a my gist, the link below. One is using the code generated path data and then painting with gimp, that is the goal rendering. The other is the actually generated image from my code.

gist example: [https://gist.github.com/ismaelharunid/49df974a59ad98be6934deb8d38154ce

from PIL import Image
import aggdraw
from math import sqrt, cos, sin, pi
lerp = lambda a, b=0.0, t=0.5: a + (b-a) * t
circle4k = 0.55191502449

radius_circle_bpd = lambda radius: \
    ( 0.,               radius
    , circle4k*radius,  radius
    , radius,           circle4k*radius
    , radius,           0.
    , radius,           -circle4k*radius
    , circle4k*radius,  -radius
    , 0.,               -radius
    , -circle4k*radius, -radius
    , -radius,          -circle4k*radius
    , -radius,          0.
    , -radius,          circle4k*radius
    , -circle4k*radius, radius
    , 0.,               radius )

rectangle_pd = lambda x0,y0, x1,y1: (x0,y0, x1,y0, x1,y1, x0,y1, x0,y0)

pd_translate = lambda pd, offset, nc=2: tuple( pd[ir+ic] + offset[ic] \
    for ir in range(0, len(pd), nc) for ic in range(nc) )

pd_scale = lambda pd, delta, nc=2: tuple( pd[ir+ic] * delta[ic] \
    for ir in range(0, len(pd), nc) for ic in range(nc) )

pd_transform = lambda pd, m, nc=2: \
    tuple(sum(pd[ir+i]*m[ic+i*nc] for i in range(nc)) \
    for ir in range(0, len(pd), nc) for ic in range(nc))

new_draw = lambda size: aggdraw.Draw(Image.new("RGBA", size))
def bpd_add_path(*bpds, path=None):
  if path is None: path = aggdraw.Path()
  for bpd in bpds:
    path.moveto(*bpd[:2])
    for i in range(2, len(bpd), 6):
      path.curveto(*bpd[i:i+6])
  return path


def saturn_im(size=(540, 540), angle=pi/10, circle_radius=None
    , ring_inner_radius=None, ring_outer_radius=None
    , n_rings=3, ring_ratio=0.85, ring_aspect=0.5
    , circle_color='#bc75ff', rings_color='#bcff75'
    , outline_color='#ccbd14', outline_weight=4.5):

  offset = (.5 * size[0], .5 * size[1])
  if circle_radius is None:
    circle_radius = min(offset) / 3.

  if ring_inner_radius is None:
    ring_inner_radius = circle_radius / .80

  if ring_outer_radius is None:
    ring_outer_radius = offset[0]

  ca, sa = cos(angle), sin(angle)
  matrix = (ca, sa,-sa, ca)
  pdmask = \
      pd_translate( \
        pd_transform( \
          rectangle_pd(-offset[0],-offset[1], offset[0],offset[1]) \
        , matrix )
      , offset )
  bpdcircle = \
      pd_translate( \
        pd_transform( \
          radius_circle_bpd(circle_radius) \
        , matrix )
      , offset )

  r0, r1 = ring_ratio**5, ring_ratio**0
  rspan, sspan = r1 - r0, ring_outer_radius - ring_inner_radius
  rlerp = lambda i: ring_inner_radius+sspan*(ring_ratio**i-r0) / rspan
  bpdrings = tuple( \
      pd_translate( \
        pd_transform( \
          pd_scale( \
            radius_circle_bpd(rlerp(i_rings)) \
          , (1.0, ring_aspect) )
        , matrix )
      , offset ) for i_rings in range(n_rings * 2))
  for i in range(6):
    print(i, min(bpdrings[i]),max(bpdrings[i]))
  print("Paths created")

  penstroke = aggdraw.Pen(outline_color, outline_weight)
  brushcircle = aggdraw.Brush(circle_color)
  brushrings = aggdraw.Brush(rings_color)
  brusheraser = aggdraw.Brush((0,0,0,0))

  print("Resources created")

  imcircle = new_draw(size)
  imcircle.path(bpd_add_path(bpdcircle), penstroke, brushcircle)
  imcircle = imcircle.flush()

  print("Circle created")

  imrings = new_draw(size)
  #imrings.path(bpd_add_path(*bpdrings), penstroke, brushrings)
  #imrings.path(bpd_add_path(bpdrings[0]), penstroke, brushrings)
  #imrings.path(bpd_add_path(bpdrings[1]), penstroke, brusheraser)
  imrings.path(bpd_add_path(bpdrings[2]), penstroke, brushrings)
  imrings.path(bpd_add_path(bpdrings[3]), penstroke, brusheraser)
  #imrings.path(bpd_add_path(bpdrings[4]), penstroke, brushrings)
  #imrings.path(bpd_add_path(bpdrings[5]), penstroke, brusheraser)
  imrings = imrings.flush()

  print("Rings created")

  im = Image.new("RGBA", size, '#000000')

  im.paste(imrings, mask=imrings)
  im.paste(imcircle, mask=imcircle)

  print("Doughnut created")

  return im

Ok, so my question still stands but I found a work around that is probably how most people do it. Using a mask and yeah duh I can paint in L mode (greyscale) and then it's east to paint out or in what every you want do mask in or out. Then you have to apply the mask. A bit tedious but it gets the job done. The amended code that works...

from PIL import Image
import aggdraw
from math import sqrt, cos, sin, pi
lerp = lambda a, b=0.0, t=0.5: a + (b-a) * t
circle4k = 0.55191502449

radius_circle_bpd = lambda rx, ry=None: _radius_circle_bpd(rx, rx if ry is None else ry)
_radius_circle_bpd = lambda rx, ry: \
    ( 0.,           ry
    , circle4k*rx,  ry
    , rx,           circle4k*ry
    , rx,           0.
    , rx,           -circle4k*ry
    , circle4k*rx,  -ry
    , 0.,           -ry
    , -circle4k*rx, -ry
    , -rx,          -circle4k*ry
    , -rx,          0.
    , -rx,          circle4k*ry
    , -circle4k*rx, ry
    , 0.,           ry )


rectangle_pd = lambda x0,y0, x1,y1: (x0,y0, x1,y0, x1,y1, x0,y1, x0,y0)

pd_translate = lambda pd, offset, nc=2: tuple( pd[ir+ic] + offset[ic] \
    for ir in range(0, len(pd), nc) for ic in range(nc) )

pd_scale = lambda pd, delta, nc=2: tuple( pd[ir+ic] * delta[ic] \
    for ir in range(0, len(pd), nc) for ic in range(nc) )

pd_transform = lambda pd, m, nc=2: \
    tuple(sum(pd[ir+i]*m[ic+i*nc] for i in range(nc)) \
    for ir in range(0, len(pd), nc) for ic in range(nc))

new_draw = lambda size: aggdraw.Draw(Image.new("RGBA", size))
new_mask = lambda size, color="black": aggdraw.Draw(Image.new("L", size, color))


def bpd_add_path(*bpds, path=None):
  if path is None: path = aggdraw.Path()
  for bpd in bpds:
    path.moveto(*bpd[:2])
    for i in range(2, len(bpd), 6):
      path.curveto(*bpd[i:i+6])
  return path

def pd_add_path(*bpds, path=None):
  if path is None: path = aggdraw.Path()
  for bpd in bpds:
    path.moveto(*bpd[:2])
    for i in range(2, len(bpd), 2):
      path.lineto(*bpd[i:i+2])
  return path


def saturn_im(size=(540, 540), angle=pi/10, circle_radius=None
    , ring_inner_radius=None, ring_outer_radius=None
    , n_rings=3, ring_ratio=0.85, ring_aspect=0.5
    , circle_color='#bc75ff', rings_color='#bcff75'
    , outline_color='#ccbd14', outline_weight=4.5):

  offset = (.5 * size[0], .5 * size[1])
  if circle_radius is None:
    circle_radius = min(offset) / 3.

  if ring_inner_radius is None:
    ring_inner_radius = circle_radius / .80

  if ring_outer_radius is None:
    ring_outer_radius = offset[0]

  ca, sa = cos(angle), sin(angle)
  matrix = (ca, sa,-sa, ca)
  pdmask = \
      pd_translate( \
        pd_transform( \
          rectangle_pd(-offset[0],-offset[1], offset[0],0) \
        , matrix )
      , offset )
  bpdcircle = \
      pd_translate( \
        pd_transform( \
          radius_circle_bpd(circle_radius) \
        , matrix )
      , offset )

  r0, r1 = ring_ratio**(n_rings*2-1), ring_ratio**0
  rspan, sspan = r1 - r0, ring_outer_radius - ring_inner_radius
  rlerp = lambda i: ring_inner_radius+sspan*(ring_ratio**i-r0) / rspan
  bpdrings = tuple( \
      pd_translate( \
        pd_transform( \
          pd_scale( \
            radius_circle_bpd(rlerp(i_rings)) \
          , (1.0, ring_aspect) )
        , matrix )
      , offset ) for i_rings in range(n_rings * 2))
  for i in range(6):
    print(i, min(bpdrings[i]),max(bpdrings[i]))
  print("Paths created")

  penstroke = aggdraw.Pen(outline_color, outline_weight)
  penwhite = aggdraw.Pen("white", outline_weight)
  penblack = aggdraw.Pen("black", outline_weight)
  brushcircle = aggdraw.Brush(circle_color)
  brushrings = aggdraw.Brush(rings_color)
  brushwhite = aggdraw.Brush("white")
  brushblack = aggdraw.Brush("black")
  print("Resources created")

  imcircle = new_draw(size)
  imcircle.path(bpd_add_path(bpdcircle), penstroke, brushcircle)
  imcircle = imcircle.flush()
  print("Circle created")

  imcirclemask = new_mask(size, "white")
  imcirclemask.path(bpd_add_path(bpdcircle), penblack, brushblack)
  imcirclemask.path(pd_add_path(pdmask), brushwhite)
  imcirclemask = imcirclemask.flush()
  #imcirclemask.show()
  print("Circle mask created")

  imrings = new_draw(size)
  imrings.path(bpd_add_path(*bpdrings), penstroke, brushrings)
  imrings = imrings.flush()
  #imrings.show()
  print("Rings created")

  tmpmask = new_mask(size, "black")
  for i in range(0, 2*n_rings, 2):
    tmpmask.path(bpd_add_path(bpdrings[i+0]), penwhite, brushwhite)
    tmpmask.path(bpd_add_path(bpdrings[i+1]), penwhite, brushblack)
  tmpmask = tmpmask.flush()
  #tmpmask.show()
  imringsmask = Image.new("L", size, "black")
  imringsmask.paste(tmpmask, mask=imcirclemask)
  #imringsmask.show()
  print("Rings mask created")

  im = Image.new("RGBA", size, '#000000')
  im.paste(imcircle, mask=imcircle)
  im.paste(imrings, mask=imringsmask)
  print("Doughnut created")

  return im

And tada....

[https://user-images.githubusercontent.com/26759688/84541494-16bdf180-ad2a-11ea-805e-29388f992070.png

0

There are 0 best solutions below