4

It is possible to create an AppleScript that calls for the standard Mac OS Color Picker dialog (choose color) and gets the output value after user picks some color:

set the rez to choose color
set the rezStr to rez2string(rez)
set the clipboard to rezStr

on rez2string({r, g, b})
    return "(" & r & ", " & g & ", " & b & ")" as string
end rez2string

So, selected color value will be copied to the clipboard.

My question is: can I control the output of this Color Picker dialog in AppleScript? By default it returns the color value in this format: (59432, 59441, 59428), which is a 16-bit high color, I believe.

Can I specify somehow that I want to get a "standard" RGB (with 255 as a maximum value) instead? Or any other? Or 16-bit high color is the only supported output format for choose color? That would be a pity, because as far as I know there is no way to get proper RGB/Hex values from 16-bit high color as it would involve rounding and approximation.

I mean, the very same standard Color Picker dialog that is called by choose color has RGB values (along with the Hex one) on its second tab (Color Sliders), so it is capable of returning values in those formats. But are those available to get in AppleScript?

enter image description here

Update 1.

I wrote a blog-post with more details about the problem.

Update 2.

As Jon pointed out in his answer, the problem originates in the eye-dropper, so scripts are actually able to convert from 16-bit high color into "normal" 255-RGB and then HEX, but due to the fact that eye-dropper outputs incorrect value (from different color space, apparently), all further calculations are doomed.

1

First, this is the script I use:

set format to (button returned of (display dialog "Select the format of the chosen color for the clipboard." buttons {"Cancel", "HEX", "RGB"} default button 3 with icon 1 with title "Color Picker"))
set {r, g, b} to choose color
set {r, g, b} to {_16_to_8(r), _16_to_8(g), _16_to_8(b)}
if (format = "RGB") then
    set the clipboard to ("rgb(" & r & ", " & g & ", " & b & ")")
else
    set the clipboard to ("#" & {dec_to_hex(r), dec_to_hex(g), dec_to_hex(b)})
end if

on _16_to_8(n)
    return ((n / 65535) * 255) as integer
end _16_to_8

property hex_chars : "0123456789ABCDEF"'s characters
on dec_to_hex(n)
    if (n = 0) then return "00"
    return (hex_chars's item (((n div 16) mod 16) + 1)) & (hex_chars's item ((n mod 16) + 1))
end dec_to_hex

The issue you are seeing is specific to the color you chose ({236, 236, 236} / ECECEC) using the eye-dropper tool. The picker showed it as {236, 236, 236} but when returned via AppleScript, it was {59438, 59436, 59437} or something close where the RGB values are not the same (i.e., not a true gray). If you open the color picker and manually enter 236 for each of the RGB values (do not use the eye-dropper tool) and then return the color, it will return the RGB result as 3 equal values ({60652, 60652, 60652}) that will then be coerced properly by the script above.

I believe there is a rounding issue with the color picker and the eye-dropper tool as it shows, by default, 8-bit (0-255) integers instead of floating point (0.0-1.0). When you pickup the color from your example image using the eye-dropper tool and switch to the grayscale slider, it will show as 91%. This is incorrect. The value should be 92.5% but the picker doesn't show floats, only integers in the grayscale slider (you can change to floats in the RGB slider section from the action button to the right of the slider type popup button). If you are in the grayscale slider section, manually enter 92.5 and then hit the tab key to have the value accepted by the picker then hit the OK button and again, you should see the proper results back in AppleScript.

So, if the result from AppleScript is not what you are seeing in the picker, just copy the HEX value from the picker's HEX field and cancel the picker by hitting esc or the Cancel button. If you want the RGB colors, run the script again but instead of using the eye-dropper tool, paste the HEX value in the Hex field and press tab to commit the entry then hit return or OK and the proper RGB values should be on the clipboard. A pain, no doubt, but if precision is important to you, perhaps the way to go.

Off topic but using the code above, you can also convert ASCII to HEX:

string_to_hex("Convert to hexadecimal!")

property hex_chars : "0123456789ABCDEF"'s characters
on dec_to_hex(n)
    if (n = 0) then return "00"
    return (hex_chars's item (((n div 16) mod 16) + 1)) & (hex_chars's item ((n mod 16) + 1))
end dec_to_hex

on string_to_hex(s)
    set r to {}
    repeat with c in s's characters
        set end of r to dec_to_hex(ASCII number c)
    end repeat
    return r as string
end string_to_hex

UPDATE

Here's a revised script that will look to see if the user canceled the color picker with the HEX code copied to the clipboard. If both of those conditions are true, then the script will use the copied HEX code to add the RGB or HEX code to the clipboard. An extra few awkward steps (select and copy the HEX string, then cancel the color picker) but perhaps feasible for your needs.

try
    set format to (button returned of (display dialog "Select the format of the chosen color for the clipboard." buttons {"Cancel", "HEX", "RGB"} default button 3 with icon 1 with title "Color Picker"))
    set {r, g, b} to choose color
    set {r, g, b} to {_16_to_8(r), _16_to_8(g), _16_to_8(b)}
on error e number n
    if (n = -128) then --user canceled
        set clipboard_string to (get the clipboard)
        if ((clipboard_string count) = 6) then
            set {r, g, b} to hex_string_to_dec(clipboard_string)
        else
            return
        end if
    else
        log {n:n, e:e}
        return
    end if
end try
if (format = "RGB") then
    set the clipboard to ("rgb(" & r & ", " & g & ", " & b & ")")
else
    set the clipboard to ("#" & {dec_to_hex(r), dec_to_hex(g), dec_to_hex(b)})
end if

on _16_to_8(n)
    return ((n / 65535) * 255) as integer
end _16_to_8

property hex_chars : "0123456789ABCDEF"'s characters
on dec_to_hex(n)
    if (n = 0) then return "00"
    return (hex_chars's item (((n div 16) mod 16) + 1)) & (hex_chars's item ((n mod 16) + 1))
end dec_to_hex

property hex_to_dec_chars : "0.1.2.3.4.5.6.7.8.9AaBbCcDdEeFf"
on hex_to_dec(s)
    set s to s's items's reverse
    set {d, p} to {0, 0}
    repeat with c in s
        set d to d + (((offset of c in hex_to_dec_chars) div 2) * (16 ^ p))
        set p to p + 1
    end repeat
    return d as integer
end hex_to_dec

on hex_string_to_dec(s)
    if character 1 of s = "#" then set s to s's text 2 thru -1
    set r to {}
    repeat with x from 1 to length of s by 2
        set end of r to hex_to_dec(s's text x thru (x + 1))
    end repeat
    return r
end hex_string_to_dec
  • 1
    But the key is that it is possible to get the info you want out of the picker and AS, just not always with the eye-dropper tool (though sometimes it works fine). There are edge cases when the eye-dropper sets an internal value that has a rounding error that is corrected in display but not in the AS return. It is also possible to check the contents of the clipboard after running the color picker via AS and getting a cancel response to see if the contents are a HEX string (if you copied it) and then convert that HEX string to RGB (I can post code for that, too, if you're interested). – Jon Apr 3 '18 at 21:11
  • 1
    That's not precisely true. If you open the picker and instead of using the eye-dropper, manually enter 236 for each of the RGB values, you will get back the proper 16-bit result in AS that will coerce to the correct ECECEC HEX value using the code I posted. If you copy the HEX and cancel, the script can catch this and convert to RGB if you want (not shown in the code above), regardless of how the picker got the value (manual or via eye-dropper). – Jon Apr 3 '18 at 21:53
  • 1
    Digging further (as mentioned in the comment on your blog post) the issue is that when you manually enter values, the color picker uses the calibrated RGB color space (NSCalibratedRGBColorSpace) — or whatever is selected in the color space action menu — but when using the eye-dropper, it uses the sRGB IEC61966-2.1 color space and the conversion between the color spaces causes the discrepancy you are seeing. This is not related to AppleScript and, as far as I know, there is no way to configure the NSColorPanel via AS, Objective-C or otherwise to choose the color space the eye-dropper uses. – Jon Apr 4 '18 at 0:47
  • 1
    That is not correct. It is returning the proper RGB values that are coerced to the proper HEX values for the color space used by the eye-droppe:r sRGB. This is consistent and reproducible. Your issue is that you don't want the color in the sRGB color space but in a calibrated device RGB color space. The distinction may be subtle but your insistence that it is impossible to get the correct HEX value out of the color picker is not correct AND has nothing to do with AppleScript. – Jon Apr 4 '18 at 14:00
  • 1
    Using any of the scripts referenced. The issue is not the HEX conversion, it is that the eye-dropper is returning RGB values in the Device RGB color space and you want them from the sRGB (NSCalibratedRGBColorSpace) [I flipped them before, sorry]. You can specify the color space when manually entering a value but not when using the eye-dropper because it picks up the pixel values from the display in the Device RGB space. If absolute color fidelity is important, the color picker is not the tool for you or, at least, use a CMS like Pantone (Pantone palettes are available for the color picker). – Jon Apr 4 '18 at 19:47
0

I could not find the method to change the output of the ColorPicker, but you can convert the output by yourself with:

set the rez to choose color
set the rezStr to rez2string(rez)
set the clipboard to rezStr

on rez2string({r, g, b})
    return "(" & to8bit(r) & ", " & to8bit(g) & ", " & to8bit(b) & ")" as string
end rez2string

on to8bit(x)
    return (round (x / 256) rounding down) as integer
end to8bit

It uses rounding, so small values can be a little off.

  • But that's the problem - if you compare values after conversion to RGB values from Color Sliders tab, you'll see that they are different. Yes, it's a pretty close approximation/rounding, but that's not the same color. I found lots of conversion formulas, but they are faulty "by design". So the only way to get proper values is to control choose color output format (which is not possible, apparently). – retif Nov 14 '17 at 15:11
0

Since the AppleScript answer wasn't possible to achieve, I have an alternative.

If you want a stand-alone version of the native color picker that features easy access to copying color codes, I would recommend installing Hues 1.0: zachwaugh/hues: Hues color picker for Mac

I'v been using it for years and despite having bought all the others on the market, I keep coming back to Hues (and Sip). It's based on the native picker, so support for all your add-ons is there [which is why I ♡ it].


Another option would be using plugins along with the choose color AppleScript or ColorPicker:


Developer Color Picker

It still isn't a pick-color-and-automatically-copy-to-clipboard solution, but it's pretty close if you want to stick with the default color picker and don't want to use third-party apps like Sip.


Skala Color

For instance or even the old Hex Color Picker by waffle software (if it's still working).

  • 1
    It is quite a nice app, but unfortunately it produces the wrong output too (because most probably it uses the same approximation as others). Here's a screenshot comparing values of color for the same sample - as you can see, MacOS color picker shows #ECECEC (correct) and Hues shows #E7E7E7 (wrong): i.imgur.com/BXSMpyp.png – retif Feb 1 '18 at 9:48
  • 1
    @retif Thanks for the pointer. Double checked it and I'm glad I posted this here, otherwise I'd have taken the accuracy of Hues for granted. (Hm, got to work on that AppleScript solution at one point.) – patrick Feb 1 '18 at 10:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .