Kotlin bindings for ImageMagick; uses the ImageMagick wand API.
Note: This project is on hold indefinitely, and is unlikely to receive updates or fixes.
Windows and Android*
* Others may work too, but I have not tested Mac or Linux.
All downloads are in the releases section.
A full example of the below setup can be found here
- Grab the jar and sources jar.
- Add this line to your dependencies:
implementation fileTree(dir: 'libs', include: ['*.jar'])
- Place the jars in the
app/libs
folder. - Place the Android ImageMagick shared library
so
files in yourapp/src/main/jniLibs
folder along with the Androidkmagick.so
library. - Either download objenesis jar and place it in the libs folder, OR add this line to your dependencies:
implementation 'org.objenesis:objenesis:3.2'
Debug messages can be found in Android logcat under the id MAGICK
. Make sure you first set the appropriate LogLevel
to see them.
* I plan to add a Maven config sometime, but I've been too busy and tired.
(The Android ImageMagick library can be found here)
- Grab the
kmagick.dll
file along with the jar and sources jar. - Install Windows ImageMagick (dll version) and make sure the program files folder is in your
PATH
. - Setup your project to use the jar as normal.
- Make sure
kmagick.dll
is in your path as well. - Either download objenesis jar and place it along with your other jars, OR add this line (or similar depending on your build system) to your dependencies:
implementation 'org.objenesis:objenesis:3.2'
You can browse the latest docs here
As this is a low level library, crashes are not impossible. I've made every effort to make that impossible however.
If there is a low-level error/crash, this library will catch it and throw a java runtime exception instead of fatally crashing the JVM (which is what would normally happen if it was C!). Additionally, nearly all the functions in here, if they encounter a problem, will throw a related exception type (e.g. PixelWandException
, MagickWandException
, etc) along with a helpful human readable message. You should always check for these just in case something makes your code fail.
If there is a panic
(a crash
in Rust terms), please submit an issue along with reproducible steps so I can fix it. Unfortunately due to limitations the message might be a little cryptic, but provide as much info as you can (along with reproducible steps).
I have no control if the C code segfaults your project. Sorry. If there is one, that could be an ImageMagick bug and you should report it to them. However, make sure to report it here too just in case it's a bug in kmagick.
First of all, check out the official ImageMagick function reference. If you have any confusion/questions, it'll be answered there. Also, the sources jar contains comments for every function which should be good enough in most cases.
Note: The majority of API functions throw exceptions if they fail. The ones used are java/lang/RuntimeException
and a related com/MolotovCherry/kmagick/*Exception
(check the function for which exception it may return). It is strongly recommended you handle all exceptions if you don't want your program/app to crash! I realize this is Kotlin and exceptions aren't fun to handle, but because this is a low level library, many things can go wrong! Please see the Exception section below to find out how to get Exception details so you can know why it's happening to you.
There's an example under the example
directory as well.
The example shows all the different kmagick features to be aware of (which aren't all covered below).
You can use the included run-example.bat
to build and run the example (note: default example is a tutorial and won't run without editing first).
Remember that you need kmagick.dll
in your PATH
(or same cwd) for it to work
// Basic usage
// You MUST call Magick.initialize() before you can use the library.
Magick.initialize()
// You can also use this with a `use` block to automatically terminate at the end
Magick.initialize().use {
// do your stuff
}
// when you're done, you should call terminate. The `use` block above does that automatically for you.
// Note: When you call this, ALL your wands will immediately be invalidated at the C level.
// DO NOT attempt to use them after or you'll get an exception.
Magick.terminate()
Magick.initialize().use {
val a = PixelWand()
a.color = "blue"
val b = MagickWand()
b.newImage(100, 200, a)
// if you so desire, you can also destroy your wand in advance
// just don't attempt to use it afterwards.
// java might not guarantee the destructor will be called on finalize(),
// so you might have to call this yourself to keep memory sane
a.destroy()
// wands also can use the `use` blocks if needed
// just remember it'll be destroyed at the end of the block!
b.use {
it.readImage("/some/path/file.png")
}
// Any wand method may throw an exception, so make sure to handle them
try {
b.getImageHistogram()
} catch (e: MagickWandException) {
println("exception: ${e.message}")
}
} // all wands auto destroyed here due to `Magick.terminate()`
If you encounter an exception and need to know what happened, all Wand
classes define 3 functions to help you out:
clearException()
getExceptionType()
getException()
You can cross reference the ExceptionType
in the source code to see what exact error it was. getException()
also returns NativeMagickException
which will give you both an ExceptionType
and a message explaining what happened.
For more examples and information on usage, please browse the API in your IDE or check ImageMagick's website.
If you encountered a problem, please first check if you can find out what it is by running getException()
and checking the details.
If that gives you no solution, then please check whether it's merely your usage of the api. Some things may appear to be bugs, but rather are in fact not bugs; just merely it's how imagemagick works, and you need to follow the imagemagick api. Kmagick is only a direct binding to the imagemagick c api and does not alter your images in any way. So make sure to check and understand the imagemagick c api so you can do things the way imagemagick expects you to
If this doesn't help and it's a real bug, then please check your logcat
for your device and include the error log in the issue you make.
If you have a question or comment that isn't suited for an issue report, please use Discussions instead
We're using magick-rust
for our bindings. So, if some API that you need is missing, please make a feature request or send a PR to magick-rust and it'll make its way downstream to me.
Contributions are welcome! If you have an improvement, please send a PR or make an issue about it and I'll see what we can do. 😉 If you know Rust, contributions are even more welcome, especially to magick-rust (because it'll make the functions available downstream for me).
If you want to contribute Rust code to this project, check out the rust folder which has directions on how to compile it locally.
In the Rust
directory, the two crates jni-macros
and jni-tools
offers some FULLY working macros which generate JNI bindings for Rust functions. Of course, it's only Kotlin compatible (no Java; although you could edit the handle fn's to fix that). You can even use regular impl's which KEEP STATE between calls as if it was a real class instance! The jni-tools
crate offers the visible public API for it. There's also docs on it to explain its usage, however, if you want to fully know how to use it, you should take a look at my Rust and Kotlin code as the prime example.
If you found this library useful, please consider showing appreciation and help fund it by sending a donation my way.
All donations help this project continue to be supported for longer and receive more frequent updates! Thanks for your support! <3