Additional grayscale algorithms and optimzations #105
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.