/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.image;
import com.twelvemonkeys.lang.
SystemUtil;
import magick.
MagickImage;
import java.awt.image.
BufferedImage;
import java.awt.image.
BufferedImageOp;
/**
* This class accelerates certain graphics operations, using
* JMagick and ImageMagick, if available.
* If those libraries are not installed, this class silently does nothing.
* <p/>
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
* {@code false}, to disable, even if JMagick is installed.
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
*/
final class
MagickAccelerator {
private static final boolean
DEBUG =
Magick.
DEBUG;
private static final boolean
USE_MAGICK =
useMagick();
private static final int
RESAMPLE_OP = 0;
private static
Class[]
nativeOp = new
Class[1];
static {
try {
nativeOp[
RESAMPLE_OP] =
Class.
forName("com.twelvemonkeys.image.ResampleOp");
}
catch (
ClassNotFoundException e) {
System.
err.
println("Could not find class: " +
e);
}
}
private static boolean
useMagick() {
try {
boolean
available =
SystemUtil.
isClassAvailable("magick.MagickImage");
if (
DEBUG && !
available) {
System.
err.
print("ImageMagick bindings not available.");
}
boolean
useMagick =
available && !"FALSE".
equalsIgnoreCase(
System.
getProperty("com.twelvemonkeys.image.accel"));
if (
DEBUG) {
System.
err.
println(
useMagick
? "Will use ImageMagick bindings to accelerate image resampling operations."
: "Will not use ImageMagick to accelerate image resampling operations."
);
}
return
useMagick;
}
catch (
Throwable t) {
// Most probably in case of a SecurityManager
System.
err.
println("Could not enable ImageMagick bindings: " +
t);
return false;
}
}
private static int
getNativeOpIndex(
Class pOpClass) {
for (int
i = 0;
i <
nativeOp.length;
i++) {
if (
pOpClass ==
nativeOp[
i]) {
return
i;
}
}
return -1;
}
public static
BufferedImage filter(
BufferedImageOp pOperation,
BufferedImage pInput,
BufferedImage pOutput) {
if (!
USE_MAGICK) {
return null;
}
BufferedImage result = null;
switch (
getNativeOpIndex(
pOperation.
getClass())) {
case
RESAMPLE_OP:
ResampleOp resample = (
ResampleOp)
pOperation;
result =
resampleMagick(
pInput,
resample.
width,
resample.
height,
resample.
filterType);
// NOTE: If output parameter is non-null, we have to return that
// image, instead of result
if (
pOutput != null) {
//pOutput.setData(result.getRaster()); // Fast, but less compatible
// NOTE: For some reason, this is sometimes super-slow...?
ImageUtil.
drawOnto(
pOutput,
result);
result =
pOutput;
}
break;
default:
// Simply fall through, allowing acceleration to be added later
break;
}
return
result;
}
private static
BufferedImage resampleMagick(
BufferedImage pSrc, int
pWidth, int
pHeight, int
pFilterType) {
// Convert to Magick, scale and convert back
MagickImage image = null;
MagickImage scaled = null;
try {
image =
MagickUtil.
toMagick(
pSrc);
long
start = 0;
if (
DEBUG) {
start =
System.
currentTimeMillis();
}
// NOTE: setFilter affects zoomImage, NOT scaleImage
image.
setFilter(
pFilterType);
scaled =
image.
zoomImage(
pWidth,
pHeight);
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
if (
DEBUG) {
long
time =
System.
currentTimeMillis() -
start;
System.
out.
println("Filtered: " +
time + " ms");
}
return
MagickUtil.
toBuffered(
scaled);
}
//catch (MagickException e) {
catch (
Exception e) {
// NOTE: Stupid workaround: If MagickException is caught, a
// NoClassDefFoundError is thrown, when MagickException class is
// unavailable...
if (
e instanceof
RuntimeException) {
throw (
RuntimeException)
e;
}
throw new
ImageConversionException(
e.
getMessage(),
e);
}
finally {
// NOTE: ImageMagick might be unstable after a while, if image data
// is not deallocated. The GC/finalize method handles this, but in
// special circumstances, it's not triggered often enough.
if (
image != null) {
image.
destroyImages();
}
if (
scaled != null) {
scaled.
destroyImages();
}
}
}
}