Skip to content

Render a static image

A static map renders one image per request. Use it for a tile service, thumbnail job, or image export that waits for completion and reads pixels into host memory.

Static mode is fixed when the map is created. Create a dedicated static map, as Create a map describes. Reading pixels also requires a session-owned texture. The render session allocates this target and can copy its contents into host memory.

Create the map at the render target’s size. Attaching a session with a different size queues a map-size update on the map’s owner thread. Pump before requesting an image. The map then uses the new size.

still-image.c
mln_map_options options = mln_map_options_default();
options.width = width;
options.height = height;
options.scale_factor = 1.0;
options.map_mode = MLN_MAP_MODE_STATIC;
mln_map map = MLN_HANDLE_NULL;
if (mln_map_create(runtime, &options, &map) != MLN_STATUS_OK) return NULL;
// A render session draws the map's latest update whatever the subscription
// selects.
mln_map_set_event_mask(
map, MLN_RUNTIME_EVENT_MASK_MAP_STILL_IMAGE_FINISHED |
MLN_RUNTIME_EVENT_MASK_MAP_STILL_IMAGE_FAILED |
MLN_RUNTIME_EVENT_MASK_MAP_LOADING_FAILED
);

The render session allocates the texture at the extent that the descriptor names. Each render backend has its own descriptor type and attach function, and this snippet uses OpenGL through EGL.

still-image.c
mln_opengl_owned_texture_descriptor descriptor =
mln_opengl_owned_texture_descriptor_default();
descriptor.extent.width = width;
descriptor.extent.height = height;
descriptor.extent.scale_factor = 1.0;
descriptor.context = *context;
mln_render_session session = MLN_HANDLE_NULL;
const mln_status status =
mln_opengl_owned_texture_attach(map, &descriptor, &session);

Render on every turn of the loop. A static map advances loading through the render calls. The still-image-finished event can arrive before or after the render call that produced the pixels. The image is ready after both the event and a successfully rendered frame.

still-image.c
while (!(finished && rendered) && time(NULL) < deadline) {
mln_runtime_pump(runtime, 10, -1);
const still_image_state state = drain_still_image_events(runtime, map);
if (state == STILL_IMAGE_FAILED) return false;
if (state == STILL_IMAGE_FINISHED) finished = true;
mln_render_result result = MLN_RENDER_RESULT_NO_UPDATE;
bool needs_repaint = false;
if (
mln_render_session_render_update(session, &result, &needs_repaint) ==
MLN_STATUS_OK
) {
rendered = rendered || result == MLN_RENDER_RESULT_RENDERED;
}
}

Probe for the size first. The scale factor determines the physical extent. The copy uses premultiplied RGBA8 with the row stride that the probe reports.

still-image.c
// A null buffer with a capacity of 0 is a size probe that fills info.
mln_texture_image_info info = mln_texture_image_info_default();
mln_texture_read_premultiplied_rgba8(session, NULL, 0, &info);
uint8_t* pixels = malloc(info.byte_length);
if (pixels == NULL) return NULL;
const mln_status status = mln_texture_read_premultiplied_rgba8(
session, pixels, info.byte_length, &info
);