diff --git a/lib/scenic/graph/bounds.ex b/lib/scenic/graph/bounds.ex index 6bc81156..011b5591 100644 --- a/lib/scenic/graph/bounds.ex +++ b/lib/scenic/graph/bounds.ex @@ -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 diff --git a/lib/scenic/primitive/sprites.ex b/lib/scenic/primitive/sprites.ex index fc385619..608e8661 100644 --- a/lib/scenic/primitive/sprites.ex +++ b/lib/scenic/primitive/sprites.ex @@ -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. @@ -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} ]}) ``` """ @@ -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()] @@ -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 # -------------------------------------------------------- diff --git a/lib/scenic/primitive/style/paint/radial_gradient.ex b/lib/scenic/primitive/style/paint/radial_gradient.ex index ce627528..a845cf90 100644 --- a/lib/scenic/primitive/style/paint/radial_gradient.ex +++ b/lib/scenic/primitive/style/paint/radial_gradient.ex @@ -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}} ) ``` """ diff --git a/lib/scenic/primitives.ex b/lib/scenic/primitives.ex index 19f8d541..29efde50 100644 --- a/lib/scenic/primitives.ex +++ b/lib/scenic/primitives.ex @@ -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(), diff --git a/lib/scenic/script.ex b/lib/scenic/script.ex index c058d266..b0869757 100644 --- a/lib/scenic/script.ex +++ b/lib/scenic/script.ex @@ -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} -> { [ << @@ -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 ], @@ -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) diff --git a/test/scenic/graph/bounds_test.exs b/test/scenic/graph/bounds_test.exs index 96deebfc..3051e9ac 100644 --- a/test/scenic/graph/bounds_test.exs +++ b/test/scenic/graph/bounds_test.exs @@ -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 @@ -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} ]} ) diff --git a/test/scenic/graph/compile_test.exs b/test/scenic/graph/compiler_test.exs similarity index 99% rename from test/scenic/graph/compile_test.exs rename to test/scenic/graph/compiler_test.exs index c65f7ed7..93045f65 100644 --- a/test/scenic/graph/compile_test.exs +++ b/test/scenic/graph/compiler_test.exs @@ -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} = @@ -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} = diff --git a/test/scenic/primitive/sprites_test.exs b/test/scenic/primitive/sprites_test.exs index e24e1424..b9fb887b 100644 --- a/test/scenic/primitive/sprites_test.exs +++ b/test/scenic/primitive/sprites_test.exs @@ -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} ] # ============================================================================ @@ -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 @@ -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 diff --git a/test/scenic/script_test.exs b/test/scenic/script_test.exs index 2de9ec4b..6e6b6577 100644 --- a/test/scenic/script_test.exs +++ b/test/scenic/script_test.exs @@ -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()