Sign in to follow this  

Composite images for GUI

This topic is 3550 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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):

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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])

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
You all seem pretty convinced that it will work but you don't seem to be able to see what gradient i am talking about. So i have made an image highlighting the gradients:
Image Hosted by ImageShack.us
As you can see, the gradient runs from the top of the inner circle to the bottom of it. And as you can plainly see, it will be broken if i compress the image. Which is why (at least for my image) i can only stretch horizontally. I will, however, still scale the left & right sections but will keep it at a 1:1 aspect ratio.

Quote:
Original post by lightbringer
Since it seems that you're stumped on actually coding the draw function

Not really stumped, just lazy [grin]. No, i have got it pretty much under control now, thanks.

Share this post


Link to post
Share on other sites
Well, no, it won't work for that particular image. We can't see what you're talking about if the image you're talking about is totally different from what you showed us :) You can still scale it horizontally ofc, like this:
Image Hosted by ImageShack.us

You can't really stretch the left and right sections in that image, I think. Even if you keep it at the same aspect ratio, there is only a finite amount of data there, this isn't a vector graphic - after a short while, it will start looking blocky. You might be able to use Programmer16's idea for the basic shape and then adding the gradients procedurally, though I think that sounds like more trouble than it's worth - just draw your complex graduated button at all sizes you'll ever need (probably only around three different sizes or so anyway).

Quote:
Original post by XTAL256
Not really stumped, just lazy [grin]. No, i have got it pretty much under control now, thanks.


So you didn't actually have any question or problem? [rolleyes]

Share this post


Link to post
Share on other sites
I'm not trying to be a smartass or something, but why not make a separate bitmap for every button size? You can size/resize them in your photoshop or gimp and load them separately.
Instead of thinking up a complex GUI system you can do this resizing trick in your image editor in half an hour.
If you're worried about resizing your GUI: let OpenGL scale the texture for you. Pick an insane texture resolution so you won't get a pixelated effect when someone has a large screen resolution.

Just saying, it seems to be the path of the least resistance.

Share this post


Link to post
Share on other sites
Ok, first of all, it's not that big of a problem. In fact, i thought i made myself clear on numerous occasions that it was no problem at all. I am going to use horizontal divisions only and that's final! I was merely trying to convince you that it was impossible to do it the suggested way.

Quote:
Original post by lightbringer
We can't see what you're talking about if the image you're talking about is totally different from what you showed us :)

But it's not a different image!!! That's the whole point. I didn't think you could see the gradient properly in the original image since it's a subtle change in colour so i highlighted the feature by drawing the gradient with contrasting colours. The image above has the EXACT SAME gradient as the original. Take a close look :)

Quote:
Original post by lightbringer
Even if you keep it at the same aspect ratio, there is only a finite amount of data there, this isn't a vector graphic - after a short while, it will start looking blocky

Yes, obviously. There is no way to avoid that in raster graphics. But it is no big problem, if you size a Windows button that big it will become pixelated too. There is no way i am using complex vector graphics to account for a problem that will probably never be noticable.

Quote:
Original post by Structural
why not make a separate bitmap for every button size?

Because at this stage i don't really even know how many different sizes i will need. I know i will have slightly larger buttons for the main screen ("Start Game", "Quit", etc) but it's easier to code this now so i can more easily create buttons later.

Quote:
Original post by lightbringer
So you didn't actually have any question or problem?

Not any more, i've figured out how to do it! [grin]
thanks

Share this post


Link to post
Share on other sites
After zooming in until it filled my whole screen, seems like there really was a gradient in the original image ^_^; But you don't need to feel so defensive, the suggestions for nine divisions above are all about how it's normally done but don't apply to your current situation, that's all :)

Share this post


Link to post
Share on other sites
There's bilinear texture interpolation, like in HTML.
The vertical gradient can be reduced to 2 pixels or little more (9 image slices like in Programmer16's diagram) and stretched to any desired height, while the top and bottom rows keep their natural height.

You can also use 5 rows/columns: fixed size extremes (constrained by the size of fancy corners), uniform-color center (1x1 unless there is a gentle texture), and stretchable gradients in the middle that can be extended from the smallest size that looks like a gradient to a good-looking maximum, beyond which the middle monochromatic portion is stretched or tiled instead of drawing oversized gradients.

Share this post


Link to post
Share on other sites
Quote:
Original post by lightbringer
After zooming in until it filled my whole screen, seems like there really was a gradient in the original image ^_^; But you don't need to feel so defensive, the suggestions for nine divisions above are all about how it's normally done but don't apply to your current situation, that's all :)

See, i told you [grin]. I know that nine divisions is the norm and i will use that in cases where i need it, i was just trying to show you that in this particular case you can't.

Share this post


Link to post
Share on other sites

This topic is 3550 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this