mirror of
https://github.com/freedoom/freedoom.git
synced 2025-08-31 20:16:55 -04:00
Blacken all Python files
Using the black code reformatter, pass it over all our Python files. This allows for a consistent style across the code base. Exception: lumps/dmxgus/stats.py, for readability.
This commit is contained in:
parent
6b486b6332
commit
4701d8f351
30 changed files with 2528 additions and 2102 deletions
|
@ -14,237 +14,233 @@ import re
|
|||
# TODO: Add more rule for lower-case characters.
|
||||
|
||||
FONT_KERNING_RULES = {
|
||||
# Right character fits under left character:
|
||||
r'[TY][07ACOSZ]': -2,
|
||||
r'[TYty][a]': -2,
|
||||
r'P[A]': -3,
|
||||
r'P[7]': -2,
|
||||
r'P[Z]': -1,
|
||||
r'7[Z]': -1,
|
||||
r'[0OQ]A': -1,
|
||||
r'S[A]': -1,
|
||||
r'V[0OC]': -2,
|
||||
|
||||
# Left character fits under right character:
|
||||
r'L[TY]': -4,
|
||||
r'L[014COQV]': -3,
|
||||
r'L[9]': -2,
|
||||
r'[0O][4TY]': -2,
|
||||
r'[0O][1]': -1,
|
||||
r'Q[1T]': -2,
|
||||
r'Q[Y]': -1,
|
||||
r'A[TYV]': -2,
|
||||
r'A[GC]': -1,
|
||||
r'a[TYty]': -2,
|
||||
r'a[vV]': -2,
|
||||
r'a[g]': -1,
|
||||
|
||||
# Fits into "hole" in left character:
|
||||
r'[BCX8][0CGOQ]': -2,
|
||||
r'Z[0CO]': -2,
|
||||
r'Z[GQ]': -1,
|
||||
r'I[0COQ]': -1,
|
||||
r'K[0CO]': -4,
|
||||
r'K[GQ]': -3,
|
||||
r'K[E]': -1,
|
||||
r'[PR][0COQ]': -1,
|
||||
|
||||
# Fits into "hole" in right character:
|
||||
r'[O0Q][X]': -3,
|
||||
r'[O0Q][28]': -2,
|
||||
r'[O0Q][9IK]': -1,
|
||||
|
||||
# Just because.
|
||||
r'[O0][O0]': -1,
|
||||
# Right character fits under left character:
|
||||
r"[TY][07ACOSZ]": -2,
|
||||
r"[TYty][a]": -2,
|
||||
r"P[A]": -3,
|
||||
r"P[7]": -2,
|
||||
r"P[Z]": -1,
|
||||
r"7[Z]": -1,
|
||||
r"[0OQ]A": -1,
|
||||
r"S[A]": -1,
|
||||
r"V[0OC]": -2,
|
||||
# Left character fits under right character:
|
||||
r"L[TY]": -4,
|
||||
r"L[014COQV]": -3,
|
||||
r"L[9]": -2,
|
||||
r"[0O][4TY]": -2,
|
||||
r"[0O][1]": -1,
|
||||
r"Q[1T]": -2,
|
||||
r"Q[Y]": -1,
|
||||
r"A[TYV]": -2,
|
||||
r"A[GC]": -1,
|
||||
r"a[TYty]": -2,
|
||||
r"a[vV]": -2,
|
||||
r"a[g]": -1,
|
||||
# Fits into "hole" in left character:
|
||||
r"[BCX8][0CGOQ]": -2,
|
||||
r"Z[0CO]": -2,
|
||||
r"Z[GQ]": -1,
|
||||
r"I[0COQ]": -1,
|
||||
r"K[0CO]": -4,
|
||||
r"K[GQ]": -3,
|
||||
r"K[E]": -1,
|
||||
r"[PR][0COQ]": -1,
|
||||
# Fits into "hole" in right character:
|
||||
r"[O0Q][X]": -3,
|
||||
r"[O0Q][28]": -2,
|
||||
r"[O0Q][9IK]": -1,
|
||||
# Just because.
|
||||
r"[O0][O0]": -1,
|
||||
}
|
||||
|
||||
white_graphics = {
|
||||
'wibp1': 'P1',
|
||||
'wibp2': 'P2',
|
||||
'wibp3': 'P3',
|
||||
'wibp4': 'P4',
|
||||
'wicolon': ':',
|
||||
|
||||
# These files are for the title screens of Phase 1 and Phase 2
|
||||
't_phase1': 'PHASE 1',
|
||||
't_phase2': 'PHASE 2',
|
||||
|
||||
# Note: level names are also included in this dictionary, with
|
||||
# the data added programatically from the DEHACKED lump, see
|
||||
# code below.
|
||||
"wibp1": "P1",
|
||||
"wibp2": "P2",
|
||||
"wibp3": "P3",
|
||||
"wibp4": "P4",
|
||||
"wicolon": ":",
|
||||
# These files are for the title screens of Phase 1 and Phase 2
|
||||
"t_phase1": "PHASE 1",
|
||||
"t_phase2": "PHASE 2",
|
||||
# Note: level names are also included in this dictionary, with
|
||||
# the data added programatically from the DEHACKED lump, see
|
||||
# code below.
|
||||
}
|
||||
|
||||
blue_graphics = {
|
||||
'm_disopt': 'DISPLAY OPTIONS',
|
||||
'm_episod': 'Choose Chapter:',
|
||||
'm_optttl': 'OPTIONS',
|
||||
'm_skill': 'Choose Skill Level:',
|
||||
"m_disopt": "DISPLAY OPTIONS",
|
||||
"m_episod": "Choose Chapter:",
|
||||
"m_optttl": "OPTIONS",
|
||||
"m_skill": "Choose Skill Level:",
|
||||
}
|
||||
|
||||
red_graphics = {
|
||||
# Title for the HELP/HELP1 screen:
|
||||
'helpttl': 'Help',
|
||||
# Title for CREDIT
|
||||
'freettl': 'Freedoom',
|
||||
|
||||
'm_ngame': 'New Game',
|
||||
'm_option': 'Options',
|
||||
'm_loadg': 'Load Game',
|
||||
'm_saveg': 'Save Game',
|
||||
'm_rdthis': 'Read This!',
|
||||
'm_quitg': 'Quit Game',
|
||||
|
||||
'm_newg': 'NEW GAME',
|
||||
'm_epi1': 'Outpost Outbreak',
|
||||
'm_epi2': 'Military Labs',
|
||||
'm_epi3': 'Event Horizon',
|
||||
'm_epi4': 'Double Impact',
|
||||
|
||||
'm_jkill': 'Please don\'t kill me!',
|
||||
'm_rough': 'Will this hurt?',
|
||||
'm_hurt': 'Bring on the pain.',
|
||||
'm_ultra': 'Extreme Carnage.',
|
||||
'm_nmare': 'MAYHEM!',
|
||||
|
||||
'm_lgttl': 'LOAD GAME',
|
||||
'm_sgttl': 'SAVE GAME',
|
||||
|
||||
'm_endgam': 'End Game',
|
||||
'm_messg': 'Messages:',
|
||||
'm_msgoff': 'off',
|
||||
'm_msgon': 'on',
|
||||
'm_msens': 'Mouse Sensitivity',
|
||||
'm_detail': 'Graphic Detail:',
|
||||
'm_gdhigh': 'high',
|
||||
'm_gdlow': 'low',
|
||||
'm_scrnsz': 'Screen Size',
|
||||
|
||||
'm_svol': 'Sound Volume',
|
||||
'm_sfxvol': 'Sfx Volume',
|
||||
'm_musvol': 'Music Volume',
|
||||
|
||||
'm_disp': 'Display',
|
||||
|
||||
'wif': 'finished',
|
||||
'wiostk': 'kills',
|
||||
'wiosti': 'items',
|
||||
'wiscrt2': 'secret',
|
||||
'wiosts': 'scrt',
|
||||
'wifrgs': 'frgs',
|
||||
|
||||
'witime': 'Time:',
|
||||
'wisucks': 'sucks',
|
||||
'wimstt': 'Total:',
|
||||
'wipar': 'Par:',
|
||||
'wip1': 'P1', 'wip2': 'P2', 'wip3': 'P3', 'wip4': 'P4',
|
||||
'wiostf': 'f.',
|
||||
'wimstar': 'you',
|
||||
'winum0': '0', 'winum1': '1', 'winum2': '2', 'winum3': '3',
|
||||
'winum4': '4', 'winum5': '5', 'winum6': '6', 'winum7': '7',
|
||||
'winum8': '8', 'winum9': '9',
|
||||
'wipcnt': '%',
|
||||
'wiminus': '-',
|
||||
'wienter': 'ENTERING',
|
||||
|
||||
'm_pause': 'pause',
|
||||
|
||||
# Extra graphics used in PrBoom's menus. Generate these as well
|
||||
# so that when we play in PrBoom the menus look consistent.
|
||||
'prboom': 'PrBoom',
|
||||
'm_generl': 'General',
|
||||
'm_setup': 'Setup',
|
||||
'm_keybnd': 'Key Bindings',
|
||||
'm_weap': 'Weapons',
|
||||
'm_stat': 'Status Bar/HUD',
|
||||
'm_auto': 'Automap',
|
||||
'm_enem': 'Enemies',
|
||||
'm_mess': 'Messages',
|
||||
'm_chat': 'Chat Strings',
|
||||
|
||||
'm_horsen': 'horizontal',
|
||||
'm_versen': 'vertical',
|
||||
'm_loksen': 'mouse look',
|
||||
'm_accel': 'acceleration',
|
||||
|
||||
# Extra graphics from SMMU/Eternity Engine:
|
||||
'm_about': 'about',
|
||||
'm_chatm': 'Chat Strings',
|
||||
'm_compat': 'Compatibility',
|
||||
'm_demos': 'demos',
|
||||
'm_dmflag': 'deathmatch flags',
|
||||
'm_etcopt': 'eternity options',
|
||||
'm_feat': 'Features',
|
||||
'm_gset': 'game settings',
|
||||
'm_hud': 'heads up display',
|
||||
'm_joyset': 'joysticks',
|
||||
'm_ldsv': 'Load/Save',
|
||||
'm_menus': 'Menu Options',
|
||||
'm_mouse': 'mouse options',
|
||||
'm_player': 'player setup',
|
||||
'm_serial': 'serial connection',
|
||||
'm_sound': 'sound options',
|
||||
'm_status': 'status bar',
|
||||
'm_tcpip': 'tcp/ip connection',
|
||||
'm_video': 'video options',
|
||||
'm_wad': 'load wad',
|
||||
'm_wadopt': 'wad options',
|
||||
# This is from SMMU too, and if we follow things to the letter,
|
||||
# ought to be all lower-case. However, same lump name is used
|
||||
# by other ports (Zandronum) which expect a taller graphic to
|
||||
# match the other main menu graphics. Eternity Engine doesn't
|
||||
# use it any more, and on SMMU there's enough space for it.
|
||||
'm_multi': 'Multiplayer',
|
||||
# Title for the HELP/HELP1 screen:
|
||||
"helpttl": "Help",
|
||||
# Title for CREDIT
|
||||
"freettl": "Freedoom",
|
||||
"m_ngame": "New Game",
|
||||
"m_option": "Options",
|
||||
"m_loadg": "Load Game",
|
||||
"m_saveg": "Save Game",
|
||||
"m_rdthis": "Read This!",
|
||||
"m_quitg": "Quit Game",
|
||||
"m_newg": "NEW GAME",
|
||||
"m_epi1": "Outpost Outbreak",
|
||||
"m_epi2": "Military Labs",
|
||||
"m_epi3": "Event Horizon",
|
||||
"m_epi4": "Double Impact",
|
||||
"m_jkill": "Please don't kill me!",
|
||||
"m_rough": "Will this hurt?",
|
||||
"m_hurt": "Bring on the pain.",
|
||||
"m_ultra": "Extreme Carnage.",
|
||||
"m_nmare": "MAYHEM!",
|
||||
"m_lgttl": "LOAD GAME",
|
||||
"m_sgttl": "SAVE GAME",
|
||||
"m_endgam": "End Game",
|
||||
"m_messg": "Messages:",
|
||||
"m_msgoff": "off",
|
||||
"m_msgon": "on",
|
||||
"m_msens": "Mouse Sensitivity",
|
||||
"m_detail": "Graphic Detail:",
|
||||
"m_gdhigh": "high",
|
||||
"m_gdlow": "low",
|
||||
"m_scrnsz": "Screen Size",
|
||||
"m_svol": "Sound Volume",
|
||||
"m_sfxvol": "Sfx Volume",
|
||||
"m_musvol": "Music Volume",
|
||||
"m_disp": "Display",
|
||||
"wif": "finished",
|
||||
"wiostk": "kills",
|
||||
"wiosti": "items",
|
||||
"wiscrt2": "secret",
|
||||
"wiosts": "scrt",
|
||||
"wifrgs": "frgs",
|
||||
"witime": "Time:",
|
||||
"wisucks": "sucks",
|
||||
"wimstt": "Total:",
|
||||
"wipar": "Par:",
|
||||
"wip1": "P1",
|
||||
"wip2": "P2",
|
||||
"wip3": "P3",
|
||||
"wip4": "P4",
|
||||
"wiostf": "f.",
|
||||
"wimstar": "you",
|
||||
"winum0": "0",
|
||||
"winum1": "1",
|
||||
"winum2": "2",
|
||||
"winum3": "3",
|
||||
"winum4": "4",
|
||||
"winum5": "5",
|
||||
"winum6": "6",
|
||||
"winum7": "7",
|
||||
"winum8": "8",
|
||||
"winum9": "9",
|
||||
"wipcnt": "%",
|
||||
"wiminus": "-",
|
||||
"wienter": "ENTERING",
|
||||
"m_pause": "pause",
|
||||
# Extra graphics used in PrBoom's menus. Generate these as well
|
||||
# so that when we play in PrBoom the menus look consistent.
|
||||
"prboom": "PrBoom",
|
||||
"m_generl": "General",
|
||||
"m_setup": "Setup",
|
||||
"m_keybnd": "Key Bindings",
|
||||
"m_weap": "Weapons",
|
||||
"m_stat": "Status Bar/HUD",
|
||||
"m_auto": "Automap",
|
||||
"m_enem": "Enemies",
|
||||
"m_mess": "Messages",
|
||||
"m_chat": "Chat Strings",
|
||||
"m_horsen": "horizontal",
|
||||
"m_versen": "vertical",
|
||||
"m_loksen": "mouse look",
|
||||
"m_accel": "acceleration",
|
||||
# Extra graphics from SMMU/Eternity Engine:
|
||||
"m_about": "about",
|
||||
"m_chatm": "Chat Strings",
|
||||
"m_compat": "Compatibility",
|
||||
"m_demos": "demos",
|
||||
"m_dmflag": "deathmatch flags",
|
||||
"m_etcopt": "eternity options",
|
||||
"m_feat": "Features",
|
||||
"m_gset": "game settings",
|
||||
"m_hud": "heads up display",
|
||||
"m_joyset": "joysticks",
|
||||
"m_ldsv": "Load/Save",
|
||||
"m_menus": "Menu Options",
|
||||
"m_mouse": "mouse options",
|
||||
"m_player": "player setup",
|
||||
"m_serial": "serial connection",
|
||||
"m_sound": "sound options",
|
||||
"m_status": "status bar",
|
||||
"m_tcpip": "tcp/ip connection",
|
||||
"m_video": "video options",
|
||||
"m_wad": "load wad",
|
||||
"m_wadopt": "wad options",
|
||||
# This is from SMMU too, and if we follow things to the letter,
|
||||
# ought to be all lower-case. However, same lump name is used
|
||||
# by other ports (Zandronum) which expect a taller graphic to
|
||||
# match the other main menu graphics. Eternity Engine doesn't
|
||||
# use it any more, and on SMMU there's enough space for it.
|
||||
"m_multi": "Multiplayer",
|
||||
}
|
||||
|
||||
def read_bex_lump(filename):
|
||||
"""Read the BEX (Dehacked) lump from the given filename.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping from name to value.
|
||||
"""
|
||||
result = {}
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
# Ignore comments:
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[0] in '#;':
|
||||
continue
|
||||
# Just split on '=' and interpret that as an
|
||||
# assignment. This is primitive and doesn't read
|
||||
# like a full BEX parser should, but it's good
|
||||
# enough for our purposes here.
|
||||
assign = line.split('=', 2)
|
||||
if len(assign) != 2:
|
||||
continue
|
||||
result[assign[0].strip()] = assign[1].strip()
|
||||
return result
|
||||
def read_bex_lump(filename):
|
||||
"""Read the BEX (Dehacked) lump from the given filename.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping from name to value.
|
||||
"""
|
||||
result = {}
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
# Ignore comments:
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[0] in "#;":
|
||||
continue
|
||||
# Just split on '=' and interpret that as an
|
||||
# assignment. This is primitive and doesn't read
|
||||
# like a full BEX parser should, but it's good
|
||||
# enough for our purposes here.
|
||||
assign = line.split("=", 2)
|
||||
if len(assign) != 2:
|
||||
continue
|
||||
result[assign[0].strip()] = assign[1].strip()
|
||||
return result
|
||||
|
||||
|
||||
def update_level_name(lumpname, bexdata, bexname):
|
||||
"""Set the level name for the given graphic from BEX file.
|
||||
"""Set the level name for the given graphic from BEX file.
|
||||
|
||||
Args:
|
||||
lumpname: Name of output graphic file.
|
||||
bexdata: Dictionary of data read from BEX file.
|
||||
bexname: Name of entry in BEX file to use.
|
||||
"""
|
||||
if bexname not in bexdata:
|
||||
raise Exception('Level name %s not defined in '
|
||||
'DEHACKED lump!' % bexname)
|
||||
# Strip "MAP01: " or "E1M2: " etc. from start, if present:
|
||||
levelname = re.sub('^\w*\d:\s*', '', bexdata[bexname])
|
||||
white_graphics[lumpname] = levelname
|
||||
Args:
|
||||
lumpname: Name of output graphic file.
|
||||
bexdata: Dictionary of data read from BEX file.
|
||||
bexname: Name of entry in BEX file to use.
|
||||
"""
|
||||
if bexname not in bexdata:
|
||||
raise Exception(
|
||||
"Level name %s not defined in " "DEHACKED lump!" % bexname
|
||||
)
|
||||
# Strip "MAP01: " or "E1M2: " etc. from start, if present:
|
||||
levelname = re.sub("^\w*\d:\s*", "", bexdata[bexname])
|
||||
white_graphics[lumpname] = levelname
|
||||
|
||||
freedoom_bex = read_bex_lump('../../lumps/p2_deh.lmp')
|
||||
freedm_bex = read_bex_lump('../../lumps/fdm_deh.lmp')
|
||||
|
||||
freedoom_bex = read_bex_lump("../../lumps/p2_deh.lmp")
|
||||
freedm_bex = read_bex_lump("../../lumps/fdm_deh.lmp")
|
||||
|
||||
for e in range(4):
|
||||
for m in range(9):
|
||||
# HUSTR_E1M1 from BEX => wilv00
|
||||
update_level_name('wilv%i%i' % (e, m), freedoom_bex,
|
||||
'HUSTR_E%iM%i' % (e + 1, m + 1))
|
||||
for m in range(9):
|
||||
# HUSTR_E1M1 from BEX => wilv00
|
||||
update_level_name(
|
||||
"wilv%i%i" % (e, m), freedoom_bex, "HUSTR_E%iM%i" % (e + 1, m + 1)
|
||||
)
|
||||
|
||||
for m in range(32):
|
||||
# HUSTR_1 => cwilv00
|
||||
update_level_name('cwilv%02i' % m, freedoom_bex, 'HUSTR_%i' % (m + 1))
|
||||
# HUSTR_1 => dmwilv00 (from freedm.bex)
|
||||
update_level_name('dmwilv%02i' % m, freedm_bex, 'HUSTR_%i' % (m + 1))
|
||||
# HUSTR_1 => cwilv00
|
||||
update_level_name("cwilv%02i" % m, freedoom_bex, "HUSTR_%i" % (m + 1))
|
||||
# HUSTR_1 => dmwilv00 (from freedm.bex)
|
||||
update_level_name("dmwilv%02i" % m, freedm_bex, "HUSTR_%i" % (m + 1))
|
||||
|
|
|
@ -7,13 +7,13 @@ from PIL import Image, ImageFont, ImageDraw
|
|||
import sys
|
||||
import os
|
||||
|
||||
#create_caption.py <background_image> <title?> <phase?> <outfile>
|
||||
# create_caption.py <background_image> <title?> <phase?> <outfile>
|
||||
|
||||
font = ImageFont.load_default()
|
||||
|
||||
|
||||
txt1= "© 2001-2019"
|
||||
txt2= os.environ['VERSION']
|
||||
txt1 = "© 2001-2019"
|
||||
txt2 = os.environ["VERSION"]
|
||||
background_image = Image.open(sys.argv[1])
|
||||
background_image.load()
|
||||
background_image = background_image.convert("RGBA")
|
||||
|
@ -21,17 +21,37 @@ image = Image.new("RGBA", background_image.size, (0, 0, 0, 0))
|
|||
draw = ImageDraw.Draw(image)
|
||||
txt1_size = draw.textsize(txt1, font=font)
|
||||
txt2_size = draw.textsize(txt2, font=font)
|
||||
draw.text((5, int(image.height - txt1_size[1] - 5)), txt1, font=font, fill=(255,165,0,255))
|
||||
draw.text((int(image.width - txt2_size[0] - 10), int(image.height - txt2_size[1] - 5)), txt2, font=font, fill=(255,165,0,255))
|
||||
|
||||
draw.text(
|
||||
(5, int(image.height - txt1_size[1] - 5)),
|
||||
txt1,
|
||||
font=font,
|
||||
fill=(255, 165, 0, 255),
|
||||
)
|
||||
draw.text(
|
||||
(
|
||||
int(image.width - txt2_size[0] - 10),
|
||||
int(image.height - txt2_size[1] - 5),
|
||||
),
|
||||
txt2,
|
||||
font=font,
|
||||
fill=(255, 165, 0, 255),
|
||||
)
|
||||
|
||||
if len(sys.argv) > 3:
|
||||
#paste the other stuff onto the thing.
|
||||
# paste the other stuff onto the thing.
|
||||
logo = Image.open(sys.argv[2])
|
||||
logo.load()
|
||||
phase = Image.open(sys.argv[3])
|
||||
phase.load
|
||||
image.paste(logo, ((int(image.width/2) - int(logo.width/2), 18)))
|
||||
image.paste(phase, ((int(image.width/2) - int(phase.width/2)), int(image.height - phase.height - 30)))
|
||||
image.paste(logo, ((int(image.width / 2) - int(logo.width / 2), 18)))
|
||||
image.paste(
|
||||
phase,
|
||||
(
|
||||
(int(image.width / 2) - int(phase.width / 2)),
|
||||
int(image.height - phase.height - 30),
|
||||
),
|
||||
)
|
||||
outfile_name = sys.argv[4]
|
||||
else:
|
||||
outfile_name = sys.argv[2]
|
||||
|
|
|
@ -5,20 +5,21 @@ import re
|
|||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def get_image_dimensions(filename):
|
||||
"""Get image dimensions w x h
|
||||
"""Get image dimensions w x h
|
||||
|
||||
Args:
|
||||
filename: filename of the image
|
||||
"""
|
||||
with Image.open(filename) as img:
|
||||
width, height = img.size
|
||||
return (width, height)
|
||||
with Image.open(filename) as img:
|
||||
width, height = img.size
|
||||
return (width, height)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
x,y = get_image_dimensions(sys.argv[1])
|
||||
string = "%i %i" % (x, y)
|
||||
sys.stdout.write(string)
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
x, y = get_image_dimensions(sys.argv[1])
|
||||
string = "%i %i" % (x, y)
|
||||
sys.stdout.write(string)
|
||||
|
|
|
@ -23,5 +23,5 @@ else:
|
|||
img2 = img2.crop()
|
||||
|
||||
if os.path.exists(sys.argv[3]): # delete any previous result file
|
||||
os.remove(sys.argv[3])
|
||||
os.remove(sys.argv[3])
|
||||
img2.save(sys.argv[3])
|
||||
|
|
|
@ -12,143 +12,140 @@ import re
|
|||
from image_dimensions import *
|
||||
from tint import image_tint
|
||||
|
||||
DIMENSION_MATCH_RE = re.compile(r'(\d+)[x,](\d+)')
|
||||
DIMENSION_MATCH_RE = re.compile(r"(\d+)[x,](\d+)")
|
||||
|
||||
|
||||
class SmallTextGenerator(object):
|
||||
def __init__(self):
|
||||
self.get_font_widths()
|
||||
# Width of a space character in pixels.
|
||||
SPACE_WIDTH = 4
|
||||
# Height of the font.
|
||||
FONT_HEIGHT = 8
|
||||
# Regexp to match dimensions/x,y coordinate pair.
|
||||
def __init__(self):
|
||||
self.get_font_widths()
|
||||
|
||||
def compile_kerning_table(self, kerning_table):
|
||||
"""Given a dictionary of kerning patterns, compile Regexps."""
|
||||
# Width of a space character in pixels.
|
||||
SPACE_WIDTH = 4
|
||||
# Height of the font.
|
||||
FONT_HEIGHT = 8
|
||||
# Regexp to match dimensions/x,y coordinate pair.
|
||||
|
||||
result = {}
|
||||
for pattern, adjust in kerning_table.items():
|
||||
result[re.compile(pattern)] = adjust
|
||||
return result
|
||||
def compile_kerning_table(self, kerning_table):
|
||||
"""Given a dictionary of kerning patterns, compile Regexps."""
|
||||
|
||||
def get_font_widths(self):
|
||||
charfiles = glob('../stcfn*.png')
|
||||
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
|
||||
result = {}
|
||||
for pattern, adjust in kerning_table.items():
|
||||
result[re.compile(pattern)] = adjust
|
||||
return result
|
||||
|
||||
def __contains__(self, c):
|
||||
return c in self.char_widths
|
||||
def get_font_widths(self):
|
||||
charfiles = glob("../stcfn*.png")
|
||||
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 char_width(self, c):
|
||||
return self.char_widths[c]
|
||||
def __contains__(self, c):
|
||||
return c in self.char_widths
|
||||
|
||||
def char_filename(self, c):
|
||||
return '../stcfn%03d.png' % (ord(c))
|
||||
def char_width(self, c):
|
||||
return self.char_widths[c]
|
||||
|
||||
def draw_for_text(self, image, text, x, y):
|
||||
text = text.upper()
|
||||
new_image = image.copy()
|
||||
x1, y1 = x, y
|
||||
def char_filename(self, c):
|
||||
return "../stcfn%03d.png" % (ord(c))
|
||||
|
||||
for c in text:
|
||||
if c == '\n':
|
||||
y1 += self.FONT_HEIGHT
|
||||
x1 = x
|
||||
elif c == ' ':
|
||||
x1 += self.SPACE_WIDTH
|
||||
def draw_for_text(self, image, text, x, y):
|
||||
text = text.upper()
|
||||
new_image = image.copy()
|
||||
x1, y1 = x, y
|
||||
|
||||
if c not in self:
|
||||
continue
|
||||
for c in text:
|
||||
if c == "\n":
|
||||
y1 += self.FONT_HEIGHT
|
||||
x1 = x
|
||||
elif c == " ":
|
||||
x1 += self.SPACE_WIDTH
|
||||
|
||||
filename = self.char_filename(c)
|
||||
char_image = Image.open(filename)
|
||||
char_image.load()
|
||||
new_image = self.paste_image(new_image, char_image, x1, y1)
|
||||
x1 += self.char_width(c)
|
||||
return new_image
|
||||
if c not in self:
|
||||
continue
|
||||
|
||||
def paste_image(self, image, src, x, y):
|
||||
int_image = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
||||
int_image.paste(src, (x, y))
|
||||
new_image = Image.alpha_composite(image, int_image)
|
||||
return new_image
|
||||
filename = self.char_filename(c)
|
||||
char_image = Image.open(filename)
|
||||
char_image.load()
|
||||
new_image = self.paste_image(new_image, char_image, x1, y1)
|
||||
x1 += self.char_width(c)
|
||||
return new_image
|
||||
|
||||
def paste_image(self, image, src, x, y):
|
||||
int_image = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
||||
int_image.paste(src, (x, y))
|
||||
new_image = Image.alpha_composite(image, int_image)
|
||||
return new_image
|
||||
|
||||
|
||||
def parse_command_line(args):
|
||||
if len(args) < 4 or (len(args) % 2) != 0:
|
||||
return None
|
||||
if len(args) < 4 or (len(args) % 2) != 0:
|
||||
return None
|
||||
|
||||
result = {
|
||||
'filename': args[0],
|
||||
'background': None,
|
||||
'strings': [],
|
||||
}
|
||||
result = {"filename": args[0], "background": None, "strings": []}
|
||||
|
||||
m = DIMENSION_MATCH_RE.match(args[1])
|
||||
if not m:
|
||||
return None
|
||||
result['dimensions'] = (int(m.group(1)), int(m.group(2)))
|
||||
m = DIMENSION_MATCH_RE.match(args[1])
|
||||
if not m:
|
||||
return None
|
||||
result["dimensions"] = (int(m.group(1)), int(m.group(2)))
|
||||
|
||||
i = 2
|
||||
while i < len(args):
|
||||
if args[i] == '-background':
|
||||
result['background'] = args[i+1]
|
||||
i += 2
|
||||
continue
|
||||
i = 2
|
||||
while i < len(args):
|
||||
if args[i] == "-background":
|
||||
result["background"] = args[i + 1]
|
||||
i += 2
|
||||
continue
|
||||
|
||||
m = DIMENSION_MATCH_RE.match(args[i])
|
||||
if not m:
|
||||
return None
|
||||
m = DIMENSION_MATCH_RE.match(args[i])
|
||||
if not m:
|
||||
return None
|
||||
|
||||
xy = (int(m.group(1)), int(m.group(2)))
|
||||
xy = (int(m.group(1)), int(m.group(2)))
|
||||
|
||||
result['strings'].append((xy, args[i + 1]))
|
||||
i += 2
|
||||
result["strings"].append((xy, args[i + 1]))
|
||||
i += 2
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parse_command_line(sys.argv[1:])
|
||||
args = parse_command_line(sys.argv[1:])
|
||||
|
||||
if not args:
|
||||
print("Usage: smtextgen <filename> <size> [...text commands...]")
|
||||
print("Where each text command looks like:")
|
||||
print(" [x,y] [text]")
|
||||
sys.exit(0)
|
||||
if not args:
|
||||
print("Usage: smtextgen <filename> <size> [...text commands...]")
|
||||
print("Where each text command looks like:")
|
||||
print(" [x,y] [text]")
|
||||
sys.exit(0)
|
||||
|
||||
smallfont = SmallTextGenerator()
|
||||
smallfont = SmallTextGenerator()
|
||||
|
||||
if args['background'] is not None:
|
||||
background_image = Image.open(args['background'])
|
||||
background_image.load()
|
||||
background_image = background_image.convert("RGBA")
|
||||
if args["background"] is not None:
|
||||
background_image = Image.open(args["background"])
|
||||
background_image.load()
|
||||
background_image = background_image.convert("RGBA")
|
||||
|
||||
image = Image.new("RGBA", args['dimensions'],(0,0,0,0))
|
||||
image = Image.new("RGBA", args["dimensions"], (0, 0, 0, 0))
|
||||
|
||||
for xy, string in args['strings']:
|
||||
# Allow contents of a file to be included with special prefix:
|
||||
if string.startswith('include:'):
|
||||
with open(string[8:]) as f:
|
||||
string = f.read()
|
||||
for xy, string in args["strings"]:
|
||||
# Allow contents of a file to be included with special prefix:
|
||||
if string.startswith("include:"):
|
||||
with open(string[8:]) as f:
|
||||
string = f.read()
|
||||
|
||||
# Allow special notation to indicate an image file to just draw
|
||||
# rather than rendering a string.
|
||||
if string.startswith('file:'):
|
||||
src_image = Image.open(string[5:])
|
||||
src_image.load()
|
||||
image = smallfont.paste_image(image, src_image, xy[0], xy[1])
|
||||
else:
|
||||
image = smallfont.draw_for_text(image, string, xy[0], xy[1])
|
||||
# Allow special notation to indicate an image file to just draw
|
||||
# rather than rendering a string.
|
||||
if string.startswith("file:"):
|
||||
src_image = Image.open(string[5:])
|
||||
src_image.load()
|
||||
image = smallfont.paste_image(image, src_image, xy[0], xy[1])
|
||||
else:
|
||||
image = smallfont.draw_for_text(image, string, xy[0], xy[1])
|
||||
|
||||
if args['background'] is not None:
|
||||
image = Image.alpha_composite(background_image, image)
|
||||
|
||||
image.save(args['filename'])
|
||||
if args["background"] is not None:
|
||||
image = Image.alpha_composite(background_image, image)
|
||||
|
||||
image.save(args["filename"])
|
||||
|
|
|
@ -15,152 +15,152 @@ from image_dimensions import *
|
|||
from tint import image_tint
|
||||
|
||||
|
||||
|
||||
class TextGenerator(object):
|
||||
def __init__(self, fontdir, kerning_table={}):
|
||||
self.fontdir = fontdir
|
||||
self.kerning_table = self.compile_kerning_table(kerning_table)
|
||||
self.get_font_widths()
|
||||
def __init__(self, fontdir, kerning_table={}):
|
||||
self.fontdir = fontdir
|
||||
self.kerning_table = self.compile_kerning_table(kerning_table)
|
||||
self.get_font_widths()
|
||||
|
||||
# Tinting parameters for colorizing text:
|
||||
COLOR_BLUE = "#000001"
|
||||
COLOR_RED = "#010000"
|
||||
COLOR_WHITE = None
|
||||
# Tinting parameters for colorizing text:
|
||||
COLOR_BLUE = "#000001"
|
||||
COLOR_RED = "#010000"
|
||||
COLOR_WHITE = None
|
||||
|
||||
# Height of font in pixels.
|
||||
FONT_HEIGHT = 15
|
||||
FONT_LC_HEIGHT = 15 # 12
|
||||
# Height of font in pixels.
|
||||
FONT_HEIGHT = 15
|
||||
FONT_LC_HEIGHT = 15 # 12
|
||||
|
||||
# If true, the font only has uppercase characters.
|
||||
UPPERCASE_FONT = False
|
||||
# If true, the font only has uppercase characters.
|
||||
UPPERCASE_FONT = False
|
||||
|
||||
# Width of a space character in pixels.
|
||||
SPACE_WIDTH = 7
|
||||
LOWERCASE_RE = re.compile(r'^[a-z\!\. ]*$')
|
||||
# Width of a space character in pixels.
|
||||
SPACE_WIDTH = 7
|
||||
LOWERCASE_RE = re.compile(r"^[a-z\!\. ]*$")
|
||||
|
||||
def compile_kerning_table(self, kerning_table):
|
||||
"""Given a dictionary of kerning patterns, compile Regexps."""
|
||||
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
|
||||
result = {}
|
||||
for pattern, adjust in kerning_table.items():
|
||||
result[re.compile(pattern)] = adjust
|
||||
return result
|
||||
|
||||
def get_font_widths(self):
|
||||
charfiles = glob('%s/font*.png' % 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 get_font_widths(self):
|
||||
charfiles = glob("%s/font*.png" % 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 __contains__(self, c):
|
||||
return c in self.char_widths
|
||||
|
||||
def char_width(self, c):
|
||||
return self.char_widths[c]
|
||||
def char_width(self, c):
|
||||
return self.char_widths[c]
|
||||
|
||||
def char_filename(self, c):
|
||||
return '%s/font%03d.png' % (self.fontdir, ord(c))
|
||||
def char_filename(self, c):
|
||||
return "%s/font%03d.png" % (self.fontdir, ord(c))
|
||||
|
||||
def kerning_adjust(self, char_1, char_2):
|
||||
"""Get kerning adjustment for pair of characters.
|
||||
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
|
||||
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 += self.SPACE_WIDTH
|
||||
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 += self.SPACE_WIDTH
|
||||
|
||||
if c in self:
|
||||
x += self.kerning_adjust(last_c, c)
|
||||
if c in self:
|
||||
x += self.kerning_adjust(last_c, c)
|
||||
|
||||
yield c, x
|
||||
yield c, x
|
||||
|
||||
# Characters overlap by one pixel.
|
||||
x += self.char_width(c) - 1
|
||||
# Characters overlap by one pixel.
|
||||
x += self.char_width(c) - 1
|
||||
|
||||
last_c = c
|
||||
last_c = c
|
||||
|
||||
# We need to add back the missing pixel from the right side
|
||||
# of the last char.
|
||||
x += 1
|
||||
yield None, x
|
||||
# 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 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 generate_graphic(self, text, color=None):
|
||||
"""Get command to render text to a file
|
||||
with the given background color.
|
||||
"""
|
||||
def generate_graphic(self, text, color=None):
|
||||
"""Get command to render text to a file
|
||||
with the given background color.
|
||||
"""
|
||||
|
||||
if self.UPPERCASE_FONT:
|
||||
text = text.upper()
|
||||
"""Command line construction helper, used in render functions"""
|
||||
width = self.text_width(text)
|
||||
if self.UPPERCASE_FONT:
|
||||
text = text.upper()
|
||||
"""Command line construction helper, used in render functions"""
|
||||
width = self.text_width(text)
|
||||
|
||||
if self.LOWERCASE_RE.match(text):
|
||||
height = self.FONT_LC_HEIGHT
|
||||
else:
|
||||
height = self.FONT_HEIGHT
|
||||
if self.LOWERCASE_RE.match(text):
|
||||
height = self.FONT_LC_HEIGHT
|
||||
else:
|
||||
height = self.FONT_HEIGHT
|
||||
|
||||
txt_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
for c, x in self.iterate_char_positions(text):
|
||||
if c is None:
|
||||
break
|
||||
txt_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
for c, x in self.iterate_char_positions(text):
|
||||
if c is None:
|
||||
break
|
||||
|
||||
filename = self.char_filename(c)
|
||||
char_image = Image.open(filename)
|
||||
char_image.load()
|
||||
int_image = Image.new("RGBA", txt_image.size, (0, 0, 0, 0))
|
||||
int_image.paste(char_image, (x, height - self.FONT_HEIGHT))
|
||||
txt_image = Image.alpha_composite(txt_image, int_image)
|
||||
filename = self.char_filename(c)
|
||||
char_image = Image.open(filename)
|
||||
char_image.load()
|
||||
int_image = Image.new("RGBA", txt_image.size, (0, 0, 0, 0))
|
||||
int_image.paste(char_image, (x, height - self.FONT_HEIGHT))
|
||||
txt_image = Image.alpha_composite(txt_image, int_image)
|
||||
|
||||
txt_image = image_tint(txt_image, color)
|
||||
return txt_image
|
||||
txt_image = image_tint(txt_image, color)
|
||||
return txt_image
|
||||
|
||||
|
||||
def generate_graphics(font, graphics, color=None):
|
||||
for name, text in sorted(graphics.items()):
|
||||
# write a makefile fragment
|
||||
target = '%s.png' % name
|
||||
image = font.generate_graphic(text, color=color)
|
||||
image.save(target)
|
||||
for name, text in sorted(graphics.items()):
|
||||
# write a makefile fragment
|
||||
target = "%s.png" % name
|
||||
image = font.generate_graphic(text, color=color)
|
||||
image.save(target)
|
||||
|
||||
|
||||
def generate_kerning_test(font):
|
||||
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)
|
||||
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.generate_graphic(" ".join(pairs), "kerning.png")
|
||||
cmd = font.generate_graphic(" ".join(pairs), "kerning.png")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
font = TextGenerator('fontchars', kerning_table=FONT_KERNING_RULES)
|
||||
generate_graphics(font, red_graphics, color=font.COLOR_RED)
|
||||
generate_graphics(font, blue_graphics, color=font.COLOR_BLUE)
|
||||
generate_graphics(font, white_graphics, color=font.COLOR_WHITE)
|
||||
if __name__ == "__main__":
|
||||
|
||||
font = TextGenerator("fontchars", kerning_table=FONT_KERNING_RULES)
|
||||
generate_graphics(font, red_graphics, color=font.COLOR_RED)
|
||||
generate_graphics(font, blue_graphics, color=font.COLOR_BLUE)
|
||||
generate_graphics(font, white_graphics, color=font.COLOR_WHITE)
|
||||
|
|
|
@ -6,48 +6,54 @@
|
|||
|
||||
from PIL import Image, ImageColor, ImageOps
|
||||
|
||||
|
||||
def image_tint(image, tint=None):
|
||||
if tint is None:
|
||||
return image
|
||||
if image.mode not in ['RGB', 'RGBA']:
|
||||
image = image.convert('RGBA')
|
||||
if tint is None:
|
||||
return image
|
||||
if image.mode not in ["RGB", "RGBA"]:
|
||||
image = image.convert("RGBA")
|
||||
|
||||
tr, tg, tb = ImageColor.getrgb(tint)
|
||||
tl = ImageColor.getcolor(tint, "L") # tint color's overall luminosity
|
||||
if not tl:
|
||||
tl = 1 # avoid division by zero
|
||||
tl = float(tl) # compute luminosity preserving tint factors
|
||||
sr, sg, sb = map(lambda tv: tv / tl, (tr, tg, tb)
|
||||
) # per component adjustments
|
||||
tr, tg, tb = ImageColor.getrgb(tint)
|
||||
tl = ImageColor.getcolor(tint, "L") # tint color's overall luminosity
|
||||
if not tl:
|
||||
tl = 1 # avoid division by zero
|
||||
tl = float(tl) # compute luminosity preserving tint factors
|
||||
sr, sg, sb = map(
|
||||
lambda tv: tv / tl, (tr, tg, tb)
|
||||
) # per component adjustments
|
||||
|
||||
# create look-up tables to map luminosity to adjusted tint
|
||||
# (using floating-point math only to compute table)
|
||||
luts = (tuple(map(lambda lr: int(lr * sr + 0.5), range(256))) +
|
||||
tuple(map(lambda lg: int(lg * sg + 0.5), range(256))) +
|
||||
tuple(map(lambda lb: int(lb * sb + 0.5), range(256))))
|
||||
l = ImageOps.grayscale(image) # 8-bit luminosity version of whole image
|
||||
if Image.getmodebands(image.mode) < 4:
|
||||
merge_args = (image.mode, (l, l, l)) # for RGB verion of grayscale
|
||||
else: # include copy of image's alpha layer
|
||||
a = Image.new("L", image.size)
|
||||
a.putdata(image.getdata(3))
|
||||
merge_args = (image.mode, (l, l, l, a)) # for RGBA verion of grayscale
|
||||
luts += tuple(range(256)) # for 1:1 mapping of copied alpha values
|
||||
# create look-up tables to map luminosity to adjusted tint
|
||||
# (using floating-point math only to compute table)
|
||||
luts = (
|
||||
tuple(map(lambda lr: int(lr * sr + 0.5), range(256)))
|
||||
+ tuple(map(lambda lg: int(lg * sg + 0.5), range(256)))
|
||||
+ tuple(map(lambda lb: int(lb * sb + 0.5), range(256)))
|
||||
)
|
||||
l = ImageOps.grayscale(image) # 8-bit luminosity version of whole image
|
||||
if Image.getmodebands(image.mode) < 4:
|
||||
merge_args = (image.mode, (l, l, l)) # for RGB verion of grayscale
|
||||
else: # include copy of image's alpha layer
|
||||
a = Image.new("L", image.size)
|
||||
a.putdata(image.getdata(3))
|
||||
merge_args = (image.mode, (l, l, l, a)) # for RGBA verion of grayscale
|
||||
luts += tuple(range(256)) # for 1:1 mapping of copied alpha values
|
||||
|
||||
return Image.merge(*merge_args).point(luts)
|
||||
|
||||
return Image.merge(*merge_args).point(luts)
|
||||
|
||||
def main(input_image_path, tintcolor, result_image_path):
|
||||
image = Image.open(input_image_path)
|
||||
image = Image.open(input_image_path)
|
||||
|
||||
image.load()
|
||||
image.load()
|
||||
|
||||
result = image_tint(image, tintcolor)
|
||||
if os.path.exists(result_image_path): # delete any previous result file
|
||||
os.remove(result_image_path)
|
||||
result.save(result_image_path) # file name's extension determines format
|
||||
result = image_tint(image, tintcolor)
|
||||
if os.path.exists(result_image_path): # delete any previous result file
|
||||
os.remove(result_image_path)
|
||||
result.save(result_image_path) # file name's extension determines format
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
import sys
|
||||
|
||||
main(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
import sys
|
||||
|
||||
main(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue