CSS Hex到速记十六进制转换

时间:2014-09-01 14:19:42

标签: css algorithm math hex

将Hex转换为Shorthand Hex的正确算法是什么?例如:#996633很容易转换为#963。但如果它像#F362C3那样呢?

我的第一个猜测是,我只取每种颜色的第一个值,然后继续使用。因此#F362C3变为#F6C。但我不知道如何在数学上证明这种方法的合理性。

我在网上找到了这个: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);
}

这似乎有点复杂,我不确定它背后的数学理由是什么。因此#F362C3之类的内容会变成#E6C,这不是我所期望的。

这样做的正确方法是什么,以及转换如何运作的数学证明是什么?

(上面的代码是PHP但可以应用于任何语言)

1 个答案:

答案 0 :(得分:5)

以上代码正确高效,时间复杂: O(1)

您需要获取最接近颜色的初始颜色。

因为有RGB代码,所以颜色可以被视为,在 3D空间中具有整数坐标(0到255之间)

- R -> OX
- G -> OY
- B -> OZ

目的

确定与P'(r',g',b')(输入)最近的点P(r,g,b)(输出),其中:

- 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}

这是什么意思?我们希望3D空间中的 P和P'之间的最小距离

因此,我们希望D = sqrt( (r-r')^2 + (g-g')^2 + (b-b')^2 )最小化。这是3D空间中2个点之间的距离。

<强>观测值

每个成员都是>= 0

因此,如果我们想要最低D =&gt;我们想要:

  • 最低|r-r'|
  • 最低|g-g'|
  • 最低|b-b'|

因此,问题归结为:找到最接近给定十六进制数的2个相同字符的最接近的十六进制数。

如您所见,我们在xx和yy =&gt;之间有一个偶数个数字。在xx和yy的相同距离处没有数字(其中y=x+1)=&gt;我们不需要近似任何东西(例如:我们确定08更接近00而不是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

...

问题:解决方案是否独一无二?

我们提出这个问题是因为(r-r')^2 = min可以通过两种不同的方式实现:

  • r-r1'= sqrt(min)
  • r-r2'=-sqrt(min)

我们仅针对r'进行演示,因为其他颜色相似。

我们可以使用两种不同的方法显示唯一性:

  1. 让我们添加以上几行:

    r1' + r2' = 2*r

    其中r1'=xx, r2'=yy => r = zz = (x+y)/2(x+y)/2 in (00, ..., ff) 但因为r'-r是最小值而r=zz =&gt; r'=zz =&gt; r1'=r2' =&gt; 独特解决方案

  2. 根据上述问题的示例,如果我们考虑一个数字,我们找不到2个不同的数字r1'=xxr2'=yy r-r1' = {{1}因为r2'-r更接近其中一个。它只能在r时处于相同的距离,但在这种情况下,r=zz可以用作r(因为r'必须看起来像r'并且最小距离=> 0距离非常完美)。 =&GT;我们没有2个解决方案(zz)=&gt; 独特解决方案

  3. g'的类似物,b'=&gt; P'(r',g',b')是唯一的(P没有与P'接近的点。)


    您还可以看到 Java代码

    r1'=r2'