The previously YIQ-based algorithm turned out to be both slow,
and horribly inaccurate.
Another algorithm based on rotating the color value in the
RGB cube along the diagonal axis was also considered, which was
acceptable in terms of accuracy, and very fast.
In the end, I decided on a HSV-based one, because it is by far
the most accurate one, while still being a tad faster than the
YIQ solution.
Algorithm source: gamedev.stackexchange.com/a/59808/24839
A very simple GPU time benchmark when shifting a 2048^2 bitmap:
YIQ rot RGB rot HSV shift
radeon 13.4 ms 2.8 ms 11.4 ms
intel 13.0 ms 6.0 ms 10.5 ms
radeon: HD 3650 mobility
intel: N3540 integrated (Baytrail)
However hue shifting has never shown up as a bottleneck before,
so these are more academic.
Instead, update the surface with the same change. For many
consecutive getPixel() -> setPixel() calls on the same bitmap,
this avoids calling glReadPixels at every iteration.
Before, even though we did match all possible extensions,
we only took the first match and tried opening it.
If we were looking for a .png image, but there was an unrelated
.txt file with the same name (as it actually happens in RTPs),
we would potentially see the .txt first, try opening it,
and fail alltogether, even though the image file existed.
Now we try opening all matching files until we find one that
we can parse.
This fixes#101.
Previously, file lookup (ie. extension supplementing) would only
try out a few predetermined extensions based on the asset type.
This was not accurate in regard to RMXP's behavior, which will
happily match "some_asset" against "some_asset.abcef" and try
to open it.
Some games make use of this quirk and rename their ogg audio files
to "*.dat" or similar to thwart users from copying them.
This change also makes it easier to read arbitrary formats
supported by SDL_image without modifying mkxp.
This underlines that no reference inside the setter is taken,
and that these attributes are non-nullable.
Also removes a couple of superfluous attribute macros.
The gist of it is that for Etc and Font props, the assignment
operator (eg. 'sprite.color=') does not take a reference of the
right hand parameter and replaces its previous one with it (this
was the old behavior). Rather, it keeps its internal property
object and copies the parameter object into it by value.
The getter is unchanged; it still returns a reference to the
internal property object.
s = Sprite.new
c = Color.new
s.color = c
p s.color == c # => true
p s.color.object_id == c.object_id # => false (true before)
c = s.color
p s.color.object_id == c.object_id # => true
Setup active RGSS version at runtime. Desired version can be
specified via config, or as default, auto detected from the game
files. This removes the need to build specifically for each
version, which should help packaging a lot.
This also greatly reduces the danger of introducing code that
wouldn't compile on all RGSS version paths (as certain code paths
were completely ifdef'd out).
This can be optimized more, eg. not compiling shaders that aren't
needed in the active version.