Composite images for GUI

Started by
15 comments, last by XTAL256 16 years, 1 month ago
Hi, i am having trouble with the graphical user interface for my game. I want to use a single image for components and stretch/scale the image in different places to fit the size of the component. I'm not really sure what to call it, so i will say 'composite image'. Here is a diagram: Image Hosted by ImageShack.us I am thinking of defining the divisions in an xml file:
<button>
    <state="up">
        <img file="img\ui\btn_up.png"/>
    </state>
    <state="down"> 
        <img file="img\ui\btn_down.png"/>
    </state> 
    <state="hover">
        <img file="img\ui\btn_hover.png"/>
    </state>
    <div id="Left" x="0" y="0" w="20" h="50" mode="scale"/>
    <div id="Middle" x="20" y="0" w="40" h="50" mode="stretch"/>
    <div id="Right" x="60" y="0" w="20" h="50" mode="scale"/>
</button>
The idea is that whatever size the button is, the curved edges will have a 1:1 aspect ratio so as not to distort the curve, but the middle can stretch to fit the width. But my problem is how to implement it. I am using OpenGL and currently have an Image class to hold the texture id + info. I could have a subclass - CompositeImage which has the additional info or i could just make a struct containing an Image + info. But then comes the difficult part of drawing it. For the button example it's simple, draw "Left" part to proportion do the same for "Right" then draw "Middle" stretched. But what if all are to be stretched? Would it stretch each part equally, 1/3 each? Any ideas, help? thanks
[Window Detective] - Windows UI spy utility for programmers
Advertisement
Hi,

If I was you, I would do it like Java's UI manager is doing it. I don't know whether you know it, but it composes buttons and all other UI components (as far as I remember) like this:

--------------------------------------| EDGE TL* |  BORDER T+   | EDGE TR* |--------------------------------------| BORDER L~|  CENTER '    | BORDER R~|--------------------------------------| EDGE BL* |  BORDER B+   | EDGE BR* |--------------------------------------


*: always of the same size
~: Tiled or stretched horizontally
+: Tiled or stretched vertically
': Tiled or stretched both vertically and horizontally

Whether a border is stretched or tiled is specified in the XML description which is very similar to yours. The center component is the only sub-image that is scaled (or tiled) in two directions. It is normally a simple color or a gradient.

I hope this helps you. Of course it is much more work, but you won't have any logical problems with this approach. I have been using it several times now and it works very well ;)

Best regards,
Porthos

As always, sorry for my bad English.

[Edited by - Porthos on March 23, 2008 7:53:28 AM]
Wow, someone that uses Java as an example! I like Java but a lot of people prefer to go with C++. I am using C++ but i like Java's API, i am modelling my game's event system on Java's AWT/Swing event classes. I have used WindowBlinds before so i actually know more about how Windows does GUI images than Java.

As for your idea, Java's approach, i thought of that at first but i can't do that. If you look at my image closely you will see that it has a glass-like effect (i don't know what that style is called) but the "reflection" is from the top to the middle and the corners are rounded. So by stretching "BORDER L" and "BORDER R" it would distort the rounded corners. Here is a diagram highlighting that:
Image Hosted by ImageShack.us
And i do want that image, i don't want to change it to something that is tilable/scalable. Also, it's not just buttons. I need to do this for all components, although things like combo boxes (drop-down boxes) will be a set height and i will just stretch the middle out.

I think i know how i will scale it, i just don't know where to start with the coding. I will need to provide some sort of struct containing x,y,width,height of each section, that's not too hard. But how to i draw it? I will need to iterate through all sections and calculate their positions relative to the component size... I guess i will figure it out, unless someone can post some code.
thanks

p.s. your English is fine :D
[Window Detective] - Windows UI spy utility for programmers
Your bottom line just needs to be moved up and the image can be split up just fine and still be scalable.

In my GUI system, each control can be defined as either Solidstyle or Gridstyle. Solid just stretches the texture across the whole image where as Gridstyle splits it up into 9 sections. The control's style information includes: TopHeight, BottomHeight, LeftWidth, and RightWidth (this limits your controls to having the all of the top grid-nodes being the same height, all of the bottom ones being the same height, all of the left ones being the same width, and all of the right ones being the same width, but it gives enough room to play with in my opinion.)

Anyway, here's where your line needs to be:


However, you can chop the texture up to save space. This is what mine would look like (I've added spaces between each chunk to show how it's separated):
That's a good idea but i'm not sure if it would work. The image has a gradient which would be unevenly stretched i think. Well it definitely wouldn't work if i used your smaller image. I am pretty happy with my way, i wanted to make it more flexible so i can change the styles of the GUI but for simplicity i might hard code the number of divisions for each component and just specify where they are in the xml file.
[Window Detective] - Windows UI spy utility for programmers
Don't worry, Programmer16's idea will work, just try doing it.

It wouldn't get bad like you think.
I use the same technique as Programmer16 and it works just fine. You have to remember that the only things that get stretched are the four "thin" slices, and they only get stretched in the direction that makes them look the same, ie it goes from being a thin slice to being a thick slice in his second to last image. The curvy parts stay exactly the same and aren't stretched, so there's no problem. You just have to see it in action once to understand it :)
Have you thought about changing your original design to something that's going to be simpler to implement?

I prefer a minimal design myself anyway. This is an old design, but the simplicity of this gui means that it can be coded with vector-like instructions and can scale to any resolution.



You could also move to a completely vector based gui. The cairo graphics library can help there.

There is nothing wrong with having a well coded, but hard-coded gui system for your game. Unless your actual intention is to write a gui library and not a game I think you are over engineering. The windows and widget placements for your game would work well as xml, but it's quite a stretch to think that the file references for the underlying gui rendering need to be dynamic. Unless you want to be able to theme your gui, but then we get back to creating a gui library.

Why write it when you can steal it? I've heard good things about Crazy Eddie's GUI System (try to overlook the name.)

Do you want to write games, or game engines?
Quote:Original post by lightbringer
...the only things that get stretched are the four "thin" slices, and they only get stretched in the direction that makes them look the same, ie it goes from being a thin slice to being a thick slice in his second to last image. The curvy parts stay exactly the same and aren't stretched, so there's no problem. You just have to see it in action once to understand it :)

Yeah, but those thick slices should have a gradient, but they can't if they are compressed to "thin" slices.
Don't worry, i like it the way i have now (using 2 vertical divisions). And i can easily change the implementation later to include horizontal divs. btw, what i have now is a little different from my first post. The divs are now:
<div id="left" pos="20"/><div id="right" pos="30"/>

And i will hard-code the way it stretches them (for now).

Quote:Original post by Umbrae
Have you thought about changing your original design to something that's going to be simpler to implement?

Yes, but i like my design :D

Quote:Original post by Umbrae
...Unless you want to be able to theme your gui

Well i was hoping to be able to create different styles/themes/skins for my gui but i guess i'm getting a bit too far ahead of myself.

Quote:Original post by Umbrae
Do you want to write games, or game engines?

Well... i want to write my own game and game engine. I know it will be hard but i just don't want to get a bunch of libraries, stick them together and call it a game. I want to write the gui and most other things myself just so i know that i can, just for a challenge. I really don't think it will be that hard to write a GUI system (please don't correct me on that [smile])
[Window Detective] - Windows UI spy utility for programmers
Quote:Original post by XTAL256
Yeah, but those thick slices should have a gradient, but they can't if they are compressed to "thin" slices. Don't worry, i like it the way i have now (using 2 vertical divisions). And i can easily change the implementation later to include horizontal divs. btw, what i have now is a little different from my first post.


I'm not sure I understand your concern ^_^; The gradient is not along the scale of compression. You only vary the slice along one dimension, and that has the same pixels uniformly throughout, so the gradient is preserved even if the slice is just one pixel wide. Oh and you do not compress/expand the corner slices obviously, you only vary the length of the edges and stretch the center piece to accomodate. It's really a pretty standard way of handling this, it's used in AWT/Swing and a number of other GUI's as far as I know and I've used it for everything from buttons to windows and progress bars.

Well, even if you stick to your current design (you don't ever need horizontal divisions if you will never stretch the buttons vertically, after all), the recommendations from all the posters above stay valid, that is, you just stretch the center portion and leave the sides always as-is, so a short button might look like
[|==|] and a long one like [|=======|] (pardon my sad excuse for ascii art)

Since it seems that you're stumped on actually coding the draw function, here's some sample code from my Button class:

This method is called once per frame per button to draw a button with the current skin (there are different paint methods for active and for selected buttons, also). Note that it calls private methods getHeight() and getWidth() to scale the button according to the widget size:
	private void paintNormal() {		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_BOTTOM_LEFT, 				getAbsoluteX(), getAbsoluteY(), 				getAbsoluteX()+12, getAbsoluteY() + 12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_CENTER_LEFT, 				getAbsoluteX(), getAbsoluteY() + 12, 				getAbsoluteX()+12, getAbsoluteY() + getHeight() - 12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_TOP_LEFT, 				getAbsoluteX(), getAbsoluteY() + getHeight() - 12, 				getAbsoluteX()+12, getAbsoluteY() + getHeight());				getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_BOTTOM_CENTER, 				getAbsoluteX()+12, getAbsoluteY(), 				getAbsoluteX()+getWidth()-12, getAbsoluteY() + 12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_CENTER_CENTER, 				getAbsoluteX()+12, getAbsoluteY()+12, 				getAbsoluteX()+getWidth()-12, getAbsoluteY() + getHeight() - 12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_TOP_CENTER, 				getAbsoluteX()+12, getAbsoluteY() + getHeight() - 12, 				getAbsoluteX()+getWidth()-12, getAbsoluteY() + getHeight());				getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_BOTTOM_RIGHT, 				getAbsoluteX()+getWidth()-12, getAbsoluteY(), 				getAbsoluteX()+getWidth(), getAbsoluteY() + 12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_CENTER_RIGHT, 				getAbsoluteX()+getWidth()-12, getAbsoluteY()+12, 				getAbsoluteX()+getWidth(), getAbsoluteY() + getHeight()-12);		getSkin().drawElement(UiSkin.Element.BUTTON_NORMAL_TOP_RIGHT, 				getAbsoluteX()+getWidth()-12, getAbsoluteY() + getHeight()-12, 				getAbsoluteX()+getWidth(), getAbsoluteY() + getHeight());			}


What's actually happening there? Well, something like the following. The texture coordinates for the elements in the skin are hard-coded in my case, but of course loading from XML is much better.
	public void drawElement(Element el, float x, float y, float xRight, float yTop) {				GL11.glBindTexture(GL11.GL_TEXTURE_2D, skin.getTexID());				GL11.glEnable(GL11.GL_BLEND);		GL11.glBegin(GL11.GL_QUADS);		GL11.glTexCoord2f(el.x, el.y); GL11.glVertex2f(x, y);		GL11.glTexCoord2f(el.xright, el.y); GL11.glVertex2f(xRight, y);		GL11.glTexCoord2f(el.xright, el.ytop); GL11.glVertex2f(xRight, yTop);		GL11.glTexCoord2f(el.x, el.ytop); GL11.glVertex2f(x, yTop);		GL11.glEnd(); 		GL11.glDisable(GL11.GL_BLEND);				GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);	}


Yes, this is all in immediate mode (all of my GUI stuff is), so there might be room for improvement. But this implementation has the major advantage of "it works" :D And as you can see, there's no magic involved. You just calculate where everything has to go and then draw a bunch of quads on the screen.

Oh, and it's not all that hard to write a GUI system, just a bit annoying (For me, dealing with the propagation of focus for mouse and keyboard was probably the most unnerving part, but I didn't plan ahead well enough). Plan ahead with some flow charts for usability and maybe some class hierarchies and it's not that bad. Of course, you COULD always use one off-the-shelf. It's not as easy as sticking a bunch of libraries together and calling it a game, you still have to do all the game data loading / display / game logic / AI and glue code, not to mention spend all that time on assets, like writing, art, modeling, music, and sound effects... there's always enough to do :D

This topic is closed to new replies.

Advertisement