Backfill icons from provided images
Briefcase currently requires that the user provide icons of in various formats. However, if you have a high-res PNG format, any other format can be generated by image conversion.
Briefcase should try to use a correctly named file in the right size and format if it's available; but if it isn't, it should try to backfill by converting the high-res PNG to the needed size and format. This should be clearly logged so the end user knows what has happened.
Pillow can be used for most of these operations, but Pillow doesn't currently support ICNS; however, macOS ships with a tool to generate ICNS files from PNGs.
Creating an ICNS file:
Into a directory named myicon.iconset, put files with the following names and sizes:
-
[email protected] -
icon_512x512.png -
[email protected] -
icon_256x256.png -
[email protected] -
icon_128x128.png -
[email protected] -
icon_32x32.png -
[email protected] -
icon_16x16.png
A 16x16@2x icon is 32x32 pixels; so yes - you need to have 2 copies of the same 32x32 icon (if you're paying close attention to the details you can specify a different icon; however, if you're autoscaling from other sizes, you're not paying that sort of attention).
Then, invoke: iconutil -c icns myicon.iconset. This will produce myicon.icns.
pillow now knows about iconutil
https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#icns
https://github.com/python-pillow/Pillow/blob/master/src/PIL/IcnsImagePlugin.py#L306
def _save(im, fp, filename):
"""
Saves the image as a series of PNG files,
that are then converted to a .icns file
using the macOS command line utility 'iconutil'.
macOS only.
"""
and linux has png2icns / icns2png
https://icns.sourceforge.io/
https://archlinux.org/packages/community/x86_64/libicns/
usr/
usr/bin/
usr/bin/icns2png
usr/bin/icontainer2icns
usr/bin/png2icns
usr/include/
usr/include/icns.h
usr/lib/
usr/lib/libicns.so
usr/lib/libicns.so.1
usr/lib/libicns.so.1.2.0
usr/lib/pkgconfig/
usr/lib/pkgconfig/libicns.pc
usr/share/
usr/share/man/
usr/share/man/man1/
usr/share/man/man1/icns2png.1.gz
usr/share/man/man1/icontainer2icns.1.gz
usr/share/man/man1/png2icns.1.gz
an easy solution could be to include into the default app template a linux script which produces images for all platforms, for example:
#!/usr/bin/env bash
set -e -u
image_source=${1:-arkon.png}
image_target=${2:-tasker}
image_folder=${3:-.}
echo "### master image: $image_source"
echo "### image prefix: $image_target"
echo "### image folder: $image_folder"
#
hash convert || { echo "missing imagemagick/convert" ; exit 1 ; }
hash png2icns || { echo "missing libicns/png2icns" ; exit 1 ; }
[ -f ${image_source} ] || { echo "missing image_source: ${image_source}" ; exit 1 ; }
[ -d ${image_folder} ] || mkdir -p ${image_folder}
#
proper_spec_list=(
"24x1" "48x1" "64x1" "96x1" "128x1"
"20x1" "20x2" "20x3"
"29x1" "29x2" "29x3"
"40x1" "40x2" "40x3"
"60x2" "60x3"
"76x1" "76x2"
"83.5x2" "1024x1"
)
convert_proper() {
for image_spec in ${proper_spec_list[*]} ; do
size=${image_spec%x*}
scale=${image_spec##*x}
resize=$(bc <<< ${size}*${scale} )
echo "### apply ${image_source} spec: ${size}x${size}@${scale}"
convert ${image_source} \
-resize ${resize}x${resize} \
-unsharp '1.5x1+0.7+0.02' \
${image_folder}/${image_target}-${size}x${size}@${scale}x.png
done
}
android_spec_list=(
48 72 96 144 192
)
convert_android() {
convert_android_round
convert_android_square
}
convert_android_round() {
file_type="round"
for image_spec in ${android_spec_list[*]} ; do
size=${image_spec}
scale=1
resize=$(bc <<< ${size}*${scale} )
corner=$(bc <<< ${size}*0.5 ) # 50%
file_name="${image_target}-${file_type}-${size}.png"
file_path="${image_folder}/${file_name}"
mask_path="${image_folder}/round-mask.png"
echo "### apply/android spec: ${size}x${size}@${scale} file: $file_name"
# produce mask
convert -size ${resize}x${resize} xc:none \
-draw "roundrectangle 0,0,${resize},${resize},${corner},${corner}" \
${mask_path}
# produce resize
convert ${image_source} \
-resize ${resize}x${resize} \
-unsharp '1.5x1+0.7+0.02' \
${file_path}
# produce composite
convert ${file_path} \
-matte ${mask_path} \
-compose DstIn -composite \
${file_path}
# destroy mask
rm ${mask_path}
done
}
convert_android_square() {
file_type="square"
for image_spec in ${android_spec_list[*]} ; do
size=${image_spec}
scale=1
resize=$(bc <<< ${size}*${scale} )
file_name="${image_target}-${file_type}-${size}.png"
file_path="${image_folder}/${file_name}"
echo "### apply/android spec: ${size}x${size}@${scale} file: $file_name"
convert ${image_source} \
-resize ${resize}x${resize} \
-unsharp '1.5x1+0.7+0.02' \
${file_path}
done
}
ios_spec_list=(
20 29 40 58 60 76 80 87 120 152 167 180 1024
)
convert_ios() {
for image_spec in ${ios_spec_list[*]} ; do
size=${image_spec}
scale=1
resize=$(bc <<< ${size}*${scale} )
file_name="${image_target}-${size}.png"
file_path="${image_folder}/${file_name}"
echo "### apply/ios spec: ${size}x${size}@${scale} file: $file_name"
convert ${image_source} \
-resize ${resize}x${resize} \
-unsharp '1.5x1+0.7+0.02' \
${file_path}
done
}
macos_spec_list=(
16 32 48 128 256 512
)
convert_macos() {
file_list=""
for image_spec in ${macos_spec_list[*]} ; do
size=${image_spec}
scale=1
resize=$(bc <<< ${size}*${scale} )
file_name="${image_target}-${size}.png"
file_path="${image_folder}/${file_name}"
echo "### apply/macos spec: ${size}x${size}@${scale} file: $file_name"
convert ${image_source} \
-resize ${resize}x${resize} \
-unsharp '1.5x1+0.7+0.02' \
${file_path}
file_list="${file_list} ${file_path}"
done
png2icns "${image_target}.icns" ${file_list[@]}
}
windows_spec_list="16,24,32,48,64,72,96,128,256"
convert_windows() {
echo "### apply/windows spec: ${windows_spec_list}"
convert ${image_source} \
-background transparent \
-define icon:auto-resize=${windows_spec_list} \
"${image_folder}/${image_target}.ico"
}
convert_default() {
size="256"
echo "### apply/default size: ${size}"
src="${image_target}-256.png"
dst="${image_target}.png"
cp ${src} ${dst}
}
convert_android
convert_ios
convert_macos
convert_windows
convert_default