mklement0 / fileicon

macOS CLI for managing custom icons for files and folders

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Image quality too low

onury opened this issue · comments

Hi, I used a 1000x1500 pixels image to set a folder's icon but the icon's image quality (resolution) is too low and pixelated. Is it possible to improve this?

@onury: Unfortunately, the maximum resolution supported by fileicon is 128 x 128 pixels, because it uses sips -i behind the scenes (the man page states this restriction, but I can see how that's easy to miss).

sips doesn't offer any control over what image(s) and resolutions are assigned as an icon when using -i, unfortunately.

Bearing in mind that fileicon is a Bash script and can therefore only invoke CLIs: if you yourself can think of a way to remedy this, do let me know.

fair enough. thanks.

I found an alternative: seticon(1) from osxutils. Source code is here. brew install osxutils.

Unfortunately it doesn't seem to work on directories right now. I have filed an issue specious/osxutils#4. But it does work on files, so you can combine the power of seticon and fileicon... In the sense that you basically replace the sips call in fileicon with seticon -d, given an icns source file.

If you don't have an icns icon though, you can always create one with

sips -s format icns --out out.icns in.png

EDIT: Simplified the workaround.

Here's a patch that demonstrates the workaround: (requires seticon(1) to see the improvement)

diff --git a/bin/fileicon b/bin/fileicon
index 4f8040d..99499ff 100755
--- a/bin/fileicon
+++ b/bin/fileicon
@@ -196,11 +196,20 @@ setCustomIcon() {

   # Create a temp. dir. and a temp. copy of the image file inside it.
   tmpDir=$(/usr/bin/mktemp -d -t "$kTHIS_NAME") || return
-  sourceFileWithResourceFork=$tmpDir/${imgFile##*/}
-  cp "$imgFile" "$sourceFileWithResourceFork" || return
-  # Assign an icon representation of the image file to the image file itself.
-  # This creates the resource fork we'll copy.
-  sips -i "$sourceFileWithResourceFork" >/dev/null || return
+
+  if command -v seticon &>/dev/null; then
+      sourceFileWithResourceFork=$tmpDir/${imgFile##*/}.icns
+      # Convert image to icns
+      sips -s format icns --out "$sourceFileWithResourceFork" "$imgFile" &>/dev/null || return
+      # Use seticon to set icns data to its own resource fork
+      seticon -d "$sourceFileWithResourceFork" "$sourceFileWithResourceFork" || return
+  else
+      sourceFileWithResourceFork=$tmpDir/${imgFile##*/}
+      cp "$imgFile" "$sourceFileWithResourceFork" || return
+      # Assign an icon representation of the image file to the image file itself.
+      # This creates the resource fork we'll copy.
+      sips -i "$sourceFileWithResourceFork" >/dev/null || return
+  fi

   # Make sure a resource fork with icons was actually created.
   hasIconsResource "$sourceFileWithResourceFork" || { echo "Failed to create resource fork with icons. Typically, this means that the input file is not a (valid) image file: $sourceFileWithResourceFork" >&2; return 1; }

@zmwangx: I appreciate the effort, but I'd really like to keep fileicon self-contained. seticon seemingly no longer being actively maintained (and the source no longer compiling when I last tried) was the reason I created fileicon in the first place.

I'm open to a JXA-based solution (10.10+) that can be invoked with osascript from inside fileicon however - I haven't had time to look at it.

but I'd really like to keep fileicon self-contained.

Of course, that's why I didn't open a PR. Just putting out a patch for anyone interested.

JXA-based solution

That's also on my laundry list... On the one hand I haven't had time, on the other hand osascript is so obnoxious in general, I'm very reluctant to learn anything new about it ;) (Remember error -10810? I was the OP on that thread.)

Re patch: got it - and appreciated.

Re osascript - ah, yes, I remember. Yes, osascript, AppleScript, and even the (somewhat) shiny (relatively) new JXA seem to be Apple's unwanted step-children.

I've just published v2.0, which uses an embedded Python script that calls a Cocoa API, courtesy of this Ask Different answer, which replaces the previous sips -i solution, because sips -i is no longer supported in macOS 10.13 (High Sierra).

A beneficial side effect of this change in implementation is that now a set of icons is created, with the highest-resolution icon measuring 512 x 512 pixels (previously, due to use of sips -i, a single icon was created, at a resolution of 128 x 128).

Note sure why the API used tops out at 512 x 512, given that retina Macs support 1024 x 1024, but at least it's a noticeable improvement.

A beneficial side effect of this change in implementation is that now a set of icons is created, with the highest-resolution icon measuring 512 x 512 pixels (previously, due to use of sips -i, a single icon was created, at a resolution of 128 x 128).

Note sure why the API used tops out at 512 x 512, given that retina Macs support 1024 x 1024, but at least it's a noticeable improvement.

@mklement0 I wonder if there is any way to utilise /usr/bin/iconutil to get 1024 x 1024…?

@danielbayley, the iconutil utility is for converting between preexisting .iconset directories containing images and .icns files; how do you see it helping with the max. resolution problem?

@mklement0 I wrote a script that creates an .iconset from a single 1024 x 1024 source image using sips and then converts that to an .icns (containing the full set of 10 sizes) using iconutil, so it's easy to convert both ways… I was just wondering if it was possible for fileicon set to apply this full .icns set at least, if not able to also extract it with get… are you saying that the API limits this in both cases?

It sounds like it, yes:

This method uses the image to set an icon whose size is 512 by 512 pixels.

But my knowledge is superficial in this area. If you have further insights, do share them.