diff --git a/run-tests.sh b/run-tests.sh index c7f630de9..cd428ed5c 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -47,7 +47,7 @@ if [[ "$filter" == "" ]] || [[ "console" =~ $filter ]]; then echo "Testing console..." echo "uname=$(uname)" - $ASEPRITE -b --script scripts/console_assert.lua >$t/tmp 2>$t/tmp_err + $ASEPRITE -b --script scripts/console_assert.lua >$t/tmp 2>&1 ! grep -q "this should be in the output" $t/tmp && fail "print() text not found in output" ! grep -q "assertion failed" $t/tmp && fail "assert() text not found in output" grep -q "this should not be in the output" $t/tmp && fail "text that shouldn't be in the output is" @@ -55,7 +55,7 @@ if [[ "$filter" == "" ]] || [[ "console" =~ $filter ]]; then if [[ "$(uname)" =~ "MINGW" ]] || [[ "$(uname)" =~ "MSYS" ]] ; then echo Ignore console tests on Windows else - $ASEPRITE -b --script scripts/console_print.lua >$t/tmp 2>$t/tmp_err + $ASEPRITE -b --script scripts/console_print.lua >$t/tmp 2>&1 cat >$t/tmp_expected <$t/tmp 2>$t/tmp_err ; then - echo FAILED - echo STDOUT && cat $t/tmp - echo STDERR && cat $t/tmp_err + if ! $ASEPRITE -b --script $script >$t/tmp 2>&1 ; then + echo FAILED && cat $t/tmp result=1 fi done diff --git a/scripts/app_command.lua b/scripts/app_command.lua index a96a8b420..42d66c89c 100644 --- a/scripts/app_command.lua +++ b/scripts/app_command.lua @@ -558,3 +558,49 @@ do expect_img(j, { 7, 2, 1, 4, 3, 6, 5, 0 }) end + +-- Fill +do + local s = Sprite(4, 2, ColorMode.INDEXED) + local c = s.cels[1] + local i = c.image + i:clear(1) + array_to_pixels({ 1, 1, 1, 1, + 1, 1, 1, 1 }, i) + app.fgColor = Color{ index=0 } + s.selection = Selection(Rectangle(1, 1, 2, 1)) + app.command.Fill() + expect_eq(Rectangle(0, 0, 4, 2), c.bounds) + expect_img(i, { 1, 1, 1, 1, + 1, 0, 0, 1 }) + + c.position = { x=0, y=1 } + app.fgColor = Color{ index=2 } + s.selection = Selection(Rectangle(1, 0, 2, 2)) + app.command.Fill() + expect_eq(Rectangle(0, 0, 4, 3), c.bounds) + expect_img(i, { 0, 2, 2, 0, + 1, 2, 2, 1, + 1, 0, 0, 1 }) + + app.fgColor = Color{ index=0 } + s.selection = Selection(Rectangle(0, 0, 3, 3)) + app.command.Fill() + expect_eq(Rectangle(3, 1, 1, 2), c.bounds) + expect_img(i, { 1, + 1 }) + + app.undo() -- undo Fill + expect_eq(Rectangle(0, 0, 4, 3), c.bounds) + expect_img(i, { 0, 2, 2, 0, + 1, 2, 2, 1, + 1, 0, 0, 1 }) + + expect_eq(Rectangle(0, 0, 3, 3), s.selection.bounds) + app.undo() -- undo selection change + expect_eq(Rectangle(1, 0, 2, 2), s.selection.bounds) + app.undo() -- undo Fill + expect_eq(Rectangle(0, 1, 4, 2), c.bounds) + expect_img(i, { 1, 1, 1, 1, + 1, 0, 0, 1 }) +end diff --git a/scripts/color_quantization.lua b/scripts/color_quantization.lua new file mode 100644 index 000000000..b37a9c13a --- /dev/null +++ b/scripts/color_quantization.lua @@ -0,0 +1,201 @@ +-- Copyright (C) 2019-2022 Igara Studio S.A. +-- +-- This file is released under the terms of the MIT license. +-- Read LICENSE.txt for more information. + +dofile("./test_utils.lua") + +local rgba = app.pixelColor.rgba + +---------------------------------------------------------------------- +-- app.command.ColorQuantization + +do + -- One sprite with a background layer + local s = Sprite(2, 2) + app.command.BackgroundFromLayer() + + local i = s.cels[1].image + local p = s.palettes[1] + assert(#p == 256) + assert(s.colorMode == ColorMode.RGB) + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 1) + + array_to_pixels({ rgba(255, 255, 0), rgba(255, 255, 0), + rgba(255, 255, 0), rgba(255, 255, 0) }, i) + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 1) + assert(p:getColor(0) == Color(255, 255, 0)) + + array_to_pixels({ rgba(255, 0, 0), rgba(255, 0, 0), + rgba(255, 255, 0), rgba(255, 255, 0) }, i) + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 2) + assert(p:getColor(0) == Color(255, 0, 0)) + assert(p:getColor(1) == Color(255, 255, 0)) + + array_to_pixels({ rgba(255, 0, 0), rgba(255, 0, 0), + rgba(255, 255, 0), rgba(0, 0, 255) }, i) + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 3) + assert(p:getColor(0) == Color(255, 0, 0)) + assert(p:getColor(1) == Color(255, 255, 0)) + assert(p:getColor(2) == Color(0, 0, 255)) + + -- Convert the background layer to a transparent layer + + app.command.LayerFromBackground() + app.command.ColorQuantization{ algorithm="rgb5a3", withAlpha=false } + assert(#p == 4) -- One extra color for transparent layer + assert(p:getColor(0) == Color(0, 0, 0)) + assert(p:getColor(1) == Color(255, 0, 0)) + assert(p:getColor(2) == Color(255, 255, 0)) + assert(p:getColor(3) == Color(0, 0, 255)) + + app.command.ColorQuantization() + assert(#p == 4) + assert(p:getColor(0) == Color(0, 0, 0, 0)) + assert(p:getColor(1) == Color(255, 0, 0)) + assert(p:getColor(2) == Color(255, 255, 0)) + assert(p:getColor(3) == Color(0, 0, 255)) + + array_to_pixels({ rgba(0, 0, 0), rgba(255, 0, 0), + rgba(255, 0, 0), rgba(0, 0, 255) }, i) + app.command.ColorQuantization{ algorithm="rgb5a3", withAlpha=false } + assert(#p == 4) + assert(p:getColor(0) == Color(0, 0, 0)) + assert(p:getColor(1) == Color(0, 0, 0)) + assert(p:getColor(2) == Color(255, 0, 0)) + assert(p:getColor(3) == Color(0, 0, 255)) + + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 4) + assert(p:getColor(0) == Color(0, 0, 0, 0)) + assert(p:getColor(1) == Color(0, 0, 0)) + assert(p:getColor(2) == Color(255, 0, 0)) + assert(p:getColor(3) == Color(0, 0, 255)) +end + +do + -- One sprite with a transparent layer + a background layer + local s = Sprite(2, 2) + local p = s.palettes[1] + app.command.BackgroundFromLayer() + local bg = s.cels[1].image + local fg = s:newCel(s:newLayer(), 1).image + + assert(#s.frames == 1) + assert(#s.layers == 2) + assert(#s.cels == 2) + + array_to_pixels({ rgba(0, 0, 0, 0), rgba(0, 255, 0), + rgba(255, 0, 0), rgba(0, 0, 0, 0) }, fg) + array_to_pixels({ rgba(0, 0, 0), rgba(0, 0, 0), + rgba(0, 0, 0), rgba(0, 0, 255) }, bg) + + app.command.ColorQuantization{ algorithm="rgb5a3" } + assert(#p == 5) + assert(p:getColor(0) == Color(0, 0, 0, 0)) + assert(p:getColor(1) == Color(0, 0, 0)) + assert(p:getColor(2) == Color(0, 255, 0)) + assert(p:getColor(3) == Color(255, 0, 0)) + assert(p:getColor(4) == Color(0, 0, 255)) +end + +---------------------------------------------------------------------- +-- app.command.ChangePixelFormat + +do + local s = Sprite(2, 2, ColorMode.RGB) + local p = Palette(4) + p:setColor(0, Color(0, 0, 0)) + p:setColor(1, Color(101, 90, 200)) + p:setColor(2, Color(102, 91, 201)) + p:setColor(3, Color(103, 92, 203)) + s:setPalette(p) + + app.command.BackgroundFromLayer() + + local bg = s.cels[1].image + array_to_pixels({ rgba(0, 0, 0), rgba(101, 90, 200), + rgba(102, 91, 201), rgba(103, 92, 203) }, bg) + + app.command.ChangePixelFormat{ format="indexed", rgbmap="rgb5a3" } + -- Using the 5-bit precision of RGB5A3 will match everything with + -- the first palette entry. + bg = s.cels[1].image + expect_img(bg, { 0, 1, + 1, 1 }) + app.undo() + + app.command.ChangePixelFormat{ format="indexed", rgbmap="octree" } + bg = s.cels[1].image + expect_img(bg, { 0, 1, + 2, 3 }) + app.undo() + + p:setColor(0, Color(0, 0, 0, 0)) + bg = s.cels[1].image + array_to_pixels({ rgba(101, 90, 200, 0), rgba(101, 90, 200), + rgba(102, 91, 201), rgba(103, 92, 203, 0) }, bg) + app.command.ChangePixelFormat{ format="indexed", rgbmap="octree" } + bg = s.cels[1].image + expect_img(bg, { 0, 1, + 2, 0 }) + +end + +---------------------------------------------------------------------- +-- Tests for issue aseprite/aseprite#3207 +-- Conversion RGB to INDEXED color mode, in transparent layers, always +-- picks index 0 as transparent color, even if the mask color is present +-- in the palette. + +do + local s = Sprite(2, 3, ColorMode.RGB) + local p = Palette(4) + p:setColor(0, Color(0, 0, 0)) + p:setColor(1, Color(255, 0, 0)) + p:setColor(2, Color(0, 0, 0, 0)) + p:setColor(3, Color(0, 255, 0)) + s:setPalette(p) + + local bg = s.cels[1].image + array_to_pixels({ rgba(0, 0, 0),rgba(255, 0, 0), + rgba(255, 0, 0), rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0), rgba(0, 0, 0, 0), }, bg) + + app.command.ChangePixelFormat{ format="indexed", rgbmap="rgb5a3" } + -- Using the 5-bit precision of RGB5A3 will match everything with + -- the first palette entry. + bg = s.cels[1].image + expect_img(bg, { 0, 1, + 1, 2, + 2, 2 }) + app.undo() + + app.command.ChangePixelFormat{ format="indexed", rgbmap="octree" } + bg = s.cels[1].image + expect_img(bg, { 0, 1, + 1, 2, + 2, 2 }) + app.undo() + + p:setColor(2, Color(0, 0, 255)) + s:setPalette(p) + bg = s.cels[1].image + + app.command.ChangePixelFormat{ format="indexed", rgbmap="rgb5a3" } + bg = s.cels[1].image + expect_img(bg, { 2, 1, + 1, 0, + 0, 0 }) + app.undo() + + app.command.ChangePixelFormat{ format="indexed", rgbmap="octree" } + bg = s.cels[1].image + expect_img(bg, { 2, 1, + 1, 0, + 0, 0 }) +end diff --git a/scripts/layer.lua b/scripts/layer.lua index 9f4372497..6eb6ec083 100644 --- a/scripts/layer.lua +++ b/scripts/layer.lua @@ -16,4 +16,7 @@ do assert(l.name == "My Layer") assert(l.opacity == 128) assert(l.blendMode == BlendMode.MULTIPLY) + + l.data = "Data" + assert(l.data == "Data") end diff --git a/scripts/pixel_perfect.lua b/scripts/pixel_perfect.lua new file mode 100644 index 000000000..391dd8644 --- /dev/null +++ b/scripts/pixel_perfect.lua @@ -0,0 +1,361 @@ +-- Copyright (C) 2019-2021 Igara Studio S.A. +-- +-- This file is released under the terms of the MIT license. +-- Read LICENSE.txt for more information. + +dofile('./test_utils.lua') + +local spr = Sprite(6, 6) +local cel = spr.cels[1] + +-- Point size 1px, solid color, no symmetry, no tiled mode +do + local title = '1px, solid, no symmetry, no tiled' + local red = Color{ r=255, g=0, b=0 } + local r = red.rgbaPixel + local pixel=Brush{ size=1, type=BrushType.CIRCLE } + local testData = { + { + id='1 - ' .. title .. ': right then down', + points={ Point(2, 2), Point(3, 2), Point(3, 3) }, + expected={ r, 0, + 0, r } + }, + { + id='2 - ' .. title .. ': down then right', + points={ Point(2, 2), Point(2, 3), Point(3, 3) }, + expected={ r, 0, + 0, r } + }, + { + id='3 - ' .. title .. ': left then up', + points={ Point(2, 2), Point(1, 2), Point(1, 1) }, + expected={ r, 0, + 0, r } + }, + { + id='4 - ' .. title .. ': up then left', + points={ Point(2, 2), Point(2, 1), Point(1, 1) }, + expected={ r, 0, + 0, r } + } + } + + for i,v in ipairs(testData) do + app.useTool{ + tool='pencil', + freehandAlgorithm=1, + brush=pixel, + color=red, + points=v.points} + expect_img_msg(cel.image, v.expected, '\nTest \'' .. v.id .. '\' failed') + cel.image:clear(0) + end +end + +-- Point size 2px, translucent color, no symmetry, no tiled mode +do + local title = '2px, translucent, no symmetry, no tiled' + local red = Color{ r=255, g=0, b=0, a=127 } + local r = red.rgbaPixel + local square=Brush{ size=2, type=BrushType.SQUARE } + local testData = { + { + id='1 - ' .. title .. ': right then down', + points={ Point(2, 2), Point(3, 2), Point(3, 3) }, + expected={ r, r, 0, + r, r, r, + 0, r, r } + }, + { + id='2 - ' .. title .. ': down then right', + points={ Point(2, 2), Point(2, 3), Point(3, 3) }, + expected={ r, r, 0, + r, r, r, + 0, r, r } + }, + { + id='3 - ' .. title .. ': left then up', + points={ Point(2, 2), Point(1, 2), Point(1, 1) }, + expected={ r, r, 0, + r, r, r, + 0, r, r } + }, + { + id='4 - ' .. title .. ': up then left', + points={ Point(2, 2), Point(2, 1), Point(1, 1) }, + expected={ r, r, 0, + r, r, r, + 0, r, r } + } + } + + for i,v in ipairs(testData) do + app.useTool{ + tool='pencil', + freehandAlgorithm=1, + brush=square, + color=red, + points=v.points} + expect_img_msg(cel.image, v.expected, '\nTest \'' .. v.id .. '\' failed') + cel.image:clear(0) + end +end + +-- Point size 2px, translucent color, symmetry, no tiled mode +do + local pref = app.preferences + local docPref = pref.document(spr) + pref.symmetry_mode.enabled = true + docPref.symmetry.mode = 3 + docPref.symmetry.x_axis = 3 + docPref.symmetry.y_axis = 3 + + local title = '2px, translucent, symmetry on, no tiled' + local red = Color{ r=255, g=0, b=0, a=127 } + local r = red.rgbaPixel + local square=Brush{ size=2, type=BrushType.SQUARE } + local testData = { + { + id='1 - ' .. title .. ': right then down', + points={ Point(1, 1), Point(2, 1), Point(2, 2) }, + expected={ r, r, 0, 0, r, r, + r, r, r, r, r, r, + 0, r, r, r, r, 0, + 0, r, r, r, r, 0, + r, r, r, r, r, r, + r, r, 0, 0, r, r } + }, + { + id='2 - ' .. title .. ': down then right', + points={ Point(1, 1), Point(1, 2), Point(2, 2) }, + expected={ r, r, 0, 0, r, r, + r, r, r, r, r, r, + 0, r, r, r, r, 0, + 0, r, r, r, r, 0, + r, r, r, r, r, r, + r, r, 0, 0, r, r } + }, + { + id='3 - ' .. title .. ': left then up', + points={ Point(2, 2), Point(1, 2), Point(1, 1) }, + expected={ r, r, 0, 0, r, r, + r, r, r, r, r, r, + 0, r, r, r, r, 0, + 0, r, r, r, r, 0, + r, r, r, r, r, r, + r, r, 0, 0, r, r } + }, + { + id='4 - ' .. title .. ': up then left', + points={ Point(2, 2), Point(2, 1), Point(1, 1) }, + expected={ r, r, 0, 0, r, r, + r, r, r, r, r, r, + 0, r, r, r, r, 0, + 0, r, r, r, r, 0, + r, r, r, r, r, r, + r, r, 0, 0, r, r } + } + } + + for i,v in ipairs(testData) do + app.useTool{ + tool='pencil', + freehandAlgorithm=1, + brush=square, + color=red, + points=v.points} + expect_img_msg(cel.image, v.expected, '\nTest \'' .. v.id .. '\' failed') + cel.image:clear(0) + end +end + +-- Point size 2px, translucent color, no symmetry, tiled mode on +do + local pref = app.preferences + local docPref = pref.document(spr) + pref.symmetry_mode.enabled = false + docPref.tiled.mode = 3 + + local title = '2px, translucent, no symmetry, tiled' + local red = Color{ r=255, g=0, b=0, a=127 } + local r = red.rgbaPixel + local square=Brush{ size=2, type=BrushType.SQUARE } + local testData = { + -- Top left corner + { + id='1 - ' .. title .. ': on top left corner, right then down', + points={ Point(0, 0), Point(1, 0), Point(1, 1) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='2 - ' .. title .. ': on top left corner, down then right', + points={ Point(0, 0), Point(0, 1), Point(1, 1) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='3 - ' .. title .. ': on top left corner, left then up', + points={ Point(0, 0), Point(-1, 0), Point(-1, -1) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + { + id='4 - ' .. title .. ': on top left corner, up then left', + points={ Point(0, 0), Point(0, -1), Point(-1, -1) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + -- Top right corner + { + id='5 - ' .. title .. ': on top right corner, right then down', + points={ Point(6, 0), Point(7, 0), Point(7, 1) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='6 - ' .. title .. ': on top right corner, down then right', + points={ Point(6, 0), Point(6, 1), Point(7, 1) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='7 - ' .. title .. ': on top right corner, left then up', + points={ Point(6, 0), Point(5, 0), Point(5, -1) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + { + id='8 - ' .. title .. ': on top right corner, up then left', + points={ Point(6, 0), Point(5, 0), Point(5, -1) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + -- Bottom left corner + { + id='9 - ' .. title .. ': on bottom left corner, right then down', + points={ Point(0, 6), Point(1, 6), Point(1, 7) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='10 - ' .. title .. ': on bottom left corner, down then right', + points={ Point(0, 6), Point(0, 7), Point(1, 7) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='11 - ' .. title .. ': on bottom left corner, left then up', + points={ Point(0, 6), Point(-1, 6), Point(-1, 5) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + { + id='12 - ' .. title .. ': on bottom left corner, up then left', + points={ Point(0, 6), Point(0, 5), Point(-1, 5) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + -- Botomm right corner + { + id='13 - ' .. title .. ': on bottom right corner, right then down', + points={ Point(6, 6), Point(7, 6), Point(7, 7) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='14 - ' .. title .. ': on bottom right corner, down then right', + points={ Point(6, 6), Point(6, 7), Point(7, 7) }, + expected={ r, r, 0, 0, 0, r, + r, r, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + r, 0, 0, 0, 0, r } + }, + { + id='15 - ' .. title .. ': on bottom right corner, left then up', + points={ Point(6, 6), Point(5, 6), Point(5, 5) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + { + id='16 - ' .. title .. ': on bottom right corner, up then left', + points={ Point(6, 6), Point(6, 5), Point(5, 5) }, + expected={ r, 0, 0, 0, 0, r, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, r, r, + r, 0, 0, 0, r, r } + }, + } + + for i,v in ipairs(testData) do + app.useTool{ + tool='pencil', + freehandAlgorithm=1, + brush=square, + color=red, + points=v.points} + expect_img_msg(cel.image, v.expected, '\nTest \'' .. v.id .. '\' failed') + cel.image:clear(0) + end +end diff --git a/scripts/slice.lua b/scripts/slice.lua index 85e62147e..3794e4a3e 100644 --- a/scripts/slice.lua +++ b/scripts/slice.lua @@ -21,4 +21,7 @@ do assert(a.pivot == nil) a.pivot = Point(16, 17) assert(a.pivot == Point(16, 17)) + + a.data = "Data" + assert(a.data == "Data") end diff --git a/scripts/tag.lua b/scripts/tag.lua index 67b9bd1a0..d49604ac6 100644 --- a/scripts/tag.lua +++ b/scripts/tag.lua @@ -35,4 +35,7 @@ do assert(a.color == Color(0, 0, 0)) a.color = Color(255, 0, 0) assert(a.color == Color(255, 0, 0)) + + a.data = "Data" + assert(a.data == "Data") end diff --git a/scripts/test_utils.lua b/scripts/test_utils.lua index 0f8029109..28e208fad 100644 --- a/scripts/test_utils.lua +++ b/scripts/test_utils.lua @@ -32,6 +32,8 @@ function expect_img(image, expectedPixels) local h = image.height if w*h ~= #expectedPixels then print(debug.traceback()) + print('Expected pixels: #=' .. #expectedPixels) + print('Image size: w=' .. w .. ' h=' .. h .. ' #=' .. w*h) dump_img(image) assert(w*h == #expectedPixels) end @@ -75,6 +77,14 @@ function expect_img(image, expectedPixels) end end +function expect_img_msg(image, expectedPixels, msg) + local status, err = pcall(expect_img, image, expectedPixels) + if not status then + print(msg) + error(err) + end +end + function array_to_pixels(array, image) local w = image.width local h = image.height diff --git a/scripts/tilemap.lua b/scripts/tilemap.lua new file mode 100644 index 000000000..2eb38560d --- /dev/null +++ b/scripts/tilemap.lua @@ -0,0 +1,1119 @@ +-- Copyright (C) 2019-2021 Igara Studio S.A. +-- +-- This file is released under the terms of the MIT license. +-- Read LICENSE.txt for more information. + +-- This version of Aseprite doesn't support tilemaps +if TilesetMode == nil then return end + +dofile('./test_utils.lua') + +local rgba = app.pixelColor.rgba + +-- Check constants +assert(TilemapMode.PIXELS == 0) +assert(TilemapMode.TILES == 1) +assert(TilesetMode.MANUAL == 0) +assert(TilesetMode.AUTO == 1) +assert(TilesetMode.STACK == 2) + +---------------------------------------------------------------------- +-- Tests drawing in the tilemap +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32, ColorMode.INDEXED) + spr.gridBounds = Rectangle{ 0, 0, 4, 4 } + assert(spr.layers[1].isImage) + assert(not spr.layers[1].isTilemap) + + ---------------------------------------------------------------------- + -- Create a tilemap + ---------------------------------------------------------------------- + + app.command.NewLayer{ tilemap=true } + assert(#spr.layers == 2) + local tilemapLay = spr.layers[2] + assert(tilemapLay.isImage) + assert(tilemapLay.isTilemap) + assert(#tilemapLay.cels == 0) + + ---------------------------------------------------------------------- + -- Draw the first pixel on the tilemap so a new tile is created + ---------------------------------------------------------------------- + + app.useTool{ + tool='pencil', + color=1, + layer=tilemapLay, + tilesetMode=TilesetMode.STACK, + points={ Point(1, 2), Point(2, 2) }} + assert(#tilemapLay.cels == 1) + assert(tilemapLay:cel(1).image.colorMode == ColorMode.TILEMAP) + local tilemapCel = tilemapLay:cel(1) + assert(tilemapCel.bounds == Rectangle(0, 0, 4, 4)) + + assert(#spr.tilesets == 1) -- one tileset + assert(spr.tilesets[1] == tilemapLay.tileset) + + local tileset = tilemapLay.tileset + assert(#tileset == 2) -- empty tile + our tile + assert(tileset:getTile(0):isEmpty()) + assert(not tileset:getTile(1):isEmpty()) + expect_img(tileset:getTile(1), { 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,0,0,0 }) + + tilemapCel.position = Point(2, 2) + assert(tilemapCel.bounds == Rectangle(2, 2, 4, 4)) + assert(#tileset == 2) + + assert(tilemapCel.image.width == 1) + assert(tilemapCel.image.height == 1) + assert(tilemapCel.image:getPixel(0, 0) == 1) + + ---------------------------------------------------------------------- + -- Draw a second pixel with locked mode (new tiles are not generated) + ---------------------------------------------------------------------- + + app.useTool{ + tool='pencil', + color=1, + cel=tilemapCel, + tilesetMode=TilesetMode.MANUAL, + points={ Point(0, 0) }} + + assert(tilemapCel.bounds == Rectangle(2, 2, 4, 4)) + assert(#tileset == 2) + + ---------------------------------------------------------------------- + -- Draw pixels generating new tiles + ---------------------------------------------------------------------- + + app.useTool{ + tool='pencil', + color=1, + cel=tilemapCel, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0) }} + + assert(tilemapCel.bounds == Rectangle(-2, -2, 8, 8)) + assert(#tileset == 3) + assert(tileset:getTile(0):isEmpty()) + assert(not tileset:getTile(1):isEmpty()) + assert(not tileset:getTile(2):isEmpty()) + expect_img(tilemapCel.image, { 2, 0, + 0, 1 }) + + app.useTool{ + tool='pencil', + color=1, + cel=tilemapCel, + tilesetMode=TilesetMode.STACK, + points={ Point(6, 6) }} + + assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12)) + assert(#tileset == 4) + assert(not tileset:getTile(3):isEmpty()) + expect_img(tilemapCel.image, { 2, 0, 0, + 0, 1, 0, + 0, 0, 3 }) + + ---------------------------------------------------------------------- + -- Draw in manual mode to modify existent tiles + ---------------------------------------------------------------------- + + assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12)) + assert(#tileset == 4) + expect_img(tileset:getTile(1), { 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,0,0,0 }) + expect_img(tileset:getTile(2), { 0,0,0,0, + 0,0,0,0, + 0,0,1,0, + 0,0,0,0 }) + expect_img(tileset:getTile(3), { 1,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0 }) + expect_img(tilemapCel.image, { 2, 0, 0, + 0, 1, 0, + 0, 0, 3 }) + + app.useTool{ + tool='rectangle', + color=2, + cel=tilemapCel, + tilesetMode=TilesetMode.MANUAL, + points={ Point(0, 0), Point(9, 9) }} + + assert(tilemapCel.bounds == Rectangle(-2, -2, 12, 12)) + assert(#tileset == 4) + expect_img(tileset:getTile(1), { 0,0,0,0, + 0,0,0,0, + 0,1,1,0, + 0,0,0,0 }) + expect_img(tileset:getTile(2), { 0,0,0,0, + 0,0,0,0, + 0,0,2,2, + 0,0,2,0 }) + expect_img(tileset:getTile(3), { 1,0,0,2, + 0,0,0,2, + 0,0,0,2, + 2,2,2,2 }) + expect_img(tilemapCel.image, { 2, 0, 0, + 0, 1, 0, + 0, 0, 3 }) + + tilemapCel.position = Point(1, 1) + app.useTool{ + tool='line', + color=3, + cel=tilemapCel, + tilesetMode=TilesetMode.MANUAL, + points={ Point(1, 1), Point(12, 12) }} + + assert(tilemapCel.bounds == Rectangle(1, 1, 12, 12)) + assert(#tileset == 4) + expect_img(tileset:getTile(1), { 3,0,0,0, + 0,3,0,0, + 0,1,3,0, + 0,0,0,3 }) + expect_img(tileset:getTile(2), { 3,0,0,0, + 0,3,0,0, + 0,0,3,2, + 0,0,2,3 }) + expect_img(tileset:getTile(3), { 3,0,0,2, + 0,3,0,2, + 0,0,3,2, + 2,2,2,3 }) + expect_img(tilemapCel.image, { 2, 0, 0, + 0, 1, 0, + 0, 0, 3 }) + +end + +---------------------------------------------------------------------- +-- Tests drawing in the tilemap with tiles +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32) + spr.gridBounds = Rectangle(0, 0, 8, 8) + app.command.NewLayer{ tilemap=true } + + local tm = app.activeLayer + local ts = tm.tileset + assert(ts ~= nil) + expect_eq(0, ts.grid.origin.x) + expect_eq(0, ts.grid.origin.y) + expect_eq(8, ts.grid.tileSize.width) + expect_eq(8, ts.grid.tileSize.height) + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=0, b=0 }, + tilemapMode=TilesetMode.PIXELS, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0), Point(31, 31) }} + + local cel = tm.cels[1] + expect_eq(2, #ts) + expect_img(cel.image, { 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 }) + + app.useTool{ + tool='pencil', + color=Color{ index=1 }, + tilemapMode=TilemapMode.TILES, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 16) }} -- y=16 is the first pixel of 3rd row of tiles + cel = tm.cels[1] + expect_img(cel.image, { 1,0,0,0, + 0,1,0,0, + 1,0,1,0, + 0,0,0,1 }) + + app.useTool{ + tool='pencil', + color=Color{ index=1 }, + tilemapMode=TilemapMode.TILES, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0), Point(16, 0) }} -- x=16 is the first pixel of 3rd column of tiles + cel = tm.cels[1] + expect_img(cel.image, { 1,1,1,0, + 0,1,0,0, + 1,0,1,0, + 0,0,0,1 }) + + -- Move layer origin to 10, 8 + cel.position = { 10, 8 } + expect_eq(Point{ 10, 8 }, cel.position) + app.useTool{ + tool='pencil', + color=Color{ index=0 }, + tilemapMode=TilemapMode.TILES, + tilesetMode=TilesetMode.STACK, + points={ { 10, 8 }, { 18, 16 } }} -- {10,8} is the first existent tile in the tilemap + -- these are tiles 2,1 and 3,2 + cel = tm.cels[1] + expect_img(cel.image, { 0,1,1,0, + 0,0,0,0, + 1,0,1,0, + 0,0,0,1 }) + + app.useTool{ + tool='pencil', + color=Color{ index=1 }, + tilemapMode=TilemapMode.TILES, + tilesetMode=TilesetMode.STACK, + points={ Point(1, 7), Point(2, 8) }} -- Tile 0,0 and 1,1 + + cel = tm.cels[1] + expect_img(cel.image, { 1,0, 0,0,0,0, + 0,1, 0,1,1,0, + 0,0, 0,0,0,0, + 0,0, 1,0,1,0, + 0,0, 0,0,0,1 }) + +end + +---------------------------------------------------------------------- +-- Tests drawing when the grid origin is in a negative position +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32) + app.command.NewLayer{ tilemap=true } + local tilemapLay = spr.layers[2] + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=255, b=0 }, + layer=tilemapLay, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0) }} + local tilemapCel = tilemapLay:cel(1) + assert(tilemapCel.bounds == Rectangle(0, 0, 16, 16)) + + local tileset = tilemapLay.tileset + assert(#tileset == 2) -- empty tile + our tile + + tilemapCel.position = Point(-1, -1) + assert(tilemapCel.bounds == Rectangle(-1, -1, 16, 16)) + + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + cel=tilemapCel, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0) }} + + assert(tilemapCel.bounds == Rectangle(-1, -1, 16, 16)) + assert(#tileset == 3) + + local img = tileset:getTile(2) + + assert(img:getPixel(0, 0) == rgba(0, 255, 0, 255)) + assert(img:getPixel(1, 0) == rgba(0, 0, 0, 0)) + assert(img:getPixel(2, 0) == rgba(0, 0, 0, 0)) + + assert(img:getPixel(0, 1) == rgba(0, 0, 0, 0)) + assert(img:getPixel(1, 1) == rgba(255, 0, 0, 255)) + assert(img:getPixel(2, 1) == rgba(0, 0, 0, 0)) + + assert(img:getPixel(0, 2) == rgba(0, 0, 0, 0)) + assert(img:getPixel(1, 2) == rgba(0, 0, 0, 0)) + assert(img:getPixel(2, 2) == rgba(0, 0, 0, 0)) +end + +---------------------------------------------------------------------- +-- Tests that extra tiles are not created +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32) + app.command.NewLayer{ tilemap=true } + local tilemapLay = spr.layers[2] + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=255, b=0 }, + layer=tilemapLay, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0) }} + local tilemapCel = tilemapLay:cel(1) + tilemapCel.position = Point(-1, -1) + + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + cel=tilemapCel, + tilesetMode=TilesetMode.STACK, + points={ Point(30, 30) }} + + assert(tilemapCel.bounds == Rectangle(-1, -1, 32, 32)) +end + +---------------------------------------------------------------------- +-- Tests moving & copying tiles +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32) + app.command.NewLayer{ tilemap=true } + local tilemapLay = spr.layers[2] + + app.useTool{ + tool='ellipse', + color=Color{ r=0, g=0, b=0 }, + layer=tilemapLay, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0), Point(31, 31) }} + + local ts = tilemapLay.tileset + assert(#ts == 5) + + local imgs = { ts:getTile(1), + ts:getTile(2), + ts:getTile(3), + ts:getTile(4) } + + -- No op = move tile 1 before the tile 1, result=0,1,2,3,4 + app.range.tiles = { 1 } + app.command.MoveTiles{ before=1 } + expect_eq(imgs[1], ts:getTile(1)) + expect_eq(imgs[2], ts:getTile(2)) + expect_eq(imgs[3], ts:getTile(3)) + expect_eq(imgs[4], ts:getTile(4)) + expect_eq(1, #app.range.tiles) + expect_eq(1, app.range.tiles[1]) + + -- Move tile 2 before tile 1, result=0,2,1,3,4 + app.range.tiles = { 2 } + app.command.MoveTiles{ before=1 } + expect_eq(imgs[2], ts:getTile(1)) + expect_eq(imgs[1], ts:getTile(2)) + expect_eq(imgs[3], ts:getTile(3)) + expect_eq(imgs[4], ts:getTile(4)) + expect_eq(1, #app.range.tiles) + expect_eq(1, app.range.tiles[1]) + app.undo() + + -- Move tiles 1 and 2 before 4, result=0,3,1,2,4 + app.range.tiles = { 1, 2 } + app.command.MoveTiles({ before=4 }) + expect_eq(imgs[3], ts:getTile(1)) + expect_eq(imgs[1], ts:getTile(2)) + expect_eq(imgs[2], ts:getTile(3)) + expect_eq(imgs[4], ts:getTile(4)) + expect_eq(2, #app.range.tiles) + expect_eq(2, app.range.tiles[1]) + expect_eq(3, app.range.tiles[2]) + app.undo() + + -- Move tiles 1 and 3 before 3, result=0,2,1,3,4 + app.range.tiles = { 1, 3 } + app.command.MoveTiles({ before=3 }) + expect_eq(imgs[2], ts:getTile(1)) + expect_eq(imgs[1], ts:getTile(2)) + expect_eq(imgs[3], ts:getTile(3)) + expect_eq(imgs[4], ts:getTile(4)) + expect_eq(2, #app.range.tiles) + expect_eq(2, app.range.tiles[1]) + expect_eq(3, app.range.tiles[2]) + app.undo() + + -- Copy tiles 1 before 1, result=0,1,1,2,3,4 + app.range.tiles = { 1 } + app.command.CopyTiles({ before=1 }) + expect_eq(1, #app.range.tiles) + expect_eq(1, app.range.tiles[1]) + expect_eq(6, #ts) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[1])) + assert(ts:getTile(3):isEqual(imgs[2])) + assert(ts:getTile(4):isEqual(imgs[3])) + assert(ts:getTile(5):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 1 before 4, result=0,1,2,3,1,4 + app.range.tiles = { 1 } + app.command.CopyTiles({ before=4 }) + expect_eq(1, #app.range.tiles) + expect_eq(4, app.range.tiles[1]) + expect_eq(6, #ts) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[2])) + assert(ts:getTile(3):isEqual(imgs[3])) + assert(ts:getTile(4):isEqual(imgs[1])) + assert(ts:getTile(5):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 1, 2, and 4, before 5, result=0,1,2,3,4,1,2,4 + app.range.tiles = { 1, 2, 4 } + app.command.CopyTiles({ before=5 }) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[2])) + assert(ts:getTile(3):isEqual(imgs[3])) + assert(ts:getTile(4):isEqual(imgs[4])) + assert(ts:getTile(5):isEqual(imgs[1])) + assert(ts:getTile(6):isEqual(imgs[2])) + assert(ts:getTile(7):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 2, and 4, before 1, result=0,2,4,1,2,3,4 + app.range.tiles = { 2, 4 } + app.command.CopyTiles({ before=1 }) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[2])) + assert(ts:getTile(2):isEqual(imgs[4])) + assert(ts:getTile(3):isEqual(imgs[1])) + assert(ts:getTile(4):isEqual(imgs[2])) + assert(ts:getTile(5):isEqual(imgs[3])) + assert(ts:getTile(6):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 1, and 4, before 5, result=0,1,2,3,4,1,4 + app.range.tiles = { 1, 4 } + app.command.CopyTiles({ before=5 }) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[2])) + assert(ts:getTile(3):isEqual(imgs[3])) + assert(ts:getTile(4):isEqual(imgs[4])) + assert(ts:getTile(5):isEqual(imgs[1])) + assert(ts:getTile(6):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 1, and 4, before 6, result=0,1,2,3,4,-,1,4 + app.range.tiles = { 1, 4 } + app.command.CopyTiles({ before=6 }) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[2])) + assert(ts:getTile(3):isEqual(imgs[3])) + assert(ts:getTile(4):isEqual(imgs[4])) + assert(ts:getTile(5):isEmpty()) + assert(ts:getTile(6):isEqual(imgs[1])) + assert(ts:getTile(7):isEqual(imgs[4])) + app.undo() + + -- Copy tiles 3, and 4, before 8, result=0,1,2,3,4,-,-,-,3,4 + app.range.tiles = { 3, 4 } + app.command.CopyTiles({ before=8 }) + assert(ts:getTile(0):isEmpty()) + assert(ts:getTile(1):isEqual(imgs[1])) + assert(ts:getTile(2):isEqual(imgs[2])) + assert(ts:getTile(3):isEqual(imgs[3])) + assert(ts:getTile(4):isEqual(imgs[4])) + assert(ts:getTile(5):isPlain()) + assert(ts:getTile(6):isPlain()) + assert(ts:getTile(7):isPlain()) + assert(ts:getTile(8):isEqual(imgs[3])) + assert(ts:getTile(9):isEqual(imgs[4])) + app.undo() + +end + +---------------------------------------------------------------------- +-- Tests bug with alpha=0 and different RGB values +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 32) + spr.gridBounds = Rectangle(0, 0, 2, 2) + app.command.NewLayer{ tilemap=true } + + local tm = app.activeLayer + local ts = tm.tileset + expect_eq(1, #ts) + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=0, b=0, a=255 }, + tilemapMode=TilesetMode.PIXELS, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0), Point(3, 0) }} + + expect_eq(2, #ts) + + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0, a=0 }, + tilemapMode=TilesetMode.PIXELS, + tilesetMode=TilesetMode.STACK, + points={ Point(0, 0), Point(1, 0) }} + + -- If #ts is == 3, it means that the last useTool() with a r=255 a=0 + -- created a new tile, that shouldn't be the case (because a=0 + -- should ignore RGB values to compare tiles) + expect_eq(2, #ts) + +end + +---------------------------------------------------------------------- +-- Tests tiles dissapearing in AUTO mode +---------------------------------------------------------------------- + +do + local spr = Sprite(32, 16, ColorMode.INDEXED) + spr.gridBounds = Rectangle(0, 0, 2, 2) + app.command.ConvertLayer{ to="tilemap" } + app.useTool{ points={ Point(0, 0), Point(31, 31) }, color=1, tool="filled_ellipse", tilesetMode=TilesetMode.AUTO } + app.useTool{ points={ Point(4, 4), Point(27, 27) }, color=2, tool="filled_ellipse", tilesetMode=TilesetMode.AUTO } + app.useTool{ points={ Point(0, 0), Point(32, 16) }, color=3, tool="line", tilesetMode=TilesetMode.AUTO } + app.useTool{ points={ Point(0, 0), Point(31, 8), + Point(0, 8), Point(31, 16) }, + brush=4, color=3, tool="pencil", tilesetMode=TilesetMode.AUTO } + + local i = spr.cels[1].image + assert(i:getPixel(4, 6) ~= 0) + assert(i:getPixel(8, 7) ~= 0) +end + +----------------------------------------------------------------------- +-- Test CanvasSize with tilemaps when we trim out content +----------------------------------------------------------------------- + +do + local sprite = Sprite(32, 32) + sprite.gridBounds = Rectangle(0, 0, 16, 16) + + -- Create a 2x2 tilemap with 4 tiles drawing an ellipse + app.command.NewLayer{ tilemap=true } + app.useTool{ + tool='filled_ellipse', + color=Color{ r=255, g=255, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(0, 0), Point(31, 31) } + } + assert(#sprite.layers == 2) + -- remove the unused layer image + sprite:deleteLayer("Layer 1") + assert(#sprite.layers == 1) + + local celMap = app.activeLayer.cels[1] + assert(celMap.bounds == Rectangle(0, 0, 32, 32)) + + -- resize the canvas to 16x16 starting point(4,9) + -- covering a large part of the tile in 0,0 + app.command.CanvasSize{ + bounds=Rectangle(4,9,16,16), + trimOutside=true + } + + assert(#app.activeLayer.cels == 1) + + expect_eq(celMap.bounds, Rectangle(-4, -9, 32, 32)) + expect_eq(sprite.bounds.width, 16) + expect_eq(sprite.bounds.height, 16) + + -- grid is 16x16, so expected image width/height is 2 + expect_eq(celMap.image.width, 2) + expect_eq(celMap.image.height, 2) + expect_eq(celMap.image:getPixel(0,0), 1) + expect_eq(celMap.image:getPixel(1,0), 2) + expect_eq(celMap.image:getPixel(0,1), 3) + expect_eq(celMap.image:getPixel(1,1), 4) + + -- undo the last canvas resize + app.command.Undo() + + -- reposition the cel in the sprite + celMap.position = Point(0,16) + expect_eq(celMap.bounds, Rectangle(0, 16, 32, 32)) + + -- deleting the whole tilemap cel when it's outside the bounds + app.command.CanvasSize { + bounds=Rectangle(0,0,8,8), + trimOutside=true + } + + expect_eq(#app.activeLayer.cels, 0) + expect_eq(sprite.bounds.width, 8) + expect_eq(sprite.bounds.height, 8) + + -- undo canvas resize + app.command.Undo() + + -- undo set position + app.command.Undo() + expect_eq(celMap.bounds, Rectangle(0, 0, 32, 32)) + + app.command.CanvasSize{ + bounds=Rectangle(16,16,16,16), + trimOutside=true + } + + assert(celMap ~= nil) + expect_eq(celMap.bounds.x, 0) + expect_eq(celMap.bounds.y, 0) + expect_eq(celMap.image.width, 1) + expect_eq(celMap.image.height, 1) + expect_eq(celMap.position, Point(0,0)) + expect_eq(celMap.image:getPixel(0,0), 4) +end + +----------------------------------------------------------------------- +-- Test CanvasSize when the bounds of an image is shrunken +----------------------------------------------------------------------- + +do + local sprite = Sprite(32, 32) + sprite.gridBounds = Rectangle(0, 0, 8, 8) + + app.command.NewLayer{ tilemap=true } + local tilemapLayer = sprite.layers[2] + + -- tile 1 + app.useTool { + tool='pencil', color=Color{r=255, b=0, g=255}, + layer=tilemapLayer, + points={ Point(7,8), Point(0, 15) }, + tilesetMode=TilesetMode.AUTO + } + + local celMap = tilemapLayer.cels[1] + + -- tile 2 + app.useTool { + tool='pencil', color=Color{r=0, b=255, g=255}, + cel=celMap, + points={ Point(24,16), Point(31, 23) }, + tilesetMode=TilesetMode.AUTO + } + + -- tile 3 + app.useTool { + tool='pencil', color=Color{r=0, b=0, g=255}, + cel=celMap, + points={ Point(30,24), Point(24, 31) }, + tilesetMode=TilesetMode.AUTO + } + + -- tile 4 + app.useTool { + tool='pencil', color=Color{r=0, b=255, g=0}, + cel=celMap, + points={ Point(17,24), Point(23, 31) }, + tilesetMode=TilesetMode.AUTO + } + + -- crop this sprite a little below pixel(0,0) + app.command.CanvasSize { + bounds=Rectangle(8,16,20,16), + trimOutside=true, ui=false + } + + expect_eq(sprite.bounds.width, 20) + expect_eq(sprite.bounds.height, 16) + + -- ideally, we expect a 3x2 image, however, some parts of top and left + -- of the image are empty/notile, so it's optimized by shrunking it + -- we got paddings instead. + + expect_eq(celMap.position, Point(8, 0)) + expect_eq(celMap.image.width, 2) + expect_eq(celMap.image.height, 2) + expect_eq(celMap.image:getPixel(0, 0), 0) + expect_eq(celMap.image:getPixel(1, 0), 2) + expect_eq(celMap.image:getPixel(1, 1), 3) + expect_eq(celMap.image:getPixel(0, 1), 4) +end + +---------------------------------------------------------------------- +-- Tests canvas resizing in the tilemap with tiles 1x1 +---------------------------------------------------------------------- + +do + local sprite1 = Sprite(4, 4) + -- Create a tilemap layer which grid size is 1x1 + sprite1.gridBounds = Rectangle(1, 1, 1, 1) + app.command.NewLayer{ tilemap=true } + -- Create a tilemap of 2x2 tiles: + -- ______ + -- | ∏∏ | + -- | ∏∏ | + -- | | + -- |______| + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(1, 0) } + } + app.useTool{ + tool='pencil', + color=Color{ r=0, g=255, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(2, 0) } + } + app.useTool{ + tool='pencil', + color=Color{ r=0, g=0, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(1, 1) } + } + app.useTool{ + tool='pencil', + color=Color{ r=255, g=255, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(2, 1) } + } + + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,0,2,2), + trimOutside=true + } + + local cel = app.activeLayer.cels[1] + expect_eq(cel.image.width, 1) + expect_eq(cel.image.height, 2) + expect_eq(cel.bounds, Rectangle(1, 0, 1, 2)) + + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,0,2,1), + trimOutside=true + } + + cel = app.activeLayer.cels[1] + expect_eq(cel.image.width, 1) + expect_eq(cel.image.height, 1) + expect_eq(cel.bounds, Rectangle(1, 0, 1, 1)) +end + +---------------------------------------------------------------------- +-- Tests canvas resizing in the tilemap with tiles 2x2, origin 2,1 +---------------------------------------------------------------------- + +do + local sprite2 = Sprite(8, 8) + -- Create a tilemap layer which grid size is 2x2 + sprite2.gridBounds = Rectangle(0, 0, 2, 2) + app.command.NewLayer{ tilemap=true } + -- Create a tilemap of 4x4 tiles: + -- + -- ,--------- x = 2 + -- | + -- ____v______ + -- | | + -- | ∏∏XX |<-- y = 1 + -- | ∏∏XX | + -- | OO∏∏ | + -- | OO∏∏ | + -- | | + -- | | + -- |___________| + + -- Making a pixel to make a Cel + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(0, 0) } + } + + -- Moving the Cel to de desired position + app.activeLayer.cels[1].position = Point(2, 1) + + -- Filling with squares of 2x2 + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(3, 1), Point(3, 2), Point(2, 2) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=255, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(4, 1), Point(5, 1), Point(5, 2), Point(4, 2) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=0, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(2, 3), Point(3, 3), Point(3, 4), Point(2, 4) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=255, g=255, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(4, 3), Point(5, 3), Point(5, 4), Point(4, 4) } + } + + -- ====================================================================== + -- Cutting the canvas in the midle of the tilemap: + -- + -- ,--------- x = -1 + -- | + -- ___v _______ + -- | | | | + -- ∏|∏X|X <-- y = -1 + -- |----|--|----| + -- ∏|∏X|X + -- O|O∏|∏ + -- |----|--|----| + -- O|O∏|∏ + -- | | + -- | | + -- |____|__|____| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(3,2,2,2), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(-1,-1)) + expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms + -- ====================================================================== + + app.command.Undo() + + -- ====================================================================== + -- Cutting the canvas from the bottom + -- ,--------- x = 2 + -- | + -- ____v______ + -- | | + -- | ∏∏XX |<-- y = 1 + -- | ∏∏XX | + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,0,8,3), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(2,1)) + expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms + -- ====================================================================== + -- Cutting the canvas from the right + -- ,--------- x = 2 + -- | + -- ____v_ + -- | | + -- | ∏∏|<-- y = 1 + -- | ∏∏| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,0,4,3), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(2,1)) + expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms + -- ====================================================================== + + app.command.Undo() + app.command.Undo() + + -- ====================================================================== + -- Cutting the canvas from the left, partial tile: + -- + -- ,--------- x = -1 + -- | + -- v ______ + -- | | | + -- | ∏|∏XX |<-- y = 1 + -- | ∏|∏XX | + -- | O|O∏∏ | + -- | O|O∏∏ | + -- | | | + -- | | | + -- | |______| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(3,0,5,8), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(-1, 1)) + expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms + -- ====================================================================== + + app.command.Undo() + + -- ====================================================================== + -- Cutting the canvas from the top, partial tile: + -- + -- ,--------- x = 2 + -- | + -- ____|______ + -- v + -- ____∏∏XX___ <-- y = -1 + -- | ∏∏XX | + -- | OO∏∏ | + -- | OO∏∏ | + -- | | + -- | | + -- |___________| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,2,8,6), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(2, -1)) + expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms + + -- ====================================================================== + + app.command.Undo() + + -- ====================================================================== + -- Cutting the canvas from the left, cutting first tile column: + -- + -- ,--------- x = 0 + -- | + -- v____ + -- | | | + -- | |XX |<-- y = 1 + -- | |XX | + -- | |∏∏ | + -- | |∏∏ | + -- | | | + -- | | | + -- | |_____| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(4,0,4,8), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(0, 1)) + expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms + -- ====================================================================== + + app.command.Undo() + + -- ====================================================================== + -- Cutting the canvas from the top, cutting the top tile row: + -- + -- ,--------- x = 2 + -- | + -- ____|______ + -- | + -- | + -- ____v______ + -- | OO∏∏ | <-- y = 0 + -- | OO∏∏ | + -- | | + -- | | + -- |___________| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(0,3,8,5), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(2, 0)) + expect_eq(app.activeLayer.cels[1].image.width, 2) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 1) -- height in tilemap terms +end + +---------------------------------------------------------------------- +-- Tests canvas resizing in the tilemap with tiles 2x2 with shrink action +---------------------------------------------------------------------- + +do + local sprite3 = Sprite(8, 8) + -- Create a tilemap layer which grid size is 2x2 + sprite3.gridBounds = Rectangle(0, 0, 2, 2) + app.command.NewLayer{ tilemap=true } + -- Create a tilemap of 4x4 tiles: + -- + -- ,--------- x = 0 + -- | + -- v__________ + -- | | + -- |∏∏ |<-- y = 1 + -- |∏∏ | + -- |OO XX| + -- |OO XX| + -- | ∏∏| + -- | ∏∏| + -- |___________| + + -- Making a pixel to make a Cel + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(0, 0) } + } + + -- Moving the Cel to de desired position + app.activeLayer.cels[1].position = Point(0, 1) + + -- Filling with squares of 2x2 + app.useTool{ + tool='pencil', + color=Color{ r=255, g=0, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(1, 1), Point(1, 2), Point(0, 2) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=255, b=0 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(0, 3), Point(1, 3), Point(1, 4), Point(0, 4) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=0, g=0, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(6, 3), Point(7, 3), Point(6, 4), Point(7, 4) } + } + + app.useTool{ + tool='pencil', + color=Color{ r=255, g=255, b=255 }, + tilesetMode=TilesetMode.AUTO, + points={ Point(6, 5), Point(7, 5), Point(6, 6), Point(7, 6) } + } + + -- ====================================================================== + -- Cutting the canvas from the left, cutting the most left tile column: + -- + -- ,--------- x = 4 + -- | + -- ______v_ + -- | | | + -- | | | + -- | | | + -- | | XX| <-- y = 3 + -- | | XX| + -- | | ∏∏| + -- | | ∏∏| + -- | |________| + app.command.CanvasSize{ + ui=false, + bounds=Rectangle(2,0,6,8), + trimOutside=true + } + + expect_eq(app.activeLayer.cels[1].position, Point(4, 3)) + expect_eq(app.activeLayer.cels[1].image.width, 1) -- width in tilemap terms + expect_eq(app.activeLayer.cels[1].image.height, 2) -- height in tilemap terms +end