Skip to content
Nikita Krapivin edited this page Nov 25, 2020 · 4 revisions

Welcome to the GMESCAPI wiki!

Read this first, please https://github.com/nkrapivin/GMESCAPI/wiki/Generic-Docs

Here I'll explain how to use the GMESCAPI extension.

Let's suppose you've already imported the extension into your project and made an object.

Basic Usage

First, make variables for width and height, busy flag, device index, and the async id:

width = 320;
height = 240;
busy = false;
device = 0;
async_id = -1;

Then, since GameMaker doesn't let us write into the Video RAM, the only reliable way to transfer pictures is through buffers.

I made a helper function gm_escapi_offset that can calculate the buffer size needed to store n-many frames, or if you have a buffer with multiple frames, you can use this function to get the position of the n-th frame.

You use it like so:

buf = buffer_create(gm_escapi_offset(width, height, 1), buffer_fixed, 1);

Due to GM:S 1.4 being a buggy mess, the buffer align MUST BE ALWAYS SET TO 1.

Oh, and use scr_set_surface instead of buffer_set_surface, it is included in the extension assets.

The last argument is how many frames you want to store, we only want one.

The buffer will be fixed, and aligned by four bytes, since each pixel is encoded by four bytes.

Oh, also make a variable for the surface:

surf = -1;

It's not recommended to make surfaces in the Create event, we'll do it in the draw event

Then in some keypress event do this:

if (busy) exit;
busy = true;
async_id = gm_escapi_capture_async(device, width, height, buf, 0);

The last argument is the offset, but since our buffer only has space for one frame, we set it to zero.

This will initiate the capturing in a separate thread, and the function will return the async id, keep in mind that you cannot initiate two asynchronous events for a single camera, one event - one camera. Otherwise the game may hang or crash.

The result of the capture operation will be sent in the Async - Social event (as Social is the event that YoYo tells you to use in DLLs):

var ev_type = async_load[? "event_type"];
switch (ev_type)
{
    case "escapi_capture_result":
    {
        var my_id = async_load[? "escapi_async_id"];
        if (my_id != async_id) exit;

        busy = false;
    }
    default: break;
}

Here we check if the event came from GMESCAPI, and that it corresponds to our async_id, if all of this is true, we simply set the busy flag to false, although you may put some custom logic here.

Now, we can finally draw the surface... in the Draw event:

if (busy) exit;
if (!surface_exists(surf)) surf = surface_create(width, height);

buffer_set_surface(buf, surf, 0, 0, 0);
draw_surface(surf, x, y);

Here, if we're not busy capturing the frame, we create the surface if it doesn't exist, then we copy the pixels from the buffer to the surface, and draw the surf.

Now you may notice that the code works, but the image is blurry, this is because when you ask the camera to capture a photo, it's not ready to make a capture right away, it has to focus.

You can use the gm_escapi_set_focus_frames function to set the amount of frames the camera should make into the same buffer.

Put this code after all the stuff in the Create event:

gm_escapi_set_focus_frames(10); // adjust this please.

It means that the camera will make 10 pictures, instead of one, and return the 10th to you. This should let the camera focus properly. Sometimes 10 is not enough, make it adjustable.

Advanced Usage & Camera Properties

At first, you need to save the current settings, just so you can set them back when you're done:

// get current camera options
prop_values = array_create(CAPTURE_PROP_MAX);
prop_auto = array_create(CAPTURE_PROP_MAX);
gm_escapi_get_sys_capture_props(device); // or -1 to fill all attributes for all devices.
for (var i = 0; i < CAPTURE_PROP_MAX; i++) {
	prop_values[i] = gm_escapi_get_capture_prop(device, i);
	prop_auto[i] = gm_escapi_is_capture_prop_auto(device, i);
	
	var val = string(prop_values[i] * 100) + "%";
	if (val < 0) val = "N/A";
	show_debug_message(val + " " + (prop_auto[i] ? "Auto" : "Manual") + " " + gm_escapi_prop_reflection(i));
}

This will save the settings in prop_values and prop_auto arrays.

Then, let's uhhhhh, set the saturation to 100%, why not? Use your imagination, imagine what would you want to set.

// set our saturation to the highest level
// properties are (0-1)
// to get the percentage value (0-100%) multiply value by 100
gm_escapi_set_capture_prop(device, CAPTURE_SATURATION, 1, false, false);

// remember that capture params will only apply when you'll actually take a frame
// or you can use gm_escapi_set_sys_capture_props to set them anyway

Now you can take pictures, and enjoy the 100% saturation. (lol)

When you're done, remember to restore the settings back.

show_debug_message("Restoring properties...");
for (var i = 0; i < CAPTURE_PROP_MAX; i++) {
	gm_escapi_set_capture_prop(device, i, prop_values[i], prop_auto[i], false);
}

// the capture parameters are usually applied when you're taking a frame
// but here we have a special situation, we need to apply capture params without taking any frames
// so this function comes in handy.
gm_escapi_set_sys_capture_props(device);

That's it, for more info read the "Generic Docs".