Skip to content

Commit

Permalink
Merge pull request #173 from arnauddupuis/arnauddupuis/issue143
Browse files Browse the repository at this point in the history
Add the ability to tint/modulate a Sprite color
  • Loading branch information
arnauddupuis committed Oct 26, 2021
2 parents 81e15f7 + 86b658e commit bb8b8c4
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/source/pygamelib.gfx.core.Sprite.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ Sprite
~Sprite.from_text
~Sprite.load
~Sprite.load_from_ansi_file
~Sprite.modulate
~Sprite.render_to_buffer
~Sprite.scale
~Sprite.serialize
~Sprite.set_sprixel
~Sprite.set_transparency
~Sprite.sprixel
~Sprite.tint



Expand Down
78 changes: 77 additions & 1 deletion pygamelib/gfx/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ def blend(self, other_color, fraction=0.5):
"""
if type(fraction) is not float or fraction < 0.0 or fraction > 1.0:
raise base.PglInvalidTypeException(
"fraction needs to be a float between 0.0 and 1.0."
"Color.blend(other_color, fraction): fraction needs to be a float "
"between 0.0 and 1.0."
)
if not isinstance(other_color, Color):
raise base.PglInvalidTypeException(
Expand Down Expand Up @@ -1427,6 +1428,81 @@ def scale(self, ratio=1.0):
new_sprite.set_sprixel(i, j, self.sprixel(r2, c2))
return new_sprite

def tint(self, color: Color, ratio: float = 0.5):
"""Tint a copy of the sprite with the color.
This method creates a copy of the sprite and tint all its sprixels with the
color at the specified ratio.
It then returns the new sprite. **The original sprite is NOT modified**.
:param color: The tint color.
:type color: :class:`Color`
:param ratio: The tint ration between 0.0 and 1.0 (default: 0.5)
:type ratio: float
:returns: :class:`Sprite`
Example::
player_sprites = core.SpriteCollection.load_json_file("gfx/player.spr")
player_sprites["sick"] = player_sprites["normal"].tint(
core.Color(0, 255, 0), 0.3
)
"""
if ratio < 0.0 or ratio > 1.0:
raise base.PglInvalidTypeException(
"Sprite.tint(color, ratio): ratio must be a float between 0 and 1 "
f"(rate={ratio})"
)
new_sprite = Sprite(
size=self.size,
sprixels=None,
default_sprixel=self.default_sprixel,
parent=self.parent,
)
for row in range(0, self.size[1]):
for col in range(0, self.size[0]):
new_sprix: Sprixel = copy.deepcopy(self._sprixels[row][col])
if new_sprix.bg_color is not None:
new_sprix.bg_color = new_sprix.bg_color.blend(color, ratio)
if new_sprix.fg_color is not None:
new_sprix.fg_color = new_sprix.fg_color.blend(color, ratio)
new_sprite.set_sprixel(row, col, new_sprix)
return new_sprite

def modulate(self, color: Color, ratio: float = 0.5):
"""Modulate the sprite colors with the color in parameters.
This method tint all the sprixels of the sprite with the color at the specified
ratio.
**The original sprite IS modified**.
If you want to keep the original sprite intact consider using :py:meth:`tint()`.
:param color: The modulation color.
:type color: :class:`Color`
:param ratio: The modulation ratio between 0.0 and 1.0 (default: 0.5)
:type ratio: float
:returns: None
Example::
player_sprites = core.SpriteCollection.load_json_file("gfx/player.spr")
# After that, the sprite is quite not "normal" anymore...
player_sprites["normal"].modulate(core.Color(0, 255, 0), 0.3)
"""
if ratio < 0.0 or ratio > 1.0:
raise base.PglInvalidTypeException(
"Sprite.tint(color, ratio): ratio must be a float between 0 and 1 "
f"(rate={ratio})"
)
for row in range(0, self.size[1]):
for col in range(0, self.size[0]):
sprix: Sprixel = self._sprixels[row][col]
if sprix.bg_color is not None:
sprix.bg_color = sprix.bg_color.blend(color, ratio)
if sprix.fg_color is not None:
sprix.fg_color = sprix.fg_color.blend(color, ratio)

def render_to_buffer(self, buffer, row, column, buffer_height, buffer_width):
"""Render the sprite into a display buffer (not a screen buffer).
Expand Down
92 changes: 92 additions & 0 deletions tests/test_gfx_core_sprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,98 @@ def test_color(self):
self.assertEqual(c.g, 2)
self.assertEqual(c.b, 3)

def test_tinting(self):
sp = gfx_core.Sprite(
sprixels=[
[
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
],
[
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
gfx_core.Sprixel(
" ",
gfx_core.Color(255, 255, 255),
gfx_core.Color(255, 255, 255),
),
],
]
)
spr = sp.tint(gfx_core.Color(0, 255, 0), 0.2)
self.assertEqual(spr.sprixel(0, 0).bg_color.r, 204)
self.assertEqual(spr.sprixel(0, 0).bg_color.g, 255)
self.assertEqual(spr.sprixel(0, 0).bg_color.b, 204)
# Original sprite should not be modified by tint
self.assertEqual(sp.sprixel(0, 0).bg_color.r, 255)
self.assertEqual(sp.sprixel(0, 0).bg_color.g, 255)
self.assertEqual(sp.sprixel(0, 0).bg_color.b, 255)
with self.assertRaises(gfx_core.base.PglInvalidTypeException):
sp.tint(gfx_core.Color(0, 255, 0), 1.2)
with self.assertRaises(gfx_core.base.PglInvalidTypeException):
sp.tint(gfx_core.Color(0, 255, 0), -1.2)
with self.assertRaises(gfx_core.base.PglInvalidTypeException):
sp.modulate(gfx_core.Color(0, 255, 0), 1.2)
with self.assertRaises(gfx_core.base.PglInvalidTypeException):
sp.modulate(gfx_core.Color(0, 255, 0), -1.2)

sp.modulate(gfx_core.Color(0, 255, 0), 0.2)
# Original sprite should be modified by modulate
self.assertEqual(sp.sprixel(0, 0).bg_color.r, 204)
self.assertEqual(sp.sprixel(0, 0).bg_color.g, 255)
self.assertEqual(sp.sprixel(0, 0).bg_color.b, 204)


if __name__ == "__main__":
unittest.main()

0 comments on commit bb8b8c4

Please sign in to comment.