[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/splitbrain/lesserphp/src/Functions/ -> ColorOperation.php (source)

   1  <?php
   2  
   3  namespace LesserPHP\Functions;
   4  
   5  use Exception;
   6  use LesserPHP\Utils\Asserts;
   7  use LesserPHP\Utils\Color;
   8  use LesserPHP\Utils\Util;
   9  
  10  /**
  11   * Implements the Color Operation functions for LESS
  12   *
  13   * @todo inheritance from ColorChannels is only until we figure out how the alpha() method should work
  14   * @link https://lesscss.org/functions/#color-operations
  15   */
  16  class ColorOperation extends ColorChannels
  17  {
  18      /** @inheritdoc */
  19      public function getFunctions(): array
  20      {
  21          return [
  22              'saturate' => [$this, 'saturate'],
  23              'desaturate' => [$this, 'desaturate'],
  24              'lighten' => [$this, 'lighten'],
  25              'darken' => [$this, 'darken'],
  26              'fadein' => [$this, 'fadein'],
  27              'fadeout' => [$this, 'fadeout'],
  28              'fade' => [$this, 'fade'],
  29              'spin' => [$this, 'spin'],
  30              'mix' => [$this, 'mix'],
  31              'tint' => [$this, 'tint'],
  32              'shade' => [$this, 'shade'],
  33              //'greyscale' => [$this, 'greyscale'],
  34              'contrast' => [$this, 'contrast'],
  35          ];
  36      }
  37  
  38  
  39      /**
  40       * Increase the saturation of a color in the HSL color space by an absolute amount
  41       *
  42       * @link https://lesscss.org/functions/#color-operations-saturate
  43       * @throws Exception
  44       */
  45      public function saturate(array $args): array
  46      {
  47          [$color, $delta] = $this->colorArgs($args);
  48  
  49          $hsl = Color::toHSL($color);
  50          $hsl[2] = Util::clamp($hsl[2] + $delta, 100);
  51          return Color::toRGB($hsl);
  52      }
  53  
  54      /**
  55       * Decrease the saturation of a color in the HSL color space by an absolute amount
  56       *
  57       * @link https://lesscss.org/functions/#color-operations-desaturate
  58       * @throws Exception
  59       */
  60      public function desaturate(array $args): array
  61      {
  62          [$color, $delta] = $this->colorArgs($args);
  63  
  64          $hsl = Color::toHSL($color);
  65          $hsl[2] = Util::clamp($hsl[2] - $delta, 100);
  66          return Color::toRGB($hsl);
  67      }
  68  
  69      /**
  70       * Increase the lightness of a color in the HSL color space by an absolute amount
  71       *
  72       * @link https://lesscss.org/functions/#color-operations-lighten
  73       * @throws Exception
  74       */
  75      public function lighten(array $args): array
  76      {
  77          [$color, $delta] = $this->colorArgs($args);
  78  
  79          $hsl = Color::toHSL($color);
  80          $hsl[3] = Util::clamp($hsl[3] + $delta, 100);
  81          return Color::toRGB($hsl);
  82      }
  83  
  84      /**
  85       * Decrease the lightness of a color in the HSL color space by an absolute amount
  86       *
  87       * @link https://lesscss.org/functions/#color-operations-darken
  88       * @throws Exception
  89       */
  90      public function darken(array $args): array
  91      {
  92          [$color, $delta] = $this->colorArgs($args);
  93  
  94          $hsl = Color::toHSL($color);
  95          $hsl[3] = Util::clamp($hsl[3] - $delta, 100);
  96          return Color::toRGB($hsl);
  97      }
  98  
  99      /**
 100       * Decrease the transparency (or increase the opacity) of a color, making it more opaque
 101       *
 102       * @link https://lesscss.org/functions/#color-operations-fadein
 103       * @throws Exception
 104       */
 105      public function fadein(array $args): array
 106      {
 107          [$color, $delta] = $this->colorArgs($args);
 108          $color[4] = Util::clamp(($color[4] ?? 1) + $delta / 100);
 109          return $color;
 110      }
 111  
 112      /**
 113       * Increase the transparency (or decrease the opacity) of a color, making it less opaque
 114       *
 115       * @link https://lesscss.org/functions/#color-operations-fadeout
 116       * @throws Exception
 117       */
 118      public function fadeout(array $args): array
 119      {
 120          [$color, $delta] = $this->colorArgs($args);
 121          $color[4] = Util::clamp(($color[4] ?? 1) - $delta / 100);
 122          return $color;
 123      }
 124  
 125      /**
 126       * Set the absolute opacity of a color.
 127       * Can be applied to colors whether they already have an opacity value or not.
 128       *
 129       * @link https://lesscss.org/functions/#color-operations-fade
 130       * @throws Exception
 131       */
 132      public function fade(array $args): array
 133      {
 134          [$color, $alpha] = $this->colorArgs($args);
 135          $color[4] = Util::clamp($alpha / 100.0);
 136          return $color;
 137      }
 138  
 139      /**
 140       * Rotate the hue angle of a color in either direction
 141       *
 142       * @link https://lesscss.org/functions/#color-operations-spin
 143       * @throws Exception
 144       */
 145      public function spin(array $args): array
 146      {
 147          [$color, $delta] = $this->colorArgs($args);
 148  
 149          $hsl = Color::toHSL($color);
 150  
 151          $hsl[1] = $hsl[1] + $delta % 360;
 152          if ($hsl[1] < 0) $hsl[1] += 360;
 153  
 154          return Color::toRGB($hsl);
 155      }
 156  
 157      /**
 158       * mixes two colors by weight
 159       * mix(@color1, @color2, [@weight: 50%]);
 160       *
 161       * @link https://lesscss.org/functions/#color-operations-mix
 162       * @throws Exception
 163       */
 164      public function mix(array $args): array
 165      {
 166          if ($args[0] != 'list' || count($args[2]) < 2) {
 167              throw new Exception('mix expects (color1, color2, weight)');
 168          }
 169  
 170          [$first, $second] = $args[2];
 171          $first = Asserts::assertColor($first);
 172          $second = Asserts::assertColor($second);
 173  
 174          $first_a = $this->alpha($first);
 175          $second_a = $this->alpha($second);
 176  
 177          if (isset($args[2][2])) {
 178              $weight = $args[2][2][1] / 100.0;
 179          } else {
 180              $weight = 0.5;
 181          }
 182  
 183          $w = $weight * 2 - 1;
 184          $a = $first_a - $second_a;
 185  
 186          $w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
 187          $w2 = 1.0 - $w1;
 188  
 189          $new = [
 190              'color',
 191              $w1 * $first[1] + $w2 * $second[1],
 192              $w1 * $first[2] + $w2 * $second[2],
 193              $w1 * $first[3] + $w2 * $second[3],
 194          ];
 195  
 196          if ($first_a != 1.0 || $second_a != 1.0) {
 197              $new[] = $first_a * $weight + $second_a * ($weight - 1);
 198          }
 199  
 200          return Color::fixColor($new);
 201      }
 202  
 203      /**
 204       * Mix color with white in variable proportion.
 205       *
 206       * It is the same as calling `mix(#ffffff, @color, @weight)`.
 207       *
 208       *     tint(@color, [@weight: 50%]);
 209       *
 210       * @link https://lesscss.org/functions/#color-operations-tint
 211       * @throws Exception
 212       * @return array Color
 213       */
 214      public function tint(array $args): array
 215      {
 216          $white = ['color', 255, 255, 255];
 217          if ($args[0] == 'color') {
 218              return $this->mix(['list', ',', [$white, $args]]);
 219          } elseif ($args[0] == 'list' && count($args[2]) == 2) {
 220              return $this->mix([$args[0], $args[1], [$white, $args[2][0], $args[2][1]]]);
 221          } else {
 222              throw new Exception('tint expects (color, weight)');
 223          }
 224      }
 225  
 226      /**
 227       * Mix color with black in variable proportion.
 228       *
 229       * It is the same as calling `mix(#000000, @color, @weight)`
 230       *
 231       *     shade(@color, [@weight: 50%]);
 232       *
 233       * @link http://lesscss.org/functions/#color-operations-shade
 234       * @return array Color
 235       * @throws Exception
 236       */
 237      public function shade(array $args): array
 238      {
 239          $black = ['color', 0, 0, 0];
 240          if ($args[0] == 'color') {
 241              return $this->mix(['list', ',', [$black, $args]]);
 242          } elseif ($args[0] == 'list' && count($args[2]) == 2) {
 243              return $this->mix([$args[0], $args[1], [$black, $args[2][0], $args[2][1]]]);
 244          } else {
 245              throw new Exception('shade expects (color, weight)');
 246          }
 247      }
 248  
 249      // greyscale is missing
 250  
 251      /**
 252       * Choose which of two colors provides the greatest contrast with another
 253       *
 254       * @link https://lesscss.org/functions/#color-operations-contrast
 255       * @throws Exception
 256       */
 257      public function contrast(array $args): array
 258      {
 259          $darkColor = ['color', 0, 0, 0];
 260          $lightColor = ['color', 255, 255, 255];
 261          $threshold = 0.43;
 262  
 263          if ($args[0] == 'list') {
 264              $inputColor = (isset($args[2][0])) ? Asserts::assertColor($args[2][0]) : $lightColor;
 265              $darkColor = (isset($args[2][1])) ? Asserts::assertColor($args[2][1]) : $darkColor;
 266              $lightColor = (isset($args[2][2])) ? Asserts::assertColor($args[2][2]) : $lightColor;
 267              if (isset($args[2][3])) {
 268                  if (isset($args[2][3][2]) && $args[2][3][2] == '%') {
 269                      $args[2][3][1] /= 100;
 270                      unset($args[2][3][2]);
 271                  }
 272                  $threshold = Asserts::assertNumber($args[2][3]);
 273              }
 274          } else {
 275              $inputColor = Asserts::assertColor($args);
 276          }
 277  
 278          $inputColor = Color::coerceColor($inputColor);
 279          $darkColor = Color::coerceColor($darkColor);
 280          $lightColor = Color::coerceColor($lightColor);
 281  
 282          //Figure out which is actually light and dark!
 283          if (Color::toLuma($darkColor) > Color::toLuma($lightColor)) {
 284              $t = $lightColor;
 285              $lightColor = $darkColor;
 286              $darkColor = $t;
 287          }
 288  
 289          $inputColor_alpha = $this->alpha($inputColor);
 290          if ((Color::toLuma($inputColor) * $inputColor_alpha) < $threshold) {
 291              return $lightColor;
 292          }
 293          return $darkColor;
 294      }
 295  
 296  
 297      /**
 298       * Helper function to get arguments for color manipulation functions.
 299       * takes a list that contains a color like thing and a percentage
 300       *
 301       * @fixme explanation needs to be improved
 302       * @throws Exception
 303       */
 304      protected function colorArgs(array $args): array
 305      {
 306          if ($args[0] != 'list' || count($args[2]) < 2) {
 307              return [['color', 0, 0, 0], 0];
 308          }
 309          [$color, $delta] = $args[2];
 310          $color = Asserts::assertColor($color);
 311          $delta = floatval($delta[1]);
 312  
 313          return [$color, $delta];
 314      }
 315  }