Trying to copy and paste a sprite to create a pattern with a script

Hi I am trying to copy a 32px * 32px sprite repeatedly onto a 3200px * 3200px canvas but the API is tripping me up.

I can create a canvas with:
local canvasSprite = Sprite(3200,3200)
and load an image with:
local image = Image{ fromFile="../../../../../Pictures/gec.png"}

I also understand how to iterate over a 2D array from various languages although I haven’t tried it in Lua yet I’m sure I’m capable.

But I don’t understand how to draw the image onto the sprite at a specific offset.

Hi @Jenny_Ada! You can draw one image into a new sprite using the Image:drawImage() function, the only problem is finding the image of the sprite first (where gec.png will be pasted), e.g.:

local canvasSprite = Sprite(3200, 3200)
app.command.BackgroundFromLayer() -- just to avoid complications with the cel offset after drawing the image

local image = Image{ fromFile="...." }
canvasSprite.cels[1].image:drawImage(image, x, y)
1 Like

Cool got it working with one of the following file paths but it’s quite late so I’m too tired to check:
(C:\Program Files (x86)\Steam\steamapps\common\Aseprite\gec.png)
OR
(C:\Users\Jenni\AppData\Roaming\Aseprite\gec.png)

local canvasSprite = Sprite(3200, 3200)
app.command.BackgroundFromLayer()
local image = Image{fromFile="gec.png"}
local x = 0
local y = 0
canvasSprite.cels[1].image:drawImage(image,x,y)

Now onto the loops :slight_smile:
Thanks for the help!

1 Like

I seem to be misunderstanding how drawImage() works.

If I try:
local x = 31
local y = 0
canvasSprite.cels[1].image:drawImage(image,x,y)

Then the 32*32 image only renders as the last vertical strip of the image.

The API reference says that the x and y arguments are position data but it seems to be cropping the image instead?

1 Like

I know this is very old, but I’m having the same issue with drawImage clipping the source image pixels.
Did you ever get it working?

Is this a bug? I really can’t see how it could be intended behaviour.

The second – or third, if you count that self is implied by the : colon – argument is a Point, not an integer. Test script below.

Edit: nevermind - integer pairs also work. Perhaps see Olga’s response below.

local wTrgRandom = math.random(64, 1024)
local hTrgRandom = math.random(64, 1024)

local wSrcRandom = math.random(32, 64)
local hSrcRandom = math.random(32, 64)

local targetSprite = Sprite(wTrgRandom, hTrgRandom, ColorMode.RGB)
local cel = targetSprite.cels[1]

local srcImage = Image(wSrcRandom, hSrcRandom, ColorMode.RGB)
local srcPxItr = srcImage:pixels()
local xToPrc = 1.0 / (srcImage.width - 1.0)
local yToPrc = 1.0 / (srcImage.height - 1.0)

-- Fill with whatever...
for elm in srcPxItr do
    local x = elm.x
    local y = elm.y

    local a = 255
    local b = 127
    local g = math.tointeger(0.5 + 255.0 * y * yToPrc)
    local r = math.tointeger(0.5 + 255.0 * x * xToPrc)

    local srgba = (a << 0x18) | (b << 0x10) | (g << 0x08) | r
    elm(srgba)
end

local trgImage = Image(targetSprite.width, targetSprite.height, ColorMode.RGB)
local wCel = srcImage.width
local hCel = srcImage.height
local cols = math.max(1, (trgImage.width // wCel))
local rows = math.max(1, (trgImage.height // hCel))

print(
    string.format(
        "blit w x h: %d x %d\nsprite w x h: %d x %d\ncols x rows: %d x %d",
        wCel, hCel,
        trgImage.width, trgImage.height,
        cols, rows))

local flatLen = rows * cols
for i = 0, flatLen - 1, 1 do
    local x = i % cols
    local y = i // cols

    local xScaled = x * wCel
    local yScaled = y * hCel

    trgImage:drawImage(srcImage, Point(xScaled, yScaled))
end

cel.image = trgImage

app.refresh()
1 Like

this is related to the fact the image position and cel position are different things.
drawing 32x32 image in the 32x32 cel at position (31,0) will result in single pixel wide vertical strip.
so, to get it rendered properly you need to set cel.position to (31,0) and draw image in cel at (0,0).

like this:

local path = os.getenv('APPDATA') .. "/Aseprite/scripts/"
local canvasSprite = Sprite(100, 100)
--app.command.BackgroundFromLayer()
local image = Image{fromFile=path .. "gec.png"}
local x = 0
local y = 0
canvasSprite.cels[1].position = Point(31, 0)
canvasSprite.cels[1].image:drawImage(image,x,y)
1 Like

Hmm, if I do this, explicitly create a new dest image, drawImage then reassign cel image, it works as expected.
But doing the same with the cel’s pre-existing image results in it being clipped as if the source image is being offset by the same amount as the destination draw position.

You are still drawing the source image at 0,0 in the dest image and just moving the scene position of the cel. This doesn’t affect the contents of the image at all

Demo code:

local sourceSprite = Sprite(64, 64, ColorMode.RGB)
app.useTool{tool="filled_ellipse", color=Color{r=255, g=0, b=0}, points={Point(0, 0), Point(sourceSprite.width, sourceSprite.height)}}
local sourceImage = app.activeCel.image

local destSprite = Sprite(128, 128, ColorMode.RGB)
app.useTool{tool="filled_ellipse", color=Color{r=0, g=0, b=255}, points={Point(0, 0), Point(destSprite.width, destSprite.height)}}

-- Wrong
app.activeCel.image:drawImage(sourceImage, Point(16, 16))

-- Works
local destImage = Image(app.activeCel.image)
destImage:drawImage(sourceImage, Point(48, 48))
app.activeCel.image = destImage

app.refresh()

oh, i see what you mean now.
i honestly don’t have a clue why there should be a difference.
i checked dimensions of both app.activeCel.image and Image(app.activeCel.image) and they are the same even if the new sprite is empty, yet drawImage behaves differently.

Just tested your example and there is a bug when we draw an image related to a cel into other image. I’ll take and try to fix this issue for the next release.

2 Likes