Skip to content
EnergyFlexusPublic

About

ffpsd – Free Fast PSD: a C++ library to read and write Photoshop PSD/PSB files, with C, Rust and Python bindings.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

72 Commits

Folders and files

Repository files navigation

ffpsd

Read and write Photoshop PSD and PSB files from C++, C, Rust and Python:

Until 1.0.0 the APIs are not stable: a release may break source and binary compatibility. What comes next: ROADMAP.md.

What it does for now

  • PSD and PSB, and either into the other; RGB and gray documents, and either into the other.
  • Layers: raster layers, groups, the background and Levels adjustments; added, copied from this or another document, moved and removed; name, opacity, visibility, clipping and blend mode; pixels in and out.
  • Pixel masks, read, set and removed.
  • The canvas and the layers resized, turned and flipped; the canvas grown or cut from any anchor.
  • The composite the file stores, read and replaced; resolution and version info.
  • Every image resource and tagged block, raw, for what ffpsd does not interpret itself.
  • PNG, JPEG and WebP in and out.

How it works

  • Single-threaded, with no global state: a document belongs to one thread at a time, and separate documents work in separate threads at once, so a batch of files spreads over every core.
  • Nothing is lost: blocks, resources and channels ffpsd does not interpret are written back byte for byte, and a document saved unchanged is the file it was read from.
  • Lazy: a channel is decoded when its pixels are asked for and written back as stored until they change, so opening and saving cost little more than the file's size.
  • In memory: opening reads the whole file and closes it, so the file is not locked while you work; saving builds the new file in memory and writes it once. The price is memory: about the file's size for the document, twice that while opening or saving, plus any pixels you decode.
  • Self-contained: libpng, zlib-ng, libjpeg-turbo and libwebp are built in under renamed symbols, so other copies in the same program do not clash, and PNG, JPEG and WebP load and save with nothing else installed.

Getting the sources

Code style: CONTRIBUTING.md. Programs in C++, C, Rust and Python: examples/.

libpng, zlib-ng, libjpeg-turbo, libwebp, Dear ImGui, SDL3, GoogleTest and Google Benchmark are git submodules under vendor/; the test PSD files are in Git LFS (git lfs install before cloning).

git clone --recursive <url>
git submodule update --init --recursive   # in a clone made without them
git lfs pull                              # in a clone made without Git LFS

Quick build

Release, installed into ffpsd-out/:

python build.py            # shared library
python build.py --static   # static library

It checks for CMake >= 3.23, Ninja and a compiler, sets up MSVC on Windows by itself and fetches missing submodules. Flags: --clean, --jobs N, --no-apps, --no-png, --no-jpeg, --no-webp, --out DIR, and on Windows --crt static|dynamic (C runtime on Windows) and --msvc 14.29, the toolset of an older Visual Studio installed beside the latest.

ffpsd-out/bin/        img2ffpsd-cli, img2ffpsd-ui; ffpsd.dll on Windows
ffpsd-out/lib/        libffpsd.so, libffpsd.dylib, ffpsd.lib or libffpsd.a; cmake/ffpsd/
ffpsd-out/include/    ffpsd/*.hpp, ffpsd/c_api.h, ffpsd/export.h
ffpsd-out/share/      ffpsd/licenses/

libpng, zlib-ng, libjpeg-turbo and libwebp are inside the library, the static one too, and every symbol of theirs starts with ffpsd_: a program can link its own copies of them beside ffpsd. libjpeg-turbo is plain C, without SIMD, and libwebp's SIMD is C intrinsics, so no assembler is needed.

Apps

Built with FFPSD_BUILD_APPS and installed into bin/:

  • img2ffpsd - folders of PNG, JPEG and WebP pictures into layered PSD files: img2ffpsd-cli on the command line, img2ffpsd-ui in a window with FFPSD_WITH_IMGUI; build.py builds both

Build with CMake

CMake >= 3.23 and Ninja; on Windows, from an x64 Native Tools Command Prompt.

cmake --preset shared-release
cmake --build --preset shared-release
cmake --install build/shared-release     # into ffpsd-out/ unless --prefix says otherwise
Preset Library Build type
static-debug static Debug
static-release static Release
shared-debug shared Debug
shared-release shared Release
bench-release static Release, benchmarks only

Tests

The debug presets build them; the script builds and runs them from any shell:

python scripts/run_tests.py                     # static-debug
python scripts/run_tests.py --shared            # shared-debug
python scripts/run_tests.py --all               # both
python scripts/run_tests.py --filter ReadPsd    # by name
python scripts/run_tests.py --no-build          # just run

By hand: cmake --preset static-debug, cmake --build --preset static-debug, ctest --preset static-debug.

Benchmarks

Documents, layers and the picture formats on the files in tests/data/photoshop/; benchmarks/README.md lists them. The debug presets only build them; numbers come from bench-release:

python scripts/run_benchmarks.py                    # everything, 10 repetitions
python scripts/run_benchmarks.py --filter Layer/    # by regular expression
python scripts/run_benchmarks.py --save before      # keep this run as "before"
python scripts/run_benchmarks.py --compare before   # run again and compare
python scripts/run_benchmarks.py --against v0.4.1   # build that version too, run both, compare
python scripts/run_benchmarks.py --no-build         # just run

Runs are kept in build/bench-release/results/. --compare and --against use Google Benchmark's compare.py, which needs pip install -r requirements-dev.txt.

Compiler flags

  • Release: /O2 /Ob3 /Oi /Ot /Gy (MSVC) or -O3; LTO for the shared library where available. The static one has none, so any linker takes its objects, and it is position-independent.
  • Debug: /W4 /permissive- (MSVC) or -Wall -Wextra -Wpedantic -Wshadow -Wconversion, -Wold-style-cast for C++.
  • No AVX or AVX2 by default, so the builds run on any x86-64 processor. With them, writing re-encoded channels is faster: /arch:AVX2 (MSVC) or -march=native in CMAKE_CXX_FLAGS and CMAKE_C_FLAGS.

Options

Option Default Meaning
BUILD_SHARED_LIBS OFF shared instead of static
FFPSD_BUILD_APPS ON apps/
FFPSD_WITH_PNG ON PNG in ffpsd/formats.hpp, with libpng and zlib-ng
FFPSD_WITH_JPEG ON JPEG in ffpsd/formats.hpp, with libjpeg-turbo
FFPSD_WITH_WEBP ON WebP in ffpsd/formats.hpp, with libwebp
FFPSD_WITH_IMGUI OFF the apps with a window, with Dear ImGui and SDL3
FFPSD_BUILD_TESTS OFF, ON in the debug presets tests/
FFPSD_BUILD_BENCHMARKS OFF, ON in bench-release and the debug presets benchmarks/
FFPSD_BUILD_EXAMPLES OFF, ON in the debug presets examples/ in C and C++
FFPSD_MSVC_STATIC_RUNTIME OFF MSVC: /MT instead of /MD, see C runtime on Windows

On top of a preset: cmake --preset shared-release -DFFPSD_BUILD_APPS=OFF.

Use from another CMake project

find_package(ffpsd 0.4.7 REQUIRED)
target_link_libraries(my_app PRIVATE ffpsd::ffpsd)

The package defines FFPSD_STATIC for a static build, so ffpsd/export.h picks the right linkage. On Windows the program also has to match the C runtime, below.

C runtime on Windows

MSVC links the C runtime as a DLL (/MD, /MDd in Debug) or statically (/MT, /MTd). ffpsd and the libraries inside it take /MD, as CMake gives any program; -DFFPSD_MSVC_STATIC_RUNTIME=ON or build.py --crt static makes it /MT.

ffpsd The program needs
static the same CRT and configuration, or the link fails (LNK2038)
shared, the C++ API the same CRT, compiler and configuration: std::string and std::vector cross over
shared, the C API any

A shared library built with /MT has its own CRT and heap, so only the C API is safe with it; CMake warns about it. A program that wants /MT with a static ffpsd built so:

# quoted: the shell would read < > as redirection
cmake -S . -B build '-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>'

The libraries on the Releases page are Release builds with /MD, so a Debug program can use only the C API of the shared one. The apps there are built with /MT and need no Visual C++ runtime installed.

They are built with the toolset of Visual Studio 2019, MSVC 14.29, so Visual Studio 2019 and later link them.

Trademarks

Adobe and Photoshop are either registered trademarks or trademarks of Adobe in the United States and/or other countries. ffpsd is not affiliated with or endorsed by Adobe.

About

ffpsd – Free Fast PSD: a C++ library to read and write Photoshop PSD/PSB files, with C, Rust and Python bindings.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages