#pagebreak()
= Rendering

As mentioned in the last section, Messenger uses _virtual coordinates_ to render. To transform from virtual coordinates to real coordinates on screen, users can use functions `posToReal`, `lengthToReal` from `Messenger.Coordinate.Coordinates`.

However, usually those low-level functions aren't necessary to draw figures. Instead, there are predefined functions in `Messenger.Render.Sprite`, `Messenger.Render.Text`, `Messenger.Render.Shape` modules.

== Sprite

To render a sprite (image asset), users need to import it to the project because in Messenger all images needed to be loaded before the game starts. If the game fails to load the assets, it will directly abort.

To import an asset, add an entry to `Lib/Resources.elm`:

```elm
allTexture : Dict String String
allTexture =
    Dict.fromList
        [ ( "bg", "assets/bg.png" )
        ]
```

Here "bg" is the asset ID. It should be unique for every images. Users will need to use those IDs to access sprites later. `assets/bg.png` is the image path.

Now put some image to `assets/bg.png` (better to be a background image), and edit the model in the layer (or scene):

```elm
view env data =
  renderSprite env.globalData [] ( 0, 0 ) ( 1920, 1080 ) "bg"
```

Now `make` and see the result!

*Hint.* The game will not start until all textures are loaded. If users found that they cannot load any images, please check whether a static file hosting tool is used to host the game.

*Hint.* In case of drawing pixel art, users may want to use `imageSmoothing False` setting to avoid blurring.

*Hint.* Messenger also provides `renderSpriteWithRev` and `renderSpriteCropped` to do advanced sprite rendering. See the documentation in Elm to learn more.

=== Image Auto-scale

Messenger knows the size of the images users load (except for some non-hinted SVG images). Therefore it also is able to infer the width given the height.

You may omit height or width to let Messenger automatically infer the other one when using `renderSprite`, like this:

```elm
view env data =
  renderSprite env.globalData [] ( 0, 0 ) ( 1920, 0 ) "bg"
```

If the "bg" image is 16:9, then Messenger will calculate its height to be 1080.

Moreover, you may omit both width and height:

```elm
view env data =
  renderSprite env.globalData [] ( 0, 0 ) ( 0, 0 ) "bg"
```

In that case, Messenger will use the size of the image as the virtual size directly.

== Text

Rendering text in Canvas is a little bit tricky.

Text has many properties: font, color, size, position, alignment, baseline, and text content. Alignment determines whether the position is the horizontal start/center/end of the text; baseline determines whether the position is the (vertical) top/middle/bottom of the text.

Messenger provides several functions to render text in `Messenger.Render.Text`:

- `renderText`: Renders a text with black color, start alignment and top baseline
- `renderTextWithColorCenter`: Most commonly used. Renders a text with center
alignment and middle baseline
- `renderTextWithSettings`: Renders a text with manual settings
- And more in that module

== Basic Shapes

*Review.* How to draw a rectangle in `elm-canvas`?

We use `shapes` and `rect` to draw a rectangle.

```elm
viewModel env model =
    shapes [ fill Color.black ]
        [ rect ( 0, 0 ) 100 100
        ]
```
However, that coordinates are in real coordinates. To draw a rectangle in virtual coordinates, use `posToReal` and `lengthToReal` from `Messenger.Coordinate.Coordinates`:

```elm
viewModel env model =
    shapes [ fill Color.black ]
        [ rect (posToReal env.globalData ( 0, 0 )) (lengthToReal env.globalData 100) (lengthToReal env.globalData 100)
        ]
```

Messenger also provides a function to draw a rectangle in virtual coordinates in `Messenger.Render.Shape` to generate shapes more easily. However, most `elm-canvas` shapes need to be generated by using low-level Messenger functions like above.

== Sprite Sheet

If users want to render a sprite sheet, they need to add their sprite sheet configuration to `Lib/Resources.elm`. For example:

```elm
allSpriteSheets : SpriteSheet
allSpriteSheets =
    Dict.fromList
        [ ( "spritesheet1"
        , [ ( "sp1"
            , { realStartPoint = ( 0, 0 )
                , realSize = ( 100, 100 )
                }
            )
            , ( "sp2"
            , { realStartPoint = ( 100, 0 )
                , realSize = ( 100, 100 )
                }
            )
            ]
        )
        ]
```

`SpriteSheet` is a dictionary that maps sprite sheet *image ID* to a list of sprites. Each sprite is a pair of sprite ID and sprite configuration. The sprite configuration contains the real start point and real size of the sprite. The unit of real start point and real size is in pixel.

Then users can render a sprite sheet by:

```elm
renderSprite env.globalData [ imageSmoothing False ] ( 0, 0 ) ( 100, 0 ) ("spritesheet1.sp1")
```

Messenger will load all the sprites in `allSpriteSheets` to memory when the game starts, so users can use `renderSprite` to render it just like a normal sprite.

*Note.* Users will still need to put `spritesheet1` in `allTexture` as normal since it is only an ID, not a path. However, that texture will be loaded to sprite sheet so `renderSprite` on `spritesheet1` will not work.

Full example is in #link("https://github.com/linsyking/messenger-examples/tree/main/spritesheet")[spritesheet].
