Skip to content

Instantly share code, notes, and snippets.

@mentlerd
Last active February 21, 2023 20:38
Show Gist options
  • Save mentlerd/8e2f73540aa2e2d0eb55bbe94b8030ce to your computer and use it in GitHub Desktop.
Save mentlerd/8e2f73540aa2e2d0eb55bbe94b8030ce to your computer and use it in GitHub Desktop.
Transparent Windows user avatar
import argparse
import subprocess
import pathlib
import winreg
#
# This script turned out to be quite large, so might as well document the recipe here..
#
# Windows (like many services...) does not support transparent backgrounds for profile
# pictures natively, it squashes beautiful, lossless, transparent .png files into
# various .jpg files with some horrible rescaling algorithm.
#
# Luckily it can be coerced into showing .png files all around the OS, and even gets
# transparency right!
#
# (As long as you are using a local user account, this won't work with Microsoft accounts.)
#
# To do so, you want to change some registry keys:
#
# HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\<SID>
# Image32=...
# Image40=...
# Image64=...
#
# This however cannot be done by mere mortals, not even administrators. You have to use the
# hidden SYSTEM user. For which it is best to use PsExec from SysInternals
#
# So the full recipe is:
# Generate PNG files from your SVG with this very script
# > $ whoami
# > dave-pc\dave
#
# > wmic useraccount where name = 'dave'
# > SID
# > ...
#
# > psexec -i -d -s c:\windows\regedit.exe
#
# PS.: I am aware this is not particularly nice/very Pythonic code - it does what I need, nothing more
#
parser = argparse.ArgumentParser(
prog = "avatargen",
description = "Generate various resolution images for using as a profile picture from a vector image",
epilog = ".. yes the hundredth iteration turned out great, but can we do better?"
)
parser.add_argument('filename')
# Configuration
file = pathlib.Path(parser.parse_args().filename)
targets = [
# Windows
{
"name": "{}",
"area": "bounds_circle",
"sizes": [32, 40, 48, 64, 96, 192, 208, 240, 424, 448, 1080],
},
# Special sizes for "special" sites
{
"name": "github{}",
"area": "bounds_circle",
"sizes": [460]
},
{
"name": "google{}",
"area": "bounds_circle",
"sizes": [500]
},
# Generic
{
"name": "square{}",
"area": "bounds_square",
"sizes": [512]
},
{
"name": "circle{}",
"area": "bounds_circle",
"sizes": [512]
}
]
print("Scanning for required software")
def enum_reg_keys(node):
for index in range(1024):
try:
yield winreg.EnumKey(node, index)
except EnvironmentError:
break
inkscape = None
magick = None
roots = [
r'Software\Microsoft\Windows\CurrentVersion\Uninstall',
r'Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
]
for root in roots:
root = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, root)
for app in enum_reg_keys(root):
app = winreg.OpenKey(root, app)
icon = None
try:
icon = winreg.QueryValueEx(app, 'DisplayIcon')[0]
except EnvironmentError:
continue
icon = pathlib.Path(icon)
if icon.stem == "inkscape" and icon.suffix == ".exe":
inkscape = icon.with_suffix('.com')
if icon.stem == "ImageMagick":
magick = icon.with_name('magick.exe')
print(f"Found Inkscape: {inkscape}")
print(f"Found ImageMagick: {magick}")
bounds = {}
print("Enumerating SVG elements looking for boundary elements")
output = subprocess.check_output([inkscape, '--query-all', file], shell=True)
for line in output.decode('utf-8').splitlines():
line = line.split(',', 5)
if line[0].startswith("bounds_"):
x = float(line[1])
y = float(line[2])
w = float(line[3])
h = float(line[4])
bounds[line[0]] = [int(x), int(y), int(x+w), int(y+h)]
print("Rendering to intermediary supersized bitmaps")
actions = [
f"file-open:{file}",
f"export-type:png",
]
for i, target in enumerate(targets):
for size in target.get('sizes'):
name = target.get('name').format(size)
area = bounds.get(target.get('area'))
area = ':'.join([str(v) for v in area])
res = size * 2
actions += [
f"export-filename: out/{name}",
f"export-width: {res}",
f"export-height: {res}",
f"export-id: beaver",
f"export-id-only",
f"export-area: {area}",
f"export-do"
]
actions = "; ".join(actions)
subprocess.check_call([inkscape, f'--actions={actions}'], shell=True)
print("Downsampling to final resolutions")
print(targets)
for target in targets:
for size in target.get('sizes'):
name = target.get('name').format(size)
subprocess.check_call([
magick,
f'out/{name}.png', '-resize', '50%',
f'out/{name}.png'
], shell=True)
print("Finished")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment