Программно осветить цвет

Мотивация

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

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

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

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

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

Ответы

Ответ 1

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

Ответ 2

Как сказал Клин, вы хотите размножаться, чтобы сделать вещи ярче, но это работает только до тех пор, пока один из цветов не станет насыщенным (т.е. достигает 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)

Ответ 3

В С#:

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

Я использовал это повсюду.

Ответ 4

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

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

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

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

Ответ 5

Преобразуйте его в 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

Чтобы получить более светлую или более темную версию данного цвета, вы должны изменить его яркость. Вы можете сделать это легко даже без преобразования цвета в 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);

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

Ответ 7

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

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

Ответ 8

Я использовал Andrew answer и Mark answer, чтобы сделать this (начиная с 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

Ответ 9

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

Ответ 10

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

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

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

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

-Аль.

Ответ 11

ЕСЛИ вы хотите произвести угасание градиента, я бы предложил следующую оптимизацию: вместо того, чтобы делать RGB- > HSB- > RGB для каждого отдельного цвета, вы должны только рассчитать целевой цвет. Как только вы узнаете целевой RGB, вы можете просто вычислить промежуточные значения в пространстве RGB без необходимости конвертировать туда и обратно. Независимо от того, вычисляете ли вы линейный переход использования, какая-то кривая зависит от вас.

Ответ 13

Притворись, что альфа смешана с белым:

oneMinus = 1.0 - amount
r = amount + oneMinus * r
g = amount + oneMinus * g
b = amount + oneMinus * b

где amount - от 0 до 1, при этом 0 возвращает исходный цвет и 1 возвращает белый.

Вы можете смешать с тем, что имеет цвет фона, если вы осветливаете, чтобы отобразить что-то отключенное:

oneMinus = 1.0 - amount
r = amount * dest_r + oneMinus * r
g = amount * dest_g + oneMinus * g
b = amount * dest_b + oneMinus * b

где (dest_r, dest_g, dest_b) - цвет, который смешивается, и amount составляет от 0 до 1, с возвратом нуля (r, g, b) и 1 возвращающим (dest.r, dest.g, dest.b)

Ответ 14

Я не нашел этот вопрос до тех пор, пока он не стал связанным с моим вопросом вопросом.

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

Программно осветить или затемнить шестицветный (или цвета rgb и blend)

Его версия метода 1. Но с учетом насыщения. Как сказал Кейт в своем ответе выше; используйте Лерпа, чтобы, похоже, решить ту же проблему, о которой упоминал Марк, но без перераспределения. Результаты shadeColor2 должны быть намного ближе к правильному пути с HSL, но без накладных расходов.

Ответ 15

Сначала я бы попробовал номер # 1, но # 2 звучит неплохо. Попытайтесь сделать это сами и посмотрите, удовлетворены ли вы результатами, похоже, вам понадобится 10 минут, чтобы опрокинуть тест.

Ответ 16

Технически я не думаю, что это правильно, но я считаю, что вам нужен вариант варианта № 2. Проблема в том, что принятый RGB 990000 и "облегчение" он действительно просто добавит на красный канал (Value, Brightness, Lightness), пока вы не попадете в FF. После этого (сплошной красный), он будет снимать насыщенность до полного беления.

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

Ответ 18

У меня есть сообщение в блоге, в котором показано, как это сделать в Delphi. Его довольно просто, потому что функции ColorRGBToHSL и ColorHLSToRGB являются частью стандартной библиотеки.

Ответ 19

Вот пример освещения RGB-цвета в Python:

def lighten(hex, amount):
    """ Lighten an RGB color by an amount (between 0 and 1),

    e.g. lighten('#4290e5', .5) = #C1FFFF
    """
    hex = hex.replace('#','')
    red = min(255, int(hex[0:2], 16) + 255 * amount)
    green = min(255, int(hex[2:4], 16) + 255 * amount)
    blue = min(255, int(hex[4:6], 16) + 255 * amount)
    return "#%X%X%X" % (int(red), int(green), int(blue))

Ответ 20

Немного поздно для вечеринки, но если вы используете javascript или nodejs, вы можете использовать библиотеку tinycolor и управлять цветом так, как вы хотите:

tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d"