This PR adds two additional grayscale algorithms and optimizes some color operations.
Grayscale
The default TImage32.Grayscale algorithm changes the saturation of the image, but that is not what SVG gray-scaling does (WebBrowsers, Skia). They use an algorithm that uses the perceptive contrast to generate the gray scale image.
This patch doesn't change the default behavior of the Grayscale method, but adds two parameters to it. The first sets the algorithm (Saturation, Linear, Colorimetric) and the second defines the merge amount [0.0-1.0]. The amount parameter doesn't have any effect if "Saturation" is used.
I thought about changing the default to "Linear", which is also faster than "Saturation". That would help SVG images. But if someone already uses it for non-SVG things, it would change the behavior.
Performance optimizations
The functions InvertColors and InvertAlphas use PStaticColor32Array instead of incrementing the pointer.
The AdjustHue, AdjustLuminance and AdjustSaturation functions only calculate the new color if the original color is different from the previous color, giving them a huge speed up for larger same color blocks or fully transparent parts in an image.
ClampByte(Round(...))
ClampByte has two overloads. One for Integer and one for Double. The (Delphi) compiler always calls the Double-overload if you call ClampByte with the result of Round. This happens because Round returns an Int64 and that doesn't fit into an Integer, thus ClampByte(Integer) can't be used and the next "best" overload is the Double-overload.
So we get "Double->Int64->Double->Int64->Byte" instead of "Double->Integer->Byte".
To prevent this, all the code that uses ClampByte(Round(...)) is replaced with ClampByte(Integer(Round(...))).
An Int64-overload for ClampByte would have been also possible, but the 32bit compiler would become slower, as it would have to push the Int64 onto the stack instead of using a CPU register for the ClampByte parameter.
Cleanup
The $IF not declared(NativeInt) isn't necessary. All compilers that are supported (Delphi 7+ and FPC) have that datatype. The only problem with Delphi 7-2007 is that NativeInt can't be used with FOR-loops. So this PR also introduces the SizeInt Datatype for Delphi, that FPC already has. It is an "Integer" for Delphi 7-2007 and a "NativeInt" for all newer Delphi versions, so it can be used with FOR-loops.
This PR adds two additional grayscale algorithms and optimizes some color operations.
Grayscale
The default
TImage32.Grayscale
algorithm changes the saturation of the image, but that is not what SVG gray-scaling does (WebBrowsers, Skia). They use an algorithm that uses the perceptive contrast to generate the gray scale image.This patch doesn't change the default behavior of the
Grayscale
method, but adds two parameters to it. The first sets the algorithm (Saturation, Linear, Colorimetric) and the second defines the merge amount [0.0-1.0]. The amount parameter doesn't have any effect if "Saturation" is used.I thought about changing the default to "Linear", which is also faster than "Saturation". That would help SVG images. But if someone already uses it for non-SVG things, it would change the behavior.
Performance optimizations
The functions
InvertColors
andInvertAlphas
use PStaticColor32Array instead of incrementing the pointer.The
AdjustHue
,AdjustLuminance
andAdjustSaturation
functions only calculate the new color if the original color is different from the previous color, giving them a huge speed up for larger same color blocks or fully transparent parts in an image.ClampByte(Round(...))
ClampByte
has two overloads. One forInteger
and one forDouble
. The (Delphi) compiler always calls the Double-overload if you callClampByte
with the result ofRound
. This happens becauseRound
returns an Int64 and that doesn't fit into an Integer, thusClampByte(Integer)
can't be used and the next "best" overload is the Double-overload.So we get "Double->Int64->Double->Int64->Byte" instead of "Double->Integer->Byte". To prevent this, all the code that uses
ClampByte(Round(...))
is replaced withClampByte(Integer(Round(...)))
.An Int64-overload for
ClampByte
would have been also possible, but the 32bit compiler would become slower, as it would have to push the Int64 onto the stack instead of using a CPU register for theClampByte
parameter.Cleanup
The
$IF not declared(NativeInt)
isn't necessary. All compilers that are supported (Delphi 7+ and FPC) have that datatype. The only problem with Delphi 7-2007 is that NativeInt can't be used with FOR-loops. So this PR also introduces theSizeInt
Datatype for Delphi, that FPC already has. It is an "Integer" for Delphi 7-2007 and a "NativeInt" for all newer Delphi versions, so it can be used with FOR-loops.