# `EmergeSkia.Native`
[🔗](https://github.com/emerge-elixir/emerge/blob/v0.3.4/lib/emerge_skia/native.ex#L1)

NIF bindings for the Skia renderer.

# `duration_stats`

```elixir
@type duration_stats() :: %{
  count: non_neg_integer(),
  avg_ms: float(),
  min_ms: float(),
  max_ms: float()
}
```

# `layout_cache_stats`

```elixir
@type layout_cache_stats() :: %{
  intrinsic_measure_hits: non_neg_integer(),
  intrinsic_measure_misses: non_neg_integer(),
  intrinsic_measure_stores: non_neg_integer(),
  subtree_measure_hits: non_neg_integer(),
  subtree_measure_misses: non_neg_integer(),
  subtree_measure_stores: non_neg_integer(),
  resolve_hits: non_neg_integer(),
  resolve_misses: non_neg_integer(),
  resolve_stores: non_neg_integer()
}
```

# `renderer_cache_kind_stats`

```elixir
@type renderer_cache_kind_stats() :: %{
  candidates: non_neg_integer(),
  visible_candidates: non_neg_integer(),
  suppressed_by_parent: non_neg_integer(),
  admitted: non_neg_integer(),
  hits: non_neg_integer(),
  misses: non_neg_integer(),
  stores: non_neg_integer(),
  evictions: non_neg_integer(),
  stale_evictions: non_neg_integer(),
  rejected: non_neg_integer(),
  current_entries: non_neg_integer(),
  current_bytes: non_neg_integer(),
  current_gpu_payloads: non_neg_integer(),
  current_cpu_payloads: non_neg_integer(),
  evicted_bytes: non_neg_integer(),
  stale_evicted_bytes: non_neg_integer(),
  gpu_payload_stores: non_neg_integer(),
  cpu_payload_stores: non_neg_integer(),
  prepare_successes: non_neg_integer(),
  prepare_failures: non_neg_integer(),
  direct_fallbacks_after_admission: non_neg_integer(),
  rejected_ineligible: non_neg_integer(),
  rejected_admission: non_neg_integer(),
  rejected_oversized: non_neg_integer(),
  rejected_payload_budget: non_neg_integer(),
  rejected_fractional_placement: non_neg_integer(),
  rejected_unsupported_transform: non_neg_integer(),
  prepare: duration_stats(),
  draw_hit: duration_stats()
}
```

# `renderer_cache_stats`

```elixir
@type renderer_cache_stats() :: %{paint_layer: renderer_cache_kind_stats()}
```

# `stats_command`

```elixir
@type stats_command() :: :peek | :take | :reset | {:configure, %{enabled: boolean()}}
```

# `stats_snapshot`

```elixir
@type stats_snapshot() :: %{
  version: pos_integer(),
  kind: String.t(),
  enabled: boolean(),
  window: %{elapsed_ms: non_neg_integer(), reset_on_read: boolean()},
  frames: %{
    fps: float(),
    display_fps: float(),
    display_frame_ms: float(),
    frame_count: non_neg_integer()
  },
  timings: %{
    render: duration_stats(),
    render_draw: duration_stats(),
    render_flush: duration_stats(),
    render_gpu_flush: duration_stats(),
    render_submit: duration_stats(),
    present_submit: duration_stats(),
    pipeline: duration_stats(),
    pipeline_submit_to_tree_start: duration_stats(),
    pipeline_tree: duration_stats(),
    pipeline_render_queue: duration_stats(),
    pipeline_submit_to_swap: duration_stats(),
    pipeline_swap_to_frame_callback: duration_stats(),
    layout: duration_stats(),
    refresh: duration_stats(),
    event_resolve: duration_stats(),
    patch_tree_process: duration_stats()
  },
  drm: %{
    forced_gpu_finish_before_swap: duration_stats(),
    forced_gpu_finish_after_swap: duration_stats(),
    gpu_queue_completion: duration_stats(),
    egl_swap_buffers: duration_stats(),
    gbm_lock_front_buffer: duration_stats(),
    framebuffer_lookup: duration_stats(),
    prepared_to_commit: duration_stats(),
    previous_flip_to_commit: duration_stats(),
    atomic_commit_ioctl: duration_stats(),
    commit_to_kernel_page_flip: duration_stats(),
    kernel_page_flip_interval: duration_stats(),
    page_flip_dispatch_delay: duration_stats(),
    commit_to_event_processed: duration_stats(),
    page_flip_events: non_neg_integer(),
    page_flip_sequence_steps: non_neg_integer(),
    missed_vblanks: non_neg_integer()
  },
  counters: %{
    layout_cache: layout_cache_stats(),
    renderer_cache: renderer_cache_stats()
  }
}
```

Native stats payload. Current schema version: 17.

# `configure_assets_nif`

```elixir
@spec configure_assets_nif(
  reference(),
  [String.t()],
  boolean(),
  [String.t()],
  boolean(),
  non_neg_integer(),
  [String.t()]
) :: :ok
```

Configure native asset loading policy and source roots.

# `is_running`

```elixir
@spec is_running(reference()) :: boolean()
```

Check if the renderer is still running.

# `load_font_nif`

```elixir
@spec load_font_nif(String.t(), non_neg_integer(), boolean(), binary()) ::
  {:ok, boolean()} | {:error, String.t()}
```

Load a font from binary data and register it with a name.

## Parameters
- `name` - Font family name to register (e.g., "my-font")
- `weight` - Font weight (100-900, 400=normal, 700=bold)
- `italic` - Whether this is an italic variant
- `data` - Binary font data (TTF file contents)

## Example
    {:ok, data} = File.read("fonts/MyFont-Bold.ttf")
    {:ok, true} = EmergeSkia.Native.load_font_nif("my-font", 700, false, data)

# `measure_text`

```elixir
@spec measure_text(String.t(), float()) :: {float(), float(), float(), float()}
```

Measure text dimensions.

Returns `{width, line_height, ascent, descent}`.

# `render_tree_to_pixels_nif`

```elixir
@spec render_tree_to_pixels_nif(
  binary(),
  map()
) :: binary() | {:ok, binary()} | {:error, String.t()}
```

Render a tree to an RGBA pixel buffer (synchronous, no window).

The tree is provided as an encoded EMRG binary. Asset policy mirrors
`EmergeSkia.start/1`, with an additional offscreen asset mode.

# `render_tree_to_png_nif`

```elixir
@spec render_tree_to_png_nif(
  binary(),
  map()
) :: binary() | {:ok, binary()} | {:error, String.t()}
```

Render a tree to an encoded PNG binary (synchronous, no window).

The tree is provided as an encoded EMRG binary. Asset policy mirrors
`EmergeSkia.start/1`, with an additional offscreen asset mode.

# `renderer_patch`

```elixir
@spec renderer_patch(reference(), binary()) :: :ok
```

Apply EMRG patches, run layout, and render immediately.
Window dimensions come from the initial start config and resize events.

# `renderer_upload`

```elixir
@spec renderer_upload(reference(), binary()) :: :ok
```

Upload a full EMRG tree, run layout, and render immediately.
Window dimensions come from the initial start config and resize events.

# `set_input_mask`

```elixir
@spec set_input_mask(reference(), non_neg_integer()) :: :ok
```

Set the input event mask to filter which events are sent.

Mask bits:
- 0x01: Key events
- 0x02: Codepoint (text input) events
- 0x04: Cursor position events
- 0x08: Cursor button events
- 0x10: Cursor scroll events
- 0x20: Cursor enter/exit events
- 0x40: Resize events
- 0x80: Focus events
- 0xFF: All events

# `set_input_target`

```elixir
@spec set_input_target(reference(), pid() | nil) :: :ok
```

Set the target process to receive input events.

Input events are sent directly to the target process as
`{:emerge_skia_event, event}` messages.

# `set_log_target`

```elixir
@spec set_log_target(reference(), pid() | nil) :: :ok
```

Set the target process to receive native renderer log messages.

Native logs are sent directly to the target process as
`{:emerge_skia_log, level, source, message}` messages.

# `start`

```elixir
@spec start(String.t(), non_neg_integer(), non_neg_integer()) ::
  reference() | {:ok, reference()} | {:error, term()}
```

Start the Skia renderer with a window.

Returns a renderer resource that can be used with other functions.

# `start_opts`

```elixir
@spec start_opts(%{
  backend: String.t(),
  title: String.t(),
  width: non_neg_integer(),
  height: non_neg_integer(),
  drm_card: String.t() | nil,
  drm_startup_retries: non_neg_integer(),
  drm_retry_interval_ms: non_neg_integer(),
  drm_force_gpu_finish: boolean(),
  asset_sources: [String.t()],
  asset_runtime_enabled: boolean(),
  asset_allowlist: [String.t()],
  asset_follow_symlinks: boolean(),
  asset_max_file_size: pos_integer(),
  asset_extensions: [String.t()],
  drm_cursor: [
    %{
      icon: String.t(),
      source: String.t(),
      hotspot_x: float(),
      hotspot_y: float()
    }
  ],
  scroll_line_pixels: float(),
  hw_cursor: boolean(),
  input_log: boolean(),
  render_log: boolean(),
  close_signal_log: boolean(),
  stats_enabled: boolean(),
  renderer_stats_log: boolean(),
  renderer_animation_log: boolean(),
  renderer_cache: %{
    enabled: boolean(),
    max_new_payloads_per_frame: non_neg_integer(),
    paint_layer: %{
      max_entries: non_neg_integer(),
      max_bytes: non_neg_integer(),
      max_entry_bytes: non_neg_integer(),
      min_visible_before_store: non_neg_integer(),
      max_stale_frames: non_neg_integer()
    }
  }
}) :: reference() | {:ok, reference()} | {:error, term()}
```

Start the Skia renderer with backend options.

Mirrors `EmergeSkia.start/1` keyword options.

# `stop`

```elixir
@spec stop(reference()) :: :ok
```

Stop the renderer and close the window.

# `tree_clear`

```elixir
@spec tree_clear(reference()) :: :ok
```

Clear the tree.

# `tree_is_empty`

```elixir
@spec tree_is_empty(reference()) :: boolean()
```

Check if the tree is empty.

# `tree_layout`

```elixir
@spec tree_layout(reference(), float(), float(), float()) ::
  {:ok, [{binary(), float(), float(), float(), float()}]} | {:error, String.t()}
```

Compute layout for the tree with given width/height constraints and scale factor.

Returns a list of `{id_binary, x, y, width, height}` tuples for all elements.
The `id_binary` is the element `id` encoded as `<<id::unsigned-big-64>>`.

Scale is applied to all pixel-based attributes (px sizes, padding, spacing,
border radius, border width, font size). Use scale > 1.0 for high-DPI displays.

# `tree_new`

```elixir
@spec tree_new() :: reference()
```

Create a new empty tree resource.

# `tree_node_count`

```elixir
@spec tree_node_count(reference()) :: non_neg_integer()
```

Get the number of nodes in the tree.

# `tree_patch`

```elixir
@spec tree_patch(reference(), binary()) :: {:ok, boolean()} | {:error, String.t()}
```

Apply patches to an existing tree.

# `tree_patch_roundtrip`

```elixir
@spec tree_patch_roundtrip(reference(), binary()) ::
  binary() | {:ok, binary()} | {:error, String.t()}
```

Apply patches to an existing tree and return the encoded EMRG binary.

# `tree_roundtrip`

```elixir
@spec tree_roundtrip(binary()) :: binary() | {:error, String.t()}
```

Decode an EMRG binary in Rust and re-encode it.

# `tree_upload`

```elixir
@spec tree_upload(reference(), binary()) :: {:ok, boolean()} | {:error, String.t()}
```

Upload a full tree from EMRG binary format.
Replaces any existing tree contents.

# `tree_upload_roundtrip`

```elixir
@spec tree_upload_roundtrip(reference(), binary()) ::
  binary() | {:ok, binary()} | {:error, String.t()}
```

Upload a full tree and return the encoded EMRG binary.

# `video_target_new`

```elixir
@spec video_target_new(
  reference(),
  String.t(),
  pos_integer(),
  pos_integer(),
  String.t()
) ::
  reference() | {:ok, reference()} | {:error, String.t()}
```

Create a renderer-owned video target.

# `video_target_submit_prime`

```elixir
@spec video_target_submit_prime(reference(), map()) ::
  {:ok, boolean()} | {:error, String.t()}
```

Submit a DRM Prime descriptor to a video target.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
