freedoom/graphics/text/textgen
RjY 48accfcda2 parallel_textgen: run textgen in parallel
textgen: instead of running ImageMagick yourself, output a Makefile rule
for each graphic. Dependencies are passed on the command line.

Makefile: Add rule for 'textgen.mk' which is built by running textgen.
graphics.stamp depends on textgen.mk and all the built graphics files.
Then, each built graphic depends on textgen.mk, ensuring it is made
first. textgen.mk itself depends on textgen config, font, and dehacked.
(Makefile itself also depends on textgen.mk implicitly via include)

This duplicates previous behaviour where changing textgen config remakes
all text graphics, but one Make rule for each, allowing parallel make
with -j instead of the script doing one at a time.

The built IWADs are byte-for-byte identical before and after this patch
series is applied (provided VERSION is set to the same thing of course)

One problem is 'make clean' rebuilds textgen.mk because Make thinks the
Makefile needs it, but then immediately deletes it. Not sure how to fix.
Use git clean -fdx instead, or just don't clean twice in succession.

----

This gives the following improvement in build time (-j1 as control,
built on 4 cores and a large tmpfs so disk speed isn't a factor)

(master)

    make -j1  4.83s user 6.77s system 93% cpu 12.444 total
    make -j1  4.74s user 6.72s system 93% cpu 12.267 total
    make -j1  4.72s user 6.68s system 92% cpu 12.292 total

    make -j4  5.72s user 6.77s system 109% cpu 11.414 total
    make -j4  5.39s user 6.85s system 107% cpu 11.419 total
    make -j4  5.66s user 6.79s system 109% cpu 11.383 total

parallel_textgen

    make -j1  4.57s user 6.66s system 92% cpu 12.185 total
    make -j1  4.73s user 6.57s system 93% cpu 12.152 total
    make -j1  4.60s user 6.72s system 93% cpu 12.152 total

    make -j4  5.62s user 7.72s system 262% cpu 5.084 total
    make -j4  5.82s user 7.76s system 262% cpu 5.165 total
    make -j4  5.79s user 7.73s system 261% cpu 5.161 total
2017-03-14 17:56:14 -07:00

189 lines
4.7 KiB
Python
Executable file

#!/usr/bin/env python
# SPDX-License-Identifier: BSD-3-Clause
#
# Script to generate menu and intermission screen 'text' graphics for
# Freedoom, by compositing individual font character graphics together.
#
from glob import glob
import re
import sys
from config import *
from common import *
# ImageMagick -colorize parameters for colorizing text:
COLOR_BLUE = (100, 100, 0)
COLOR_RED = (0, 100, 100)
COLOR_WHITE = (0, 0, 0)
# Background color for output files.
BACKGROUND_COLOR = '#00ffff'
# Height of font in pixels.
FONT_HEIGHT = 15
FONT_LC_HEIGHT = 15 #12
# If true, the font only has uppercase characters.
UPPERCASE_FONT = False
# Width of a space character in pixels.
SPACE_WIDTH = 7
class Font(object):
def __init__(self, fontdir, kerning_table={}):
self.fontdir = fontdir
self.kerning_table = self.compile_kerning_table(kerning_table)
self.get_font_widths()
def compile_kerning_table(self, kerning_table):
"""Given a dictionary of kerning patterns, compile Regexps."""
result = {}
for pattern, adjust in kerning_table.items():
result[re.compile(pattern)] = adjust
return result
def get_font_widths(self):
charfiles = glob('%s/font*.gif' % self.fontdir)
self.char_widths = {}
for c in range(128):
filename = self.char_filename(chr(c))
if filename not in charfiles:
continue
w, _ = get_image_dimensions(filename)
self.char_widths[chr(c)] = w
def __contains__(self, c):
return c in self.char_widths
def char_width(self, c):
return self.char_widths[c]
def char_filename(self, c):
return '%s/font%03d.gif' % (self.fontdir, ord(c))
def kerning_adjust(self, char_1, char_2):
"""Get kerning adjustment for pair of characters.
Zero means no adjustment. A negative value adjusts to the
left and a positive value adjusts to the right.
"""
for pattern, adjust in self.kerning_table.items():
if pattern.match(char_1 + char_2):
return adjust
else:
return 0
def iterate_char_positions(self, text):
"""Iterate over characters in string, yielding character with
position it should be placed at in the output file.
"""
x = 0
last_c = ' '
for c in text:
if c == ' ':
x += SPACE_WIDTH
if c in self:
x += self.kerning_adjust(last_c, c)
yield c, x
# Characters overlap by one pixel.
x += self.char_width(c) - 1
last_c = c
# We need to add back the missing pixel from the right side
# of the last char.
x += 1
yield None, x
def text_width(self, text):
"""Given a string of text, get text width in pixels."""
for c, x in self.iterate_char_positions(text):
if c is None:
return x
def _make_command_line(self, text, color):
"""Command line construction helper, used in render functions"""
width = self.text_width(text)
if LOWERCASE_RE.match(text):
height = FONT_LC_HEIGHT
else:
height = FONT_HEIGHT
command_line = [
CONVERT_COMMAND,
'-size', '%ix%i' % (width, height),
'xc:none',
]
for c, x in self.iterate_char_positions(text):
if c is None:
break
filename = self.char_filename(c)
command_line.extend([
'-draw',
'image over %i,%i 0,0 %s' %
(x, height - FONT_HEIGHT, filename)
])
command_line.extend([
'-colorize', '%i,%i,%i,0' % color,
])
return command_line
def get_command(self, text, output_filename,
color=COLOR_WHITE, bgcolor=BACKGROUND_COLOR):
"""Get command to render text to a file
with the given background color.
"""
if UPPERCASE_FONT:
text = text.upper()
command_line = self._make_command_line(text, color)
if bgcolor is not None:
command_line.extend([
'-background', bgcolor, '-flatten'])
command_line.append(output_filename)
return command_line
def generate_graphics(graphics, color=COLOR_WHITE, bgcolor=BACKGROUND_COLOR):
for name, text in sorted(graphics.items()):
print("# %s.gif: '%s'" % (name, text))
# write a makefile fragment
target = '%s.gif' % name
cmd = font.get_command(text, target,
color=color, bgcolor=bgcolor)
print("%s: %s" % (target, " ".join(sys.argv[1:])))
print("\t" + " ".join("'%s'" % i for i in cmd))
def generate_kerning_test():
pairs = []
for c1 in sorted(font.char_widths):
char1 = "%c" % c1
for c2 in sorted(font.char_widths):
char2 = "%c" % c2
if font.kerning_adjust(char1, char2) != 0:
pairs.append(char1 + char2)
cmd = font.get_command(" ".join(pairs), "kerning.gif")
invoke_command(cmd)
font = Font('fontchars', kerning_table=FONT_KERNING_RULES)
# Enable to generate test image file for tweaking kerning values:
#generate_kerning_test()
generate_graphics(red_graphics, color=COLOR_RED)
generate_graphics(blue_graphics, color=COLOR_BLUE)
generate_graphics(white_graphics, color=COLOR_WHITE)