Замените один цвет с помощью фильтров SVG

Можно ли сделать снимок IMG и заменить один точный цвет другим, скажем, #fff на #000, сохранив все остальные цвета без изменений? Возможно, поможет цветовая матрица из фильтров SVG?


person Jury    schedule 13.01.2017    source источник


Ответы (1)


Это тривиально сделать с Canvas. Также можно сделать с SVG, но это запутанно. Следующий метод работает с обычными полностью непрозрачными изображениями. Во-первых, вы конвертируете каждое несовпадающее значение цвета в каждом канале в ноль, а каждое совпадающее значение цвета в 1 с помощью длинного ComponentTransfer (индекс единственной «1» в ваших 256-элементных массивах tableValues ​​должен соответствовать вашим r, g и b заменить значение). Затем вы обнуляете альфа-канал всего, кроме результирующих белых пикселей, используя цветовую матрицу. Вы используете результат в качестве маски с feFlood вашего целевого цвета и накладываете результат поверх исходной графики. Например, следующий код заменяет определенный цвет — rgb(87, 78, 29) — синим.

<svg width="600px" height="600px" viewBox="0 0 600 600">
  <defs>
    <filter id="color-replace" color-interpolation-filters="sRGB">
      <!-- Replace rgb(87,78,29) with blue. -->
      <feComponentTransfer >
        <feFuncR type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
        <feFuncG type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
        <feFuncB type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
      </feComponentTransfer>

      <feColorMatrix type="matrix" values="1 0 0 0 0
                                           0 1 0 0 0
                                           0 0 1 0 0
                                           1 1 1 1 -3" result="selectedColor"/>

      <feFlood flood-color="blue"/>
      <feComposite operator="in" in2="selectedColor"/>
      <feComposite operator="over" in2="SourceGraphic"/>
    </filter>
  </defs>
  <g filter="url(#color-replace)">
    <rect  x="50" y="50" height="100" width="100" fill="rgb(86,77,28)"/>
    <rect  x="250" y="50" height="100" width="100" fill="rgb(86,77,29)"/>
    <rect  x="450" y="50" height="100" width="100" fill="rgb(86,78,29)"/>
    <rect  x="50" y="250" height="100" width="100" fill="rgb(87,77,29)"/>
    <rect  x="250" y="250" height="100" width="100" fill="rgb(87,78,29)"/>
    <rect  x="450" y="250" height="100" width="100" fill="rgb(87,78,30)"/>
    <rect  x="50" y="450" height="100" width="100" fill="rgb(88,78,30)"/>
    <rect  x="250" y="450" height="100" width="100" fill="rgb(88,79,29)"/>
    <rect  x="450" y="450" height="100" width="100" fill="rgb(88,79,30)"/>
  </g>
</svg>

ОБНОВИТЬ:

Если вы хотите заменить исходный цвет цветом с непрозрачностью менее 1, вам нужно сделать дополнительный шаг — удалить выбранный цвет перед его заменой. Вы можете расширить фильтр таким образом, чтобы добиться этого.

  <--snipping first half of filter -->

  <feColorMatrix type="matrix" values="1 0 0 0 0
                                       0 1 0 0 0
                                       0 0 1 0 0
                                       1 1 1 1 -3" result="selectedColor"/>

  <feComposite operator="out" in="SourceGraphic" result="notSelectedColor"/>
  <feFlood flood-color="blue" flood-opacity="0.5"/>
  <feComposite operator="in" in2="selectedColor"/>
  <feComposite operator="over" in2="notSelectedColor"/>
</filter>
person Michael Mullany    schedule 04.02.2017
comment
Я просто использовал вариант этого кода, чтобы заменить цвет фона в iframe. Спасибо! - person Menno van den Heuvel; 17.05.2018
comment
Это работает очень хорошо, но что, если вы также хотите, чтобы ваш целевой цвет имел другую альфу, например 0,5? flood-color="blue" flood-opacity="0.5" не работает, равно как и установка 128 0 и 128 1 в <feFuncA/> tableValues. - person Todd Main; 08.10.2019
comment
Добавлено обновление, позволяющее использовать замещающий цвет с переменной непрозрачностью. - person Michael Mullany; 08.10.2019
comment
Этот подход не работает в Chrome (тестирование в 77) или точно в любом браузере на основе webkit. Только Firefox справляется с этим правильно. Проблема, похоже, связана с feComponentTransfer, где tableValues довольно странным образом интерпретируется Chrome. - person Greegus; 23.10.2019
comment
Это все еще работает в моем Chrome 77/Win10. Есть пара флагов графического процессора Chrome, которые фильтруют фильтры — может быть, у вас установлен один из них? - person Michael Mullany; 24.10.2019
comment
Я также не могу заставить его работать в Chrome 78/OSX 10.14.5, когда я пытаюсь использовать его в кодовой панели или запускаю фрагмент кода через SO - person mjkaufer; 12.12.2019