You can display native images (such as game textures, a minimap, or offscreen renders) directly on a page. HTML and CSS then position, size, and animate them like any standard web image.

To connect an image to the page, place an `.imgsrc` file in the web assets and register an `ImageSource` in native code under the same name.

## Creating the .imgsrc File

An `.imgsrc` file is a plain-text asset containing the format header on the first line and an identifier on the second.

```text
IMGSRC-V1
minimap
```

Place this file alongside the web assets and reference it anywhere the page accepts an image URL.

```html
<img src="minimap.imgsrc" />
```

> 👍 Registration Order Does Not Matter
>
> The page can load before you register the image source. The image appears on the page as soon as you add the matching source.

## Displaying a Bitmap

Displaying an in-memory bitmap is the simplest way to show native images on a page— it works with both the CPU and GPU renderers without custom driver code.

To expose a bitmap to the page, wrap a `Bitmap` (see [Working with Bitmaps](/docs/2.0/working-with-bitmaps)) with `ImageSource::CreateFromBitmap()` and register it with `ImageSourceProvider`.

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

RefPtr<Bitmap> portrait;
RefPtr<ImageSource> portrait_source;

void RegisterPortrait() {
  ///
  /// Draw the portrait into a 128 by 128 bitmap (a blank one will do for now).
  ///
  portrait = Bitmap::Create(128, 128, BitmapFormat::BGRA8_UNORM_SRGB);
  portrait->Erase();

  ///
  /// Wrap it and register it under the identifier from portrait.imgsrc.
  ///
  portrait_source = ImageSource::CreateFromBitmap(portrait);
  ImageSourceProvider::instance().AddImageSource("portrait", portrait_source);
}
```

When the page renders an element using the image source, the library samples the bitmap's pixels directly.

To load block-compressed bitmaps and `.dds` files directly from markup instead, see [Compressed Textures](/docs/2.0/compressed-textures) (requires Pro edition or higher).

## Displaying a GPU Texture

You can composite a live GPU texture (such as a render target or a video stream) directly into a page without copying pixels back to CPU memory.

> 📘 GPU Renderer Required
>
> Displaying a GPU texture requires a view configured with `ViewConfig::is_accelerated = true` and a custom GPU driver. The driver binds the native texture whenever the renderer draws the element (see [GPU Renderer Overview](/docs/2.0/gpu-renderer-overview)).

### Registering the Texture

To expose a GPU texture to the page, reserve an identifier from the GPU driver, wrap it with `ImageSource::CreateFromTexture()`, and register it with `ImageSourceProvider`.

```cpp
RefPtr<ImageSource> minimap_source;

void RegisterMinimap() {
  ///
  /// Reserve a texture id from our GPUDriver (0 is reserved for "no texture").
  ///
  uint32_t texture_id = Platform::instance().gpu_driver()->NextTextureId();

  // Pseudo-code, create your native texture here and have your driver bind it
  // whenever texture_id shows up in a draw command.

  ///
  /// Create a 512 by 512 image source that samples the whole texture.
  ///
  Rect uv = { 0.0f, 0.0f, 1.0f, 1.0f };
  minimap_source = ImageSource::CreateFromTexture(512, 512, texture_id, uv);

  ///
  /// Register it under the identifier from minimap.imgsrc.
  ///
  ImageSourceProvider::instance().AddImageSource("minimap", minimap_source);
}
```

Calling `GPUDriver::NextTextureId()` generates a non-zero identifier for the texture— ID 0 is reserved to indicate no texture. When creating the `ImageSource`, pass the image's dimensions in pixels for page layout, the reserved texture ID, and the texture UV coordinates to sample.

### Binding the Texture in the GPU Driver

When the page renders an element using the image source, the library emits geometry with the specified texture ID and UV coordinates. The GPU driver must intercept this ID and bind the matching native texture (see [Implementing a GPUDriver](/docs/2.0/implementing-a-gpudriver)).

### Providing a Backing Bitmap

You can pass an optional `Bitmap` as the final parameter of `ImageSource::CreateFromTexture()`. The library samples this bitmap when rendering on a CPU-backed view or when operations require raw pixel data on the host— without a backing bitmap, those views have nothing to draw.

When providing a backing bitmap, you must update its pixel buffer whenever the GPU texture changes to keep both in step.

## Updating and Removing Images

### Redrawing After Changes

Call `ImageSource::Invalidate()` whenever you modify an image's underlying pixels to schedule a redraw on the next frame.

```cpp
void OnMinimapChanged() {
  ///
  /// We drew a new frame into the texture; ask the page to redraw the image.
  ///
  minimap_source->Invalidate();
}
```

The library does not track changes to native textures or bitmap memory automatically. Without calling `Invalidate()`, the page continues displaying the previous frame.

### Removing an Image Source

When an image is no longer needed, unregister it by passing its identifier to `ImageSourceProvider::RemoveImageSource()`.

```cpp
void Shutdown() {
  ///
  /// Unregister both identifiers when we are done with them.
  ///
  ImageSourceProvider::instance().RemoveImageSource("minimap");
  ImageSourceProvider::instance().RemoveImageSource("portrait");
}
```

> 🚧 Call from the Renderer's Thread
>
> Register, invalidate, and remove image sources on the Renderer's thread only. These calls reach the page directly.
