From 293746c72a3686870c8efc696ae17350849d42f3 Mon Sep 17 00:00:00 2001
From: Alex Dolski Attempts to reads an image as efficiently as possible, utilizing its
- * tile layout and/or sub-images, if possible.
After reading, clients should check the reader hints to see whether * the returned image will require cropping.
* - * @param imageFile Image file to read. - * @param sourceFormat + * @param imageFile Image file to read. + * @param sourceFormat Format of the source image. * @param ops - * @param fullSize Full size of the source image. + * @param fullSize Full size of the source image. * @param reductionFactor {@link ReductionFactor#factor} property will be * modified to reflect the reduction factor of the * returned image. - * @param hints Will be populated by information returned by the reader. - * @return RGB BufferedImage best matching the given parameters. Clients - * should check the hints set to see whether they need to perform + * @param hints Will be populated by information returned by the reader. + * @return BufferedImage best matching the given parameters, guaranteed to + * not be of {@link BufferedImage#TYPE_CUSTOM}. Clients should + * check the hints set to see whether they need to perform * additional cropping. * @throws IOException * @throws ProcessorException @@ -87,22 +112,22 @@ public BufferedImage read(final File imageFile, } /** - * @see #read(File, SourceFormat, OperationList, Dimension, - * ReductionFactor, Set< ReaderHint >) - * * @param readableChannel Image channel to read. - * @param sourceFormat + * @param sourceFormat Format of the source image. * @param ops - * @param fullSize Full size of the source image. + * @param fullSize Full size of the source image. * @param reductionFactor {@link ReductionFactor#factor} property will be * modified to reflect the reduction factor of the * returned image. - * @param hints Will be populated by information returned by the reader. - * @return RGB BufferedImage best matching the given parameters. Clients - * should check the hints set to see whether they need to perform + * @param hints Will be populated by information returned by the reader. + * @return BufferedImage best matching the given parameters, guaranteed to + * not be of {@link BufferedImage#TYPE_CUSTOM}. Clients should + * check the hints set to see whether they need to perform * additional cropping. * @throws IOException * @throws ProcessorException + * @see #read(File, SourceFormat, OperationList, Dimension, + * ReductionFactor, Set< ReaderHint >) */ public BufferedImage read(final ReadableByteChannel readableChannel, final SourceFormat sourceFormat, @@ -116,21 +141,20 @@ public BufferedImage read(final ReadableByteChannel readableChannel, } /** - * @see #read(File, SourceFormat, OperationList, Dimension, - * ReductionFactor, Set< ReaderHint >) - * - * @param inputSource {@link ReadableByteChannel} or {@link File} - * @param sourceFormat + * @param inputSource {@link ReadableByteChannel} or {@link File} + * @param sourceFormat Format of the source image. * @param ops - * @param fullSize Full size of the source image. - * @param rf {@link ReductionFactor#factor} property will be modified to - * reflect the reduction factor of the returned image. - * @param hints Will be populated by information returned by the reader. - * @return RGB BufferedImage best matching the given parameters. Clients + * @param fullSize Full size of the source image. + * @param rf {@link ReductionFactor#factor} property will be modified to + * reflect the reduction factor of the returned image. + * @param hints Will be populated by information returned by the reader. + * @return BufferedImage best matching the given parameters. Clients * should check the hints set to see whether they need to perform * additional cropping. * @throws IOException * @throws ProcessorException + * @see #read(File, SourceFormat, OperationList, Dimension, + * ReductionFactor, Set< ReaderHint >) */ private BufferedImage multiLevelAwareRead(final Object inputSource, final SourceFormat sourceFormat, @@ -139,65 +163,45 @@ private BufferedImage multiLevelAwareRead(final Object inputSource, final ReductionFactor rf, final SetReturns an image for the requested source area by reading the tiles - * of the source image and joining them into a single image.
- * + * (or strips) of the source image and joining them into a single image. + * + *This method is intended to be compatible with all source images, no + * matter the data layout (tiled or not). For some image types, including + * GIF and PNG, a tiled reading strategy will not work, and so this method + * will read the entire image.
+ * *This method may populate hints
with
* {@link ReaderHint#ALREADY_CROPPED}, in which case cropping will have
* already been performed according to the
- * requestedSourceArea
parameter and no further cropping will
- * be necessary (assuming it would have covered the same area).
requestedSourceArea
parameter.
*
- * @param reader ImageReader with input source already set
- * @param imageIndex Index of the image to read from the ImageReader.
+ * @param reader ImageReader with input source already set
+ * @param imageIndex Index of the image to read from the ImageReader.
* @param requestedSourceArea Source image area to retrieve. The returned
* image will be this size or smaller if it
* would overlap the right or bottom edge of the
* source image.
- * @param hints Will be populated by information returned by the reader.
+ * @param hints Will be populated by information returned by the reader.
* @return Cropped image
* @throws IOException
* @throws IllegalArgumentException If the source image is not tiled.
@@ -342,11 +351,11 @@ private BufferedImage tileAwareRead(final ImageReader reader,
final SetImplementations should use the sourceSize parameter and not their - * own {#link #getSize} method to avoid reusing a potentially unreusable - * InputStream.
+ *Implementations should get the full size of the source image from + * the sourceSize parameter instead of their {#link #getSize} method, + * for efficiency.
* * @param ops OperationList of the image to process. * @param sourceFormat Format of the source image. Will never be * {@link SourceFormat#UNKNOWN}. * @param sourceSize Scale of the source image. - * @param readableChannel Stream from which to read the image. - * Implementations should not close it. + * @param channelSource Source for acquiring channels from which to read + * the imagee. * @param writableChannel Writable channel to write the image to. * Implementations should not close it. * @throws UnsupportedOutputFormatException @@ -49,7 +51,7 @@ Dimension getSize(ReadableByteChannel readableChannel, * @throws ProcessorException */ void process(OperationList ops, SourceFormat sourceFormat, - Dimension sourceSize, ReadableByteChannel readableChannel, + Dimension sourceSize, ChannelSource channelSource, WritableByteChannel writableChannel) throws ProcessorException; } diff --git a/src/main/java/edu/illinois/library/cantaloupe/processor/GraphicsMagickProcessor.java b/src/main/java/edu/illinois/library/cantaloupe/processor/GraphicsMagickProcessor.java index 00fa6e9b8..2f63b31a9 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/processor/GraphicsMagickProcessor.java +++ b/src/main/java/edu/illinois/library/cantaloupe/processor/GraphicsMagickProcessor.java @@ -10,6 +10,7 @@ import edu.illinois.library.cantaloupe.image.SourceFormat; import edu.illinois.library.cantaloupe.image.OutputFormat; import edu.illinois.library.cantaloupe.image.Transpose; +import edu.illinois.library.cantaloupe.resolver.ChannelSource; import edu.illinois.library.cantaloupe.resource.iiif.ProcessorFeature; import org.apache.commons.configuration.Configuration; import org.im4java.core.ConvertCmd; @@ -167,17 +168,28 @@ public SetAttempts to reads an image as efficiently as possible, utilizing its + * tile layout and/or subimages, if possible.
+ * + * @param imageFile Image file to read. + * @param sourceFormat Format of the source image. + * @param ops + * @param reductionFactor {@link ReductionFactor#factor} property will be + * modified to reflect the reduction factor of the + * returned image. + * @return RenderedImage best matching the given parameters. + * @throws IOException + * @throws ProcessorException + */ + public RenderedImage read(final File imageFile, + final SourceFormat sourceFormat, + final OperationList ops, + final ReductionFactor reductionFactor) + throws IOException, ProcessorException { + return multiLevelAwareRead(imageFile, sourceFormat, ops, + reductionFactor); + } + + /** + * @see #read(File, SourceFormat, OperationList, ReductionFactor) + * + * @param channelSource Source of image channels to read + * @param sourceFormat Format of the source image + * @param ops + * @param reductionFactor {@link ReductionFactor#factor} property will be + * modified to reflect the reduction factor of the + * returned image. + * @return BufferedImage best matching the given parameters. + * @throws IOException + * @throws ProcessorException + * @see #read(File, SourceFormat, OperationList, ReductionFactor) + */ + public RenderedImage read(final ChannelSource channelSource, + final SourceFormat sourceFormat, + final OperationList ops, + final ReductionFactor reductionFactor) + throws IOException, ProcessorException { + return multiLevelAwareRead(channelSource, sourceFormat, + ops, reductionFactor); + } + + /** + * @param inputSource {@link ChannelSource} or {@link File} + * @param sourceFormat Format of the source image. + * @param ops + * @param rf {@link ReductionFactor#factor} property will be modified to + * reflect the reduction factor of the returned image. + * @return BufferedImage best matching the given parameters. + * @throws IOException + * @throws ProcessorException + * @see #read(File, SourceFormat, OperationList, ReductionFactor) + */ + private RenderedImage multiLevelAwareRead(final Object inputSource, + final SourceFormat sourceFormat, + final OperationList ops, + final ReductionFactor rf) + throws IOException, ProcessorException { + final ImageReader reader = newImageReader(inputSource, sourceFormat); + RenderedImage image = null; + try { + switch (sourceFormat) { + case TIF: + Crop crop = new Crop(); + crop.setFull(true); + Scale scale = new Scale(); + scale.setMode(Scale.Mode.FULL); + for (Operation op : ops) { + if (op instanceof Crop) { + crop = (Crop) op; + } else if (op instanceof Scale) { + scale = (Scale) op; + } + } + image = readSmallestUsableSubimage(reader, crop, scale, rf); + break; + // This is similar to the TIF case, except it doesn't scan for + // subimages, which is costly to do. + default: + crop = null; + for (Operation op : ops) { + if (op instanceof Crop) { + crop = (Crop) op; + break; + } + } + if (crop != null) { + image = reader.readAsRenderedImage(0, + reader.getDefaultReadParam()); + } else { + image = reader.read(0); + } + break; + } + } finally { + reader.dispose(); + } + if (image == null) { + throw new UnsupportedSourceFormatException(sourceFormat); + } + return image; + } + + /** + * Reads the smallest image that can fulfill the given crop and scale from + * a multi-resolution image. + * + * @param reader ImageReader with input source already set + * @param crop Requested crop + * @param scale Requested scale + * @param rf {@link ReductionFactor#factor} will be set to the reduction + * factor of the returned image. + * @return The smallest image fitting the requested crop and scale + * operations from the given reader. + * @throws IOException + */ + private RenderedImage readSmallestUsableSubimage(final ImageReader reader, + final Crop crop, + final Scale scale, + final ReductionFactor rf) + throws IOException { + final Dimension fullSize = new Dimension( + reader.getWidth(0), reader.getHeight(0)); + final Rectangle regionRect = crop.getRectangle(fullSize); + final ImageReadParam param = reader.getDefaultReadParam(); + RenderedImage bestImage = null; + if (scale.isNoOp()) { + bestImage = reader.readAsRenderedImage(0, param); + logger.debug("Using a {}x{} source image (0x reduction factor)", + bestImage.getWidth(), bestImage.getHeight()); + } else { + // Pyramidal TIFFs will have > 1 image, each half the dimensions of + // the next larger. The "true" parameter tells getNumImages() to + // scan for images, which seems to be necessary for at least some + // files, but is slower. + int numImages = reader.getNumImages(false); + if (numImages > 1) { + logger.debug("Detected {} subimage(s)", numImages - 1); + } else if (numImages == -1) { + numImages = reader.getNumImages(true); + if (numImages > 1) { + logger.debug("Scan revealed {} subimage(s)", numImages - 1); + } + } + if (numImages == 1) { + bestImage = reader.read(0, param); + logger.debug("Using a {}x{} source image (0x reduction factor)", + bestImage.getWidth(), bestImage.getHeight()); + } else if (numImages > 1) { + // Loop through the reduced images from smallest to largest to + // find the first one that can supply the requested scale + for (int i = numImages - 1; i >= 0; i--) { + final int reducedWidth = reader.getWidth(i); + final int reducedHeight = reader.getHeight(i); + + final double reducedScale = (double) reducedWidth / + (double) fullSize.width; + boolean fits = false; + if (scale.getMode() == Scale.Mode.ASPECT_FIT_WIDTH) { + fits = (scale.getWidth() / (float) regionRect.width <= reducedScale); + } else if (scale.getMode() == Scale.Mode.ASPECT_FIT_HEIGHT) { + fits = (scale.getHeight() / (float) regionRect.height <= reducedScale); + } else if (scale.getMode() == Scale.Mode.ASPECT_FIT_INSIDE) { + fits = (scale.getWidth() / (float) regionRect.width <= reducedScale && + scale.getHeight() / (float) regionRect.height <= reducedScale); + } else if (scale.getMode() == Scale.Mode.NON_ASPECT_FILL) { + fits = (scale.getWidth() / (float) regionRect.width <= reducedScale && + scale.getHeight() / (float) regionRect.height <= reducedScale); + } else if (scale.getPercent() != null) { + float pct = scale.getPercent(); + fits = ((pct * fullSize.width) / (float) regionRect.width <= reducedScale && + (pct * fullSize.height) / (float) regionRect.height <= reducedScale); + } + if (fits) { + rf.factor = ProcessorUtil. + getReductionFactor(reducedScale, 0).factor; + logger.debug("Using a {}x{} source image ({}x reduction factor)", + reducedWidth, reducedHeight, rf.factor); + bestImage = reader.readAsRenderedImage(i, param); + break; + } + } + } + } + return bestImage; + } + } diff --git a/src/main/java/edu/illinois/library/cantaloupe/processor/JaiProcessor.java b/src/main/java/edu/illinois/library/cantaloupe/processor/JaiProcessor.java index 4a12eb3e9..5e7fbf311 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/processor/JaiProcessor.java +++ b/src/main/java/edu/illinois/library/cantaloupe/processor/JaiProcessor.java @@ -31,8 +31,6 @@ */ class JaiProcessor implements FileProcessor, ChannelProcessor { - // TODO: this should be used in conjunction with tile offset to match the crop region - private static final int JAI_TILE_SIZE = 512; private static final SetinputSource
is invalid
- */
- public static RenderedImage readImageWithTiffImageDecoder(
- Object inputSource, OperationList ops, Dimension fullSize,
- ReductionFactor reductionFactor) throws IOException {
- RenderedImage image = null;
- try {
- ImageDecoder dec;
- if (inputSource instanceof ReadableByteChannel) {
- dec = ImageCodec.createImageDecoder("tiff",
- Channels.newInputStream((ReadableByteChannel) inputSource), null);
- } else if (inputSource instanceof File) {
- dec = ImageCodec.createImageDecoder("tiff",
- (File) inputSource, null);
- } else {
- throw new IllegalArgumentException("Invalid inputSource parameter");
- }
- if (dec != null) {
- Crop crop = new Crop();
- crop.setFull(true);
- Scale scale = new Scale();
- scale.setMode(Scale.Mode.FULL);
- for (Operation op : ops) {
- if (op instanceof Crop) {
- crop = (Crop) op;
- } else if (op instanceof Scale) {
- scale = (Scale) op;
- }
- }
- image = getSmallestUsableImage(dec, fullSize, crop, scale,
- reductionFactor);
- }
- } finally {
- if (inputSource instanceof InputStream) {
- ((InputStream) inputSource).close();
- }
- }
- return image;
- }
-
- /**
- * Returns the smallest image fitting the requested size from the given
- * reader. Useful for e.g. pyramidal TIFF.
- *
- * @param decoder ImageDecoder with input source already set
- * @param fullSize
- * @param crop Requested crop
- * @param scale Requested scale
- * @param rf Set by reference
- * @return
- * @throws IOException
- */
- private static RenderedImage getSmallestUsableImage(ImageDecoder decoder,
- Dimension fullSize,
- Crop crop,
- Scale scale,
- ReductionFactor rf)
- throws IOException {
- RenderedImage bestImage = null;
- if (!scale.isNoOp()) {
- // Pyramidal TIFFs will have > 1 "page," each half the dimensions of
- // the next larger.
- int numImages = decoder.getNumPages();
- if (numImages > 1) {
- logger.debug("Detected multi-resolution image with {} levels",
- numImages);
- final Rectangle regionRect = crop.getRectangle(fullSize);
-
- // Loop through the tiles from smallest to largest to find the
- // first one that fits the requested scale
- for (int i = numImages - 1; i >= 0; i--) {
- final RenderedImage tile = decoder.decodeAsRenderedImage(i);
- final double tileScale = (double) tile.getWidth() /
- (double) fullSize.width;
- boolean fits = false;
- if (scale.getMode() == Scale.Mode.ASPECT_FIT_WIDTH) {
- fits = (scale.getWidth() / (float) regionRect.width <= tileScale);
- } else if (scale.getMode() == Scale.Mode.ASPECT_FIT_HEIGHT) {
- fits = (scale.getHeight() / (float) regionRect.height <= tileScale);
- } else if (scale.getMode() == Scale.Mode.ASPECT_FIT_INSIDE) {
- fits = (scale.getWidth() / (float) regionRect.width <= tileScale &&
- scale.getHeight() / (float) regionRect.height <= tileScale);
- } else if (scale.getMode() == Scale.Mode.NON_ASPECT_FILL) {
- fits = (scale.getWidth() / (float) regionRect.width <= tileScale &&
- scale.getHeight() / (float) regionRect.height <= tileScale);
- } else if (scale.getPercent() != null) {
- float pct = scale.getPercent();
- fits = ((pct * fullSize.width) / (float) regionRect.width <= tileScale &&
- (pct * fullSize.height) / (float) regionRect.height <= tileScale);
- }
- if (fits) {
- rf.factor = ProcessorUtil.
- getReductionFactor(tileScale, 0).factor;
- logger.debug("Using a {}x{} source tile ({}x reduction factor)",
- tile.getWidth(), tile.getHeight(), rf.factor);
- bestImage = tile;
- break;
- }
- }
- }
- }
- if (bestImage == null) {
- bestImage = decoder.decodeAsRenderedImage();
- }
- return bestImage;
- }
-
/**
* @param inImage Image to reformat
* @param tileSize JAI tile size
diff --git a/src/main/java/edu/illinois/library/cantaloupe/processor/KakaduProcessor.java b/src/main/java/edu/illinois/library/cantaloupe/processor/KakaduProcessor.java
index 6c45b5f3e..8d97212f5 100644
--- a/src/main/java/edu/illinois/library/cantaloupe/processor/KakaduProcessor.java
+++ b/src/main/java/edu/illinois/library/cantaloupe/processor/KakaduProcessor.java
@@ -466,8 +466,8 @@ private void postProcessUsingJai(final ReadableByteChannel readableChannel,
final ReductionFactor reductionFactor,
final WritableByteChannel writableChannel)
throws IOException, ProcessorException {
- RenderedImage renderedImage =
- JaiUtil.readImage(readableChannel);
+ RenderedImage renderedImage = new ImageIoImageReader().
+ readRendered(readableChannel, SourceFormat.BMP);
RenderedOp renderedOp = JaiUtil.reformatImage(
RenderedOp.wrapRenderedImage(renderedImage),
new Dimension(512, 512));
diff --git a/src/main/java/edu/illinois/library/cantaloupe/processor/OpenJpegProcessor.java b/src/main/java/edu/illinois/library/cantaloupe/processor/OpenJpegProcessor.java
index 8a97afd2e..ce9f51419 100644
--- a/src/main/java/edu/illinois/library/cantaloupe/processor/OpenJpegProcessor.java
+++ b/src/main/java/edu/illinois/library/cantaloupe/processor/OpenJpegProcessor.java
@@ -433,8 +433,8 @@ private void postProcessUsingJai(final ReadableByteChannel readableChannel,
final ReductionFactor reductionFactor,
final WritableByteChannel writableChannel)
throws IOException, ProcessorException {
- RenderedImage renderedImage =
- JaiUtil.readImage(readableChannel);
+ RenderedImage renderedImage = new ImageIoImageReader().
+ readRendered(readableChannel, SourceFormat.BMP);
RenderedOp renderedOp = JaiUtil.reformatImage(
RenderedOp.wrapRenderedImage(renderedImage),
new Dimension(512, 512));
diff --git a/src/test/java/edu/illinois/library/cantaloupe/processor/JaiUtilTest.java b/src/test/java/edu/illinois/library/cantaloupe/processor/JaiUtilTest.java
index 3da192706..5af365fb3 100644
--- a/src/test/java/edu/illinois/library/cantaloupe/processor/JaiUtilTest.java
+++ b/src/test/java/edu/illinois/library/cantaloupe/processor/JaiUtilTest.java
@@ -49,23 +49,13 @@ public void testFilterImage() {
// TODO: write this
}
- @Test
- public void testReadImageWithFile() {
- // this will be tested in ProcessorTest
- }
-
- @Test
- public void testReadImageWithInputStream() {
- // this will be tested in ProcessorTest
- }
-
@Test
public void testReformatImage() throws Exception {
- final Dimension fullSize = new Dimension(100, 88);
final OperationList ops = new OperationList();
final ReductionFactor reductionFactor = new ReductionFactor();
- RenderedImage image = JaiUtil.readImage(
- TestUtil.getFixture("jpg"), SourceFormat.JPG, ops, fullSize,
+ ImageIoImageReader reader = new ImageIoImageReader();
+ RenderedImage image = reader.read(
+ TestUtil.getFixture("jpg"), SourceFormat.JPG, ops,
reductionFactor);
PlanarImage planarImage = PlanarImage.wrapRenderedImage(image);
RenderedOp renderedOp = JaiUtil.reformatImage(planarImage,
@@ -147,11 +137,11 @@ public void testWriteImage() {
}
private RenderedOp getFixture(final String name) throws Exception {
- final Dimension fullSize = new Dimension(100, 88);
final OperationList ops = new OperationList();
final ReductionFactor reductionFactor = new ReductionFactor();
- RenderedImage image = JaiUtil.readImage(
- TestUtil.getFixture(name), SourceFormat.JPG, ops, fullSize,
+ ImageIoImageReader reader = new ImageIoImageReader();
+ RenderedImage image = reader.read(
+ TestUtil.getFixture(name), SourceFormat.JPG, ops,
reductionFactor);
PlanarImage planarImage = PlanarImage.wrapRenderedImage(image);
return JaiUtil.reformatImage(planarImage, new Dimension(512, 512));
diff --git a/website/changes.html b/website/changes.html
index 1e43411ac..cf1b1d9c6 100644
--- a/website/changes.html
+++ b/website/changes.html
@@ -4,6 +4,12 @@
To reiterate: most processors can "read the TIFF format," but not all can read it efficiently. Currently, Java2dProcessor and JaiProcessor both support multi-resolution TIFF, which is to say that they actually do read the embedded sub-images and choose the smallest one that can fulfill the request. Java2dProcessor additionally exploits tiled sub-images, so it should currently be the processor of choice for dealing with high-resolution TIFF images.
+To reiterate: most processors can "read the TIFF format," but not all can read it efficiently. Currently, Java2dProcessor and JaiProcessor both support multi-resolution TIFF, which is to say that they actually do read the embedded sub-images and choose the smallest one that can fulfill the request. Additionally, both exploit tiled sub-images. JaiProcessor, however, is able to use the JAI processing pipeline to do this more efficiently, so it is currently the performance champ for suitably-encoded high-resolution TIFF images.
From c2f6708855435b0f9a56fb9176458c07ab6fd96e Mon Sep 17 00:00:00 2001 From: Alex DolskiUpgrading is theoretically just a matter of downloading a new version and running it. Since instances are self-contained, new versions can run happily alongside existing ones, with each using its own configuration file. Sometimes there are backwards-incompatible changes to the file structure, though, so check below to see if there is anything more to be done.
+Upgrading is theoretically just a matter of downloading a new version and running it. Since instances are self-contained, new versions can run happily alongside existing ones, with each using its own configuration file. Sometimes there are changes to the file, though, so check below to see if there is anything more to be done.
+ +JaiProcessor.*
keys from the sample configuration.JaiProcessor.*
keys from the sample configuration.Java2dProcessor.tif.compression
key from the sample configuration.Java Advanced Imaging (JAI) is a sophisticated image processing library developed by Sun until around 2006. JaiProcessor uses an updated fork called JAI-EXT.
+Java Advanced Imaging (JAI) is a sophisticated image processing library developed in the 2000s by Sun. It offers several advantages over Java 2D: a pull-based rendering pipeline that can reduce memory usage, and efficient region-of-interest decoding with some formats.
-JAI offers several theoretical advantages over Java 2D for this application: a more efficient rendering pipeline that should reduce memory usage, and capability of region-of-interest decoding with some formats. Whether these advantages play out in reality is an open question; the author's own profiling seems to indicate maybe not.
- -JaiProcessor can read and write the same formats as Java2dProcessor.
+As JaiProcessor and Java2dProcessor use the same ImageIO readers and writers, they can read and write the same formats.
Years ago, Sun published platform-native accelerator JARs called mediaLib for Windows, Linux, and Solaris, which improved JAI's performance. It is unknown whether these still work on modern platforms.
-JaiProcessor.*
keys from the sample configuration.Java2dProcessor.tif.compression
key from the sample configuration.log.*.SyslogAppender.*
keys from the sample configuration.Appenders direct log messages to various destinations, like files and/or the console. (The default configuration file logs only to the console.) There are three available appenders — ConsoleAppender, FileAppender, and RollingFileAppender — which can be enabled or disabled in any combination.
+Appenders direct log messages to various destinations, like files and/or the console. (The default configuration file logs only to the console.) There are several available appenders, which can be enabled or disabled in any combination.
Currently, the only available rolling policy (log.application.RollingFileAppender.policy
) is TimeBasedRollingPolicy
, which rolls over the log file based on the value of log.application.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern
. log.application.RollingFileAppender.TimeBasedRollingPolicy.max_history
defines how many rolled-over log files will be kept.
SyslogAppender appends log messages to the syslog, managed by syslogd, on a local or remote host.
+Access logs are written in the W3C Extended Log File Format. To enable or disable the access log, set the value of log.access.ConsoleAppender.enabled
to true
or false
.
KLZ*U+~Zi=tQtERi*7(v$gIE>{XKcGV(mAsBh;)TzO-
z3Lmnvbz~$s84bkrq#~yI0D#i0#HlGoeI93L%aUeFvsmA5qU*Zkyw13#NKb-{24ayE
zr+7yly>m7
x(LStF9=9LOn=IX7jR>6nc7kdXj#asxTA)sV@f<
z08>@9J$vnXq%?L_CKLl&bA<3FgJ^9AJN-qPDvAP1DHvm@7HugdJkJBA6erKj;o9{A
z``H5>F!L4H$rh