How to load a custom scene Texture?
-
Hi, I am trying to build a scene with custom textures, which I pre-process using PIL. From PIL I then convert them to ui.Image and then I want to load them as a Texture. Unfortunately, I cannot get them to load at all. Included is a minimum (non-) working example with a normal png in my project directory.
import ui, scene image = ui.Image('sprite.png') image.show() # this works texture = scene.Texture(image) # this fails ''' Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/73760516-2464-4322-8DCC-0525D46DFFC3/Pythonista3/Documents/smw/minimal_example.py", line 5, in <module> texture = scene.Texture(image) ValueError: Could not load image ''' texture = scene.Texture('sprite.png') # this fails as well ''' Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/73760516-2464-4322-8DCC-0525D46DFFC3/Pythonista3/Documents/smw/minimal_example.py", line 6, in <module> texture = scene.Texture('sprite.png') ValueError: Image not found '''
The sprite.png is a 32x32 PNG with a size of 981 bytes. I had no luck getting this to work with other images so far.
Any help is appreciated.
-
@Moe I'had tried with a png. Could you rename your .jpg as .png and retry
-
I found the problem: The images I used For testing where saved in P / Indexed Color mode. This can be checked with PIL
from PIL import Image img = Image.open('sprite.png') print(img.mode) #> 'P'
I converted them with
img = img.convert('RGB')
and saved them again. Now it works. It seems that the Texture class cannot handle files in mode P and does not fail gracefully. This is probably an oversight.
Thank you again @cvp for taking your time to help! Much appreciated.
-
@Moe Nice job! 🍾
My tested ping mode was RGBA
-
You could use
io. BytesIO
in a Context manager soyou dont need to save and open the images. and when you useui.Image.from_data(image_data[, scale])
and set scale to your IOS device pixel/point ratio. most comonly 2. (1:1, 2:1, 3:1 ...) this will scale your image properly to the screen. you can get this by callingscene.get_screen_scale()
heres an example:
import scene import Image import io cache = dict({ "img1":"./image1.png", "img2":"./image2.png", }) for k,v in cache.items(): with Image.open(v) as img: resized_img=img.resize( (int(img.size[0]/2), int(img.size[1]/2)), Image.ANTIALIAS) with io.BytesIO() as byteStream: resized_img.save(byteStream, "tiff") ns.cache[k]=scene.Texture(ui.Image.from_data(byteStream.getvalue(), scene.get_screen_scale()))
-
@cvp said:
@Moe sure that sprite.png in the same folder as your script? For me, it is ok
import ui, scene image = ui.Image('sprite.png') image.show() # this works texture = scene.Texture(image) # this works texture = scene.Texture('sprite.png') # this works
from my understanding shoudn't it be
ui.Image.named('sprite.png')
-
@stephen said:
from my understanding shoudn't it be ui.Image.named('sprite.png')
You're right but try it, it works also without .named
-
well look at that... i think its all beena lie... lol jk but i do wonder what the method
named
might be doing that may be a benefit?
-
last edited by
-
My current approach is to load the tileset.png which contains all the tiles. I then use
Image.crop()
for every tile I want to extract. I then upscale them usingImage.resize()
by some arbitrary factor, because if I would let the scene upscale the 8x8 textures to something like 64x64, the performance drops hard. UsingBytesIO
I convert them toui.Image
without saving them on disk and from there I can load them as ascene.Texture
.But thank you for the hint that I can load the image and scale them in one operation!
All I had to do to fix the original problem was converting the tileset.png to RGB mode.