Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional grayscale algorithms and optimzations #105

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ahausladen
Copy link
Contributor

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.

"ClampByte(Round(...))" calls the ClampByte(double) function because Round() returns an Int64 which is too large for ClampByte(integer)). Casting the Round() return value to an Integer fix the "Double=>Int64=>Double=>Byte" conversion.
Added Linear and Colorimetric gray scaling modes. (Linear is the one needed by SVG's filter)
- InvertColors, InvertAlphas:
  Use of PStaticColor32Array

- AdjustHue, AdjustLuminance, AdjustSaturation:
  Only calculate the new color if the old color is different from the previous color
NativeInt is available in Delphi 7 and newer and FPC. That are all supported compilers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant