- PVSM.RU - https://www.pvsm.ru -
Delphi давно славится тем, что disabled иконки по умолчанию выглядят как-то так:
А хотелось бы, чтоб они выглядели вот как-то так:
Воспользуемся тем, что Delphi позволяет заменить disabled иконки своими, указав дополнительный список изображений. Но рисовать и подключать такие иконки каждый раз занятие утомительное. Поэтому мы создадим этот список изображений динамически, во время выполнения программы.
Создавать такой список изображений мы будем в специальной функции CreateSpecialImageList(). В качестве аргумента нам понадобится список с оригинальными иконками, а в качестве возвращаемого значения уже будет нужный нам TImageList. Тогда подключить наши новые иконки мы сможем при создании формы следующей строчкой кода:
ActionManager.DisabledImages := CreateSpecialImageList(ImageList);
Расширим возможности функции, добавим возможность делать не только серые полупрозрачные иконки, но и делать их ярче или контрастнее. Это может понадобиться при создании иконок для свойств HotImages. Таким образом, у нас будет универсальная функция для создания иконок нескольких специальных состояний.
function CreateSpecialImageList(ASource: TCustomImageList;
ABrightness, AContrast, AGrayscale, AAlpha: Single): TCustomImageList;
type
PBGRA = ^TBGRA;
TBGRA = packed record
B, G, R, A: Byte;
end;
TByteLUT = array [Byte] of Byte;
function ByteRound(const Value: Single): Byte; inline;
begin
if Value < 0 then
Result := 0
else if Value > 255 then
Result := 255
else
Result := Round(Value);
end;
procedure GetLinearLUT(var LUT: TByteLUT; X1, Y1, X2, Y2: Integer);
var
X, DX, DY: Integer;
begin
DX := X2 - X1;
DY := Y2 - Y1;
for X := 0 to 255 do
LUT[X] := ByteRound((X - X1) * DY / DX + Y1);
end;
function GetBrightnessContrastLUT(var LUT: TByteLUT; const Brightness, Contrast: Single): Boolean;
var
B, C: Integer;
X1, Y1, X2, Y2: Integer;
begin
X1 := 0;
Y1 := 0;
X2 := 255;
Y2 := 255;
B := Round(Brightness * 255);
C := Round(Contrast * 127);
if C >= 0 then
begin
Inc(X1, C);
Dec(X2, C);
Dec(X1, B);
Dec(X2, B);
end
else
begin
Dec(Y1, C);
Inc(Y2, C);
Inc(Y1, B);
Inc(Y2, B);
end;
GetLinearLUT(LUT, X1, Y1, X2, Y2);
Result := (B <> 0) or (C <> 0);
end;
var
LImageInfo: TImageInfo;
LDibInfo: TDibSection;
I, L: Integer;
P: PBGRA;
R, G, B, A: Byte;
LUT: TByteLUT;
LHasLUT: Boolean;
begin
Result := nil;
if (ASource = nil) or (ASource.ColorDepth <> cd32bit) then
Exit;
Result := TImageList.Create(ASource.Owner);
try
Result.ColorDepth := cd32bit;
Result.Assign(ASource);
Result.Masked := False;
FillChar(LImageInfo, SizeOf(LImageInfo), 0);
ImageList_GetImageInfo(Result.Handle, 0, LImageInfo);
FillChar(LDibInfo, SizeOf(LDibInfo), 0);
GetObject(LImageInfo.hbmImage, SizeOf(LDibInfo), @LDibInfo);
P := LDibInfo.dsBm.bmBits;
LHasLUT := GetBrightnessContrastLUT(LUT, ABrightness, AContrast);
for I := 0 to LDibInfo.dsBm.bmHeight * LDibInfo.dsBm.bmWidth - 1 do
begin
A := P.A;
R := MulDiv(P.R, $FF, A);
G := MulDiv(P.G, $FF, A);
B := MulDiv(P.B, $FF, A);
if LHasLUT then
begin
R := LUT[R];
G := LUT[G];
B := LUT[B];
end;
if AGrayscale > 0 then
begin
L := (R * 61 + G * 174 + B * 21) shr 8;
if AGrayscale >= 1 then
begin
R := L;
G := L;
B := L;
end
else
begin
R := ByteRound(R + (L - R) * AGrayscale);
G := ByteRound(G + (L - G) * AGrayscale);
B := ByteRound(B + (L - B) * AGrayscale);
end;
end;
if AAlpha <> 1 then
begin
A := ByteRound(A * AAlpha);
P.A := A;
end;
P.R := MulDiv(R, A, $FF);
P.G := MulDiv(G, A, $FF);
P.B := MulDiv(B, A, $FF);
Inc(P);
end;
except
FreeAndNil(Result);
end;
end;
Я думаю, исходный код функции большинству дельфистов будет понятен, но считаю нужным пояснить несколько моментов.
Во первых, DIB изображение в TImageList хранится в premulted формате и для каждого пиксела мы должны выполнить операцию получения исходного значения цветового канала серией вызовов MulDiv(), выполнить операции, а затем вернуть исходный формат пиксела обратно.
Изменение яркости и контраста выполняется с использованием LUT (Lookup Table) — таблицы поиска. Мы строим таблицу на основе линейной функции по 2-м точкам. Параметры яркости и контраста просто сдвигают исходные точки прямой коррекции по нужным направлениям. Так как мы выполняем одинаковую коррекцию по всем цветовым каналам, то используем только одну LUT.
Изменение градаций серого для пиксела выполняется простым вычислением смещения от исходного значения к значению общей яркости цвета. Чем больше значение аргумента AGrayscale, тем ближе новое значение к значению общей яркости, что в совокупности для всех цветовых каналов дает приближение к серому цвету.
Для использования функции вы можете просто скопировать исходный код в свой проект. Код функции самодостаточен и не требует сторонних компонентов или модулей. Для удобства я написал небольшую демку, в которой вы можете поэкспериментировать с аргументами функции. Исходный код демки можно скачать тут [1].
Надеюсь, приведенная здесь функция пригодится вам в ваших разработках. Приятного кодинга!
______________________
Автор: Bytexpert
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/delphi/21252
Ссылки в тексте:
[1] тут: http://yadi.sk/d/zPXWwwns0w038
[2] Редакторе Блогов: http://www.softcoder.ru/blogeditor/
[3] Источник: http://habrahabr.ru/post/160455/
Нажмите здесь для печати.