Программно облегчить цвет - PullRequest
70 голосов
/ 27 сентября 2008

Мотивация

Я бы хотел найти способ взять произвольный цвет и осветлить его на несколько оттенков, чтобы я мог программно создать хороший градиент от одного цвета к более светлой версии. Градиент будет использоваться в качестве фона в пользовательском интерфейсе.

Возможность 1

Очевидно, я могу просто разделить значения RGB и увеличить их по отдельности на определенную величину. Это на самом деле то, что я хочу?

Возможность 2

Моя вторая мысль состояла в том, чтобы преобразовать RGB в HSV / HSB / HSL (оттенок, насыщенность, значение / яркость / яркость), немного увеличить яркость, немного уменьшить насыщенность, а затем преобразовать его обратно в RGB. Будет ли это иметь желаемый эффект в целом?

Ответы [ 20 ]

53 голосов
/ 27 сентября 2008

Как сказал Клин , вы хотите умножить, чтобы сделать вещи ярче, но это работает только до тех пор, пока один из цветов не станет насыщенным (то есть достигнет 255 или больше). В этот момент вы можете просто зафиксировать значения до 255, но вы будете слегка менять цвет по мере того, как становитесь светлее. Чтобы сохранить оттенок, вы должны поддерживать соотношение (средний-низкий) / (самый высокий-самый низкий).

Вот две функции в Python. Первый реализует наивный подход, который просто ограничивает значения RGB до 255, если они превышаются. Вторая перераспределяет избыточные значения, чтобы сохранить нетронутым оттенок.

def clamp_rgb(r, g, b):
    return min(255, int(r)), min(255, int(g)), min(255, int(b))

def redistribute_rgb(r, g, b):
    threshold = 255.999
    m = max(r, g, b)
    if m <= threshold:
        return int(r), int(g), int(b)
    total = r + g + b
    if total >= 3 * threshold:
        return int(threshold), int(threshold), int(threshold)
    x = (3 * threshold - total) / (3 * m - total)
    gray = threshold - x * m
    return int(gray + x * r), int(gray + x * g), int(gray + x * b)

Я создал градиент, начиная со значения RGB (224,128,0) и умножая его на 1,0, 1,1, 1,2 и т. Д. До 2,0. Верхняя половина - это результат использования clamp_rgb, а нижняя - результат redistribute_rgb. Я думаю, что легко видеть, что перераспределение переполнений дает намного лучший результат без необходимости покидать цветовое пространство RGB.

Lightness gradient with clamping (top) and redistribution (bottom)

Для сравнения вот тот же градиент в цветовых пространствах HLS и HSV, который реализован модулем Python colorsys. Был изменен только компонент L, и результирующие значения RGB были выполнены. Результаты аналогичны, но требуют преобразования цветового пространства для каждого пикселя.

Lightness gradient with HLS (top) and HSV (bottom)

38 голосов
/ 27 сентября 2008

Я бы пошел на второй вариант. Вообще говоря, пространство RGB не очень хорошо для манипулирования цветами (создания перехода от одного цвета к другому, осветления / затемнения цвета и т. Д.). Ниже приведены два сайта с быстрым поиском для преобразования из / в RGB в / из HSL:

23 голосов
/ 27 сентября 2008

В C #:

public static Color Lighten(Color inColor, double inAmount)
{
  return Color.FromArgb(
    inColor.A,
    (int) Math.Min(255, inColor.R + 255 * inAmount),
    (int) Math.Min(255, inColor.G + 255 * inAmount),
    (int) Math.Min(255, inColor.B + 255 * inAmount) );
}

Я использовал это повсеместно.

11 голосов
/ 21 октября 2008

Класс ControlPaint в пространстве имен System.Windows.Forms имеет статические методы Light and Dark:

public static Color Dark(Color baseColor, float percOfDarkDark);

Эти методы используют частную реализацию HLSColor. Я хотел бы, чтобы эта структура была публичной и в System.Drawing.

Кроме того, вы можете использовать GetHue, GetSaturation, GetBrightness для Color struct для получения компонентов HSB. К сожалению, я не нашел обратного преобразования.

8 голосов
/ 27 сентября 2008

Преобразование в RGB и линейная интерполяция между исходным цветом и целевым цветом (часто белым) Итак, если вы хотите 16 оттенков между двумя цветами, вы делаете:


for(i = 0; i < 16; i++)
{
  colors[i].R = start.R + (i * (end.R - start.R)) / 15;
  colors[i].G = start.G + (i * (end.G - start.G)) / 15;
  colors[i].B = start.B + (i * (end.B - start.B)) / 15;
}
6 голосов
/ 24 октября 2012

Чтобы получить более светлую или более темную версию данного цвета, вы должны изменить его яркость. Вы можете сделать это легко даже без преобразования вашего цвета в HSL или HSB. Например, чтобы сделать цвет светлее, вы можете использовать следующий код:

float correctionFactor = 0.5f;
float red = (255 - color.R) * correctionFactor + color.R;
float green = (255 - color.G) * correctionFactor + color.G;
float blue = (255 - color.B) * correctionFactor + color.B;
Color lighterColor = Color.FromArgb(color.A, (int)red, (int)green, (int)blue);

Если вам нужно больше подробностей, прочитайте полную историю в моем блоге .

3 голосов
/ 23 января 2013

Я использовал ответ Эндрю и ответ Марка, чтобы сделать этим (по состоянию на 1/2013 нет диапазона ввода для ff).

function calcLightness(l, r, g, b) {
    var tmp_r = r;
    var tmp_g = g;
    var tmp_b = b;

    tmp_r = (255 - r) * l + r;
    tmp_g = (255 - g) * l + g;
    tmp_b = (255 - b) * l + b;

    if (tmp_r > 255 || tmp_g > 255 || tmp_b > 255) 
        return { r: r, g: g, b: b };
    else 
        return { r:parseInt(tmp_r), g:parseInt(tmp_g), b:parseInt(tmp_b) }
}

enter image description here

3 голосов
/ 27 сентября 2008

Очень похожий вопрос с полезными ответами был задан ранее: Как определить вариант более темного или более светлого цвета данного цвета?

Краткий ответ: умножьте значения RGB на константу, если вам просто нужно "достаточно хорошо", переведите в HSV, если вам нужна точность.

2 голосов
/ 27 сентября 2008

Я сделал это в обе стороны - вы получаете намного лучшие результаты с возможностью 2.

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

Возможно, вы захотите заглянуть в Посс 1, если (1) вы можете ограничить используемые цвета и яркость, и (2) вы много выполняете вычисления при рендеринге.

Для генерации фона для пользовательского интерфейса не потребуется много вычислений затенения, поэтому я предлагаю Вариант 2.

-Аль.

2 голосов
/ 27 сентября 2008

Преобразование в HS (LVB), увеличение яркости и последующее преобразование в RGB - единственный способ надежно осветлить цвет, не влияя на значения оттенка и насыщенности (т.е. только осветлить цвет, не изменяя его каким-либо другим способом) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...