# `Image.Lqip.Css`
[🔗](https://github.com/elixir-image/image/blob/v0.72.0/lib/image/lqip/css.ex#L1)

Encodes an image as an [LQIP CSS](https://dev.to/frzi/image-placeholders-in-pure-css-or-defying-gods-with-math-and-color-3a5d)
(Low Quality Image Placeholder) value.

LQIP CSS represents a placeholder as a single 32-bit value packed into an RGBA
hex code such as `#a1b2c3d4`. A static CSS rule then unpacks that value into
three colors and paints two radial gradients over a background color, producing
a blurred gradient placeholder using only the browser's CSS engine.

This module is the encoder only: it extracts the three colors and returns
the packed hex string. The CSS is static and identical for every image.

See the [LQIP CSS guide](lqip_css.md) for the copy-and-paste stylesheet.

### How it works

The image is resized to a 3x3 thumbnail and three pixels are sampled:

* the top-left pixel becomes the background color (`--lqip-c0`)
* the center pixel becomes the first radial gradient (`--lqip-c1`)
* the bottom-right pixel becomes the second radial gradient (`--lqip-c2`)

Each color is quantized ([adjusted for chroma](#module-chroma-aware-packing))
and bit-packed: the first two into 11 bits (`RRRR GGGG BBB`) and the third
into 10 bits (`RRR GGGG BBB`). They are then combined into a single 32-bit
value rendered as an 8-digit `#RRGGBBAA` hex string.

### Usage

Compute the value once and store it. Then set a CSS variable using inline
styles or use a data attribute, and include the LQIP CSS stylesheet:

    hex = Image.Lqip.Css.encode!(image)
    # => "#a1b2c3d4"

    # In your template:
    # <img src="photo.jpg" style="--lqip: #a1b2c3d4" />
    # or
    # <img src="photo.jpg" data-lqip="#a1b2c3d4" />

See the [LQIP CSS guide](lqip_css.md) for more usage information.

### Chroma-aware packing

Because each channel is packed into only 3 or 4 bits, independent per-channel
rounding can give near-grey colors a visible tint. To reduce that, the encoder
chooses each color's packed value to be the one closest to the source in the
CIELAB space, measured with CIEDE2000 (via `Color.Distance.delta_e_2000/3`).

This keeps near-greys closer to neutral while still preserving chroma for
saturated colors.

### Example

    iex> image = Image.open!("./test/support/images/Kip_small.jpg")
    iex> Image.Lqip.Css.encode(image)
    {:ok, "#22333091"}

# `encode`

```elixir
@spec encode(image :: Vix.Vips.Image.t()) ::
  {:ok, String.t()} | {:error, Image.error()}
```

Encodes an image as a packed LQIP hex value.

### Arguments

* `image` is any `t:Vix.Vips.Image.t/0`. It is converted to the `:srgb`
  colorspace before sampling, and any alpha channel is ignored.

### Returns

* `{:ok, hex}` where `hex` is an 8-digit `#RRGGBBAA` string, or

* `{:error, reason}`

### Example

    iex> image = Image.open!("./test/support/images/Kip_small.jpg")
    iex> Image.Lqip.Css.encode(image)
    {:ok, "#22333091"}

# `encode!`

```elixir
@spec encode!(image :: Vix.Vips.Image.t()) :: String.t() | no_return()
```

Encodes an image as a packed LQIP hex value, or raises on error.

See `encode/1`.

### Example

    iex> image = Image.new!(32, 32, color: :white)
    iex> Image.Lqip.Css.encode!(image)
    "#ffffffff"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
