EmbroidePy / pyembroidery

pyembroidery library for reading and writing a variety of embroidery formats.
MIT License
195 stars 35 forks source link

Feature request: raw (rgba) image output #111

Closed andreymal closed 3 years ago

andreymal commented 3 years ago

I want to generate a bitmap and post-process it using Pillow. PyEmbroidery has PngWriter, but writing and reading PNG is not an optimal way. It would be nice to have direct access to the RGBA draw buffer, which I could pass to Pillow without unnecessarily saving to PNG.

In the simplest case it could be easily refactored from the existing PngWriter module by simply removing write_png (although this may not be the best api):

def write(pattern, settings):
    guides = settings.get("guides", False)
    extends = pattern.bounds()
    pattern.translate(-extends[0], -extends[1])
    width = int(extends[2] - extends[0])
    height = int(extends[3] - extends[1])
    draw_buff = PngBuffer(width, height)  # Rename this class to DrawBuffer?

    # ... drawing stuff ...

    return bytes(bytearray(draw_buff.buf)), draw_buff.width, draw_buff.height

Then I could transfer it to Pillow:

from PIL import Image
from pyembroidery.RawWriter import write as write_raw
data, w, h = write_raw(pattern.get_normalized_pattern({"scale": 0.75}), settings={})
with Image.frombytes("RGBA", (w, h), data) as im:
    im.show()
tatarize commented 3 years ago

It seems like if you're importing pillow you can do most of that stuff anyway. I don't think anything in the PNG write routine that is that impressive. I think it's mostly just drawing stuff that can be done with drawline in ImageDraw anyway. Writes it all to the PngBuffer that can write a PNG with that nifty bit of code. This seems like it's trying to salvage some of the png writer code, I'm not sure it's drawing anything extremely useful. And really drawing that in Pillow itself might actually be pretty significantly faster.

tatarize commented 3 years ago

Whatever the code is you'd want that with just do:

    from PIL import Image, ImageDraw
    extends = pattern.bounds()
    pattern.translate(-extends[0], -extends[1])
    width = int(extends[2] - extends[0])
    height = int(extends[3] - extends[1])
    draw_buff = Image.new('RGBA', (width, height))
    drawer = ImageDraw.Draw(draw_buff)
    for stitches, color in pattern.get_as_stitchblock():
        if len(stitches) == 0:
            continue
        b = [(q[0], q[1]) for q in stitches]
        drawer.line(b, fill=color.hex_color(), width=3)

Instead of using my silly lists pretending to be an array. This code will be much faster and you can still save it as anything you'd like.

draw_buff.save('mybitmap.bmp')

andreymal commented 3 years ago

Ok this probably makes sense

In general, I planned to reuse the existing implementation so as not to reinvent it, but performance does hint this is not the best idea :)

(However a little refactoring would still be good, I think)

tatarize commented 3 years ago

I figured you were trying to not reinvent the wheel. But, showing you the new flashy already assembled wheel might dissuade you from asking me to do more work.