[I6] Are scalable graphics possible in a Glulx game?

I’ve written a Glulx game that has images for every location. Unfortunately, the bloke that I’m writing it for had already contracted someone else to do the images and didn’t give any thought to the platforms that it would be deployed to. The original images are 1024 x 768 (or sometimes smaller). That’s a ratio of 4:3. As most computer screens are the same ratio (or wider), this means that the image takes up the entire screen and there’s no room left for the text. Also, the file sizes are quite large.

As a result, I’ve cropped and scaled the images to 320 x 240, optimised the colours and reduced the file sizes to an acceptable size. These look pretty good when using your interpreter in a small window, but my “client” wants them bigger. If I make them bigger, they get truncated if using a narrow window, which you need to do for text readability (unless using a giant-sized font). No matter what I do, I can’t win.

I’ve tried the 320 x 240 graphics on a mobile phone and the game is unplayable, as the graphics and keyboard take up the entire screen and there’s no room left for the text. (My game has lots of text.)

So, after that long lead-in, here’s the question. Is it possible to define your graphics window in a Glulx game in such a way that the graphics scale to fit within the space provided for the window and still maintain the aspect ratio?

I don’t think I’ve ever seen any examples of this. If there are any, perhaps you could point them out to me.

You can, see Counterfeit Monkey for an example.

It may take some custom coding, I’m not sure to what extent the various extensions handle this.

Thanks Dannii. It’s an Inform 6 project, so extensions aren’t an issue.

Oh yeah. Well I have no idea what the current state of the art for I6 graphics libraries is. But it’s definitely possible.

If it’s possible, I’ll work it out. I just didn’t want to waste my time if it wasn’t possible.

Does anyone know of any source code examples out there? I’d hate to re-invent the wheel if it’s already been done.

I’m not really sure about the state of things in I6, but I’ve recently been playing around with “Simple Graphical Window by Emily Short” in I7, which defines a graphics window proportionally scaled to the main text window, and then draws a specific image in that window either actual size or scaled down automatically (never scaled up). There’s a related extension that handles changing images automatically as you move between locations as well.

Most of the code is low-level I6 embedded anyway (or easy enough to follow) so it probably shouldn’t be too hard to translate it back to I6. Or just compile a minimal I7 story using it and inspect the I6 output to extract the parts you need.

Thanks for the suggestion. After a bit of mucking about, I found Emily Short’s “Simple Graphical Window” on github. Between this, Andrew Plotkin’s Glulx graphwintest.inf and a few ideas floating around in my head, I should be able to work something out.

I did a bit of mucking about with this and have come to the conclusion that what I want is not possible. In the only examples I could find, scaling just squashes or stretches the image in the horizontal or vertical direction. It does not maintain the aspect ratio. In order to do that, you have to do some simple calculations, but I just discovered that all divison in Inform 6 is integer division. I could use a floating point extension, but that seems a bit extravagant for one (maybe two) division operations. I might be able to find an integer based alternative, but then I strike another problem.

I think I need to use glk_window_set_arrangement to dynamically resize the window, but I can’t find this documented anywhere, I can’t find any example code that uses it (the Inform 7 extensions don’t use it) and when I experimented with it, I couldn’t get it to do anything. The C source code for the API call is:

void glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin);

I have no idea what the last parameter is. Everything I tried gave me a programming error, except for 0, which I assume means NULL.

I thought I might be able to get the last parameter with glk_window_get_arrangement, but I’ve got no idea how to convert this to an Inform 6 call, other than guesswork. The C for the API is:

void glk_window_get_arrangement(winid_t win, glui32 *methodptr, glui32 *sizeptr, winid_t *keywinptr);

I think I’ll give this away. It’s not worth the time or the trauma.

Reading this I’m not entirely sure exactly what you want, but I think what you need is two things:

  1. Use a proportional window split so that the graphics window is the top 50% (or 40%, or whatever) of the window. So as the window is made smaller or bigger, the graphics window will be resized by the interpreter to be some fraction of the overall window. If you do that, you won’t need to mess around with glk_window_set_arrangement().

  2. When you come to draw the graphics window after its been resized, your image doesn’t have to fill the entire graphics window. Suppose my image is (say) 200x200, and my graphics window comes out as 600x300. Then what you need to do is draw your graphics window by clearing it, and then drawing the scaled image at 300x300 in the centre of the window.

Handling things like aspect ratios is certainly possible with integer arithmetic, you just need to be careful that division is the last operation in any arithmetic calculations. If you could post a working example it is likely that someone could offer more concrete help.

The Glk spec explain it pretty well: https://www.eblong.com/zarf/glk/Glk-Spec-075.html#window_changing

Though the API is still pretty complex! I think David is right, and you probably don’t need to be manually resizing windows or changing their arrangements. Unless (as you go on to say) you want to change the proportions…

I was doing (1). The trouble is that you don’t know what is an appropriate size for the split. If the UI window is wide (think landscape), then you might want the graphics window to take up 40-50% of the UI window, but if the window is tall (think portrait, like on mobile phone), then you might want it to take up 20%. The split needs to be dynamic and the calculations for that can be messy. Quite frankly, I don’t know what the ideal splits would be or how best to calculate them.

Regarding (2), that’s exactly right, but you don’t want blank space vertically and horizontally. The approach I was taking was to try to fill the width of the screen unless the height is unreasonable, then you adjust the height to something that is reasonable (as calculated in (1)) and add spacing to the left and right. You don’t really want to be in a situation where you add spacing above and below the image, as this is valuable space that can be used by text. Hence the vertical size of the graphics window needs to be changeable depending on the size of the user’s UI window.

Did I explain that clearly? Probably not…

That’s what I’m using as my primary reference. However this is a spec for the API for developers writing Glk-compatible interpreters, not for people writing games. The window-changing examples are for text windows, not graphics windows and the parameters are not explained well. That 4th parameter is not explained at all and there is no other reference to it anywhere in the spec. I tried using my rock value and my graphics window number and both returned runtime errors.

Come to think of it, as the resizing of the graphics window only accurs at initialisation and after resizing the UI window, you could probably close the graphics window completely and recreate it.

I think you’re going to have to decide on what your algorithm actually is for handling an arbitrarily sized window. There’s not going to be one obvious solution with no drawbacks, particularly in the “narrow and tall” window case

On the glk_window_set_arrangement() front, it is documented in detail in section 3.3 of here: https://www.eblong.com/zarf/glk/Glk-Spec-075.html. Searching the document for “key window” will also explain what is going on more. However, the most relevant sentence is this:

you can pass NULL to mean “leave the key window unchanged.”

So 0 is the right thing for the keywin argument, since you’re changing the size of the window, but not the overall arrangement.

You can ask what the sizes of the windows actually are, and use that to calculate your preferred graphics window size and/or orientation (eg. putting it on the left or right if you detect an unusually wide screen). Coming up with a suitable algorithm to decide this is the tricky part, but also a bit domain-specific because it depends on the type and size of the images you want to display as well.

One caveat is that you’ll probably need to listen for the arrange event to catch the user resizing their interpreter (and hence the contained windows), which might make you want to change arrangements. Or if the graphics window is only shown temporarily then you can perhaps get away with ignoring this until the next time you reopen it. (Also I’m not sure interpreters consistently raise the arrange event for window resizing anyway.)

I am getting lots of suggestions that have no bearing on the original question. This doesn’t help. Just to refresh your memories:

In order to fit the graphics within the space provided, the space provided has to adapt using a suitable algorithm. The graphics window arrangement doesn’t change, only the graphics window size.

As I have never seen this done and no one has been able to provide any Inform 6 source code that does it and my experiments have all failed dismally, I have come to the conclusion that it is not possible. I think @zarf is the only one that can prove me wrong.

Right, it won’t happen automatically, but the code to implement it isn’t overly complex, and ideally there’d be a library (though it may not have been written yet).

1 Like

Well, the suggestions seem relevant to me. Like Dannii says, it’s doable, but you can’t expect it to work automatically. As you say, “the space provided has to adapt using a suitable algorithm”. But there’s no one algorithm that will do what everyone wants, so you’ve got to decide what algorithm you want, and implement it.

If it were me, the algorithm that occurs would be to initially open the graphics window with a 50% proportional split. Then I’d measure the size of the graphics window, and if the height is too great compared to the width reduce the split by whatever is an appropriate amount.

As for the other suggestions, I think I answered your question about the last argument to glk_window_set_arrangement(). If you can’t get that to work then the standard approach would be to produce a reduced test case and post it.

For what it is worth, this is how Counterfeit Monkey does it. It is not Inform 6, but I’d think this is the kind of thing that isn’t too hard to translate.

Two phrases are run every time the user changes the size of the window.
First one that sizes the graphics window:

To adjust width of the graphics window:
	let original width be the width of the graphics window;
	now ideal-width is the width of the measuring window / 2;
	if ideal-width > the maximum size of the graphics window:
		now ideal-width is maximum size of the graphics window;
	[ Currently maximum size of the graphics window is 722 pixels. This is large enough to never happen on most screens. ]
	let real-ideal be 0.0;
	now real-ideal is ideal-width;
	let ratio be real-ideal divided by the height of the graphics window;
	if ratio > 0.8395: [ Too low to show the entire map when scaled to window width ]
		now ideal-width is (height of graphics window * 0.8395) to the nearest whole number;
	if ideal-width is not original width: [ The width has changed ]
		force the size of graphics window to ideal-width.

“The measuring window” is a second graphic window of height zero that spans across the top of the entire game window, in order to get the width of the window in pixels.

The code tries to keep the graphics window at half screen width, but if that is too short and wide to fit the entire map picture (scaled to the width of the window), then the graphic window is made narrower.

Then another phrase draws the image scaled to fit, while keeping the ratio:

To redraw the map and compass:
	if the graphics window is g-present:
		let total height be height of the graphics window;
		let scaled height be (ideal-width / 0.8395) to the nearest whole number;
		draw the local map of the location in graphics window at x 0 and y ((total height - scaled height) / 2) scaled to width ideal-width and height scaled height;
		[ Draw the blue background below the map and add a pixel to the height to ensure that odd heights don't leave a 1 pixel black line ]
		let padding height be (total height - scaled height) / 2 + 1;
		draw figure of background colour in graphics window at x 0 and y (((total height - scaled height) / 2) + scaled height) scaled to width ideal-width and height padding height;
		[ Draw a black square at the top to cover any artifacts left over after changing height ]
		draw a rectangle of color "$000000" in graphics window at x 0 and y 0 of width ideal-width and height (total height - scaled height) / 2;

Using the magic number 0.8395 for the image ratio is not very elegant, it should probably use a definition for this instead.

1 Like

Thanks David. I appreciate all the suggestions. I just haven’t been able to get anything to work properly yet and can’t afford to spend any more time on it at the moment. Maybe later.

All the Inform 7 developers have been telling me what the routines are SUPPOSED to do, but in my experiments, they don’t necessarily do what they’re supposed to do. I basically need to sit down and methodically work through all the routines to see exactly what they REALLY do. For example, it appears that you cannot change the graphics window size if you started out with winmethod_Fixed, but it is easier to work with winmethod_Fixed, rather than winmethod_Proportional, because you are dealing with integer pixels. The calculations involved with winmethod_Proportional aren’t possible in Inform 6 because there is no floating point division.