367 lines
9.1 KiB
ObjectPascal
367 lines
9.1 KiB
ObjectPascal
// SPDX-License-Identifier: LGPL-3.0-linking-exception
|
|
|
|
{ Colors stored as integers with maximum value 65536 but allowing overflow }
|
|
unit BGRAColorInt;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$if defined(CPUI386) and not defined(openbsd)}
|
|
{$define BGRACOLORINT_USEASM}
|
|
{$endif}
|
|
{$ifdef DARWIN}
|
|
{$undef BGRACOLORINT_USEASM}
|
|
{$endif}
|
|
|
|
interface
|
|
|
|
uses
|
|
BGRABitmapTypes;
|
|
|
|
type
|
|
{ RGBA color defined by integers from 0 to 65536 }
|
|
TColorInt65536 = packed record
|
|
r,g,b,a: integer;
|
|
end;
|
|
|
|
function ColorInt65536(r,g,b,a: integer): TColorInt65536; inline; overload;
|
|
function ColorInt65536(r,g,b: integer): TColorInt65536; inline; overload;
|
|
function ColorFToColorInt65536(colorF: TColorF): TColorInt65536; inline;
|
|
function ColorInt65536ToColorF(color: TColorInt65536): TColorF;
|
|
operator +(const color1,color2: TColorInt65536): TColorInt65536; inline;
|
|
operator *(const color1,color2: TColorInt65536): TColorInt65536;
|
|
operator *(const color1: TColorInt65536; factor65536: integer): TColorInt65536;
|
|
function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean = false): TBGRAPixel;
|
|
function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean = false): TColorInt65536;
|
|
function BGRAToColorIntMultiply(const color1: TBGRAPixel; const color2: TColorInt65536): TColorInt65536;
|
|
|
|
implementation
|
|
|
|
function ColorInt65536(r, g, b, a: integer): TColorInt65536;
|
|
begin
|
|
result.r := r;
|
|
result.g := g;
|
|
result.b := b;
|
|
result.a := a;
|
|
end;
|
|
|
|
function ColorInt65536(r, g, b: integer): TColorInt65536;
|
|
begin
|
|
result.r := r;
|
|
result.g := g;
|
|
result.b := b;
|
|
result.a := 65536;
|
|
end;
|
|
|
|
function ColorFToColorInt65536(colorF: TColorF): TColorInt65536;
|
|
begin
|
|
result.r := round(colorF[1]*65536);
|
|
result.g := round(colorF[2]*65536);
|
|
result.b := round(colorF[3]*65536);
|
|
result.a := round(colorF[4]*65536);
|
|
end;
|
|
|
|
function ColorInt65536ToColorF(color: TColorInt65536): TColorF;
|
|
const oneOver65536 = 1/65536;
|
|
begin
|
|
result[1] := color.r*oneOver65536;
|
|
result[2] := color.g*oneOver65536;
|
|
result[3] := color.b*oneOver65536;
|
|
result[4] := color.a*oneOver65536;
|
|
end;
|
|
|
|
operator+(const color1, color2: TColorInt65536): TColorInt65536;
|
|
begin
|
|
result.r := color1.r+color2.r;
|
|
result.g := color1.g+color2.g;
|
|
result.b := color1.b+color2.b;
|
|
result.a := color1.a+color2.a;
|
|
end;
|
|
|
|
operator*(const color1, color2: TColorInt65536): TColorInt65536;
|
|
{$ifdef BGRACOLORINT_USEASM} {$asmmode intel} assembler;
|
|
asm
|
|
push edx
|
|
push ebx
|
|
push esi
|
|
mov ebx, Color1
|
|
mov esi, Color2
|
|
//ecx = @result
|
|
|
|
mov eax, [ebx] //r
|
|
imul dword ptr [esi]
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx], edx
|
|
|
|
mov eax, [ebx+4] //g
|
|
imul dword ptr [esi+4]
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+4], edx
|
|
|
|
mov eax, [ebx+8] //b
|
|
imul dword ptr [esi+8]
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+8], edx
|
|
|
|
mov eax, [ebx+12] //a
|
|
imul dword ptr [esi+12]
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+12], edx
|
|
|
|
pop esi
|
|
pop ebx
|
|
pop edx
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
result.r := int64(color1.r)*color2.r shr 16;
|
|
result.g := int64(color1.g)*color2.g shr 16;
|
|
result.b := int64(color1.b)*color2.b shr 16;
|
|
result.a := int64(color1.a)*color2.a shr 16;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
operator*(const color1: TColorInt65536; factor65536: integer): TColorInt65536;
|
|
{$ifdef BGRACOLORINT_USEASM} {$asmmode intel} assembler;
|
|
asm
|
|
push edx
|
|
push ebx
|
|
push esi
|
|
mov ebx, Color1
|
|
mov esi, factor65536
|
|
//ecx = @result
|
|
|
|
mov eax, [ebx] //r
|
|
imul esi
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx], edx
|
|
|
|
mov eax, [ebx+4] //g
|
|
imul esi
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+4], edx
|
|
|
|
mov eax, [ebx+8] //b
|
|
imul esi
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+8], edx
|
|
|
|
mov eax, [ebx+12] //a
|
|
imul esi
|
|
shl edx, 16
|
|
shr eax, 16
|
|
or edx, eax
|
|
mov [ecx+12], edx
|
|
|
|
pop esi
|
|
pop ebx
|
|
pop edx
|
|
end;
|
|
{$else}
|
|
var prod: int64;
|
|
begin
|
|
prod := int64(color1.r)*factor65536;
|
|
if prod >= 0 then result.r := prod shr 16
|
|
else result.r := -((-prod) shr 16);
|
|
prod := int64(color1.g)*factor65536;
|
|
if prod >= 0 then result.g := prod shr 16
|
|
else result.g := -((-prod) shr 16);
|
|
prod := int64(color1.b)*factor65536;
|
|
if prod >= 0 then result.b := prod shr 16
|
|
else result.b := -((-prod) shr 16);
|
|
prod := int64(color1.a)*factor65536;
|
|
if prod >= 0 then result.a := prod shr 16
|
|
else result.a := -((-prod) shr 16);
|
|
end;
|
|
{$endif}
|
|
|
|
function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean): TColorInt65536;
|
|
begin
|
|
if AGammaExpansion then
|
|
begin
|
|
result.r := GammaExpansionTab[AColor.red] + (AColor.red shr 7);
|
|
result.g := GammaExpansionTab[AColor.green] + (AColor.green shr 7);
|
|
result.b := GammaExpansionTab[AColor.blue] + (AColor.blue shr 7);
|
|
end else
|
|
begin
|
|
result.r := AColor.red shl 8 + AColor.red + (AColor.red shr 7);
|
|
result.g := AColor.green shl 8 + AColor.green + (AColor.green shr 7);
|
|
result.b := AColor.blue shl 8 + AColor.blue + (AColor.blue shr 7);
|
|
end;
|
|
result.a := AColor.alpha shl 8 + AColor.alpha+ (AColor.alpha shr 7);
|
|
end;
|
|
|
|
function BGRAToColorIntMultiply(const color1: TBGRAPixel;
|
|
const color2: TColorInt65536): TColorInt65536;
|
|
{$ifdef BGRACOLORINT_USEASM} {$asmmode intel} assembler;
|
|
asm
|
|
push ebx
|
|
push esi
|
|
|
|
mov esi, Color2
|
|
mov ebx, result
|
|
mov ecx, [Color1]
|
|
|
|
mov eax, ecx
|
|
shr eax, TBGRAPixel_RedShift
|
|
and eax, 255
|
|
mov edx, eax
|
|
shr edx, 7
|
|
add eax, edx
|
|
imul dword ptr [esi]
|
|
shl edx, 24
|
|
shr eax, 8
|
|
or edx, eax
|
|
mov [ebx], edx
|
|
|
|
mov eax, ecx
|
|
shr eax, TBGRAPixel_GreenShift
|
|
and eax, 255
|
|
mov edx, eax
|
|
shr edx, 7
|
|
add eax, edx
|
|
imul dword ptr [esi+4]
|
|
shl edx, 24
|
|
shr eax, 8
|
|
or edx, eax
|
|
mov [ebx+4], edx
|
|
|
|
mov eax, ecx
|
|
shr eax, TBGRAPixel_BlueShift
|
|
and eax, 255
|
|
mov edx, eax
|
|
shr edx, 7
|
|
add eax, edx
|
|
imul dword ptr [esi+8]
|
|
shl edx, 24
|
|
shr eax, 8
|
|
or edx, eax
|
|
mov [ebx+8], edx
|
|
|
|
mov eax, ecx
|
|
shr eax, TBGRAPixel_AlphaShift
|
|
and eax, 255
|
|
mov edx, eax
|
|
shr edx, 7
|
|
add eax, edx
|
|
imul dword ptr [esi+12]
|
|
shl edx, 24
|
|
shr eax, 8
|
|
or edx, eax
|
|
mov [ebx+12], edx
|
|
|
|
pop esi
|
|
pop ebx
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
result.r := int64(color2.r)*(color1.red shr 7+color1.red) shr 8;
|
|
result.g := int64(color2.g)*(color1.green shr 7+color1.green) shr 8;
|
|
result.b := int64(color2.b)*(color1.blue shr 7+color1.blue) shr 8;
|
|
result.a := int64(color2.a)*(color1.alpha shr 7+color1.alpha) shr 8;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean): TBGRAPixel;
|
|
var maxValue,invMaxValue,r,g,b: integer;
|
|
begin
|
|
if AColor.a <= 0 then
|
|
result.alpha := 0;
|
|
if AColor.a >= 65536 then
|
|
result.alpha := 255
|
|
else
|
|
result.alpha := AColor.a shr 8 - (AColor.a shr 15);
|
|
|
|
maxValue := AColor.r;
|
|
if AColor.g > maxValue then maxValue := AColor.g;
|
|
if AColor.b > maxValue then maxValue := AColor.b;
|
|
|
|
if maxValue <= 0 then
|
|
begin
|
|
result.red := 0;
|
|
result.green := 0;
|
|
result.blue := 0;
|
|
exit;
|
|
end;
|
|
|
|
if AGammaCompression then
|
|
begin
|
|
if maxValue <= 65535 then
|
|
begin
|
|
if AColor.r <= 0 then result.red := 0 else
|
|
result.red := GammaCompressionTab[AColor.r - (AColor.r shr 15)];
|
|
|
|
if AColor.g <= 0 then result.green := 0 else
|
|
result.green :=GammaCompressionTab[AColor.g - (AColor.g shr 15)];
|
|
|
|
if AColor.b <= 0 then result.blue := 0 else
|
|
result.blue := GammaCompressionTab[AColor.b - (AColor.b shr 15)];
|
|
exit;
|
|
end;
|
|
|
|
invMaxValue := (1073741824+maxValue-1) div maxValue;
|
|
|
|
maxValue := (maxValue-65535) shr 1;
|
|
if AColor.r < 0 then r := maxValue else
|
|
r := AColor.r*invMaxValue shr 14 + maxValue;
|
|
if AColor.g < 0 then g := maxValue else
|
|
g := AColor.g*invMaxValue shr 14 + maxValue;
|
|
if AColor.b < 0 then b := maxValue else
|
|
b := AColor.b*invMaxValue shr 14 + maxValue;
|
|
|
|
if r >= 65535 then result.red := 255 else
|
|
result.red := GammaCompressionTab[r];
|
|
if g >= 65535 then result.green := 255 else
|
|
result.green := GammaCompressionTab[g];
|
|
if b >= 65535 then result.blue := 255 else
|
|
result.blue := GammaCompressionTab[b];
|
|
end else
|
|
begin
|
|
if maxValue <= 65535 then
|
|
begin
|
|
if AColor.r <= 0 then result.red := 0 else
|
|
result.red := AColor.r shr 8 - (AColor.r shr 15);
|
|
|
|
if AColor.g <= 0 then result.green := 0 else
|
|
result.green := AColor.g shr 8 - (AColor.g shr 15);
|
|
|
|
if AColor.b <= 0 then result.blue := 0 else
|
|
result.blue := AColor.b shr 8 - (AColor.b shr 15);
|
|
exit;
|
|
end;
|
|
|
|
invMaxValue := (1073741824+maxValue-1) div maxValue;
|
|
|
|
maxValue := (maxValue-65535) shr 9;
|
|
if AColor.r < 0 then r := maxValue else
|
|
r := AColor.r*invMaxValue shr 22 + maxValue;
|
|
if AColor.g < 0 then g := maxValue else
|
|
g := AColor.g*invMaxValue shr 22 + maxValue;
|
|
if AColor.b < 0 then b := maxValue else
|
|
b := AColor.b*invMaxValue shr 22 + maxValue;
|
|
|
|
if r >= 255 then result.red := 255 else
|
|
result.red := r;
|
|
if g >= 255 then result.green := 255 else
|
|
result.green := g;
|
|
if b >= 255 then result.blue := 255 else
|
|
result.blue := b;
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|
|
|