# textures

> Drop `.png`, `.jpg` or `.webp` files into `model/textures/` and blit them onto a canvas with `Draw.Image(wall, …)`. The name is checked at compile time, so a typo is an error rather than a blank sprite. Textures are the raster half of your app's art — the vector half is `Svg(name)`, and a single-colour glyph is `Icon(name)`.

<!-- id: ui-textures · area: ui · stability: preview · html: https://osysharp.com/reference/ui/textures/ -->

## Summary        {#summary}
A texture is a **file in your app**, not data. Put a `.png` in `model/textures/` and it becomes part of your app's
vocabulary:

```text
model/
  textures/
    wall.png
    floor.png
    sprites.png
```

`wall.png` is now drawable as `Draw.Image(wall, …)`. There is nothing to register and nothing to import.

Textures are the **third kind of art** an app can ship, and they divide by what the picture *is* rather than by
taste:

| you have | put it in | draw it with |
|---|---|---|
| a single-colour glyph that should follow your text | `model/icons/` | `Icon(Icons.Search)` |
| a vector illustration, logo or background | `model/art/` | `Svg(Art.Hexgrid)` |
| a **bitmap** — a wall texture, a sprite sheet, a photograph | `model/textures/` | `Draw.Image(wall, …)` |

## Signature      {#signature}
```osy syntax
Draw.Image(wall, dx, dy, dw, dh)                          // the whole texture, into a destination rectangle
Draw.Image(wall, sx, sy, sw, sh, dx, dy, dw, dh)          // a SOURCE rectangle of it, into a destination one
Draw.Image(url, …)                                        // the same two forms, over a runtime url
```

## Description    {#description}

### The name is checked   {#names}
`Draw.Image(wall, …)` names the texture by a **bare identifier**, checked against the textures your app actually
ships. A typo is a compile error that lists what there is:

```text
`Draw.Image` takes a declared texture or a url — 'walll' is neither a variable in scope nor a
declared texture (declared: floor, wall). Did you mean 'wall'?
```

Because the name is an identifier, a texture's **file name must be one too** — `brick_wall.png`, not
`brick-wall.png`. A kebab-case file is rejected with the rename to make.

### A url still works, and a collision is refused   {#url-form}
Unlike `Icon` and `Svg`, this first argument can legitimately be an **expression**: a url is genuinely a runtime
value sometimes — a user's uploaded avatar, a signed download link.

```osy title="a url decided at runtime" test app=arcade-avatar
[Page("/avatar")]
[AllowAnonymous]
component Avatar() {
  string src = "/uploads/me.png";

  on frame (double dt) {
    Draw.Clear("#111");
    Draw.Image(src, 8, 8, 64, 64);       // an expression — the url form
  }

  render { Canvas(w: 80, h: 80); }
}
```

The texture form is the same call with a **declared name** in that first position — and both argument forms in one
page, which is the whole surface:

```osy title="a tiled wall, and one tile magnified" test app=arcade
[Page("/wall")]
[AllowAnonymous]
component Wall() {
  on frame (double dt) {
    Draw.Clear("#1a1a22");
    Draw.Image(Textures.Brick1, 0, 0, 32, 32);                 // the WHOLE texture, into a destination rectangle
    Draw.Image(Textures.Brick1, 0, 0, 4, 4, 40, 0, 64, 64);    // a SOURCE rectangle of it, into a destination one
  }

  render { Canvas(w: 112, h: 64); }
}
```

That example is compiled on every docs build **against a real image file** — `canvas-texture` is a complete sample
app shipping `textures/wall.png`, and `osy docs sample canvas-texture` hands you the whole thing.

So a bare identifier could mean either, and when it means **both** the compiler refuses rather than picking:

```text
`Draw.Image(wall, …)` is ambiguous — 'wall' is both a declared texture and a variable in scope,
and the two draw different things. Rename one of them.
```

Every precedence rule here would produce a silent bug in one direction or the other — a texture drawn where a
variable was meant, or a local added months later quietly changing what a call site draws. Renaming one of the two
costs seconds; finding either of those costs an afternoon.

### Which formats, and why the file name does not decide   {#formats}
`.png`, `.jpg`/`.jpeg` and `.webp` — the three raster formats a browser decodes into a canvas.

The type a texture is **served** under is read from its **bytes**, never from its extension. A consequence worth
knowing: a file whose name disagrees with its contents is a compile error rather than a quiet re-label.

```text
'wall.png' is really a JPEG, whatever its extension says — rename it to 'wall.jpg'.
```

An `.svg` in `model/textures/` is **not** a texture. A vector image is markup that has to be sanitized before it
reaches a page, which is what `model/art/` and `Svg(name)` are for.

### Sampling a source rectangle   {#source-rect}
The nine-argument form takes a rectangle **of the texture** and scales it into a rectangle **of the canvas**. It is
what a sprite sheet needs, and what a textured raycaster needs — a one-pixel-wide column of the texture stretched
to a wall's height:

```osy syntax
// one screen column: texel column `texX` of a 64x64 texture, over the wall's full height
Draw.Image(wall, texX, 0, 1, 64, x, top, colWidth, wallHeight);
```

### Shading a texture   {#shading}
A blit paints the texture's own pixels, so lighting is a second pass over the top rather than a colour argument —
draw the texture, then wash it with a translucent rectangle:

```osy syntax
Draw.Image(wall, texX, 0, 1, 64, x, top, colWidth, wallHeight);
Draw.Rect(x, top, colWidth, wallHeight, "rgba(0,0,0,0.35)");   // distance falloff
```

### One texture per name   {#one-per-name}
Two files claiming the same stem is an error naming both, because a name has to resolve to one file. The same rule
applies when a kit vendors its textures into your tree.

### Where texture files live, and how to move them   {#where}
The default is any `textures/` folder in your source tree. Declare the role in `app.osy` to put them somewhere
else:

```osy syntax
app Arcade {
  model    "model/**/*.osy";
  textures "assets/textures/*.png";
}
```

### Reading a texture's pixels   {#reading}
`Texture.Pixels(wall)` answers the texture's pixels as a `List<int>` — one packed `0xRRGGBB` colour per pixel, in
row order — so a software renderer can sample it. `Texture.Width(wall)` and `Texture.Height(wall)` give its size.

It answers the **whole buffer**, once, rather than a texel at a time, and that is a performance contract rather
than a convenience: a call costs roughly seven times an arithmetic operation, so asking per texel would cap a
per-pixel effect at a few thousand pixels a frame before any of its own work. Read it into a field, index it in the
loop.

> ⚠ A texture is decoded by the browser, so `Texture.Pixels` answers an **empty list** until it has been — the same
> "one frame away" rule `Draw.Image` follows. Read it in the frame body until it arrives, not in `on mount`, which
> runs once and would lose the race permanently.

```osy syntax
List<int> texels = new List<int>();

on frame (double dt) {
  if (texels.Count == 0) { texels = Texture.Pixels(wall); }
  // …now index it: texels[Texture.Width(wall) * y + x]
}
```

Colours are 24-bit RGB, not 32-bit ARGB, because an Osy# `int` is a signed 32-bit integer — any alpha above `0x7F`
would overflow it and hand you negative colours. A pixel buffer is opaque; transparency is expressed by not
drawing.

### Size, and the limits   {#limits}
A texture's **intrinsic size is read at compile time**, from the image header — so it is known before anything
decodes, and a header claiming an absurd size is refused there rather than becoming a very large canvas later. A
texture may be at most 8192×8192 and 8 MB.

### Passing a texture around — `Textures` is a type   {#as-a-value}
`Textures` is a type, so a texture is a value you can pass and store like any other. A helper that blits one takes
it as a parameter:

```osy syntax
void Blit(Textures tex, int x) {
  Draw.Image(tex, x, 0, 128, 128);
}

// at the call site
Blit(Textures.Wall1, 0);
```

The same holds for a return type, a local, and a field on an entity. Because a stored texture is keyed by the file's
**name** rather than by a position in a list, adding a new file never changes what an already-stored row means.

## See also   {#see-also}
- [Canvas](https://osysharp.com/reference/ui/canvas/) — the surface `Draw.Image` paints on, `Draw.Pixels` for a whole pixel buffer, and the rest of the `Draw.*` vocabulary.
- [icons](https://osysharp.com/reference/ui/icons/) — the single-colour glyph vocabulary, and the closest analogue to how a texture is named.
- [component](https://osysharp.com/reference/ui/component/) — where an `on frame` body lives.
