# Glycin

Glycin allows to decode, edit, and create images and read metadata. On Linux, decoding happens in sandboxed modular *image loaders*. We use the term 'image loader' for simplicity, but image loaders also handle image editing and creation.

- [glycin](https://docs.rs/glycin/) – The Rust image library
    - [libglycin](https://gnome.pages.gitlab.gnome.org/glycin/libglycin/) – C-Bindings for the library
    - [libglycin-gtk4](https://gnome.pages.gitlab.gnome.org/glycin/libglycin-gtk4/) – C-Bindings to convert glycin frames to GDK Textures
    - [libglycin-rebind](https://crates.io/crates/libglycin-rebind) - Safe Rust bindings to libglycin
- [glycin-loaders](glycin-loaders) – Glycin loaders for several formats
- [glycin-thumbnailer](glycin-thumbnailer) – Glycin thumbnailer using the installed loaders

Bindings for other languages:

- [GJS](https://gjs-docs.gnome.org/gly2~2/)

Other rust crates:

- [glycin-utils](https://docs.rs/glycin-utils/) – Utilities to write loaders for glycin
- [glycin-common](https://docs.rs/glycin-common/) – Components shared between the glycin-utils and glycin crates.

## Usage and Packaging

The Rust client library is available as [glycin on crates.io](https://docs.rs/glycin/). For other programming languages, the libglycin C client library can be used. For the client libraries to work on Linux, **loader binaries must also be installed**. The loader binaries provided by the glycin project cover a lot of common image formats (see below). Both, the loader binaries and libglycin can be built from the released [glycin tarballs](https://download.gnome.org/sources/glycin/). By using `-Dglycin-thumbnailer=false`, `-Dglycin-loaders=false`, `-Dlibglycin=false`, or `-Dlibglycin-gtk4=false` it is possible to build only specific components. In distributions, the loaders are usually packaged as *glycin-loaders*, and libglycin as *libglycin-2*. However, each loader binary could be also packaged as its own package.

### Example

```rust
let file = gio::File::for_path("image.jpg");
let image = Loader::new(file).load().await?;

let height = image.info().height();
let texture = image.next_frame().await?.texture();
```

## Limitations

Glycin sandboxing and modularity is based on technologies like memfds, unix sockets, and linux namespaces. Therefore, these features are only available on Linux. Other operating systems are supported by embedding the image loaders directly into the glycin/libglycin libraries. This happens automatically when building with a non-linux target.

## Supported Image Formats

The following formats are supported by the glycin image loaders provided in the [loaders](loaders) directory. You can learn more about the supported features for each format on the [glycin website](https://gnome.pages.gitlab.gnome.org/glycin/#supported-image-formats).

| Format | Glycin Loader | Decoder |
|-|-|-|
| Animated PNG image | glycin-image-rs |[png](https://crates.io/crates/png) (Rust) |
| AVIF image (.avif) | glycin-heif |[libheif](https://github.com/strukturag/libheif) (C++) |
| Windows BMP image (.bmp) | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| GIF image (.gif) | glycin-image-rs |[gif](https://crates.io/crates/gif) (Rust) |
| HEIF image (.heic) | glycin-heif |[libheif](https://github.com/strukturag/libheif) (C++) |
| JPEG-2000 JP2 image | glycin-image-rs |[hayro-jpeg2000](https://crates.io/crates/hayro-jpeg2000) (Rust) |
| JPEG image (.jpg) | glycin-image-rs |[zune-jpeg](https://crates.io/crates/zune-jpeg) (Rust) |
| JPEG XL image (.jxl)² | glycin-jxl |[libjxl](https://github.com/libjxl/libjxl) (C++) |
| PNG image (.png) | glycin-image-rs |[png](https://crates.io/crates/png) (Rust) |
| Quite OK Image Format (.qoi) | glycin-image-rs |[qoi](https://crates.io/crates/qoi) (Rust) |
| SVG image | glycin-svg |[librsvg](https://gitlab.gnome.org/GNOME/librsvg) (C/Rust) |
| Compressed SVG image | glycin-svg |[librsvg](https://gitlab.gnome.org/GNOME/librsvg) (C/Rust) |
| TIFF image (.tiff) | glycin-image-rs |[tiff](https://crates.io/crates/tiff) (Rust) |
| Windows icon (.ico) | glycin-image-rs |[image](https://crates.io/crates/image) (Rust), [bmp](https://crates.io/crates/bmp) (Rust), [png](https://crates.io/crates/png) (Rust) |
| WebP image (.webp) | glycin-image-rs |[image-webp](https://crates.io/crates/image-webp) (Rust) |
| Adobe DNG negative¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| Canon CR2 raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| DirectDraw surface (.dds) | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| image/x-epson-erf type¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| EXR image (.exr) | glycin-image-rs |[exr](https://crates.io/crates/exr) (Rust) |
| Minolta MRW raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| Olympus ORF raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| Panasonic raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| Panasonic raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| Pentax PEF raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| PNM image | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| PBM image | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| PGM image | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| PPM image | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| Sony SRF raw image¹³ | glycin-raw |[libopenraw](https://crates.io/crates/libopenraw) (Rust) |
| TGA image (.tga) | glycin-image-rs |[image](https://crates.io/crates/image) (Rust) |
| Windows cursor | glycin-image-rs |[image](https://crates.io/crates/image) (Rust), [bmp](https://crates.io/crates/bmp) (Rust), [png](https://crates.io/crates/png) (Rust) |
| XBM image | glycin-image-rs |[image-extras](https://crates.io/crates/image-extras) (Rust) |
| XPM image | glycin-image-rs |[image-extras](https://crates.io/crates/image-extras) (Rust) |

1. Support for this format is still experimental the corresponding loader is not built by default
2. The loader for this format depends on GPL-3.0-or-later licenced software
3. The loader for this format depends on LGPL-3.0-or-later licenced software

## Image Loader Configuration

Loader configurations are read by the client library from `XDG_DATA_DIRS` and `XDG_DATA_HOME`. The location is typically of the form:

```
<data-dir>/share/glycin-loaders/<compat-version>+/conf.d/<loader-name>.conf
```

for example:

```
<data-dir>/share/glycin-loaders/2+/conf.d/glycin-image-rs.conf
```

The configs are [glib KeyFiles](https://docs.gtk.org/glib/struct.KeyFile.html) of the form:

```ini
[loader:image/png]
Exec = /usr/libexec/glycin-loaders/2+/glycin-image-rs
```

Where the part behind `loader` is a mime-type and the value of `Exec` can be any executable path.

### Existing Compatibility Versions

Not every new major version of the library has to break compatibility with the loaders. If a glycin version X breaks compatibility, the new compatibility version will be called X+. Only glycin X and newer versions will be compatible with X+ until a new compatibility version is used. The definition of the API of each compatibility version is available in [`docs/`](docs/). The following compatibility versions currently exist

| compat-version | Compatible With                |
|----------------|--------------------------------|
| 0+             | glycin 0.x                     |
| 1+             | glycin 1.x, 2.x; libglycin 1.x |
| 2+             | glycin 3.x; libglycin 2.x      |

## Sandboxing and Inner Workings

This section only applies to Linux. See [Limitations](#Limitations) for details. By default, glycin spawns one process per image loader. The loaders can handle multiple requests in parallel. Running loaders are terminated after not being used for 30 seconds. All these values can be configured. The communication between glycin and the loader takes place via peer-to-peer D-Bus over a Unix socket.

Glycin supports a sandbox mechanism inside and outside of Flatpaks. Outside of Flatpaks, the following mechanisms are used: The image loader binary is spawned via `bwrap`. The bubblewrap configuration only allows for minimal interaction with the host system. Only necessary parts of the filesystem are mounted and only with read access. There is no direct network access. Environment variables are not passed to the sandbox. Before forking the process the memory usage is limited via calling `setrlimit` and syscalls are limited to an allow-list via seccomp filters.

Inside of Flatpaks the `flatpak-spawn --sandbox` command is used. This restricts the access to the filesystem in a similar way as the direct `bwrap` call. The memory usage is limited by wrapping the loader call into a `prlimit` command. No additional seccomp filters are applied to the existing Flatpak seccomp rules.

The GFile content is streamed to the loader via a Unix socket. This way, loaders can load contents that require network access, without having direct network access themselves. Formats like SVG set the `ExposeBaseDir = true` option in their config. This option causes the original image file's directory to be mounted into the sandbox to include external image files from there. The `ExposeBaseDir` option has no effect for `flatpak-spawn` sandboxes since they don't support this feature.

The loaders provide the texture data via a memfd that is sealed by glycin and then given as an mmap to GDK. For animations and SVGs the sandboxed process is kept alive for new frames or tiles as long as needed.

For information on how to implement a loaders, please consult the [`glycin-utils` docs](https://docs.rs/glycin-utils/).

## Building and Testing

- The `-Dloaders` option allows to only build certain loaders.
- The `-Dtest_skip_ext` option allows to skip certain image filename extensions during tests. The option `-Dtest_skip_ext=heic` might be needed if x265 is not available.
- Running integration tests requires the glycin loaders to be installed. By default, `meson test` creates a separate installation against which the tests are run. This behavior can be changed by setting `-Dtest_skip_install=true`, requiring to manually calling `meson install` before running the tests.

## Packaging Status

[![Packaging Status](https://repology.org/badge/vertical-allrepos/glycin.svg?exclude_unsupported=1&header=&minversion=2)](https://repology.org/project/glycin/versions)

## Software Using Glycin

### Apps

- [Bazaar](https://flathub.org/apps/io.github.kolunmi.Bazaar)
- [Camera (Snapshot)](https://apps.gnome.org/Snapshot/)
- [Contact](https://apps.gnome.org/Contacts/)
- [Fotema](https://flathub.org/apps/app.fotema.Fotema)
- [Fractal](https://flathub.org/apps/org.gnome.Fractal)
- [Identity](https://flathub.org/apps/org.gnome.gitlab.YaLTeR.Identity)
- [Image Viewer (Loupe)](https://apps.gnome.org/Loupe/)
- [Nautilus](https://apps.gnome.org/Nautilus/)
- [Robots](https://flathub.org/apps/org.gnome.Robots)
- [Shortwave](https://flathub.org/apps/de.haeckerfelix.Shortwave)

### Other

- [Mutter](https://developer.gnome.org/components/#mutter)
- [gdk-pixbuf](https://developer.gnome.org/components/#gdk-pixbuf)

## The Name

[Glycin](https://en.wikipedia.org/wiki/Glycin) (ˈɡlaɪsiːn) is a photographic developing agent. There is no deeper meaning behind the name choice but using a somewhat unique name that is related to images. Glycin is often confused with the amino acid [glycine](https://en.wikipedia.org/wiki/Glycine), which is called glycin in other languages, like German.

## License

SPDX-License-Identifier: MPL-2.0 OR LGPL-2.1-or-later

The camera raw loader uses the crate `libopenraw` which is licensed as LGPL-3.0-or-later. The JPEG XL loader uses the `jpegxl-rs` and `jpegxl-sys` crates which are licensed as GPL-3.0-or-later. Given these are only separate executables, only the `glycin-raw` and the `glycin-jxl` binary falls under said licenses, and doesn't precludes using `glycin` under MPL-2.0 OR LGPL-2.1-or-later. This is not legal advice.
