Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Image-level Rect Clipping
- - By meustrus Date 2011-01-18 22:41
What is the preferred way of rendering a sprite from a spritesheet? Looking at the API, it would appear to be to load the file with Gosu::Image.load_tiles so you have many separate Images. What if that's not a good idea, because for example you want to load a huge image once (with 10,000 tiles)? Or if you weren't sure about the tile size until after it was loaded (tile size proportional to the size of the image)?

What I would like is a function in Gosu::Image that can draw from a source rectangle, like Gosu::Image.draw_rect(dest_x, dest_y, src_x, src_y, src_width, src_height). It would be even more awesome to have the :draw_rot arguments in there too. Am I missing something? Is there a better way? I wouldn't think that using "@window.clip_to(dest_x, dest_y, src_width, src_height) { @image.draw(-src_x, -src_y) }" would be a good idea, because wouldn't that try to draw the whole image? Is this what the "tileable" flag is for that I haven't been able to find any reference to?
Parent - By jlnr (dev) Date 2011-01-19 00:39

> What is the preferred way of rendering a sprite from a spritesheet?


Either load_tiles, or splitting the spritesheet up with another library such as RMagick, preferrably on the dev machine.

> Or if you weren't sure about the tile size until after it was loaded (tile size proportional to the size of the image)?


For that particular use case, you can give negative numbers to load_tiles, i.e. -4, -3 will load 4x3 tiles, no matter how large the image is.

> Gosu::Image.draw_rect(dest_x, dest_y, src_x, src_y, src_width, src_height)


The drawing functions have plenty of arguments and I wouldn't want to overload them with four more again. Also, you are on the right track with tileability:

http://libgosu.org/rdoc/files/reference/Tileability_rdoc.html

What happens internally is that Gosu will pad the loaded images on the texture, so you can stretch and rotate them without worries. This breaks when you can specify a source rectangle.

There are several possible use cases for source rectangles. For example, if you had 10,000 tiles, then you still wouldn't want to keep them all in memory and draw_rect portions of the whole sheet. What's the best solution depends on the concrete problem.
Parent - - By RavensKrag Date 2011-01-19 10:05

> What if that's not a good idea, because for example you want to load a huge image once (with 10,000 tiles)?


I believe it is documented that #load_tiles can take either a filename or a RMagick instance.  However, I believe that in Gosu RMagick instances and Gosu::Image instances are interchangeable.  Thus, you can load the whole spritesheet into memory first and then split it.

The code would be something like this.
spritesheet = Gosu::Image.new($window, path_to_file, false)
sprite_array = Gosu::Image::load_tiles($window, spritesheet, width, height, false)

I can confirm that this works, as it is how I am doing it.  However, I'm only doing it this way because I need to composite multiple images into a single spritesheet, and divide the resultant image into tiles.

However, if you're loading that much into memory at once, I would say that you have a problem.  I would suggest loading only what you need into memory, and thus storing the spritesheet in smaller chunks.  However, reading from the disk is quite slow... so I guess it's more of a personal choice.  I personally don't like it when apps take more memory than then need though.  I imagine that 10,000 tiles would take up a good deal of memory, however.

Once you get used to the flow of Gosu I believe you will see that it is better (ie more intuitive) to chop up an image into sprites/tiles and draw those.  I am assuming you're new to the engine as you are new to the forum.  Please excuse me if I am mistaken.

> I wouldn't think that using ... would be a good idea


Yeah, I would advise against that as it looks unwieldy, and it is probably going to be slow.  However, if you really want a similar feel, you could take the array generated from #load_tiles and parse the elements into a hash with the keys being related to the position of the tile in the overall spritesheet.  Although honestly, like I said before, I really don't understand why you would want to do it that way.  Feel free to explain it to me though, I am genuinely interested.
Parent - - By meustrus Date 2011-01-19 16:44
I think I may end up loading tiles from an existing image, even though that would seem to take up 2x as much memory. I could just load tiles to start with, but I'm trying to preserve a familiar interface. I could imagine only loading the tiles I need, but there doesn't seem to be an interface for that unless I accept loading one tile at a time, opening the file separately each time.

In the spirit of the familiar interface, I would also need the ability to grab from an arbitrary source rectangle. Since I don't anticipate huge files for this purpose, right now I plan on using Texplay to generate a blank image of the right size and splice the right part onto it. Would this be faster or slower than using :clip_to, and if it depends on the image size, about how big would it have to be before it's slower?

Assuming 32-bit color values (RGBA), a 10,000 tile image with 32x32 tiles would take up 40mb.

I've also been wondering if there's any way to free the memory of an unused image. There doesn't seem to be an explicit :free or :dispose function.
Parent - - By banister Date 2011-01-19 23:53
the image will be garbage collected when no references to it exist.
Parent - By RavensKrag Date 2011-01-20 06:54
I was just about to mention this.  Although, I have heard that ruby's gc is slow, it doesn't seem to matter (speaking anecdotally) unless you do it really frequently.
Parent - - By jlnr (dev) Date 2011-01-20 05:25
Have you benchmarked load_tiles on a 10,000 tile image so that we know how huge of a problem it is right now?

I really think splitting the file up ahead of time is the way to go. Once you have, say, 100 tiles per image, the actual image file parsing will be more neglectable. It's all about heuristics for the actual, concrete use case though; for example, the tiles on the spritesheet may or may not be ordered in a sane way, keeping related tiles close to each other.
Parent - By RavensKrag Date 2011-01-20 07:25
Totally agree about slicing up the image.  Though I would argue that if your tiles are not organized in a "sane way, keeping related tiles close to each other" that your tiles need to be rearranged.  Though, if you're chopping the image up anyway, it shouldn't be that big of a deal.

Well, that's a bit of an oversimplification... ^_^; but still.
Parent - - By RavensKrag Date 2011-01-20 07:26
I'm still not so sure why you're so intent on preserving this interface.  If you're worried about the efficiency at a low-level, wouldn't it be more efficient to cut the image into tiles, so that way you don't have to compute the coordinates of which image to use every frame?
Parent - - By meustrus Date 2011-01-20 23:13
I guess that's one part of what I was asking, whether it would be more efficient to calculate the drawing rect each time it is drawn or just have lots of tiles floating around. Ignoring any calculations for source rect, would it be more efficient to store one really large image or many small tiles of that one image?

As for preserving the interface, if I can use an existing Gosu::Image in place of an RMagick image, I can create a cached image using Gosu::Image.new($window, @image, rect[0], rect[1], rect[2], rect[3]) instead of using Texplay. For my own use I will redesign the interface to be more efficient, but I'm hoping to have a drop-in replacement for a certain proprietary graphics library that I can share.
Parent - By RavensKrag Date 2011-01-21 03:34
I believe it would be the classic memory for speed trade off, but the amount of space would probably be negligible.  Why don't you try it yourself and benchmark it, then let us know? I'd be interested in how that works out myself.

Ah, ok, well if you already have a graphics library, then I suppose it might be a bit of a pain to retool... err, I suppose that's more like refactoring? idk.
Up Topic Gosu / Gosu Exchange / Image-level Rect Clipping

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill