Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Quintaros on Sun 17/04/2016 15:25:02

Title: Invert Transparency Mask
Post by: Quintaros on Sun 17/04/2016 15:25:02
Is there a way to invert the transparency mask from an existing sprite before copying it to a dynamic sprite?
Title: Re: Invert Transparency Mask
Post by: Monsieur OUXX on Sun 17/04/2016 22:01:07
No automated way.
Title: Re: Invert Transparency Mask
Post by: Quintaros on Mon 18/04/2016 13:34:15
Any ideas on how to script it?
Title: Re: Invert Transparency Mask
Post by: Monsieur OUXX on Thu 21/04/2016 10:15:53
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.
Title: Re: Invert Transparency Mask
Post by: 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?
Title: Re: Invert Transparency Mask
Post by: Monsieur OUXX on Thu 21/04/2016 14:20:57
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.
Title: Re: Invert Transparency Mask
Post by: Quintaros on Thu 21/04/2016 14:36:51
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.
Title: Re: Invert Transparency Mask
Post by: Snarky on Thu 21/04/2016 16:21:48
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.
Title: Re: Invert Transparency Mask
Post by: Snarky on Fri 22/04/2016 02:45:10
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.
Title: Re: Invert Transparency Mask
Post by: Crimson Wizard on Fri 22/04/2016 02:57:00
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.
Title: Re: Invert Transparency Mask
Post by: Snarky on Fri 22/04/2016 08:02:41
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.
Title: Re: Invert Transparency Mask
Post by: Danvzare on Fri 22/04/2016 09:39:18
Someone should make that into a module.
Title: Re: Invert Transparency Mask
Post by: Khris on Fri 22/04/2016 10:23:27
Snarky, that's ingenious. Wish I had thought of that :D
Title: Re: Invert Transparency Mask
Post by: 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.
Title: Re: Invert Transparency Mask
Post by: Quintaros on Fri 22/04/2016 17:16:13
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. 
(http://i.imgur.com/xTVbnKI.png)
(http://i.imgur.com/dXIkTa5.png)
(http://i.imgur.com/C69n3MR.png)

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.

Title: Re: Invert Transparency Mask
Post by: 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 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.
Title: Re: Invert Transparency Mask
Post by: Quintaros on Fri 22/04/2016 17:50:20
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.



Title: Re: Invert Transparency Mask
Post by: 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-)
Title: Re: Invert Transparency Mask
Post by: Quintaros on Fri 22/04/2016 18:56:44
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.
Title: Re: Invert Transparency Mask
Post by: Danvzare on Sat 23/04/2016 10:33:24
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)
Title: Re: Invert Transparency Mask
Post by: Monsieur OUXX on Mon 25/04/2016 08:52:49
Quote from: Quintaros on Fri 22/04/2016 17:50:20
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.


Yes.
Step 1 : Render the character all white on an emtpty sprite that you'll call "spHalo". (as I said, I think that yoiu can use "tint" or something to render it all white without any effort)
Step 2: Create an empty sprite, and draw spHalo four times or more, slightly shifted (4 pixels up, 4 pixels down, 4 pixels left, 4 pixels right). Now you got your "halo"
Step 3: In the liddle of the same sprite, draw the original sprite.

Now you've got your original sprite with a white outline around it.
Title: Re: Invert Transparency Mask
Post by: selmiak on Mon 25/04/2016 10:51:09
Quote from: Monsieur OUXX on Mon 25/04/2016 08:52:49
Step 2: Create an empty sprite, and draw spHalo four times or more, slightly shifted (4 pixels up, 4 pixels down, 4 pixels left, 4 pixels right). Now you got your "halo"
sometimes drawing it 8 times it better, the diagonals could be 3pixels up + 3 pixel left and so on to make it seem more rounded...
Title: Re: Invert Transparency Mask
Post by: Snarky on Mon 25/04/2016 11:44:22
But that's not the effect he wants, though. He wants the rim lighting on the character, because that's, you know, right.
Title: Re: Invert Transparency Mask
Post by: selmiak on Mon 25/04/2016 12:35:39
*enters read mode from now on*

...after this post that is.

...probably ;)
Title: Re: Invert Transparency Mask
Post by: Monsieur OUXX on Mon 25/04/2016 14:51:19
Quote from: Snarky on Mon 25/04/2016 11:44:22
But that's not the effect he wants, though. He wants the rim lighting on the character, because that's, you know, right.

Oh yes sorry, I didn't understand it properly. I thought he wanted a halo, I didn't understand "rim lighting" (language barrier).
That said, if it comes to GetPixel level, maybe he'd be better off with some sort of shader that would draw white pixels by travelling around the outline. I'm sure that with the proper formula the function would never wander too far off the outline, inside the sprite, hence greatly reducing the processing time.

EDIT: hey my original idea might still work :
1) Create a white version of the sprite using the appropriate tint variation
2) Create a pink version of the sprite using the appropriate tint variation
3) draw the pink sprite on the white sprite, slightly shifted to the left or the right -- hence keeping only the wanted bit of white sprite.
4) use the remaiing white sprite where needed (i.e. onto the original sprite).
Benefit : never go down to pixel level.

That process is basically the one described by Quintaros. However it will work only if tint (and... I can never remember the name... luminance?) are flexible enough, which would require some testing to be sure.



Title: Re: Invert Transparency Mask
Post by: Snarky on Mon 25/04/2016 15:16:03
Pretty sure that won't work. You probably won't be able to tint the character to "magic pink", and if you do manage to do so, drawing it on top of the other sprite will achieve nothing, since drawing transparent on top of non-transparent doesn't wipe out the region below, AFAICR.