Custom export layer to image?

I am using the tiled scripting API to write my own JSON format in JS and I would like to take a layer and encode its image data into base64

Using the API I have achieved this with the map as a whole using

Base64.encode(map.toImage().saveToData(‘png’))

and I wonder if I could do the same but with just 1 layer?

There’s nothing like layer.toImage, but you can temporarily hide the other layers and then map.toImage().

is it possible to toggle the hide on layers with the API?

Yes, layer.visible.

IIRC the map passed into MapFormat.write() is read-only, so you might not be able to modify it in this way. If that turns out to be the case, you’d need to first clone the map or make separate maps for each layer you need to turn into an image, and copy the desired layer to them. You can probably reuse code from this script for copying entire layers along with their data.

Thank you for your help, I had a good read through the code and some others and I now understand better, however I am still unable to produce the result I want, this is the code I am using:

function copyLayer(curLayer) {
    const newLayer = new TileLayer();
    newLayer.width = curLayer.width;
    newLayer.height = curLayer.height;
    let region = curLayer.region().boundingRect;
    if (region.width > 0 && region.height > 0) {
        let layerEdit = newLayer.edit();
        for (let x = region.x; x < region.x + region.width; ++x) {
            for (let y = region.y; y < region.y + region.height; ++y) {
                layerEdit.setTile(x, y, curLayer.tileAt(x, y), curLayer.flagsAt(x, y));
            }
        }
        layerEdit.apply();
    }

    let properties = curLayer.properties();
    newLayer.setProperties(properties);

    newLayer.name = curLayer.name;
    newLayer.offset = curLayer.offset;
    newLayer.locked = curLayer.locked;
    newLayer.opacity = curLayer.opacity;
    newLayer.tintColor = curLayer.tintColor;
    newLayer.visible = curLayer.visible;
    newLayer.parallaxFactor = curLayer.parallaxFactor;
    return newLayer;
}

tiled.registerMapFormat('game', {
    name: "game",
    extension: "json",

    write(map, fileName) {
       #...
       # Relevant bit:
       if (layer.name === 'above-player') {
                const newMap = new TileMap()
                newMap.width = map.width
                newMap.height = map.height;
                newMap.infinite = map.infinite;
                newMap.addLayer(copyLayer(layer))
                output.aboveImage += Base64.encode(newMap.toImage().saveToData('png'))
            }
       #...
    }
})

as you can see I only need a trimmed down version of the copying, but is this too trimmed down? Maybe I am missing something for image rendering to work, currently this just outputs an empty string for the image.

You may need to set the map’s tile size - it’s possible that by default it’s 0, so you’re getting a 0x0px map.

It’s easier to debug this sort of thing if you output the map you create by assigning it to tiled.activeAsset, so you can see the results directly.

it was the tile size, you got it in 1 hit, brilliant ty so much :smiley:

1 Like