---
title: "Introduction to bittermelon"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Introduction to bittermelon}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
### Table of Contents
* [Overview](#overview)
* [Examples](#examples)
- [Bitmap font glyphs](#ex-glyphs)
- [{gridpattern} matrices](#ex-gridpattern)
- [{mazing} mazes](#ex-mazes)
- [Sprites](#ex-sprites)
* [Builtin Fonts](#fonts)
* [GNU Unifont via {hexfont}](#hexfont)
## Overview
* `{bittermelon}` provides functions for creating and modifying bitmaps.
* It can print bitmaps to the R terminal.
* It features [over a dozen functions](https://trevorldavis.com/R/bittermelon/dev/reference/index.html#modify-bitmaps-and-pixmaps) that can modify individual bitmaps or every bitmap within a "bitmap list" or "bitmap font".
* There is a special emphasis on bitmap fonts and their glyphs. It provides native read/write support for the 'hex' and 'yaff' bitmap font formats and if [monobit](https://github.com/robhagemans/monobit) is also installed then it can read/write [several more bitmap font formats](https://github.com/robhagemans/monobit?tab=readme-ov-file#supported-bitmap-formats).
* Besides supporting the builtin `bm_bitmap()` and `bm_pixmap()` objects it also supports modifying `{magick}`'s "magick-image" objects and base R's "nativeRaster" and "raster" objects.
## Examples
```{r hidden, echo = FALSE}
knitr::opts_chunk$set(fig.cap = '', comment = '', class.output = "bitmap")
```
```{css, echo=FALSE}
pre,code {
font-family: Dejavu Sans Mono,FreeMono,monospace;
line-height: 1.0;
font-size: 100%;
}
```
### Bitmap font glyphs
```{r print}
library("bittermelon") # remotes::install_github("trevorld/bittermelon")
font_file <- system.file("fonts/spleen/spleen-8x16.hex.gz", package = "bittermelon")
font <- read_hex(font_file)
bml <- as_bm_list("RSTATS", font = font)
# With vertical compression
bm <- bml |> bm_call(cbind) |> bm_compress("vertical")
print(bm)
# Upside down with ASCII characters
bm <- bml |>
bm_flip("both") |>
bm_call(cbind, direction = "RTL")
print(bm, px = px_ascii)
# With a shadow effect and borders
bm <- bml |>
bm_pad(sides = 2L) |>
bm_shadow() |>
bm_extend(sides = c(2L, 1L), value = 3L) |>
bm_call(cbind) |>
bm_pad(sides = 2L, value = 3L)
print(bm)
```
We can also print colored terminal output with help of `{cli}`:
```{r print_color, eval=FALSE}
if (cli::num_ansi_colors() >= 16L)
print(bm, px = " ",
bg = c(cli::bg_br_white, cli::bg_blue, cli::bg_cyan, cli::bg_red))
```
```{r plot, fig.width = 6, fig.height = 2, fig.alt = "Stylized bitmap image that says 'RSTATS`."}
plot(bm, col = c("white", "blue3", "cyan3", "red3"))
```
### {gridpattern} matrices
```{r gridpattern, eval=requireNamespace("gridpattern")}
# Also supports {gridpattern} matrices
gridpattern::pattern_weave("twill_herringbone", nrow=14L, ncol = 50L) |>
as_bm_bitmap() |>
print(compress = "vertical")
```
```{r plot_gridpattern, fig.width = 6, fig.height = 1.5, fig.alt = "Rainbow squares", eval=requireNamespace("gridpattern")}
gridpattern::pattern_square(subtype=8L, nrow=8L, ncol = 50L) |>
as_bm_pixmap(s, col = grDevices::rainbow(8L)) |>
plot()
```
### {mazing} mazes
```{r maze, eval=requireNamespace("mazing")}
# Also supports {mazing} mazes
set.seed(42)
m <- mazing::maze(16L, 32L)
m |> as_bm_bitmap(walls = TRUE) |>
print(compress = "vertical")
```
```{r plot_maze, fig.width = 6, fig.height = 3, fig.alt = "A maze", eval=requireNamespace("mazing")}
# Can also visualize the maze solutions
pal <- grDevices::palette.colors()
m |> as_bm_pixmap(start = "top", end = "bottom",
col = c(pal[6L], "white", pal[7L], pal[5L])) |>
bm_pad(sides = 1L) |>
plot()
```
### Sprites
```{r farming_crops}
# Contains some built-in farming crops sprites
crops <- farming_crops_16x16()
names(crops)
corn <- crops$corn$portrait
grapes <- crops$grapes$portrait
orange <- crops$orange$stage5
tulip <- crops$tulip$portrait
pm <- cbind(corn, grapes, orange, tulip)
```
We can pretty print sprites to the terminal with help of `{cli}`:
```{r print_crops, eval=FALSE}
if (cli::is_utf8_output() && cli::num_ansi_colors() >= 256L)
print(pm, compress = "v", bg = "white")
```
```{r plot_sprites, fig.width = 6, fig.height = 1.5, fig.alt = "Sprites of some food crops"}
plot(pm)
```
## Builtin Fonts
`{bittermelon}` has a builtin versions of the 8x16 [Spleen](https://github.com/fcambus/spleen) font as well as 4x6 and 6x13 [Fixed](https://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html) fonts.
```{r builtin}
spleen_8x16 <- read_hex(system.file("fonts/spleen/spleen-8x16.hex.gz",
package = "bittermelon"))
fixed_4x6 <- read_yaff(system.file("fonts/fixed/4x6.yaff.gz",
package = "bittermelon"))
fixed_5x8 <- read_yaff(system.file("fonts/fixed/5x8.yaff.gz",
package = "bittermelon"))
fixed_6x13 <- read_yaff(system.file("fonts/fixed/6x13.yaff.gz",
package = "bittermelon"))
as_bm_bitmap("RSTATS", font = spleen_8x16) |> bm_compress("v")
as_bm_bitmap("RSTATS", font = fixed_4x6) |> bm_compress("v")
as_bm_bitmap("RSTATS", font = fixed_5x8) |> bm_compress("v")
as_bm_bitmap("RSTATS", font = fixed_6x13) |> bm_compress("v")
```
## GNU Unifont via {hexfont}
The [{hexfont}](https://github.com/trevorld/hexfont) package includes a helper function `unifont()` which loads several GNU Unifont hex fonts as a single `{bittermelon}` `bm_font()` object. [GNU Unifont](https://unifoundry.com/unifont/index.html) is a monoscale bitmap font (8x16 and 16x16 glyphs) that pretty much covers all of the official Unicode glyphs plus several of the artificial scripts in the [(Under-)ConScript Unicode Registry](https://www.kreativekorp.com/ucsur/).
```{r unifont, eval=requireNamespace("hexfont") && file.exists(hexfont:::unifont_cache_filename())}
library("hexfont")
system.time(font <- unifont()) # Unifont is a **big** font
length(font) |> prettyNum(big.mark = ",") # number of glyphs
object.size(font) |> format(units = "MB") # memory used
# Faster to load from a cache
system.time(font <- unifont(cache = TRUE))
# Or just load the subset of GNU Unifont you need
s <- "R很棒!"
system.time(font_s <- unifont(ucp = str2ucp(s)))
# Mandarin Chinese
as_bm_bitmap(s, font = font_s) |> bm_compress("v")
# Emoji
as_bm_bitmap("🐭🐲🐵", font = font) |> bm_compress("v")
# Klingon
as_bm_list("", font = font) |>
bm_pad(type = "trim", left = 1L, right = 1L) |>
bm_call(cbind) |>
bm_compress("v")
```