Invert Transparency Mask

Started by Quintaros, Sun 17/04/2016 15:25:02

Previous topic - Next topic

Quintaros

Is there a way to invert the transparency mask from an existing sprite before copying it to a dynamic sprite?

Monsieur OUXX

 

Quintaros

Any ideas on how to script it?

Monsieur OUXX

Quote from: Quintaros on Mon 18/04/2016 13:34:15
Any ideas on how to script it?

Hmmmm.... I don't know. I don't think so, but I might be mistaken. The reason is : I think you can't access the alpha value of each pixel individually. You can access their RGB values, but not their alpha value. The access to transparency values is very recent (see "DynamicSprite.CopyTransparencyMask")and not completely implemented.
But I might be overlooking something.
 

Khris

What exactly do you mean by "inverting"? Is this about alpha transparency or "pink transparency"? Which RGB color would a 100% transparent pixel turn into?

Monsieur OUXX

#5
Quote from: Khris on Thu 21/04/2016 14:06:48
What exactly do you mean by "inverting"? Is this about alpha transparency or "pink transparency"? Which RGB color would a 100% transparent pixel turn into?

Khris makes a good point :
- If your scenario is : "I want to transform 100% opaque pixels into 100% transparent pixels and vice versa", then you're good to go, because 100% transparent pixels can rely on their color ("magic pink", a.k.a. "COLOR_TRANSPARENT) instead of their alpha value. Therefore you can read and write this color to your sprite.
- If your scenario is "I want to transform X% opaque pixels into 100-X% transparent pixels and vice versa" then you can only rely on the alpha value, and that's the scenario that has no solution.

Note that scenario 1 is not self-sufficient: since the color is the same piece of information as the transparency (both rely on the RGB value), then you need extra information to decide what should be the actual color of the pixel that was transparent and has been turned opaque by the inversion.

A technical implementation could be this: having the entire image as a sprite (with no transparency anywhere), that you keep as a source, and two sprites that will be used as masks : One with pink pixels wherever you want (the other pixels can be any color, it's just a mask), and the other with pink pixels exactly reversed from the first mask. Each time you want to "invert" image, then you'll start from the original image and then apply either mask 1 or mask 2.
 

Quintaros

The scenario I have in mind is with pink transparency, not alpha.  I want to create an intermediate dynamic sprite that converts the pink areas to white and the non-pink areas to pink and then use this intermediate sprite with the CopyTransparencyMask function to apply to another dynamic sprite.  I'd like to do this all with dynamic sprites rather than importing additional sprites with the desired masks to provide flexibility and keep sprite file size down.

Snarky

I think this is the only way to do it in AGS script, and I can't guarantee that it will work:

-Create a dynamic sprite dsInvertedMask of the same dimensions as your mask source, clear it to COLOR_TRANSPARENT
-Loop through every pixel in the sprite whose mask you want to invert with DrawingSurface.GetPixel(). Compare it to COLOR_TRANSPARENT. If transparent, put a pixel of any color (e.g. white) in dsInvertedMask

This should give you an inverted mask, and you can use DynamicSprite.CopyTransparencyMask() to apply it to the sprite you want to mask.

However, this is likely to be very slow.

Snarky

It just occurred to me how you could do it with alpha-opacity, though this is going to be really slow:

-Create two dynamic sprites, fill one with black and one with white.
-Copy the transparency mask from your source to the black sprite.
-Get the drawing surfaces for each, and draw the black onto the white.

This should give the reverse mask as a black-white mask (with grays for semi-transparency).

Now unfortunately there's no way to directly apply that mask, or even to draw an image pixel-by-pixel with transparency. However, you can draw surfaces with transparency. So what you'll have to do is:

-Create a blank "transparent pixel surface" and a target dynamic sprite, both the same size as the sprite you need to mask.
-Loop through the sprite pixel by pixel.
-For each pixel:
*Use GetPixel() to get the AGS color from the sprite you want to mask, and put a pixel of that color on the transparent pixel surface in the same position.
*Also use GetPixel() to read the opacity from the reverse mask you created. (You need to decode the value.)
*Now get use DrawSurface() to draw the transparent pixel surface onto the target dynamic sprite, at the appropriate opacity.
*Clear the transparent pixel surface.

Crimson Wizard

#9
Quote from: Snarky on Fri 22/04/2016 02:45:10
*Use GetPixel() to get the AGS color from the sprite you want to mask, and put a pixel of that color on the transparent pixel surface in the same position.
*Also use GetPixel() to read the opacity from the reverse mask you created. (You need to decode the value.)
Unfortunately you won't get alpha value with GetPixel, because AGS simply does not return one with this function, only RGB component.
I am not sure whether DrawingColor will let you define alpha value either.

Snarky

#10
CW, I don't think you read that carefully enough. In the first chunk, you convert the alpha value to a black-white gradient by copying it over to a black sprite (with CopyTransparencyMask()), which you then draw on top of a white one (with DrawSurface()). Now you can use GetPixel() on the result and check the brightness of that color to find the alpha of the original mask. (Actually it might be better to use a red-black gradient, because that way you can get the intensity from a simple right-shift of the AGS color value. Edit: No, use a green-black gradient, since the green channel has an additional bit of precision. But you still only get 6 bits of the 8-bit alpha, and then you have to convert to an integer percentage in the next step, so there's quite a bit of loss of precision in the process.)

And the fact that DrawingColor doesn't let you set alpha is the whole reason for the convoluted workaround where you use DrawSurface() to draw each pixel individually, since DrawSurface() does allow you to specify the opacity.

Danvzare

Someone should make that into a module.

Khris

Snarky, that's ingenious. Wish I had thought of that :D

Monsieur OUXX

Quintaros, out of curiosity, could you describe how you intend on using this feature? I can't think of a scenario where this would be useful.
 

Quintaros

#14
Quote from: Monsieur OUXX on Fri 22/04/2016 14:05:30
Quintaros, out of curiosity, could you describe how you intend on using this feature? I can't think of a scenario where this would be useful.


I'd like to create a dynamic sprite that combines a character's silhouette with an offset inverted silhouette to create rim lighting effects. 




I've done some testing based on the suggestions in this thread and it seems like I'm getting somewhere.  Whether or not the operations will be fast enough to update on every frame of animation is yet to be determined.


Monsieur OUXX

Very interesting.
Another approach could be to first copy the offsetted sprites on an empty sprite, then draw the original character sprite on top of it all?
If you use "tint" (or similar) to make the offseted sprites all white, you won't need to access the sprite at pixel level at any stage of the process.
 

Quintaros

Quote from: Monsieur OUXX on Fri 22/04/2016 17:44:37
Very interesting.
Another approach could be to first copy the offsetted sprites on an empty sprite, then draw the original character sprite on top of it all?
If I follow, that would create a halo around the character rather than creating a rim that overlaps the character.




selmiak

I'd just prerender the few frames in PS and free precious cycletime
But I love reading your technomumbojumbo answers while pretending to be a nerd and somehow understanding this 8-)

Quintaros

Quote from: selmiak on Fri 22/04/2016 18:37:53
I'd just prerender the few frames in PS and free precious cycletime.

It's many frames.  And I want to be able to change the offset for different lighting directions in different rooms, etc.

Danvzare

Quote from: selmiak on Fri 22/04/2016 18:37:53
I'd just prerender the few frames in PS and free precious cycletime
But I love reading your technomumbojumbo answers while pretending to be a nerd and somehow understanding this 8-)
Same here. (laugh)

SMF spam blocked by CleanTalk