Reputation: 196
I need to be able to change the width or height of an image before loading it into a sprite. I currently am using numpy to get either the center row or column and keep inserting it back into the image until it is the correct size. I do not know if this code even works yet, because I have also been having trouble loading the raw_image back into a sprite and displaying it.
def set_image_size(img, x, y):
raw_img = img.get_image_data()
format = 'RGBA'
pitch = raw_img.width * len(format)
rgba = np.array(list(img.get_image_data().get_data(format, pitch))).reshape(-1, raw_img.width, len(format))
mid_y = rgba[round(raw_img.height/2),:] # This is needed to stretch along Y
while rgba.shape[0] < y:
rgba = np.insert(rgba, round(raw_img.height/2), mid_y, 0)
mid_x = rgba[:,round(raw_img.width/2)] # This is needed to stretch along X
while rgba.shape[1] < x:
rgba = np.insert(rgba, round(raw_img.width/2), mid_x, 1)
raw_img.set_data(format, pitch, ''.join(map(chr, rgba.tostring())))
return raw_img
When I try blitting this onto an abstract image and blitting that to the screen, I get a weird striped red version of my image. I don't need to worry too much about quality, I just want to "stretch/tile" only the inside of the image so that the edges don't get warped. Also, my images are very small, only 15 by 15 pixels or so.
How can I fix either of these issues?
EDIT:
When I use sprite.scale, it gets stretched in weird ways:
self.sprite = pyglet.sprite.Sprite(self.image, self.x, self.y, batch=batch)
self.sprite.scale_x = self.width / 16
Stretched (with sprite.scale):
This isn't as much of a problem for this sprite, because I can use OpenGL Quads to draw it, but I need to start using more complex sprites that need to be scaled in the above way.
Upvotes: 1
Views: 553
Reputation: 23500
So the problem lies with the scale of the image resolution you're originally working with, being 16px
wide and 7px
high. Up-scaling an image by default won't do any clever neighbor interpolation and "fill in the blanks" in the way you expect it to.
To solve this, you can tell OpenGL that each new pixel in your up-scaled image should take "inspiration" from its neighbors. You do this by adding to your render loop the following each time you render the sprite:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
It's important that this is called before every render of the sprites you need to add the filter to. Not sure what the technical jargon is for this, but it has to be setup each render sequence, because the next time you loop over your render function it gets reset. There's a fancy word for this I'm sure of it.
Here's a working example:
from pyglet import *
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.x, self.y = 0, 0
glEnable(GL_TEXTURE_2D) # Strictly speaking not needed, I think this is default or won't affect this test image in any significant way.
self.keys = {}
self.mouse_x = 0
self.mouse_y = 0
# The image itself is 800x400 originally
# Positioned at x=0 and y=50 will place it at the bottom essentially
self.sprite = pyglet.sprite.Sprite(pyglet.image.load('test.png'), 50, 50, batch=None)
self.sprite.scale = 20 # Scale the height, not the width
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.keys[symbol] = True
def render(self):
self.clear()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
self.sprite.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
And here's a test-image you can use:
(<-- Small white square, not zoomed in for effect)
And here's the result:
Upvotes: 2