Scaling images, in any language, is a memory and CPU intensive operation. In highly-scalable/threaded environments, firing off MANY scale operations at the same time (e.g. a 100 users upload images that need thumbnails generated at the same time to your web app) can result in unexpected failures with a particular sub-set of the image operations as the VM will eventually run out of memory to hold all that decoded image data in ram at the same time.
imgscalr shines on the server and to make it's use easier/safer, consider creating a ScalrService that can control for this.
Based on the research from Issue #27 (#27) and what I assume will end up being fairly bad behavior once the heap is blown, the addition of a Singleton, ThreadPoolExecutor-based ScalrService that can easily be used in heavily mulit-threaded environments to more easily trim down the number of simultaneous scale operations to avoid blowing the VM heap
In a heavily multi-threaded app, it might be easier than expected to initiate so many simultaneous scale operations at the same time, that all that image data is decided and pulled into the VM simultaneously, possibly leading to the VM heap size running out of room to grow and some of the scaling operations failing while others would succeed.
The purpose of the service is to create a simple, user-configurable, intentional "choke point" to control a worst-case scenario ceiling.
For example, for a webapp that processes image uploads, if your average image upload is (say) 1MB in size, and your heap is 64MB... you may want to set the ScalrService to never do more than 2 simultaneous scale operations as fully decoded image data is typically much larger than the compressed source files. By limiting simultaneous scale ops in your app to 2 at a time, you don't need any more complex code in your app to control this.
If you have a monster dedicated image-scaling server with 72GB of ram and some hellacious dual-CPU, quad-core, HT-enabled CPU setup, you could up the ScalrService to do 100s or 1000s of scale operations simultaneously, but in your code you wouldn't need to change anything, simply call ScalrService.resize... and it will do the right thing for you.
This design also has the added advantage of taking advantage of multi-core/multi-threaded CPUs for scaling operations as the threads in the thread pool can be scheduled onto different cores/threads by the OS as they are executed.
Another advantage is the Threads are not created/destroyed every time a call is made; they would be created at service initialization time, or lazily created at first-use, but otherwise will stay around until the app is shut down.
OPTIONAL: An asynchronous approach may be considered with the core design, returning a Future object; if the caller wants an synchronous execution pattern, they can just immediately call future.getValue which will block anyway.
To keep the API simple/consistent, the async design may be the approach; it is not worth polluting the API to double up the methods and have sync ones and async ones all on the same class; ugggg