morristech
1/17/2019 - 11:31 PM

A script to convert "material dark" icons to "material" icons, by adjusting the alpha value. The right adjustment seems to be 140 (= increas

A script to convert "material dark" icons to "material" icons, by adjusting the alpha value. The right adjustment seems to be 140 (= increase by 40%).

#!/usr/bin/python

import sys
import argparse
import os
import re
import png

MAPPING = {
	"drawable-mdpi" : "drawable-mdpi",
	"drawable-hdpi" : "drawable-hdpi",
	"drawable-xhdpi" : "drawable-xhdpi-v14",
	"drawable-xxhdpi" : "drawable-xxhdpi-v14",
	# more
	"mdpi" : "drawable-mdpi",
	"hdpi" : "drawable-hdpi",
	"xhdpi" : "drawable-xhdpi-v14",
	"xxhdpi" : "drawable-xxhdpi-v14",
	}

class Scaler:
	def __init__(self, scale):
		self.scale = scale

	def apply(self, alpha):
		alpha = alpha * scale / 100
		if alpha > 255:
			alpha = 255
		return alpha

def shorten_path_pair(path1, path2):
	common = os.path.commonprefix([path1, path2])
	if common == path1:
		return os.path.basename(path1)
	return "{0} -> {1}".format(path1[len(common):], path2[len(common):])

def convert_one(scaler, srcname, dstname):
	pixelsAsList = None
	w = None

	try:
		srcfile = open(srcname, 'r')
	except IOError as x:
		print x
		exit(1)
	with srcfile:
		r=png.Reader(srcfile)
		width, height, pixels, metadata = r.read()
		pixelsAsList = list(pixels)

	if not pixelsAsList:
		print "Error decoding the image"
		exit(1)

	print "{0}: {1}x{2} -> {3}".format(os.path.basename(srcname), width, height, os.path.basename(dstname))

	#print metadata

	if metadata['bitdepth'] == 8 and metadata['planes'] == 4 and metadata['alpha']:
		for row in pixelsAsList:
			for offset in xrange(3, width * 4, 4):
				row[offset] = scaler.apply(row[offset])
		w = png.Writer(width=width, height=height, bitdepth=8, alpha=True)
	elif metadata['bitdepth'] == 8 and metadata['planes'] == 2 and metadata['alpha']:
		for row in pixelsAsList:
			for offset in xrange(1, width * 2, 2):
				row[offset] = scaler.apply(row[offset])
		w = png.Writer(width=width, height=height, bitdepth=8, greyscale=True, alpha=True)

	if w:
		try:
			dstfile = open(dstname, 'w')
		except IOError as x:
			print x
			exit(1)
		with dstfile:
			w.write(dstfile, pixelsAsList)

def convert_xdens(scaler, srcname, dstname):
	# Do the specified pair
	convert_one(scaler, srcname, dstname)

	# Find the rest
	srcbase = os.path.dirname(os.path.dirname(os.path.abspath(srcname)))
	dstbase = os.path.dirname(os.path.dirname(os.path.abspath(dstname)))
	for srcnm in os.listdir(srcbase):
		srcdir = os.path.join(srcbase, srcnm)
		if os.path.isdir(srcdir):
			if srcnm.startswith('drawable-'):
				dstnm = MAPPING.get(srcnm, srcnm)
				dstdir = os.path.join(dstbase, dstnm)

				if os.path.exists(dstdir) and os.path.isdir(dstdir):
					src1 = os.path.join(srcdir, os.path.basename(srcname))
					dst1 = os.path.join(dstdir, os.path.basename(dstname))
					if os.path.exists(src1) and not os.path.samefile(srcname, src1):
						print "\nDirectory {0}".format(shorten_path_pair(srcdir, dstdir))
						convert_one(scaler, src1, dst1)

def convert_dir(scaler, dirname):
	dirprinted = False

	for nm in os.listdir(dirname):
		nmf = os.path.join(dirname, nm)
		if os.path.isdir(nmf):
			if nm.startswith('drawable-'):
				convert_dir(scaler, nmf)
		else:
			m = re.match('(?:bb_)?ic_menu_[a-zA-Z_]+_holo_dark\\.png', nm)
			if m != None:
				if not dirprinted:
					print "\n>> {0}".format(dirname)
					dirprinted = True
				mt = nm.replace("_holo_dark", "_material")
				convert_one(scaler, nmf, os.path.join(dirname, mt))

if __name__ == "__main__":
	'''
	Parse command line arguments
	'''
	parser = argparse.ArgumentParser(description='Scales alpha values of png images')
	parser.add_argument('-s', dest='scale', required=True, help='scale value (percent)')
	parser.add_argument('-d', dest='dir', required=False, help='convert all files under directory')
	parser.add_argument('-x', dest='xdens', action='store_true', required=False, help='automatically apply to resources under other desnities')
	parser.add_argument('src', nargs='?', help='source file')
	parser.add_argument('dst', nargs='?', help='destination file')
	args = parser.parse_args()

	scale = 100
	try:
		scale = int(args.scale)
	except ValueError as x:
		print x
		exit(1)

	scaler = Scaler(args.scale)

	if args.dir:
		if args.src or args.dst:
			print "Source and destination files should not be there"
			exit(1)
		convert_dir(scaler, args.dir)
		pass
	else:
		if not args.src or not args.dst:
			print "Source and destination files are required"
			exit(1)
		if args.xdens:
			print "Scaling all densities"
			convert_xdens(scaler, args.src, args.dst)
		else:
			convert_one(scaler, args.src, args.dst)
			pass