
# Images

An image draws as a paint fill, like a gradient: build a handle, hand it to
`render.paint():image(handle, x, y, w, h)`, then draw any shape with that paint. The rect maps
the image in canvas space and the shape clips it.

```lua
local logo = image.url("https://http.cat/200.jpg")

module:event("render_2d", function(render)
    if not logo:ready() then return end
    render.rect(8, 8, 96, 96, render.paint():image(logo, 8, 8, 96, 96), 12)
end)
```

## Loaders

| Loader | Loads |
|--------|-------|
| `image.file(path)` | PNG or JPEG next to your scripts |
| `image.url(url)` | PNG or JPEG from the web |
| `image.identifier(id[, smooth])` | A Minecraft texture. Nearest by default, `smooth = true` for linear |
| `image.head(source[, overlay])` | Player head, face plus hat. `source` is a player entity, name or uuid |
| `image.item(source)` | Item GUI render. `source` is an item handle or an id string |

`file` and `url` load asynchronously, `head` and `item` bake on the GPU, so a fresh handle may
draw nothing for a frame or two. Guard with `handle:ready()` or skip the empty frames. Handles
are cheap to build at top level and safe to reuse.

### tab: File

```lua
local logo = image.file("logo.png")   -- <scripts>/logo.png
```

### tab: URL

```lua
local cat = image.url("https://http.cat/200.jpg")
```

### tab: Texture

```lua
local dirt = image.identifier("minecraft:textures/block/dirt.png")
local ice  = image.identifier("minecraft:textures/block/ice.png", true)
```

### tab: Head

```lua
local head = image.head("Notch")        -- name, uuid or player entity
local bare = image.head(player, false)  -- without the hat overlay
```

### tab: Item

```lua
local sword = image.item("minecraft:netherite_sword")
```

## Handle

| Method | Returns |
|--------|---------|
| `handle:ready()` | `true` once it can draw |
| `handle:width()`, `handle:height()` | Native pixel size, `0` until ready |

The paint's color is ignored while an image is set, but its alpha still fades the image, so
`render.paint(0x80FFFFFF):image(...)` draws at half opacity. The sixth argument of `:image`
(`tile`, default `"clamp"`) repeats the image outside its rect.

```lua
local head = image.head(target)
local sword = image.item("minecraft:netherite_sword")

module:event("render_2d", function(render)
    render.rect(8, 8, 32, 32, render.paint():image(head, 8, 8, 32, 32), 6)
    render.rect(48, 8, 24, 24, render.paint():image(sword, 48, 8, 24, 24))
end)
```

Transforms, clips and `save_layer_alpha` apply to image fills. Image-filled text behaves like
gradient-filled text: nothing draws until `ready()`, but the measured width is already right.
