mapeditor / tiled

Flexible level editor
https://www.mapeditor.org/
Other
11.24k stars 1.76k forks source link

Scripting API: New image format? #2695

Open Phlosioneer opened 4 years ago

Phlosioneer commented 4 years ago

Hey, I'm trying to use the new scripting API to allow tiled to read a new image format for tilesets. However, the API only allows registering handlers for Tileset files and TileMap files.

I'd like to suggest this as a workable API:

tiled.registerImageFormat(shortName: string, imageFormat: object): void

The `imageFormat` object is expected to have the following properties:
- name: String
    Name of the format as shown in the file dialog
- extension: String
    File extension used by the format
- read: function(fileName: string): Image
    A function that reads an image from the given file. Can use TextFile or BinaryFile to read the file.
- hasChanged: function(fileName: string, timeOfLastRead: date): bool
    A function that is called periodically to check if the image has updated. This function should be as fast as possible. timeOfLastRead is the timestamp when `read` was last called.
bjorn commented 4 years ago

What image format are you looking to support? Qt only supports adding custom image formats through C++ plugins and I don't think it even supports even a C++ API to register custom formats. If this is about adding support for a common image format then maybe we could ship another image format plugin.

Alternatively, we could of course route all image loading through a custom function that also takes registered formats from JavaScript into account.

Why the custom hasChanged check? As long as the image is simply a file on disk, Tiled already watches it for changes (if it's a tileset image, because individual tile images and image layer images are not yet watched for changes...).

Phlosioneer commented 4 years ago

I'm trying to use a generated image as a tileset image. That generated image has two input files. So the normal "watch the file" approach that's built in wouldn't work.

Currently I have to remember to update it whenever either file changes, and I have to perform the generation in a particular order. It's error prone.

Phlosioneer commented 4 years ago

As for the image format, in this case I'm just going to make a temporary png file and load that, after running the generator (if needed). So QT doesn't have to bend over backwards for the format, I just need to be able to intercept reads and override the auto-checking behavior.

The exact process in my case is that I'm taking a .ase file, exporting it to PNG, and then running a postprocessing script afterwards. If the script or the ase file change, I want the output to update accordingly.

Phlosioneer commented 4 years ago

In the more general case, could we allow a script to generate BMP-formatted image data and then "trick" qt into thinking that it's a bmp file?

bjorn commented 4 years ago

Currently I have to remember to update it whenever either file changes, and I have to perform the generation in a particular order. It's error prone.

I think the best way to support this case would be to expose the FileSystemWatcher class in the script API and give the script a way to trigger a reload.

In the more general case, could we allow a script to generate BMP-formatted image data and then "trick" qt into thinking that it's a bmp file?

We can definitely also expose a script function to turn a byte array into an image with any supported format. Essentially exposing QImage::fromData.

samhocevar commented 4 years ago

@bjorn exposing QImage::fromData would be great, yes. Right now there is no equivalent to the Python way of doing Tileset.data().loadFromImage().

samhocevar commented 4 years ago

Hi, sorry for not getting back to you sooner about this feature, but I had trouble building Tiled and then got busy with other things.

The above branch (which I synced with master in #2906) works very well for me. I successfully ported my plugin from Python to JavaScript.

I only came across two minor problems: