imagy

Node.js binding for the Imagy c++ library

Image conversion and manipulation library with node binding

Requires either x64 Windows 10 (might run on Windows Server, not tested) or x64 Debian-based system with support for glibc 2.31 or higher. You can check your glibc version with:

ls /lib/x86_64-linux-gnu/ | grep -E '^libc-[0-9]\.[0-9]{1,2}\.so$'

Features

Supported Image Formats:

  • Conversion in all directions: BMP, PNG, JPEG, WEBP, TIFF
  • Conversion in one direction only: GIF -> animated WEBP
  • [experimental] Conversion in one direction only: AVIF -> BMP, PNG, JPEG, WEBP, TIFF

Image Manipulation:

  • Resizing (unanimated images only)
  • Flipping (unanimated images only)

Imagy is a library that uses a mixture of existing libraries (Boost::GIL, libpng, libjpeg, libwebp) and self-built decoders to provide an easy-to-use library interface for image conversion and manipulation. All images are internally stored as a (decoded) pixel array to allow fast operations on the data. De- and encoding are only ever done upon FileIO.

Additionally, the en- and decoding routines for animated images make use of multithreading to speed up the process.

Unlike other packages, imagy will NOT need to download additional files in order to run as an npm package. The size you see under Unpacked Size on npm is the actual size.

Usage

This library comes with pre-built binaries. You can install the package with

npm i imagy

Inlude the package into your js application with:

const imagy = require('imagy');

For quick image conversions the library implements the convert and convertSync functions:

const imagy = require('imagy');

imagy.convertSync({
            'image': 'path/to/file.jpg',
            'outName': 'path/to/target.png',
        });

// Alternate way without intermediate object:
imagy.convertSync("inputFile", "outputFile");
const imagy = require('imagy');

// Converts asynchronously and returns a promise
await imagy.convert({
            'image': 'path/to/file.jpg',
            'outName': 'path/to/target.png',
        });

// Alternate way without intermediate object:
await imagy.convert("inputFile", "outputFile");

These functions take an Object with two properties as an argument. The first image is the path to the file to be converted and the second outName is the target. The function tries to infer the input's image type from the file extension, or if none is found or the one used is unknown, tries to parse the file header to get information on the image type. The convert function returns a promise that, if resolved, returns and Object with finished, error and img(path to target) keys.

You can also use the Image class:

const imagy = require('imagy');

const img = new imagy.Image('path/to/input/file.png');

img.writeToFileSync('some/image.jpg');

// async variant
await img.writeToFile('another/image.bmp');

Note though that this way the underlying image data are only freed once the Image instance gets garbage collected, which triggers the underlying cpp-class's destructor.

Image objects also have access to the rescale and rescaleSync methods, which return the calling object, so you might use them as follows:

const imagy = require('imagy');
const img = new imagy.Image('path/to/input/file.png');

//          (HEIGHT, WIDTH)
img.rescaleSync(0, 4000).writeToFileSync(targetFile);

A 0 argument here indicates, that the dimension should be calculated in such a way that the image proportions are retained.

See the the according doc file for a full documentation.

Build Requirements

This package has been built and tested on Windows 10, Kali Linux 2021.4 x64 and Ubuntu 20.04.4 LTS via WSL2.

Library Specifcs:

  • C++ 17 or higher
  • Ninja Build System v1.11+
  • msvc (windows) or gcc (linux)
  • CMake Version 3.9+
  • Windows x64 or a debian-based Linux distro x64
  • A few libraries are needed, refer to the Installation reference HERE

Node-Binding Specifics:

  • npm 8.0+
  • Node v14.0+
  • node-gyp v9.0+ $ npm install -g node-gyp Note though that node-gyp needs at least Python v3.7 to be installed. Windows: https://www.python.org/downloads/ For Ubuntu systems:

sudo apt install software-properties-common -y

sudo add-apt-repository ppa:deadsnakes/ppa -y

sudo apt update

sudo apt install python3.8 -y

Verify via python3.8 --version

Easiest way to make it accessable to node-gyp is to setup a PYTHON environment variable to the path of the Python executable.

Installation

WIP

Windows

  • Use the VS Dev Console for the c++ library and another shell for the binding.
  • Download, compile and install the following libraries: Boost Header-only, libpng, libjpeg-turbo, libwebp and zlib. Header files should be copied into the src/headers directory, the compiled libraries into the src/libs/PRESET one.

Linux

Testing

The project includes test suites for both the c++ library and the node binding. The c++ tests are done via Google's GTest and can be triggered by going into the tests directory inside the build folder. Note though that this is currently Windows only.

The binding includes the Jest test framework and testing can be triggered after the build process via executing npm test or npx jest inside the root directory.

Troubleshooting

  • Problem: Compiling the c++ library succeeds but compiling the binding with node-gypthrows linker errors.

Solution: Check whether both, the library and the binding, are build for the same architecture (32 vs 64 bit), the same build type (Release etc.) and the same Toolset and runtime libraries. If that doesnt help, try running the node-gyp commands in sequence: node-gyp configure and node-gyp build.

  • Problem: I linked everything and every library has been found by cmake but it still throws linker errors on Linux.

Solution: Try executing ninja with root privileges

TODO:

  • Expand the README
  • Support more formats (working on jpeg 2000 and avif)
  • Expose the libweb configs api to enable finetuning of animated images

Attributions

All images used for unit testing have their source credited HERE (needs the related submodule to be installed).