Intersection with CSS hex transform with short hex
What is the correct Hex to Shorthand Hex conversion algorithm? For example: #996633
easily converts to #963
. But what if it's something like #F362C3
?
My first guess was that I just take the first value of each color and agree with that. So it #F362C3
becomes #F6C
. But I don't know how to mathematically justify this approach.
I found this online: http://www.ipaste.org/Jch
function hex_to_shorthand($hex, $uppercase=true)
{
// Remove preceding hash if present
if ($hex[0] == "#") $hex = substr($hex, 1);
// If it already is shorthand, nothing more to do here
if (strlen($hex) == 3) return "#$hex";
// If it is not 6 characters long then it is invalid
elseif (strlen($hex) !== 6) return "";
// The final shorthand HEX value
$final = "";
// Get the triplets
$triplets = str_split($hex, 2);
// Go over each triplet separately
foreach ($triplets as $t)
{
// Get the decimal equivalent of triplet
$dec = base_convert($t, 16, 10);
// Find the remainder
$remainder = $dec % 17;
// Go to the nearest decimal that will yield a double nibble
$new = ($dec%17 > 7) ? 17+($dec-$remainder) : $dec-$remainder;
// Convert decimal into HEX
$hex = base_convert($new, 10, 16);
// Add one of the two identical nibbles
$final .= $hex[0];
}
// Return the shorthand HEX colour value
return $uppercase ? strtoupper($final) : strtolower($final);
}
This seems a little more complicated and again, I'm not sure there is a math behind it. So, something seems to #F362C3
become #E6C
, which I did not expect.
What is the correct way to do this and what is the mathematical proof of how the transformation works?
(the above code is PHP, but can apply to any language)
source to share
The code above is correct and efficient , with time complexity: O (1) .
You need to get the closest color to the original color.
Since there is an RGB code, each color can be thought of as a point that has integer coordinates (0 to 255) in 3D space :
- R -> OX
- G -> OY
- B -> OZ
purpose
Define a point P'(r',g',b')
(output) that is the closest point to P(r,g,b)
(input), where:
- r', g', b' are in {0=0x00, 17=0x11, 34=0x22, ... 255=0xff}
(because only 0x?? can be reduced to ? in CSS, where 0x represents base 16)
- r, g, b are in {0,1,2,3, ..., 255}
What does it mean? We want the minimum distance between P and P 'in 3D space .
So we want it to D = sqrt( (r-r')^2 + (g-g')^2 + (b-b')^2 )
be minimal. This is the distance between two points in 3D space.
obs
Every member >= 0
.
So, if we want minimum D
=>, we want:
- minimum
|r-r'|
- minimum
|g-g'|
- minimum
|b-b'|
Thus, the problem boils down to the following: find the nearest hexadecimal number of two identical characters that is closest to the given hexadecimal number.
As you can see, we have an even number of numbers between xx and yy =>, there is no number at the same distance from xx and yy (where y=x+1
) => we don't need to zoom in anything (For example: we know for sure that 08 is closer to 00 than by 11.):
00, 01, 02, 03, 04, 05, 06, 07, 08 -> close to 00
09, 0A, 0B, 0C, 0D, 0E, 0F, 10, 11 -> close to 11
11, 12, 13, 14, 15, 16, 17, 18, 19 -> close to 11
1A, 1B, 1C, 1D, 1E, 1F, 20, 21, 22 -> close to 22
...
Question : Is the solution unique?
We ask this question because it (r-r')^2 = min
can be achieved in two different ways:
-
r-r1'= sqrt(min)
-
r-r2'=-sqrt(min)
We are only showing this for r'
, because other colors are similar.
We can show uniqueness using two different methods:
-
Add the above lines:
r1' + r2' = 2*r
where
r1'=xx, r2'=yy => r = zz = (x+y)/2(x+y)/2 in (00, ..., ff)
but sincer'-r
is minimal andr=zz
=>r'=zz
=>r1'=r2'
=> unique solution -
Based on the examples above, the question, if we look at a number, cannot find 2 different numbers
r1'=xx
andr2'=yy
havingr-r1'
=r2'-r
because it isr
closer to one of them. It can be at the same distance only ifr=zz
, but in this caser
can be used asr'
(because itr'
should look likezz
and have a minimum distance => a 0 the distance is pretty perfect). => we don't have two solutions (r1'=r2'
) => unique solution
The analog for g ', b' => P '(r', g ', b') is unique (points P are not as close as P ').
You can also see the Java code :
public static void main(String[] args) {
String s = "#F362C3";
System.out.println(hexToShort(s));
}
private static String hexToShort(String hex) {
// if it is short, return
if ( hex.length() == 4 ){
return hex;
}
// remove #
if ( hex.charAt(0) == '#' ) {
hex = hex.substring(1);
}
// check that hex is valid
if ( hex.length() != 6 ) {
return "";
}
String r = hex.substring(0,2);
String g = hex.substring(2,4);
String b = hex.substring(4,6);
return "#" + shortVal(r) + shortVal(g) + shortVal(b);
}
private static String shortVal(String c) {
int ci = Integer.parseInt(c, 16);
return Integer.toString((ci%17 > 7) ? (17+ci-ci%17) : (ci-ci%17), 16).substring(0,1).toUpperCase();
}
source to share