/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.image.jai;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
import java.awt.image.renderable.ContextualRenderedImageFactory;
import java.awt.image.renderable.RenderedImageFactory;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.OperationRegistry;
import javax.media.jai.registry.RIFRegistry;
import javax.media.jai.registry.RenderedRegistryMode;
import org.geotools.util.logging.Logging;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.AbstractGridCoverage;
import org.geotools.resources.i18n.Loggings;
import org.geotools.resources.i18n.LoggingKeys;
/**
* A set of static methods for managing JAI's {@linkplain OperationRegistry operation registry}.
*
* @since 2.2
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public final class Registry {
/**
* Do not allows instantiation of this class.
*/
private Registry() {
}
/**
* Unconditionnaly registers all JAI operations provided in the {@link org.geotools.image.jai}
* package. This method usually don't need to be invoked, since JAI should parse automatically
* the {@code META-INF/registryFile.jai} file at startup time. However, this default mechanism
* may fail when the geotools JAR file is unreachable from the JAI class loader, in which case
* the {@link org.geotools.coverage.processing} package will invoke this method as a fallback.
* <p>
* Note to module maintainer: if this method is updated, remember to update the
* {@code META-INF/registryFile.jai} file accordingly.
*
* @param registry The operation registry to register with.
* @return {@code true} if all registrations have been successful.
*/
public static boolean registerGeotoolsServices(final OperationRegistry registry) {
LogRecord record;
String op = "org.geotools";
try {
op = CombineDescriptor.OPERATION_NAME;
registry.registerDescriptor(new CombineDescriptor());
RIFRegistry.register(registry, op, "org.geotools", new CombineCRIF());
op = HysteresisDescriptor.OPERATION_NAME;
registry.registerDescriptor(new HysteresisDescriptor());
RIFRegistry.register(registry, op, "org.geotools", new HysteresisCRIF());
op = NodataFilterDescriptor.OPERATION_NAME;
registry.registerDescriptor(new NodataFilterDescriptor());
RIFRegistry.register(registry, op, "org.geotools", new NodataFilterCRIF());
record = Loggings.format(Level.CONFIG, LoggingKeys.REGISTERED_JAI_OPERATIONS);
op = null;
} catch (IllegalArgumentException exception) {
/*
* Logs a message with the WARNING level, because DefaultProcessing class initialization
* is likely to fails (since it tries to load operations declared in META-INF/services,
* and some of them depend on JAI operations).
*/
record = Loggings.getResources(null).getLogRecord(Level.WARNING,
LoggingKeys.CANT_REGISTER_JAI_OPERATION_$1, op);
record.setThrown(exception);
}
log("registerGeotoolsServices", record);
return op == null;
}
/**
* Allows or disallow native acceleration for the specified operation on the given JAI instance.
* By default, JAI uses hardware accelerated methods when available. For example, it make use of
* MMX instructions on Intel processors. Unfortunatly, some native method crash the Java Virtual
* Machine under some circonstances. For example on JAI 1.1.2, the {@code "Affine"} operation on
* an image with float data type, bilinear interpolation and an {@link javax.media.jai.ImageLayout}
* rendering hint cause an exception in medialib native code. Disabling the native acceleration
* (i.e using the pure Java version) is a convenient workaround until Sun fix the bug.
* <p>
* <strong>Implementation note:</strong> the current implementation assumes that factories for
* native implementations are declared in the {@code com.sun.media.jai.mlib} package, while
* factories for pure java implementations are declared in the {@code com.sun.media.jai.opimage}
* package. It work for Sun's 1.1.2 implementation, but may change in future versions. If this
* method doesn't recognize the package, it does nothing.
*
* @param operation The operation name (e.g. {@code "Affine"}).
* @param allowed {@code false} to disallow native acceleration.
* @param jai The instance of {@link JAI} we are going to work on. This argument can be
* omitted for the {@linkplain JAI#getDefaultInstance default JAI instance}.
*
* @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4906854">JAI bug report 4906854</a>
*
* @since 2.5
*/
public synchronized static void setNativeAccelerationAllowed(final String operation,
final boolean allowed,
final JAI jai)
{
final String product = "com.sun.media.jai";
final OperationRegistry registry = jai.getOperationRegistry();
// TODO: Check if we can remove SuppressWarnings with a future JAI version.
@SuppressWarnings("unchecked")
final List<RenderedImageFactory> factories = registry.getOrderedFactoryList(
RenderedRegistryMode.MODE_NAME, operation, product);
if (factories != null) {
RenderedImageFactory javaFactory = null;
RenderedImageFactory nativeFactory = null;
Boolean currentState = null;
for (final RenderedImageFactory factory : factories) {
final String pack = factory.getClass().getPackage().getName();
if (pack.equals("com.sun.media.jai.mlib")) {
nativeFactory = factory;
if (javaFactory != null) {
currentState = Boolean.FALSE;
}
}
if (pack.equals("com.sun.media.jai.opimage")) {
javaFactory = factory;
if (nativeFactory != null) {
currentState = Boolean.TRUE;
}
}
}
if (currentState!=null && currentState.booleanValue()!=allowed) {
RIFRegistry.unsetPreference(registry, operation, product,
allowed ? javaFactory : nativeFactory,
allowed ? nativeFactory : javaFactory);
RIFRegistry.setPreference(registry, operation, product,
allowed ? nativeFactory : javaFactory,
allowed ? javaFactory : nativeFactory);
final LogRecord record = Loggings.format(Level.CONFIG,
LoggingKeys.NATIVE_ACCELERATION_STATE_$2,
operation, Integer.valueOf(allowed ? 1 : 0));
log("setNativeAccelerationAllowed", record);
}
}
}
/**
* Allows or disallow native acceleration for the specified operation on the
* {@linkplain JAI#getDefaultInstance default JAI instance}. This method is
* a shortcut for <code>{@linkplain #setNativeAccelerationAllowed(String,boolean,JAI)
* setNativeAccelerationAllowed}(operation, allowed, JAI.getDefaultInstance())</code>.
*
* @see #setNativeAccelerationAllowed(String, boolean, JAI)
*/
public static void setNativeAccelerationAllowed(final String operation, final boolean allowed) {
setNativeAccelerationAllowed(operation, allowed, JAI.getDefaultInstance());
}
/**
* Register the "SampleTranscode" image operation to the operation registry
* of the specified JAI instance. This method is invoked by the static
* initializer of {@link GridSampleDimension}.
* @param jai is he {@link JAI} instance in which we ant to register this operation.
* @param descriptor is the {@link OperationDescriptor} for the JAI operation to register.
* @param name is the name of the operation to register.
* @param crif is the rendered image facotry for this operation.
* @return <code>true</code> if everything goes well, <code>false</code> otherwise.
*/
public static boolean registerRIF(
final JAI jai,
final OperationDescriptor descriptor,
final String name,
final ContextualRenderedImageFactory crif) {
final OperationRegistry registry = jai.getOperationRegistry();
try {
registry.registerDescriptor(descriptor);
registry.registerFactory(RenderedRegistryMode.MODE_NAME,
name, "geotools.org",crif);
return true;
} catch (IllegalArgumentException exception) {
final LogRecord record = Loggings.format(Level.SEVERE,
LoggingKeys.CANT_REGISTER_JAI_OPERATION_$1, name);
// Note: GridSampleDimension is the public class that use this
// transcoder.
record.setSourceClassName(GridSampleDimension.class.getName());
record.setSourceMethodName("<classinit>");
record.setThrown(exception);
record.setLoggerName(AbstractGridCoverage.LOGGER.getName());
AbstractGridCoverage.LOGGER.log(record);
}
return false;
}
/**
* Logs the specified record.
*/
private static void log(final String method, final LogRecord record) {
record.setSourceClassName(Registry.class.getName());
record.setSourceMethodName(method);
final Logger logger = Logging.getLogger(Registry.class);
record.setLoggerName(logger.getName());
logger.log(record);
}
}