PNG alpha channel problems

Started by Akril15, Tue 19/02/2013 03:39:43

Previous topic - Next topic

Akril15

I've been working on a game with another person who is supplying the graphics for it. Recently, I encountered a strange problem when I was working on the game's inventory.

The inventory items are going to be PNGs with shadows created using a Photoshop's Drop Shadow effect. The PNGs look fine once they are exported from Photoshop, but when they're imported to the game and I run the game, this is what happens:

The image on the right is what I see in the game. The shadow creates a semi-transparent "cut-out" in the inventory window,
revealing the background behind it. At other times, the shadows appear completely opaque, both in the editor and the game. I have no idea why this is happening. Both the game and the PNGs are 32-bit, I'm not importing the PNGs incorrectly either. Does anyone know what's going on, and how I can resolve this issue?

Khris

I believe this happens if the Inventory GUI's background image isn't also a 32bit PNG with transparency. Try making it one even if it doesn't have to be.

Cogliostro

Akril15,

PNG Transparency doesn't work in the GUI's.  Use is as an object or a character and everything is fine.  Use in in the inventory screen and things go wrong.

- Cogliostro
"First things first, but not necessarily in that order." - Dr. Who

monkey0506

No, Khris is right. Please don't post wrong answers when the right answer has been given (7 hours ago previously! It's not like you didn't see the post...). It makes people hate you.

Personally I think it's a bug in AGS that the GUI background should have to be 32-bit with alpha channel in order for GUIControls on the GUI to have alpha transparent sprites, but that is the correct way of making it work ATM.

Akril15

The GUI background I'm using *is* a 32-bit PNG with alpha channel. I tried searching the forums for a confirmation on the "buttons/inventory items can't use alpha channel" statement, but I was unable to find anything conclusive.

I was able to find a work-around for the alpha channel problem which involved making the inventory window background a completely transparent graphic and positioning another GUI with the inventory window graphic underneath it. This solved the problem fairly well (it's still a little annoying to deal with, though).

Khris

Quote from: Akril15 on Tue 19/02/2013 03:39:43At other times, the shadows appear completely opaque, both in the editor and the game.

Do you mean that other inventory items show up opaque in-game? Or does it vary for the same sprite? (I believe the editor runs at 16bit, so alpha-PNGs won't look as expected but will in-game.)
Also, could you upload a faulty item's graphic and your background somewhere so we can check it?

Akril15

I actually discovered that just the buttons that use PNGs have opaque shadows. The inventory items are the only ones with shadows that create that "punch-out" effect.

As for uploading graphics from the game, I want to first make sure that it's okay with the artist if I upload them.

Akril15

All right -- here's an inventory item, and here's the inventory window (it's actually a cropped version of the one I'm working with, but it still produces the exact same problem).

(And since I neglected to mention it in the initial post, I'm using version 3.2.1.111 of AGS.)

Khris

Did some testing, unfortunately nothing worked.

Assigning the item image to a room object works perfectly fine, but if it's an inv item, the transparent area cuts a hole, and if it's a button's image, the shadow is a uniform grey with colored pixels near the edge.

Crimson Wizard

#9
Changing "GUI alpha rendering style" (General settings) to "Classic" will make inventory item render same to button.
Same happens if I dont use an image for GUI background, but use any plain color instead.
However, if I make GUI fully transparent (background color = 0) makes both inventory item and button graphics work properly (with translucent shadows).
E: ah right, Akril15 mentioned that already.


EDIT: The engine code actually shows, that inventory items are rendered using different operation, than buttons. That explains the difference.
I think the item image is being just plain copied to the GUI background, making it translucent too.

The button rendering is another problem. I can now seewhat is wrong there, but I can't understand what were Chris Jones intentions about that.
Thing is, the function that blends button with gui background 1) summs alpha channels 2) uses control colors 3) discards background colors. This means that no matter what you do, the result will always have control colors, and no background colors at all :-/.

Summing up, the GUI translucency works rather weird. I never used it, and I do not know the story of how this feature was added, and what it was expected to do. I may be mistaken, but, in my opinion, the feature is either broken, or have limited usage.

Khris

So I guess it's workaround time then.

I can see two possibilities:
a) Make the inventory GUI's background image use a tiled pattern beneath the inventory window and include the background in the item graphic
b) use Calin's AGSBlend plugin to dynamically combine the item graphic with the part of the GUI background graphic beneath it and assign the result as item graphic

Crimson Wizard

#11
Found how make a fix (changed engine code):

[imgzoom]http://img46.imageshack.us/img46/369/67318096.jpg[/imgzoom]

Left is Inventory item in the Inv. Window (wrong render), right is Button (correct render).
(Don't mind the nuke, I am making test games with graphics ripped from random AGS games ;))

Now, the plan is to make tests and see if it does not break other cases.

Crimson Wizard

#12
Does anyone know any games where the GUI controls with alpha channel were used?

Also, I posted my ideas about the possible fix here: http://www.adventuregamestudio.co.uk/forums/index.php?topic=47699.0
Unfortunately no one made any comment;  should have posted in this forum instead.

I ended up with this blender function:
Code: cpp

union
{
    struct
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
    } rgba;
    unsigned long color;
} col_x, col_y, col_r;

// x is color of overlaying surface pixel
// y is color of underlying surface pixel
// n is not used for this blender
unsigned long _fixed_additive_alpha_blender(unsigned long x, unsigned long y, unsigned long n)
{
    col_x.color = x;
    col_y.color = y;

    if (col_y.rgba.a == 0)
        return x;
    if (col_x.rgba.a == 0xff)
        return x;
    if (col_x.rgba.a == 0)
        return y;

    col_r.rgba.a = col_x.rgba.a | col_y.rgba.a;
    col_r.rgba.r = col_y.rgba.r - (((col_y.rgba.r - col_x.rgba.r) * col_x.rgba.a) / 0xff);
    col_r.rgba.g = col_y.rgba.g - (((col_y.rgba.g - col_x.rgba.g) * col_x.rgba.a) / 0xff);
    col_r.rgba.b = col_y.rgba.b - (((col_y.rgba.b - col_x.rgba.b) * col_x.rgba.a) / 0xff);
    return col_r.color;
}


I think it is also possible to speed it up a little by replacing "/ 0xff" with ">> 8" at the cost of loosing a small bit of precision (~1/255).
BTW, it is not 100% precise anyway, because I removed the branch for X color component larger than Y component. In such case the value is the result of 8-bit variable overflow; but since color component has the same value range as unsigned 8-bit variable, it will be generally correct, with the loss of precision only about 1/255 (should not be noticeable by human eye).

Crimson Wizard

I was thinking more about this, and I am starting to think I better not do any "quick fix" at all :-\.

Reason is, there could not be any ultimate "correct" way to blend one layer (gui controls) into another (main gui surface).

What Chris Jones did is a blender that summs alpha (decreases total translucence of gui) and replaces main gui colors with gui control ones.
What I suggested to do up there is to summ alpha and combine colors.
These are two distinctive methods, but it cannot be said that the first is incorrect and the second correct. They both are correct - for what they are supposed to do. We may speak only about choosing one as "default", depending on what game creator usually expects from translucent gui controls being drawn over (possibly) translucent gui window. And what this "default" expectation is? I don't know. What do you think?

Ideally there should be a blending option for every gui window, at least, or even gui control. But that is more work than a common hotfix.

Snarky

#14
There are old threads around somewhere that explain how this feature came to be the way it is (Edit: here). It was an attempt to fix artifacts in an older, even brokener method, but it still doesn't really work right.

I think there is a "correct" way, which is to treat them as if they're drawings on transparent glass plates (or plastic cels, if you prefer an animation metaphor) in front of the rest of the screen. In other words, same as a Photoshop layer with "normal" blend mode.

Quote from: Crimson Wizard on Wed 24/04/2013 20:58:20
What I suggested to do up there is to summ alpha and combine colors.

That's not quite right either; the new alpha shouldn't be a simple sum. Rather, if alpha is a value from 0 (transparent) to 1 (opaque), the combination is combined_alpha = 1 - (1-front.alpha)*(1-back.alpha) (where front is the gui control and back the gui). That way, two 50% transparent layers on top of each other combine correctly to 75% opacity, rather than 100%.

The color value is pretty straightforward: combined_rgb = front.rgb * front.alpha + back.rgb * (1 - front.alpha). This is all in pseudo-code, obviously. If you want to have multiple controls on top of each other (which I don't even remember if can happen in AGS), just run the calculation repeatedly, starting from the back.

There could be an argument to support the old method for backwards compatibility reasons, and other blending modes could be added as a way to achieve special effects (like in Photoshop), but the correct normal behavior is pretty clear, IMO.

Crimson Wizard

Well, I wasn't aware of what this meant for, that's why I kept summing alphas; the global setting is called "Additive Opacity" after all. But I can clearly see this is not what normally people would like, because when you put an alpha control with e.g. a shadow on top of translucent gui, the shadow part actually makes main gui brighter (or rather more saturated), because alpha is increased.

The method you suggested sounds right. I guess overlaying controls may work in current engine, because controls are drawn with regard to z-order, and their colors will summ up at the main gui surface (or temporary bitmap, to be precise).

Regarding backward compatibility, the question is, were there games with translucent guis and gui controls made with the new blending method in such a way, that would be broken if this method is fixed. This new blending function was introduced in AGS 3.0.2, so it is there for a while already. In the worst case, we may add another option which would select the fixed blender for the game - this would act as a temporary fix before per-control blending property is implemented.

Calin Leafshade

"additive opacity" refers to the alpha within a surface.

So the GUI is a surface in and of itself and if you have a button with .5 alpha and the background graphic has .5 alpha, then the total alpha should be 1 but *only* within the surface. If the GUI has its alpha set to .5 this does not effect the alpha of the surface itself but rather the drawing of the surface to the back buffer.

Crimson Wizard

Quote from: Calin Leafshade on Thu 25/04/2013 15:46:12
"additive opacity" refers to the alpha within a surface.

So the GUI is a surface in and of itself and if you have a button with .5 alpha and the background graphic has .5 alpha, then the total alpha should be 1 but *only* within the surface. If the GUI has its alpha set to .5 this does not effect the alpha of the surface itself but rather the drawing of the surface to the back buffer.
Yes... now I remember thinking like that.
And also I remember why there was a problem to do this correctly.
Both the gui and controls are drawn directly over room background, there's no intermediate step which would allow to draw gui controls with their opacity on gui background, then draw main gui on room with its own opacity. When gui controls are drawn, they are drawn over room, which already has main gui pixels over it :-\.

Crimson Wizard

Wait wait wait, actually nevermind the post above... I probably had brain block.
Main gui surface is rendered over "temporary" bitmap, and then controls are rendered over the same temporary bitmap.

Then temporary bitmap will be drawn over screen.

Snarky

Quote from: Calin Leafshade on Thu 25/04/2013 15:46:12
"additive opacity" refers to the alpha within a surface.

So the GUI is a surface in and of itself and if you have a button with .5 alpha and the background graphic has .5 alpha, then the total alpha should be 1 but *only* within the surface. If the GUI has its alpha set to .5 this does not effect the alpha of the surface itself but rather the drawing of the surface to the back buffer.

By "GUI alpha" I assume you mean GUI.Transparency (or 100 - GUI.Transparency, to be precise).

That's how it does work (as far as alpha goes) under this additive opacity mode, but I don't think it's how it should work. "Additive opacity" is a very rough approximation of the correct calculation, and it would be better to (at least have the option to) use the correct formula.

Anyway, is this really something AGS has to solve? Aren't there already library functions in Allegro or whatever that allows you to combine two graphics with alpha-channels and get the correct result?

SMF spam blocked by CleanTalk