Official repository for the FSI file format specification and IO implementation written in C++17.
Friendly Shade Image Format (FSI for short) is a modern, future-proof image file format designed in 2022 by Sebastian Zapata from Friendly Shade. We are releasing this with the hope that 3D and image editing software companies will implement it, so we no longer struggle with the rather limited and ancient file formats such as EXR, TIF, PNG, etc. It supports images up to 1,048,575 by 1,048,575 pixels and 1,048,575 channels. It also supports 8, 16, 32 and 64 bit-depths as signed and unsigned integers, as well as 32 and 64-bit floating-point data types. These are the two main goals:
- Having a simpler image file format without layers, tags or other convoluted features for simple texturing and rendering tasks.
- Sharing 2D image data across software without file size, resolution, bit-depth or channel-count limitations.
See Frequently Asked Questions (FAQ) where common questions are answered.
For more questions about implementation or the specification in general, please contact us at https://friendlyshade.com/contact
A C++17 implementation for encoding/decoding this format is also available as shared, static or even as a header-only library. It supports callbacks for reporting progress, canceling or even pausing read and write operations (see "sample_read_write" example for more details).
Please note that this implementation does not account for endianness; it assumes operation on a little-endian computer.
Simple read operation excluding error handling
#include <friendlyshade/fsi/core/Depth.h>
#include <friendlyshade/fsi/core/Header.h>
#include <friendlyshade/fsi/core/Reader.h>
int main()
{
using fsi::Reader;
using fsi::Header;
Reader reader;
reader.open("path/to/image.fsi");
Header header = reader.header();
Image image(headerReader.width, headerReader.height, headerReader.channels, headerReader.depth);
reader.read(image.data);
reader.close();
return 0;
}
Note that calling Reader::close()
is optional. If it's not explicitly called, it will be invoked automatically during the destruction of the Reader
object. However, the file will remain locked by the application until then.
Simple write operation excluding error handling
#include <friendlyshade/fsi/core/Depth.h>
#include <friendlyshade/fsi/core/Header.h>
#include <friendlyshade/fsi/core/Writer.h>
int main()
{
using fsi::Writer;
using fsi::Header;
using fsi::Depth;
Image image(1024, 1024, 3, Depth::Uint16);
Writer writer;
Header header;
header.width = image.width;
header.height = image.height;
header.channels = image.channels;
header.depth = image.depth;
writer.open("path/to/image.fsi", header, fsi::FormatVersion::Latest);
writer.write(image.data);
writer.close();
return 0;
}
Note that calling Writer::close()
is optional. If it's not explicitly called, it will be invoked automatically during the destruction of the Writer
object. However, the file will remain locked by the application until then.
All data should be read/written in little-endian byte order.
Description | Size (in bytes) |
---|---|
Header Section Image Header Thumbnail Header |
26 21 5 |
Data Section Thumbnail Data Image Data |
Variable 262,144 Variable |
Description | Required Value / Range | Size |
---|---|---|
Format signature | 'f' | 1 byte (char) |
Format signature | 's' | 1 byte (char) |
Format signature | 'i' | 1 byte (char) |
Format signature | 'f' | 1 byte (char) |
Format version | 2 | 4 bytes (uint32_t) |
Image width | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image height | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image channel count | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image depth | 1..10 | 1 byte (uint8_t) |
Description | Required Value / Range | Size |
---|---|---|
Has thumbnail | 0 or 1 (non-zero for true) | 1 byte (uint8_t) |
Thumbnail width | 1..256 (0 if no thumbnail) | 2 bytes (uint16_t) |
Thumbnail height | 1..256 (0 if no thumbnail) | 2 bytes (uint16_t) |
Value | Data Type | Size (in bytes) |
---|---|---|
1 | int8_t | 1 |
2 | int16_t | 2 |
3 | int32_t | 4 |
4 | int64_t | 8 |
Value | Data Type | Size (in bytes) |
---|---|---|
5 | uint8_t | 1 |
6 | uint16_t | 2 |
7 | uint32_t | 4 |
8 | uint64_t | 8 |
Value | Data Type | Size (in bytes) |
---|---|---|
9 | float | 4 |
10 | double | 8 |
- Thumbnail data section is always 262,144 bytes (for max thumbnail size of 256x256).
- Thumbnail data is RGBA (4 channels) at 8-bit (unsigned)
- Thumbnail dimensions are specified in the Thumbnail Header.
- Thumbnail data is stored in interleaved/packed order = RGBA RGBA RGBA, etc.
- Image data is stored in interleaved/packed order = RGB RGB RGB, etc.
- Total size of the data in bytes = width * height * channels * sizeof_data_type_in_bytes.
- It's recommended to read this section in chunks for efficiency, and then reinterpret it as an array of the data type of the Image depth specified in the Image header.
All data should be read/written in little-endian byte order.
Description | Required Value | Size |
---|---|---|
Format signature | 'f' | 1 byte (char) |
Format signature | 's' | 1 byte (char) |
Format signature | 'i' | 1 byte (char) |
Format signature | 'f' | 1 byte (char) |
Format version | 1 | 4 bytes (uint32_t) |
Image width | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image height | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image channel count | 1..2^20-1 (0..1,048,575) | 4 bytes (uint32_t) |
Image depth | 1..10 | 4 bytes (uint32_t) |
Value | Data Type | Size (in bytes) |
---|---|---|
1 | int8_t | 1 |
2 | int16_t | 2 |
3 | int32_t | 4 |
4 | int64_t | 8 |
Value | Data Type | Size (in bytes) |
---|---|---|
5 | uint8_t | 1 |
6 | uint16_t | 2 |
7 | uint32_t | 4 |
8 | uint64_t | 8 |
Value | Data Type | Size (in bytes) |
---|---|---|
9 | float | 4 |
10 | double | 8 |
-
Data is stored in interleaved/packed order = RGB RGB RGB, etc.
-
Total size of the data in bytes = width * height * channels * sizeof_data_type_in_bytes.
-
It's recommended to read this section in chunks for efficiency, and then reinterpret it as an array of the data type of the Image depth of the image.