Самый быстрый способ рассчитать цвета для градиента? - PullRequest
8 голосов
/ 24 ноября 2011

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

У меня уже есть это в основномсделано ниже.Кроме того, у меня есть два основных вопроса:

  1. Как рассчитать промежуточный цвет каждого канала RGB на заданный процент?(См. Ниже, где у меня есть [???])
  2. Какой самый быстрый способ выполнить то, что я делаю (сохраняя две разные функции)?

Вот код:

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  StrUtils, StdCtrls, Math;

type
  TColorArray = array of TColor;

implementation

function ColorsBetween(const ColorA, ColorB: TColor; const Count: Integer): TColorArray;
var
  X: Integer; //Loop counter
begin
  SetLength(Result, Count);
  for X:= 0 to Count - 1 do 
    Result[X]:= ColorBetween(ColorA, ColorB, Round((X / Count) * 100)); //Correct?
end;

function ColorBetween(const ColorA, ColorB: TColor; const Percent: Single): TColor;
var
  R1, G1, B1: Byte;
  R2, G2, B2: Byte;
begin
  R1:= GetRValue(ColorA);
  G1:= GetGValue(ColorA);
  B1:= GetBValue(ColorA);
  R2:= GetRValue(ColorB);
  G2:= GetGValue(ColorB);
  B2:= GetBValue(ColorB);
  Result:= RGB(
    EnsureRange(([???]), 0, 255),
    EnsureRange(([???]), 0, 255),
    EnsureRange(([???]), 0, 255)
  );
end;

РЕДАКТИРОВАТЬ: Изменено Percent: Integer на Percent: Single для получения более плавного эффекта - не ограничено 100 возможных значений.

Ответы [ 2 ]

7 голосов
/ 24 ноября 2011

Похоже, вы хотите заменить свой ??? с

Round((R1*Percent + R2*(100-Percent))/100.0)

EnsureRange в вашем коде не является необходимым, поскольку эта функция должна возвращать значения в диапазоне от 0 до 255 при условии, что Percent находится в диапазоне от 0 до 100. Я думаю, что я бы применил EnsureRange к Percent (принудительно установите его в диапазоне от 0,0 до 100,0) и затем используйте следующий код:

Result := RGB(
  Round((R1*Percent + R2*(100-Percent))/100.0),
  Round((G1*Percent + G2*(100-Percent))/100.0),
  Round((B1*Percent + B2*(100-Percent))/100.0),
);

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

for X:= 0 to Count - 1 do
  Result[X]:= ColorBetween(ColorA, ColorB, (X+1) / (Count+1) * 100.0);

Это дает одинаковое поведение на обоих концах массива. Или, возможно, вы хотите, чтобы ColorA и ColorB были включены. Тогда вы будете использовать:

X / (Count-1) * 100.0

Но если вы сделаете это, помните, что Count должен быть больше 1, иначе вы будете делиться на ноль. Это никогда не получается !!


Не беспокойтесь о производительности. Код можно сделать немного быстрее, без сомнения, но это не будет узким местом. Вы возьмете эти цвета и начертите их. Это потребует значительно больше ресурсов, чем эти простые процедуры.


Один последний момент. Интерполяция в пространстве RGB не будет выглядеть особенно гладкой или линейной для человеческого глаза. Использование процента с плавающей запятой не может избежать этого факта. Для получения наилучших результатов при просмотре вам нужно будет интерполировать в другом цветовом пространстве.

4 голосов
/ 24 ноября 2011

Я не знаю, является ли это самым быстрым способом, но он работает:

function ColorBetween(const ColorA, ColorB: TColor; const Percent: Integer): TColor;
var
  R1, G1, B1: Byte;
  R2, G2, B2: Byte;
begin
  R1:= GetRValue(ColorA);
  G1:= GetGValue(ColorA);
  B1:= GetBValue(ColorA);
  R2:= GetRValue(ColorB);
  G2:= GetGValue(ColorB);
  B2:= GetBValue(ColorB);

  Result:= RGB(
    Percent * (R2-R1) div 100 + R1,
    Percent * (G2-G1) div 100 + G1,
    Percent * (B2-B1) div 100 + B1
  );
end;

function ColorsBetween(const ColorA, ColorB: TColor; const Count: Integer): TColorArray;
var
  X : integer;
begin
  SetLength(Result, Count);
  for X := 0 to Count - 1 do
    Result[X] := ColorBetween(ColorA, ColorB, Round((X / (Count-1)) * 100));  //Note: Divide by count-1
end;
...