Table of Contents

How old is SAIL?

SAIL is rebranded ksquirrel-libs rewritten in C, improved and with high-level APIs. Ksquirrel-libs was a set of C++ image codecs for the KSquirrel image viewer. See http://ksquirrel.sourceforge.net.

Technically, SAIL (ksquirrel-libs) was founded in 2003 making it one of the oldest image decoding libraries.

Is SAIL cross-platform?

Yes. It's written in pure C11 and is highly portable. However, only the Windows, macOS, and Linux platforms are officially supported. SAIL may or may not compile on other platforms. Pull requests to support more platforms are highly welcomed.

What's the preferred way of installation?
  • Windows: Conan, vcpkg
  • macOS: Conan, brew, vcpkg
  • Linux: native packages if available, Conan, vcpkg

See Building.

Does SAIL support static linking?

Yes. Compile with -DBUILD_SHARED_LIBS=OFF. This automatically enables SAIL_COMBINE_CODECS.

What are the competitors of SAIL?
Describe the high-level APIs

SAIL provides four levels of high-level APIs:

  • Junior - I just want to load this JPEG from a file or memory
  • Advanced - I want to load this animated GIF from a file or memory
  • Deep diver - I want to load this animated GIF from a file or memory and have control over selected codecs and meta data
  • Technical diver - I want everything above and my custom I/O source
Does SAIL provide simple single-line APIs?

Yes. SAIL provides four levels of APIs, depending on your needs: junior, advanced, deep diver, and technical diver. Junior API is what you want.

In what pixel format SAIL loading functions output images?

SAIL always tries to output pixel format close to the source pixel format as much as possible. Ideally (but not always), it outputs the same pixel format as stored in the image.

For example, SAIL outputs BPP24-BGR images from full-color BMP files without transparency.

You can also consider conversion functions from libsail-manip.

What pixel formats can SAIL save?

SAIL codecs always try to support as much output pixel formats as possible. SAIL doesn't convert one pixel format to another in saving operations. Images are always written as is.

The list of pixel formats that can be written by SAIL is codec-specific and is publicly available in every .codec.info file. It can be accessed through sail_codec_info_from_extension() -> codec_info -> save_features -> pixel_formats.

Does SAIL support animated and multi-paged images?

Yes. Just continue loading the image file until the loading functions return SAIL_OK. If no more frames are available, the loading functions return SAIL_ERROR_NO_MORE_FRAMES.

Does SAIL support loading from memory?

Yes. SAIL supports loading/saving from/to files and memory. For technical divers, it's also possible to use custom I/O sources.

See sail_start_loading_from_file(), sail_start_loading_from_memory(), and sail_start_loading_from_io().

How does SAIL support image formats?

SAIL supports image formats through dynamically loaded SAIL codecs (unless compiled with SAIL_COMBINE_CODECS). End-users never work with the codecs directly. They always work with the abstract high-level APIs.

Does SAIL preload codecs in the initialization routine?

SAIL_COMBINE_CODECS is OFF

SAIL doesn't preload codecs in the initialization routine (sail_init()). It loads them on demand. However, you can preload them explicitly with sail_init_with_flags(SAIL_FLAG_PRELOAD_CODECS).

SAIL_COMBINE_CODECS is ON

All codecs get loaded on application startup.

How does SAIL look for codecs?

Codecs path search algorithm (first found path wins):

Conan recipe on any platform

Codecs are combined into a dynamically linked library, so no need to search them.

VCPKG port on any platform

Codecs are combined into a dynamically linked library, so no need to search them.

Manually compiled on any platform with SAIL_COMBINE_CODECS=ON

Codecs are combined into a dynamically linked library, so no need to search them.

Manually compiled on Windows with SAIL_COMBINE_CODECS=OFF (the default)

  1. SAIL_CODECS_PATH environment variable
  2. <SAIL DEPLOYMENT FOLDER>\lib\sail\codecs
  3. Hardcoded SAIL_CODECS_PATH in config.h

Manually compiled on Unix (including macOS) SAIL_COMBINE_CODECS=OFF (the default)

  1. SAIL_CODECS_PATH environment variable
  2. Hardcoded SAIL_CODECS_PATH in config.h
<FOUND PATH>/lib is added to LD_LIBRARY_PATH.

On all platforms, SAIL_THIRD_PARTY_CODECS_PATH environment variable with a list of ';'-separated paths is searched if SAIL was compiled with SAIL_THIRD_PARTY_CODECS_PATH enabled (the default), so you can load your own codecs from there.

How can I point SAIL to my custom codecs?

If SAIL was compiled with SAIL_THIRD_PARTY_CODECS_PATH enabled (the default), you can set the SAIL_THIRD_PARTY_CODECS_PATH environment variable to a list of ';'-separated paths containing your custom SAIL codecs.

On Windows, sail.dll location and SAIL_THIRD_PARTY_CODECS_PATH/lib are the only places where DLL dependencies are searched. No other paths are searched. Use WIN32 API AddDllDirectory to add your own DLL dependencies search path. On other platforms, SAIL_THIRD_PARTY_CODECS_PATH/lib is added to LD_LIBRARY_PATH in runtime.

If SAIL was compiled with SAIL_THIRD_PARTY_CODECS_PATH disabled, loading third-party codecs is disabled.

I'd like to reorganize the standard SAIL folder layout on Windows (for standalone build or bundle)

You can surely do that. With the standard layout SAIL detects the codecs' location automatically. If you reorganize the standard SAIL folder layout, you'll need to specify the new codecs' location by setting the SAIL_CODECS_PATH environment variable.

Describe the memory management techniques implemented in SAIL
The memory management technique implemented in SAIL

Internally, SAIL always cleans up on errors. If you encounter a memory leak on error, please report it.

C only: However, if an engineer encounters an error in the middle of loading or saving an image with the advanced or a deeper API, it's always a responsibility of the engineer to stop loading or saving with sail_stop_loading() or sail_stop_saving(). These functions execute a proper cleanup in the underlying codec. If you don't call sail_stop_loading() or sail_stop_saving() in this situation, be prepared for memory leaks.

C++ only: C++ engineers are more lucky. The C++ binding executes the necessary cleanup automatically in this situation in ~image_input() or ~image_output().

Convention to call SAIL functions

It's always recommended (but not required) to use the SAIL_TRY() macro to call SAIL functions. It's also always recommended to clean up on error with the SAIL_TRY_OR_CLEANUP() macro if you need to.

External pointers stay untouched on error

External pointers that are allocated or modified by SAIL functions stay untouched on error. For example:

struct sail_image *image = NULL;
SAIL_TRY(sail_alloc_image(&image));

/*
 * If sail_alloc_palette_for_data() fails, the palette pointer stays untouched (NULL).
 * This code prints NULL value on error.
 */
SAIL_TRY_OR_CLEANUP(sail_alloc_palette_for_data(SAIL_PIXEL_FORMAT_BPP24_RGB, color_count, &image->palette));
                    /* cleanup */ printf("%p\n", image->palette),
                                  sail_destroy_image(image));

void *state = NULL;
struct sail_image *image = NULL;

SAIL_TRY(sail_start_loading_from_file(..., &state));

/*
 * If sail_load_next_frame() fails, the image pointer stays untouched (NULL).
 * This code prints NULL value on error.
 */
SAIL_TRY_OR_CLEANUP(sail_load_next_frame(state, &image),
                    /* cleanup */ printf("%p\n", image),
                                  sail_stop_loading(state));

Always set a pointer to state to NULL (C only)

C loading and saving functions require a local void pointer to save state. Always set it to NULL before loading or saving. For example:

void *state = NULL;

SAIL_TRY(sail_start_loading_from_file(..., &state));

SAIL_TRY_OR_CLEANUP(sail_load_next_frame(state, ...),
                    /* cleanup */ sail_stop_loading(state));

Can I implement an image codec in C++?

Yes. Your codec just needs to export a set of public functions so SAIL can recognize and use it. Theoretically, you can implement your codec in any programming language.

Describe codec info file format

Let's take a llok at a hypothetical codec info:

# This section describes the codec per se.
#
[codec]

# Codec layout is a set of functions the codec exports. Different layouts generations are not compatible.
# libsail supports just a single (current) layout. Cannot be empty.
#
layout=8

# Semantic codec version. Cannot be empty.
#
version=1.0.0

# Codec priority. SAIL uses this property to sort the enumerated codecs by priority
# to speed up search of popular image formats by functions like sail_codec_info_from_path().
#
# HIGHEST = one of the most popular image formats like JPEG
# HIGH    = pretty popular and common image format like SVG
# MEDIUM  = moderate popularity
# LOW     = rare image format
# LOWEST  = very rare and/or too specific and/or ancient image format
#
priority=MEDIUM

# Short codec name. Must be uppercase letters on numbers only. Cannot be empty.
#
name=ABC

# Codec description. Any human-readable string. Cannot be empty.
#
description=Some ABC Format

# ';'-separated list of hex-encoded magic numbers indentifying this image format.
# Can be empty only if the list of file extensions below is not empty.
#
magic-numbers=34 AB;FA 55

# ';'-separated list of file extensions indentifying this image format.
# Can be empty only if the list of magic numbers above is not empty.
#
extensions=abc;bca

# ';'-separated list of MIME types indentifying this image format. Can be empty.
#
mime-types=image/abc

# Section of various features describing what the image codec can actually load.
#
[load-features]

# ';'-separated list of what the image codec can actually read.
# Can be empty if the image codec cannot load images.
#
# Possible values:
#    STATIC       - Can load static images.
#    ANIMATED     - Can load animated images.
#    MULTI-PAGED  - Can load multi-paged (but not animated) images.
#    META-DATA    - Can load image meta data like JPEG comments or EXIF.
#    ICCP         - Can load embedded ICC profiles.
#    SOURCE-IMAGE - Can populate source image information in sail_image.source_image.
#
features=STATIC;META-DATA;INTERLACED;ICCP

# ';'-separated list of codec-specific tuning options. For example, it's possible
# to disable ABC codec filtering with setting abc-filtering to 0 in load options.
# Tuning options' names must start with the codec name to avoid confusing.
#
# The list of possible values for every tuning option is not current available
# programmatically. Every codec must document them in the codec info file.
#
# For example:
#   - abc-filtering: Tune filtering. Possible values: 0 (disable), 1 (light), 2 (hard).
#
tuning=abc-filtering

# Section of various features describing what the image codec can actually save.
#
[save-features]

# ';'-separated list of what the image codec can actually write.
# Can be empty if the image codec cannot save images.
#
# Possible values:
#    STATIC      - Can save static images.
#    ANIMATED    - Can save animated images.
#    MULTI-PAGED - Can save multi-paged (but not animated) images.
#    META-DATA   - Can save image meta data like JPEG comments or EXIF.
#    INTERLACED  - Can save interlaced images.
#    ICCP        - Can save embedded ICC profiles.
#
features=STATIC;META-DATA;INTERLACED;ICCP

# ';'-separated list of codec-specific tuning options. For example, it's possible
# to disable ABC codec filtering with setting abc-filtering to 0 in save options.
# Tuning options' names must start with the codec name to avoid confusing.
#
# The list of possible values for every tuning option is not current available
# programmatically. Every codec must document them in the codec info file.
#
# For example:
#   - abc-filtering: Tune filtering. Possible values: 0 (disable), 1 (light), 2 (hard).
#
tuning=abc-filtering

# ';'-separated list of pixel formats the image codec can write.
# Can be empty if the image codec cannot save images.
#
# Note: SAIL doesn't convert images while saving. It writes them 1:1. The image codec
# can take as input and save 8-bit indexed, 24-bit RGB, and 32-bit RGBA images.
#
pixel-formats=BPP8-INDEXED;BPP24-RGB;BPP32-BGRA

# ';'-separated list of compressions the image codec can write. Cannot be empty if the image codec
# can save images. If the image codec cannot select compressions, specify UNSUPPORTED.
#
compression-types=DEFLATE;RLE

# Default compression from the list above to use when no explicit compression was selected
# by a client application. Can be empty if the image codec cannot save images.
#
default-compression=DEFLATE

# Minimum compression level. This parameter is not used and must be 0 if the list
# of supported compressions has more than one compression or if the list is empty.
#
# The data type is double. C function atof() is used to convert the value to double.
# Decimal-point character is determined by the current C locale.
#
compression-level-min=1

# Maximum compression level. This parameter is not used and must be 0 if the list
# of supported compressions has more than one compression or if the list is empty.
#
# The data type is double. C function atof() is used to convert the value to double.
# Decimal-point character is determined by the current C locale.
#
compression-level-max=9

# Default compression level to use when no explicit compression level was selected
# by a client application. This parameter is not used and must be 0 if the list
# of supported compressions has more than one compression or if the list is empty.
# Must be in the range of the min/max limits above.
#
# The data type is double. C function atof() is used to convert the value to double.
# Decimal-point character is determined by the current C locale.
#
compression-level-default=6

# Step to increment or decrement compression level. This parameter is not used
# and must be 0 if the list of supported compressions has more than one compression
# or if the list is empty.
#
# The data type is double. C function atof() is used to convert the value to double.
# Decimal-point character is determined by the current C locale.
#
compression-level-step=1

Can I compile codecs dependencies (like libjpeg) directly into SAIL?

Unlike FreeImage, SAIL doesn't allow compiling external dependencies into a single library and I have no plans to allow that.

You can still achieve that using external build systems like vcpkg.

If you prefer compiling SAIL manually as a shared library, you can compile it against the static vcpkg triplet like x64-windows-static. For example:

# Install vcpkg using the official installation guide into your preferred path.
# For example, into F:/vcpkg.

# Install SAIL in vcpkg just to install all its dependencies
# as static libs. We'll compile SAIL manually later.
cd F:/vcpkg/
vcpkg install sail[all] --triplet x64-windows-static

# Go to the cloned SAIL sources
cd F:/sail/

# Compile SAIL against vcpkg
mkdir build
cd build
cmake -DSAIL_COMBINE_CODECS=ON -DCMAKE_TOOLCHAIN_FILE=F:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static ..
cmake --build . --config Release

This way all codecs and their dependencies will be compiled into sail-codecs.dll.

Are there any C/C++ examples?

Yes. See the examples directory in the source tree.

Are there any bindings to other programming languages?

Yes. Currently SAIL supports the following bindings:

  1. C++
Pull requests to support more programming languages are highly welcomed.

How many image formats are you going to implement?

Ksquirrel-libs supported around 60 image formats. I have no plans to port them all. However, the most popular image formats will be definitely ported from ksquirrel-libs.

I have problems with include paths with vcpkg without CMake

Add VcpkgInstalledDir/include/sail to the project include path. See https://learn.microsoft.com/en-us/vcpkg/users/buildsystems/msbuild-integration.

I have questions, issues, or proposals

Opening a GitHub issue is the preferred way of communicating, discussing features, and solving problems.

Pull requests are always welcomed after a proper discussion in an issue.