peftGDS=>PNG render improvement for GitHub Actions and local (#28)

* Add --create-png-preview option; more-robust GDS to PNG rendering for GHA

* Minor update to --create-png-preview

* Change --create-png-preview to just be a --create-png replacement

* Minor --create-png improvement

---------

Co-authored-by: Anton Maurovic <algo@foogle.com>
This commit is contained in:
Anton Maurovic
2023-09-19 07:27:59 +03:00
committed by GitHub
co-authored by Anton Maurovic
parent e981222768
commit 76835903c0
2 changed files with 106 additions and 7 deletions
+101 -5
View File
@@ -490,17 +490,113 @@ class Project():
if p.returncode != 0:
logging.error("pdf command failed")
# SVG and PNG renders of the GDS
def create_svg(self):
# Read and return top-level GDS data from the final GDS file, using gdstk:
def get_final_gds_top_cells(self):
gds = glob.glob(os.path.join(self.local_dir, 'runs/wokwi/results/final/gds/*gds'))
if self.args.openlane2:
gds = glob.glob(os.path.join(self.local_dir, 'runs/wokwi/final/gds/*gds'))
library = gdstk.read_gds(gds[0])
top_cells = library.top_level()
top_cells[0].write_svg('gds_render.svg')
return top_cells[0]
# SVG render of the GDS.
# NOTE: This includes all standard GDS layers inc. text labels.
def create_svg(self):
top_cells = self.get_final_gds_top_cells()
top_cells.write_svg('gds_render.svg')
# Try various QUICK methods to create a more-compressed PNG render of the GDS,
# and fall back to cairosvg if it doesn't work. This is designed for speed,
# and in particular for use by the GitHub Actions.
# For more info, see:
# https://github.com/TinyTapeout/tt-gds-action/issues/8
def create_png(self):
logging.info('Loading GDS data...')
top_cells = self.get_final_gds_top_cells()
# Remove label layers (i.e. delete text), then generate SVG:
sky130_label_layers = [
(64,59), # 64/59 - pwell.label
(64,5), # 64/5 - nwell.label
(67,5), # 67/5 - li1.label
(68,5), # 68/5 - met1.label
(69,5), # 69/5 - met2.label
(70,5), # 70/5 - met3.label
(71,5), # 71/5 - met4.label
]
top_cells.filter(sky130_label_layers)
svg = 'gds_render_preview.svg'
logging.info('Rendering SVG without text label layers: {}'.format(svg))
top_cells.write_svg(svg, pad=0)
# We should now have gds_render_preview.svg
# Try converting using 'rsvg-convert' command-line utility.
# This should create gds_render_preview.png
png = 'gds_render_preview.png'
logging.info('Converting to PNG using rsvg-convert: {}'.format(png))
cmd = 'rsvg-convert --unlimited {} -o {} --no-keep-image-data'.format(svg, png)
logging.debug(cmd)
p = subprocess.run(cmd, shell=True, capture_output=True)
if p.returncode == 127:
logging.warning('rsvg-convert not found; is package "librsvg2-bin" installed? Falling back to cairosvg. This might take a while...')
# Fall back to cairosvg:
cairosvg.svg2png(url=svg, write_to=png)
elif p.returncode != 0 and b'cannot load more than' in p.stderr:
logging.warning('Too many SVG elements ("{}"). Reducing complexity...'.format(p.stderr.decode().strip()))
# Remove more layers that are hardly visible anyway:
sky130_buried_layers = [
(64,16), # 64/16 - nwell.pin
(65,44), # 65/44 - tap.drawing
(68,16), # 68/16 - met1.pin
(68,44), # 68/44 - via.drawing
(81,4 ), # 81/4 - areaid.standardc
(70,20), # 70/20 - met3.drawing
# Important:
# (68,20), # 68/20 - met1.drawing
# Less important, but keep for now:
# (69,20), # 69/20 - met2.drawing
]
top_cells.filter(sky130_buried_layers)
svg_alt = 'gds_render_preview_alt.svg'
logging.info('Rendering SVG with more layers removed: {}'.format(svg_alt))
top_cells.write_svg(svg_alt, pad=0)
logging.info('Converting to PNG using rsvg-convert: {}'.format(png))
cmd = 'rsvg-convert --unlimited {} -o {} --no-keep-image-data'.format(svg_alt, png)
logging.debug(cmd)
p = subprocess.run(cmd, shell=True, capture_output=True)
if p.returncode != 0:
logging.warning('Still cannot convert to SVG ("{}"). Falling back to cairosvg. This might take a while...'.format(p.stderr.decode().strip()))
# Fall back to cairosvg, and since we're doing that, might as well use the original full-detail SVG anyway:
cairosvg.svg2png(url=svg, write_to=png)
# By now we should have gds_render_preview.png
# Compress with pngquant:
final_png = 'gds_render.png'
if self.yaml['project']['tiles'] == '8x2':
quality = '0-10' # Compress more for 8x2 tiles.
else:
quality = '0-30'
logging.info('Compressing PNG further with pngquant to: {}'.format(final_png))
cmd = 'pngquant --quality {} --speed 1 --nofs --strip --force --output {} {}'.format(quality, final_png, png)
logging.debug(cmd)
p = subprocess.run(cmd, shell=True, capture_output=True)
if p.returncode == 127:
logging.warning('pngquant not found; is package "pngquant" installed? Using intermediate (uncompressed) PNG file')
os.rename(png, final_png)
elif p.returncode !=0:
logging.warning('pngquant error {} ("{}"). Using intermediate (uncompressed) PNG file'.format(p.returncode, p.stderr.decode().strip()))
os.rename(png, final_png)
logging.info('Final PNG is {} ({:,} bytes)'.format(final_png, os.path.getsize(final_png)))
if self.args.create_png:
cairosvg.svg2png(url='gds_render.svg', write_to='gds_render.png')
def print_warnings(self):
warnings = []
+4 -1
View File
@@ -71,5 +71,8 @@ if __name__ == '__main__':
if args.create_pdf:
project.create_pdf()
if args.create_svg or args.create_png:
if args.create_png:
project.create_png()
if args.create_svg:
project.create_svg()