Преобразование цвета HSL в RGB и HEX с помощью SQL

Проблема началась, когда мне нужно было применить условное форматирование к таблице с плавным изменением цвета в службах отчетов MS SQL Server (SSRS). Это невозможно со стандартной функциональностью SSRS. Но вы можете использовать табличные данные для плавного изменения цвета с параметром Lightness в цветовой модели HSL.

Вопрос в том, как преобразовать HSL в используемые в SSRS HEX или RGB цветовые коды с помощью SQL.

Ни на Stackoverflow, ни где-либо еще ответов не нашел, только для других языков программирования

Существует прямое математическое соответствие между цветовыми пространствами HSL и RGB. Не то чтобы вы действительно должны делать это в T-SQL, но что мешает вам сделать это? Что вы пробовали?

AlwaysLearning 25.11.2022 13:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ведь я пришел к тому, чтобы переписать функцию VBA с здесь на SQL

Он имеет следующие параметры:

  • @HueDegree [0,360]
  • @Насыщенность [0,1]
  • @Яркость [0,1]
  • @Формат ("RGB" или "HEX")

Результат - цветовой код в выбранном формате

create function dbo.f_convertHSL (
    @HueDegree numeric(3,0), 
    @Saturation numeric(6,3), 
    @Lightness numeric(6,3), 
    @Format varchar(3) )
returns varchar(100)
as

begin

    declare @HuePercent numeric(6,3),
            @Red numeric(6,3), 
            @Green numeric(6,3), 
            @Blue numeric(6,3),
            @Temp1 numeric(6,3), 
            @Temp2 numeric(6,3),
            @TempR numeric(6,3),
            @TempG numeric(6,3),
            @TempB numeric(6,3), 
            @Result varchar(100);

    if @Saturation = 0 
    begin
        select @Red = @Lightness * 255,
               @Green = @Lightness * 255,
               @Blue = @Lightness * 255;

        if @Format = 'RGB'
            select @Result = cast(cast(@Red as int) as varchar) + ', '
                             + cast(cast(@Green as int) as varchar) + ', '
                             + cast(cast(@Blue as int) as varchar);
        else if @Format = 'HEX'
            select @Result = '#' + convert(varchar(2), convert(varbinary(1), cast(@Red as int)), 2)
                                 + convert(varchar(2), convert(varbinary(1), cast(@Green as int)), 2)
                                 + convert(varchar(2), convert(varbinary(1), cast(@Blue as int)), 2);
        else select @Result = 'Format should be RGB or HEX';

        return @Result;
    end;

    if @Lightness < 0.5
        select @Temp1 = @Lightness * (1 + @Saturation);
    else
        select @Temp1 = @Lightness + @Saturation - @Lightness * @Saturation;

    select @Temp2 = 2 * @Lightness - @Temp1
         , @HuePercent = @HueDegree / 360.0;

    select @TempR = @HuePercent + 0.333
         , @TempG = @HuePercent
         , @TempB = @HuePercent - 0.333;

    if @TempR < 0 select @TempR = @TempR + 1;
    if @TempR > 1 select @TempR = @TempR - 1;
    if @TempG < 0 select @TempG = @TempG + 1;
    if @TempG > 1 select @TempG = @TempG - 1;
    if @TempB < 0 select @TempB = @TempB + 1;
    if @TempB > 1 select @TempB = @TempB - 1;

    if @TempR * 6 < 1 select @Red = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempR
    else if @TempR * 2 < 1 select @Red = @Temp1
    else if @TempR * 3 < 2 select @Red = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempR) * 6
    else select @Red = @Temp2;

    if @TempG * 6 < 1 select @Green = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempG
    else if @TempG * 2 < 1 select @Green = @Temp1
    else if @TempG * 3 < 2 select @Green = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempG) * 6
    else select @Green = @Temp2;

    if @TempB * 6 < 1 select @Blue = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempB
    else if @TempB * 2 < 1 select @Blue = @Temp1
    else if @TempB * 3 < 2 select @Blue = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempB) * 6
    else select @Blue = @Temp2;

    select @Red = round(@Red * 255, 0),
           @Green = round(@Green * 255, 0),
           @Blue = round(@Blue * 255, 0);

    if @Format = 'RGB'
        select @Result = cast(cast(@Red as int) as varchar) + ', '
                            + cast(cast(@Green as int) as varchar) + ', '
                            + cast(cast(@Blue as int) as varchar);
    else if @Format = 'HEX'
        select @Result = '#' + convert(varchar(2), convert(varbinary(1), cast(@Red as int)), 2)
                             + convert(varchar(2), convert(varbinary(1), cast(@Green as int)), 2)
                             + convert(varchar(2), convert(varbinary(1), cast(@Blue as int)), 2);
    else select @Result = 'Format should be RGB or HEX';

    return @Result;

end;

Примеры использования:

select dbo.f_convertHSL(24, 0.83, 0.74, 'RGB')
result: 244, 178, 134
select dbo.f_convertHSL(24, 0.83, 0.74, 'HEX')
result: #F4B286

Результат можно подтвердить например здесь

Должен признать, для T-SQL этот вид очень многословен; похоже, что это написано программно. Я также ожидаю, что если вы предоставляете 3 параметра для H, S и L, то вы должны получить 3 значения для R, G и B, а не одно значение с разделителями (и денормализованное). Для шестнадцатеричного значения значение binary(3) имело бы больше смысла. Вероятно, здесь вам нужны две функции: одна для возврата набора данных со значениями R, G и B, а другая — для возврата значения binary(3) для Hex. Кроме того, обе, вероятно, должны быть встроенными функциями табличного значения, а не многострочной скалярной функцией.

Larnu 25.11.2022 13:20

Функция представляет собой пошаговый sql-перевод упомянутой функции VBA. И основной целью в моем случае было использовать его для SSRS, который принимает значение varchar с цветовым кодом HEX. Разумеется, функцию можно не использовать как есть, а оптимизировать и модифицировать для любых целей.

Roman Udaltsov 25.11.2022 14:12

Да, и это действительно проблема. VB(A) и SQL совершенно разные; VB(A) — язык программирования, SQL — язык запросов.

Larnu 25.11.2022 14:13

@Larnu, буду очень признателен, если вы предложите более оптимальный вариант этой функции в отдельном ответе

Roman Udaltsov 25.11.2022 14:20
Ответ принят как подходящий

Я взял за основу следующее решение этой статьи. Как уже упоминалось, здесь я использую 2 функции, и я также возвращаю набор данных в обеих (3 столбца для RGB, 1 для шестнадцатеричного):

CREATE OR ALTER FUNCTION dbo.HSLtoRGB (@H numeric(3,0),@S numeric(4,3), @L numeric(4,3)) 
RETURNS table
AS RETURN
    SELECT CONVERT(tinyint,ROUND((RGB1.R1+m.m)*255,0)) AS R,
           CONVERT(tinyint,ROUND((RGB1.G1+m.m)*255,0)) AS G,
           CONVERT(tinyint,ROUND((RGB1.B1+m.m)*255,0)) AS B
    FROM (VALUES(@H, @S, @L))HSL(Hue,Saturation,Lightness)
         CROSS APPLY(VALUES((1-ABS((2*HSL.Lightness - 1))) * HSL.Saturation)) C(Chroma)
         CROSS APPLY(VALUES(HSL.Hue/60,C.Chroma * (1 - ABS((HSL.Hue/60) % 2 - 1))))H([H`],X)
         CROSS APPLY(SELECT TOP (1) * --It's unlikely there would be 2 rows, but just incase limit to 1
                     FROM (VALUES(C.Chroma,H.X,0,0,1),
                                 (H.X,C.Chroma,0,1,2),
                                 (0,C.Chroma,H.X,2,3),
                                 (0,H.X,C.Chroma,3,4),
                                 (H.X,0,C.Chroma,4,5),
                                 (C.Chroma,0,H.X,5,6))V(R1,G1,B1,S,E)
                    WHERE V.S <= H.[H`] AND H.[H`] <= V.E
                    ORDER BY V.E DESC) RGB1 
         CROSS APPLY (VALUES(HSL.Lightness - (C.Chroma / 2)))m(m);
GO
CREATE OR ALTER FUNCTION dbo.HSLtoRGB_HEX (@H numeric(3,0),@S numeric(4,3), @L numeric(4,3)) 
RETURNS table
AS RETURN
    SELECT CONVERT(binary(3),CONCAT(CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.R1+m.m)*255,0))),2),
                                    CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.G1+m.m)*255,0))),2),
                                    CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.B1+m.m)*255,0))),2)),2) AS RGB
    FROM (VALUES(@H, @S, @L))HSL(Hue,Saturation,Lightness)
         CROSS APPLY(VALUES((1-ABS((2*HSL.Lightness - 1))) * HSL.Saturation)) C(Chroma)
         CROSS APPLY(VALUES(HSL.Hue/60,C.Chroma * (1 - ABS((HSL.Hue/60) % 2 - 1))))H([H`],X)
         CROSS APPLY(SELECT TOP(1) * --It's unlikely there would be 2 rows, but just incase limit to 1
                     FROM (VALUES(C.Chroma,H.X,0,0,1),
                                 (H.X,C.Chroma,0,1,2),
                                 (0,C.Chroma,H.X,2,3),
                                 (0,H.X,C.Chroma,3,4),
                                 (H.X,0,C.Chroma,4,5),
                                 (C.Chroma,0,H.X,5,6))V(R1,G1,B1,S,E)
                    WHERE V.S <= H.[H`] AND H.[H`] <= V.E
                    ORDER BY V.E DESC) RGB1
         CROSS APPLY (VALUES(HSL.Lightness - (C.Chroma / 2)))m(m);
GO

SELECT *
FROM (VALUES(210,.79,.3),
            (24,.83,.74),
            (360,1,1),
            (0,0,0))V(H,S,L)
     CROSS APPLY dbo.HSLtoRGB(V.H, V.S, V.L) RGB
     CROSS APPLY dbo.HSLtoRGB_Hex(V.H, V.S, V.L) RGBhex;

Другие вопросы по теме