Skip to content

Commit

Permalink
Merge pull request #315 from seb3s/add-alpha-channel-to-sprites
Browse files Browse the repository at this point in the history
Add alpha channel to sprites
  • Loading branch information
crertel authored May 5, 2024
2 parents 5bc1c26 + ded8f29 commit 88839af
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 36 deletions.
2 changes: 1 addition & 1 deletion lib/scenic/graph/bounds.ex
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ defmodule Scenic.Graph.Bounds do
end

defp points(Primitive.Sprites, {_id, cmds}, _st) do
Enum.reduce(cmds, [], fn {_, _, {x, y}, {w, h}}, acc ->
Enum.reduce(cmds, [], fn {_, _, {x, y}, {w, h}, _alpha}, acc ->
[[{x, y}, {x + w, y}, {x + w, y + h}, {x, y + h}, {x, y}] | acc]
end)
end
Expand Down
61 changes: 43 additions & 18 deletions lib/scenic/primitive/sprites.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ defmodule Scenic.Primitive.Sprites do
are executed in order when the primitive renders.
`[ {{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}} ]`
or
`[ {{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}}, alpha ]`
Each draw command is an x/y position and width/height of a rectangle in
the source image, followed by the x/y position and width/height
rectangle in the destination space.
An optional alpha channel can be set in last position to apply a transparency
effect on the sprite.
In other words, This copies rectangular images from the source
indicated by image_id and draws them in the coordinate space of
the graph.
Expand Down Expand Up @@ -59,14 +64,16 @@ defmodule Scenic.Primitive.Sprites do
You should add/modify primitives via the helper functions in
[`Scenic.Primitives`](Scenic.Primitives.html#sprites/3)
This example draws the same source rectangle twice in different locations.
The first is at full size, the second is expanded 10x.
This example draws the same source rectangle three times in different locations.
The first is at full size, the second is expanded 10x, the third is with a
50% transparency effect.
```elixir
graph
|> sprites( { "images/my_sprites.png", [
{{0,0}, {10, 20}, {10, 10}, {10, 20}},
{{0,0}, {10, 20}, {100, 100}, {100, 200}},
{{0,0}, {10, 20}, {100, 100}, {100, 200}, 0.5}
]})
```
"""
Expand All @@ -81,7 +88,8 @@ defmodule Scenic.Primitive.Sprites do
{sx :: number, sy :: number},
{sw :: number, sh :: number},
{dx :: number, dy :: number},
{dw :: number, dh :: number}
{dw :: number, dh :: number},
alpha :: number
}
@type draw_cmds :: [draw_cmd()]

Expand Down Expand Up @@ -217,22 +225,39 @@ defmodule Scenic.Primitive.Sprites do
end
end

@default_alpha 1

defp validate_commands(commands) do
commands
|> Enum.reduce({:ok, commands}, fn
_, {:error, _} = error ->
error

{{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}}, acc
when is_number(src_x) and is_number(src_y) and
is_number(src_w) and is_number(src_h) and
is_number(dst_x) and is_number(dst_y) and
is_number(dst_w) and is_number(dst_h) ->
acc

cmd, _ ->
{:error, :command, cmd}
end)
validate =
Enum.reduce_while(commands, {:ok, []}, fn
{{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}}, {:ok, cmds}
when is_number(src_x) and is_number(src_y) and
is_number(src_w) and is_number(src_h) and
is_number(dst_x) and is_number(dst_y) and
is_number(dst_w) and is_number(dst_h) ->
{:cont,
{:ok,
[
{{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}, @default_alpha}
| cmds
]}}

cmd = {{src_x, src_y}, {src_w, src_h}, {dst_x, dst_y}, {dst_w, dst_h}, alpha}, {:ok, cmds}
when is_number(src_x) and is_number(src_y) and
is_number(src_w) and is_number(src_h) and
is_number(dst_x) and is_number(dst_y) and
is_number(dst_w) and is_number(dst_h) and
is_number(alpha) ->
{:cont, {:ok, [cmd | cmds]}}

cmd, _ ->
{:halt, {:error, :command, cmd}}
end)

case validate do
{:ok, cmds} -> {:ok, Enum.reverse(cmds)}
error -> error
end
end

# --------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion lib/scenic/primitive/style/paint/radial_gradient.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Scenic.Primitive.Style.Paint.RadialGradient do
```elixir
Graph.build()
|> rect( {100, 50}, fill: {:linear, {50, 25, 10, 45, :blue, :yellow}} )
|> rect( {100, 50}, fill: {:radial, {50, 25, 10, 45, :blue, :yellow}} )
```
"""

Expand Down
2 changes: 1 addition & 1 deletion lib/scenic/primitives.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,7 @@ defmodule Scenic.Primitives do

# --------------------------------------------------------
@doc """
Add a sprites list a graph.
Add a sprites list to a graph.
"""
@spec sprites(
source :: Graph.t() | Primitive.t(),
Expand Down
8 changes: 5 additions & 3 deletions lib/scenic/script.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1451,7 +1451,7 @@ defmodule Scenic.Script do

{cmds, count} =
Enum.reduce(cmds, {[], 0}, fn
{{sx, sy}, {sw, sh}, {dx, dy}, {dw, dh}}, {cmds, count} ->
{{sx, sy}, {sw, sh}, {dx, dy}, {dw, dh}, alpha}, {cmds, count} ->
{
[
<<
Expand All @@ -1462,7 +1462,8 @@ defmodule Scenic.Script do
dx::float-32-big,
dy::float-32-big,
dw::float-32-big,
dh::float-32-big
dh::float-32-big,
alpha::float-32-big
>>
| cmds
],
Expand Down Expand Up @@ -2204,10 +2205,11 @@ defmodule Scenic.Script do
dy::float-32-big,
dw::float-32-big,
dh::float-32-big,
alpha::float-32-big,
bin::binary
>> = bin

{[{{sx, sy}, {sw, sh}, {dx, dy}, {dw, dh}} | cmds], bin}
{[{{sx, sy}, {sw, sh}, {dx, dy}, {dw, dh}, alpha} | cmds], bin}
end)

cmds = Enum.reverse(cmds)
Expand Down
6 changes: 3 additions & 3 deletions test/scenic/graph/bounds_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ defmodule Scenic.Graph.BoundsTest do
end

test "finds the natural bounds of a single sprite" do
graph = Graph.build() |> sprites({:parrot, [{{0, 0}, {10, 10}, {10, 20}, {30, 15}}]})
graph = Graph.build() |> sprites({:parrot, [{{0, 0}, {10, 10}, {10, 20}, {30, 15}, 1}]})
{10.0, 20.0, 40.0, 35.0} = Graph.bounds(graph)
end

Expand All @@ -165,8 +165,8 @@ defmodule Scenic.Graph.BoundsTest do
|> sprites(
{:parrot,
[
{{0, 0}, {10, 10}, {10, 20}, {30, 15}},
{{0, 0}, {10, 10}, {40, -3}, {30, 15}}
{{0, 0}, {10, 10}, {10, 20}, {30, 15}, 1},
{{0, 0}, {10, 10}, {40, -3}, {30, 15}, 1}
]}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ defmodule Scenic.Graph.CompilerTest do
# ---------------------------------------------------------
test "graph with sprites works" do
cmds = [
{{0, 1}, {10, 11}, {2, 3}, {12, 13}},
{{2, 3}, {10, 11}, {4, 5}, {12, 13}}
{{0, 1}, {10, 11}, {2, 3}, {12, 13}, 1},
{{2, 3}, {10, 11}, {4, 5}, {12, 13}, 1}
]

{:ok, list} =
Expand Down Expand Up @@ -250,7 +250,7 @@ defmodule Scenic.Graph.CompilerTest do
end

# ---------------------------------------------------------
# Should correctly compile fill and stroke. Note that
# Should correctly compile fill and stroke. Note that
# primitives with neither fill nor stroke are eliminated completely
test "fill and stroke are compiled correctly" do
{:ok, list} =
Expand Down
15 changes: 10 additions & 5 deletions test/scenic/primitive/sprites_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ defmodule Scenic.Primitive.SpritesTest do

@cmds [
{{0, 1}, {10, 11}, {2, 3}, {12, 13}},
{{2, 3}, {10, 11}, {4, 5}, {12, 13}}
{{2, 3}, {10, 11}, {4, 5}, {12, 13}, 0.8}
]

@enhanced_cmds [
{{0, 1}, {10, 11}, {2, 3}, {12, 13}, 1},
{{2, 3}, {10, 11}, {4, 5}, {12, 13}, 0.8}
]

# ============================================================================
Expand All @@ -21,16 +26,16 @@ defmodule Scenic.Primitive.SpritesTest do
test "build works" do
p = Sprites.build({:parrot, @cmds})
assert p.module == Sprites
assert Primitive.get(p) == {:parrot, @cmds}
assert Primitive.get(p) == {:parrot, @enhanced_cmds}
end

# ============================================================================

test "validate accepts valid data" do
assert Sprites.validate({:parrot, @cmds}) == {:ok, {:parrot, @cmds}}
assert Sprites.validate({:parrot, @cmds}) == {:ok, {:parrot, @enhanced_cmds}}

assert Sprites.validate({{:test_assets, "images/parrot.png"}, @cmds}) ==
{:ok, {{:test_assets, "images/parrot.png"}, @cmds}}
{:ok, {{:test_assets, "images/parrot.png"}, @enhanced_cmds}}
end

test "validate rejects bad data" do
Expand Down Expand Up @@ -65,7 +70,7 @@ defmodule Scenic.Primitive.SpritesTest do
p = Sprites.build({:parrot, @cmds})

assert Sprites.compile(p, %{stroke_fill: :blue}) == [
{:draw_sprites, {"VvWQFjblIwTGsvGx866t8MIG2czWyIc8by6Xc88AOns", @cmds}}
{:draw_sprites, {"VvWQFjblIwTGsvGx866t8MIG2czWyIc8by6Xc88AOns", @enhanced_cmds}}
]
end
end
2 changes: 1 addition & 1 deletion test/scenic/script_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ defmodule Scenic.ScriptTest do
end

test "draw_sprites works" do
cmds = [{{10, 11}, {30, 40}, {2, 3}, {60, 70}}]
cmds = [{{10, 11}, {30, 40}, {2, 3}, {60, 70}, 1}]
expected = [{:draw_sprites, {"VvWQFjblIwTGsvGx866t8MIG2czWyIc8by6Xc88AOns", cmds}}]
assert Script.draw_sprites([], :parrot, cmds) == expected
assert expected == Script.serialize(expected) |> Script.deserialize()
Expand Down

0 comments on commit 88839af

Please sign in to comment.