GameMaker [SOLVED] Surfaces: When are they completely transparent (or close)?

jackquake

Member
Hi All,

First off, a little background... I'm creating a "paint" program for kids, but a little different than a typical paint program:
1) An untainted image is used in a sprite
2) The object then uses a shader to completely paint the sprite gray and copies to a surface
3) Then the original sprite is drawn under the surface
4) Then the "paintbrush" is used to remove the surface to reveal the real image.

All works perfectly, but when the surface has been totally depleted, we need to load another picture for them to paint.

How can I detect that the image is now fully visible or say, 95% visible and load the next image? In other words, the surface would very much be transparent at this point. How can I detect that?

As always, thank you in advance!

JQ
 

obscene

Member
Well, there's this example in the manual...

col = surface_getpixel_ext(mouse_x, mouse_y);
alpha = (col >> 24) & 255;
blue = (col >> 16) & 255;
green = (col >> 8) & 255;
red = col & 255;

Of course it also says the function is very slow. I would bet you could use it to continually test the surface, doing one line at a time or something, and saving the results. At the end of each "scan" you could figure up the percentage that is transparent. Doing it this way would mean there would delayed results but it could work.

Probably a BETTER solution would be to create a ds_grid and by default set all the cells to true. As you use to paint, you could also set the corresponding values in the grid to false. You could calculate those results in real-time.
 
I bet you could actually use a shader to do this really quickly. The idea would be to draw a single pixel, but the shader will sample every pixel of the surface, summing the alpha for each pixel. If the sum is greater than zero, then the surface is not completely "depleted". Then, in gml, only need to use surface_getpixel once.
 

jackquake

Member
@obscene Yes, that is basically what I'm doing now after posting the question. The color of the pixel after painting is 0. When I tested the nested loop on the entire surface, it went into never never land. Then, I stepped the nested loop by 5 and noticed a slight pause. I could continue to increment and test this step value for the sake of performance and at the cost of accuracy to get a reasonable value. I like the idea of trying to capture the painting in a ds_grid. I haven't tried that and will take a look.

@flyingsaucerinvasion Now that is a very interesting approach. I'll have to do some research into this as I'm weak in the shader area. Thank you for the tid-bit!

Thank you both for the suggestions!
 

NightFrost

Member
I'm unsure if calculating with a shader would be efficient. GPU speed is based on parallel processing of pixels, and running a loop takes that efficiency away. Disregarding that, yeah, you could bring in the surface as texture sampler and loop across every pixel. The draw operation that executes this would be a single pixel sized draw (like draw_rectangle(0, 0, 0, 0, false)), so the pixel pipeline processes the math only once.
 
You know what, I don't know if you can even have loops that large in a fragment shader. I'll have to look into that.

EDIT: I don't think my idea will work. Giant loops don't seem to work inside fragment shaders.

It might be possible if there is a way to stop loop unrolling, or maybe by using HLSL9 instead of glsles.
 
Last edited:

jackquake

Member
A quick follow-up on what I ended up doing (at least for now). My paintbrush is 32 pixels in diameter, so I increased the step to 32 for both the X and Y in the nested loop to check for the color being 0 using the surface_getpixel function. There is no pause at all. Now, I'll have to check this on the tablet to make sure it is still ok to do it this way, but if not, I'll definitely try the ds_grid method and compare.

After pondering this further, I thought another way would be to add a grid of tiles (or instances, but I realize instances are more costly) that are say 16x16 in size, then do collision checks with the tiles (or instances) and delete them if the paint brush hits them. Then, every couple seconds or so, do a count of tiles (or instances) to determine the percentage of how much of the unpainted image remains.

But, if I'm doing all that, it is basically the same as doing a surface_getpixel, except this function is expensive. In order to reduce the cost, doing a check against a ds_grid would be better at the time of the painting, hence @obscene 's suggestion. Especially, if I don't do it by pixel but by blocks say 16x16 in size by checking a single pixel in each block to determine if that block is a 0 or 1, then do a ds_grid_get_sum every couple seconds or so to determine the percentage.

Bottom line, thank you to all for the direction and guidance. I'm marking this one Solved.
 

jackquake

Member
Lol, that is nearly identical code to mine. The technique at the end is not as accurate but is definitely very fast. Thanks for the link!
 
Top