Awesome
Erlang Image Manipulation Process
eimp
is an Erlang/Elixir application for manipulating graphic images using
external C libraries. It supports WebP, JPEG, PNG and GIF.
Requirements
- GNU Make
- GCC
- Erlang/OTP 17 and higher
- libgd
- libwebp
- libpng
- libjpeg
NOTE: It's hard to say which versions of the C libraries are required, but it seems like not too old versions should work well.
Install
$ ./configure
$ make
Note that running the configure script is highly recommended, so you should consider adding it in pre-hooks of your rebar configuration.
If no C libraries are found at compile time, the package will still be compiled. In this case the only usable function would be get_type/1.
Application design
The C code is compiled into external native binary called eimp
, which is
connected to Erlang VM using an external port. This is done because used C libraries
are known not to be extremely stable, thus, if you wrap them into the emulator
using a driver or NIF, they can crash the whole emulator.
When being loaded, the application starts a single eimp
process per CPU core
and uses a round-robin pool to schedule tasks to them. Simple recovery mechanisms
are also supported:
- if a request to
eimp
process has failed, nexteimp
process in the pool is picked, until the pool is exhausted - if an
eimp
process is dead, it will be restarted automatically - an
eimp
process is protected against decompression bombs
Usage
Before using the application you should start it with either eimp:start()
or
application:start(eimp)
.
API
Current API is simple and supports only a few functions:
convert/2
-spec convert(In :: binary(), Format :: png|jpeg|webp|gif) -> {ok, Out :: binary()} |
{error, Reason :: error_reason()}.
Shorthand for convert(In, Format, [])
.
convert/3
-spec convert(In :: binary(), Format :: png|jpeg|webp|gif,
Options :: [convert_opt() | limit_opt()]) ->
{ok, Out :: binary()} |
{error, Reason :: error_reason()}.
The function converts incoming data In
into format Format
. Note that you don't
have to pass the format of incoming data, becasue it will be detected automatically
using get_type/1
function. In the case of an error you can use Reason
to produce
a human-readable diagnostic text using format_error/1
.
The function also accepts a proplist of Options
. Currently available options are:
{scale, {Width, Height}}
: scales image to the newWidth
andHeight
. No scaling is applied by default.{rate_limit, N}
: limit the number of calls toN
per minute, whereN > 0
. Must be used only in conjunction withlimit_by
.{limit_by, Term}
: applyrate_limit
(see above) to the entity associtated withTerm
. TheTerm
may represent any value, such as an IP address, a username and so on. TheTerm
must not be atomundefined
. For example a call toconvert(Data, Format, [{limit_by, {192,168,0,1}}, {rate_limit, 10}])
will fail with{error, too_man_requests}
if called more than 10 times within a minute.
WARNING: the maximum resolution of an incoming image is hardcoded to be 25Mpx. This is a protection against decompression bombs.
identify/1
-spec identify(Img :: binary()) -> {ok, Info :: info()} | {error, error_reason()}.
Shorthand for identify(Img, [])
.
identify/2
-spec identify(Img :: binary(), LimitOptions :: [limit_opt()]) ->
{ok, Info :: info()} | {error, error_reason()}.
The function returns information about image Img
, where Info
is represented as:
[{type, Type :: img_type()},
{width, Width :: non_neg_integer()},
{height, Height :: non_neg_integer()}]
It is safe to assume that Info
always contains all these properties.
You can set limiting options in LimitOptions
, that is rate_limit
and limit_by
.
The meaning of the limiting options is the same as in convert/3
.
NOTE: If you only need to get a type of an image, you're better off using
get_type/1
function, because it doesn't involve interaction with eimp
process
and is, thus, much faster.
format_error/1
-spec format_error(Reason :: error_reason()) -> binary().
Creates diagnostic text from an error generated by convert/2
.
The Reason
can have the following values:
-type error_reason() :: unsupported_format |
timeout |
disconnected |
encode_failure |
decode_failure |
transform_failure |
too_many_requests |
image_too_big.
get_type/1
-spec get_type(Data :: binary()) -> png | jpeg | webp | gif | unknown.
Detects image format of Data
.
is_supported/1
-spec is_supported(Format :: atom()) -> boolean.
Returns true
if Format
is known and compiled and false
otherwise.
supported_formats/0
-spec supported_formats() -> [png | jpeg | webp | gif].
Returns a list of all known and compiled formats.