You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/08/16 17:08:04 UTC
[sis] branch geoapi-4.0 updated: Add support for Horizontal
Differencing Predictor.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 55d0df0 Add support for Horizontal Differencing Predictor.
55d0df0 is described below
commit 55d0df03247d9e4c6c76197c5234f020e298b962
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Aug 16 18:42:30 2021 +0200
Add support for Horizontal Differencing Predictor.
---
.../java/org/apache/sis/internal/jdk9/JDK9.java | 26 +++++
.../apache/sis/internal/geotiff/CopyFromBytes.java | 6 +-
.../sis/internal/geotiff/HorizontalPredictor.java | 117 +++++++++++++++++++++
.../org/apache/sis/internal/geotiff/Inflater.java | 71 +++++++++----
.../sis/internal/geotiff/InflaterChannel.java | 6 +-
.../sis/internal/geotiff/InflaterPredictor.java | 80 ++++++++++++++
.../sis/storage/geotiff/CompressedSubset.java | 48 +++------
.../org/apache/sis/storage/geotiff/DataCube.java | 5 +-
.../org/apache/sis/storage/geotiff/DataSubset.java | 15 +--
.../sis/storage/UnsupportedEncodingException.java | 62 +++++++++++
10 files changed, 360 insertions(+), 76 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
index dc86420..ec1de1d 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
@@ -165,6 +165,32 @@ public final class JDK9 {
}
/**
+ * Place holder for {@code ByteBuffer.get(int, byte[])}.
+ *
+ * @param b the buffer from which to get bytes.
+ * @param index index from which the first byte will be read.
+ * @param dst destination array
+ */
+ public static void get(final ByteBuffer b, int index, final byte[] dst) {
+ JDK9.get(b, index, dst, 0, dst.length);
+ }
+
+ /**
+ * Place holder for {@code ByteBuffer.get(int, byte[], int, int)}.
+ *
+ * @param b the buffer from which to get bytes.
+ * @param index index from which the first byte will be read.
+ * @param dst destination array
+ * @param offset offset in the array of the first byte to write.
+ * @param length number of bytes to write.
+ */
+ public static void get(final ByteBuffer b, final int index, final byte[] dst, final int offset, final int length) {
+ for (int i=0; i<length; i++) {
+ dst[offset + i] = b.get(index + i);
+ }
+ }
+
+ /**
* Place holder for {@code Class.getPackageName()}.
*
* @param c the class for which to get the package name.
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/CopyFromBytes.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/CopyFromBytes.java
index 0bc6e11..1ab6cca 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/CopyFromBytes.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/CopyFromBytes.java
@@ -25,6 +25,7 @@ import java.nio.LongBuffer;
import java.nio.FloatBuffer;
import java.nio.DoubleBuffer;
import org.apache.sis.internal.storage.io.ChannelDataInput;
+import org.apache.sis.storage.UnsupportedEncodingException;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.Classes;
@@ -103,10 +104,11 @@ abstract class CopyFromBytes extends Inflater {
* @param divisor factor by which to divide sample size values. Always ≥ 1 and usually = 1.
* @param banks where to store sample values.
* @return the inflater for the given targe type.
- * @throws IllegalArgumentException if the buffer type is not recognized.
+ * @throws UnsupportedEncodingException if the buffer type is not recognized.
*/
public static CopyFromBytes create(final ChannelDataInput input, final long start,
final int count, final int size, final int[] skips, final int divisor, final Buffer banks)
+ throws UnsupportedEncodingException
{
if (banks instanceof ByteBuffer) return new Bytes (input, start, count, size, skips, divisor, (ByteBuffer) banks);
if (banks instanceof ShortBuffer) return new Shorts (input, start, count, size, skips, divisor, (ShortBuffer) banks);
@@ -114,7 +116,7 @@ abstract class CopyFromBytes extends Inflater {
if (banks instanceof LongBuffer) return new Longs (input, start, count, size, skips, divisor, (LongBuffer) banks);
if (banks instanceof FloatBuffer) return new Floats (input, start, count, size, skips, divisor, (FloatBuffer) banks);
if (banks instanceof DoubleBuffer) return new Doubles(input, start, count, size, skips, divisor, (DoubleBuffer) banks);
- throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, Classes.getClass(banks)));
+ throw new UnsupportedEncodingException(Errors.format(Errors.Keys.UnsupportedType_1, Classes.getClass(banks)));
}
/**
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/HorizontalPredictor.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/HorizontalPredictor.java
new file mode 100644
index 0000000..a303a8d
--- /dev/null
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/HorizontalPredictor.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.geotiff;
+
+import java.nio.ByteBuffer;
+import org.apache.sis.internal.jdk9.JDK9;
+
+
+/**
+ * Implementation of {@link Predictor#HORIZONTAL}.
+ * Current implementation works only on 8-bits samples.
+ *
+ * <p><b>Note:</b> if we want to support 16 bits, 32 bits <i>etc.</i> sample values,
+ * the main difficulty is that if there buffer ends in the middle of a sample value,
+ * we need to stop the processing before that last value and stores it somewhere for
+ * processing in the next call to {@link InflaterChannel#read(ByteBuffer)}.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class HorizontalPredictor extends InflaterPredictor {
+ /**
+ * Data on the previous column. The length of this array is the pixel stride.
+ */
+ private final byte[] previousColumns;
+
+ /**
+ * Number of sample values between a row and the next row (in saùme .
+ */
+ private final int scanlineStride;
+
+ /**
+ * Column index (as a count of sample values, not a count of pixels).
+ * Used for detecting when the decoding process starts a new row.
+ */
+ private int column;
+
+ /**
+ * Creates a new predictor.
+ *
+ * @param input the channel that decompress data.
+ * @param pixelStride number of sample values per pixel in the source image.
+ * @param width number of pixels in the source image.
+ */
+ HorizontalPredictor(final InflaterChannel input, final int pixelStride, final int width) {
+ super(input);
+ previousColumns = new byte[pixelStride];
+ scanlineStride = Math.multiplyExact(width, pixelStride);
+ }
+
+ /**
+ * Applies the predictor on data in the given buffer,
+ * from the given start position until current buffer position.
+ *
+ * @param buffer the buffer on which to apply the predictor.
+ * @param start position of first sample value to process.
+ */
+ @Override
+ protected void uncompress(final ByteBuffer buffer, final int start) {
+ final int pixelStride = previousColumns.length;
+ final int limit = buffer.position();
+ int position = start;
+ while (position < limit) {
+ /*
+ * This loop body should be executed on a row-by-row basis. But the `startOfRow` and `endOfRow` indices
+ * may not be the real start/end of row if the previous call to this method finished before end of row,
+ * or if current call to this method also finishes before end of row (because of buffer limit).
+ */
+ final int startOfRow = position;
+ final int endOfRow = Math.min(position + (scanlineStride - column), limit);
+ final int head = Math.min(position + pixelStride, endOfRow);
+ if (column < pixelStride) {
+ // Pixels in the first column are left unchanged.
+ position += Math.min(pixelStride, endOfRow - position);
+ }
+ while (position < head) {
+ buffer.put(position, (byte) (buffer.get(position) + previousColumns[position - startOfRow]));
+ position++;
+ }
+ while (position < endOfRow) {
+ buffer.put(position, (byte) (buffer.get(position) + buffer.get(position - pixelStride)));
+ position++;
+ }
+ column += position - startOfRow;
+ if (column >= scanlineStride) {
+ column = 0;
+ }
+ }
+ /*
+ * Save the last bytes for next invocation of this method.
+ */
+ final int capacity = limit - start;
+ if (capacity >= pixelStride) {
+ JDK9.get(buffer, limit - pixelStride, previousColumns);
+ } else {
+ final int keep = pixelStride - capacity;
+ System.arraycopy(previousColumns, keep, previousColumns, 0, capacity);
+ JDK9.get(buffer, start, previousColumns, keep, capacity);
+ }
+ }
+}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Inflater.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Inflater.java
index 9c1e841..f8d3a46 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Inflater.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Inflater.java
@@ -16,13 +16,16 @@
*/
package org.apache.sis.internal.geotiff;
-import java.nio.Buffer;
-import java.io.IOException;
import java.util.Arrays;
+import java.io.IOException;
+import java.nio.Buffer;
+import java.nio.channels.ReadableByteChannel;
import org.apache.sis.math.MathFunctions;
import org.apache.sis.internal.util.Numerics;
import org.apache.sis.internal.storage.io.ChannelDataInput;
+import org.apache.sis.storage.UnsupportedEncodingException;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Localized;
import static org.apache.sis.internal.util.Numerics.ceilDiv;
@@ -160,42 +163,66 @@ public abstract class Inflater {
* (e.g. 1 bit) if {@code pixelsPerElement} is greater than 1. If that case, the {@link #elementsPerChunk}
* and {@link #skipAfterChunks} values will be divided by {@code pixelsPerElement}.
*
- * @param compression the compression method.
- * @param input the source of data to decompress.
- * @param start stream position where to start reading.
- * @param byteCount number of bytes to read before decompression.
- * @param sourceWidth number of pixels in a row of source image.
- * @param chunksPerRow number of chunks (usually pixels) per row in target image. Must be strictly positive.
- * @param samplesPerChunk number of sample values per chunk (sample or pixel). Must be strictly positive.
- * @param skipAfterChunks number of sample values to skip between chunks. May be empty or null.
- * @param pixelsPerElement number of pixels per primitive element. Always 1 except for multi-pixels packed images.
- * @param banks where to store sample values.
- * @return the inflater for the given targe type, or {@code null} if the compression method is unknown.
+ * @param compression the compression method.
+ * @param predictor the mathematical operator to apply after decompression.
+ * @param input the source of data to decompress.
+ * @param start stream position where to start reading.
+ * @param byteCount number of bytes to read before decompression.
+ * @param sourcePixelStride number of sample values per pixel in the source image.
+ * @param sourceWidth number of pixels in a row of source image.
+ * @param chunksPerRow number of chunks (usually pixels) per row in target image. Must be strictly positive.
+ * @param samplesPerChunk number of sample values per chunk (sample or pixel). Must be strictly positive.
+ * @param skipAfterChunks number of sample values to skip between chunks. May be empty or null.
+ * @param pixelsPerElement number of pixels per primitive element. Always 1 except for multi-pixels packed images.
+ * @param banks where to store sample values.
+ * @param caller locale to use if an error message must be provided.
+ * @return the inflater for the given targe type.
* @throws IOException if an I/O operation was required and failed.
+ * @throws UnsupportedEncodingException if the compression, predictor or data type is unsupported.
*/
- public static Inflater create(final Compression compression,
- final ChannelDataInput input, final long start, final long byteCount, final int sourceWidth,
+ public static Inflater create(final Compression compression, final Predictor predictor,
+ final ChannelDataInput input, final long start, final long byteCount,
+ final int sourcePixelStride, final int sourceWidth,
final int chunksPerRow, final int samplesPerChunk, final int[] skipAfterChunks,
- final int pixelsPerElement, final Buffer banks)
- throws IOException
+ final int pixelsPerElement, final Buffer banks, final Localized caller)
+ throws IOException, UnsupportedEncodingException
{
ArgumentChecks.ensureNonNull("input", input);
ArgumentChecks.ensureNonNull("banks", banks);
final InflaterChannel inflated;
switch (compression) {
- case NONE: {
- return CopyFromBytes.create(input, start, chunksPerRow, samplesPerChunk, skipAfterChunks, pixelsPerElement, banks);
- }
case LZW: inflated = new LZW (input, start, byteCount); break;
case PACKBITS: inflated = new PackBits(input, start, byteCount); break;
case CCITTRLE: inflated = new CCITTRLE(input, start, byteCount, sourceWidth); break;
- default: return null;
+ case NONE: {
+ if (predictor == Predictor.NONE) {
+ return CopyFromBytes.create(input, start,
+ chunksPerRow, samplesPerChunk, skipAfterChunks, pixelsPerElement, banks);
+ }
+ throw unsupportedEncoding(Resources.Keys.UnsupportedPredictor_1, predictor, caller);
+ }
+ default: {
+ throw unsupportedEncoding(Resources.Keys.UnsupportedCompressionMethod_1, compression, caller);
+ }
}
- return CopyFromBytes.create(inflated.createDataInput(), 0,
+ final ReadableByteChannel channel;
+ switch (predictor) {
+ case NONE: channel = inflated; break;
+ case HORIZONTAL: channel = new HorizontalPredictor(inflated, sourcePixelStride, sourceWidth); break;
+ default: throw unsupportedEncoding(Resources.Keys.UnsupportedPredictor_1, predictor, caller);
+ }
+ return CopyFromBytes.create(inflated.createDataInput(channel), 0,
chunksPerRow, samplesPerChunk, skipAfterChunks, pixelsPerElement, banks);
}
/**
+ * Returns the exception to throw for an unsupported compression or predictor.
+ */
+ private static UnsupportedEncodingException unsupportedEncoding(final short key, final Enum<?> value, final Localized caller) {
+ return new UnsupportedEncodingException(Resources.forLocale(caller.getLocale()).getString(key, value));
+ }
+
+ /**
* Reads the given amount of sample values without storing them.
* The given value is in units of sample values, not in bytes.
*
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterChannel.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterChannel.java
index c87a655..12bcfc2 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterChannel.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterChannel.java
@@ -64,9 +64,11 @@ abstract class InflaterChannel implements ReadableByteChannel {
/**
* Creates the data input stream to use for getting uncompressed data.
* The {@linkplain #input} stream must be on the start position before to invoke this method.
+ *
+ * @param channel the channel to wrap. This is {@code this} unless a {@link Predictor} is applied.
*/
- final ChannelDataInput createDataInput() throws IOException {
- return new ChannelDataInput(input.filename, this, ByteBuffer.allocate(BUFFER_SIZE), false);
+ final ChannelDataInput createDataInput(final ReadableByteChannel channel) throws IOException {
+ return new ChannelDataInput(input.filename, channel, ByteBuffer.allocate(BUFFER_SIZE), false);
}
/**
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterPredictor.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterPredictor.java
new file mode 100644
index 0000000..1b816ab
--- /dev/null
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/InflaterPredictor.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.geotiff;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+
+/**
+ * Implementation of a {@link Predictor} to be executed after decompression.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+abstract class InflaterPredictor implements ReadableByteChannel {
+ /**
+ * The channel from which to read data.
+ */
+ private final InflaterChannel input;
+
+ /**
+ * Creates a predictor.
+ */
+ protected InflaterPredictor(final InflaterChannel input) {
+ this.input = input;
+ }
+
+ /**
+ * Applies the predictor on data in the given buffer,
+ * from the given start position until current buffer position.
+ *
+ * @param buffer the buffer on which to apply the predictor.
+ * @param start position of first sample value to process.
+ */
+ protected abstract void uncompress(ByteBuffer buffer, int start);
+
+ /**
+ * Decompresses some bytes from the {@linkplain #input} into the given destination buffer.
+ */
+ @Override
+ public int read(final ByteBuffer target) throws IOException {
+ final int start = target.position();
+ final int n = input.read(target);
+ uncompress(target, start);
+ return n;
+ }
+
+ /**
+ * Tells whether this channel is still open.
+ */
+ @Override
+ public final boolean isOpen() {
+ return input.isOpen();
+ }
+
+ /**
+ * Do nothing. The {@linkplain #input} channel is not closed by this operation
+ * because it will typically be needed again for decompressing other tiles.
+ */
+ @Override
+ public final void close() {
+ }
+}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
index 070aa26..79d66b1 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
@@ -26,9 +26,7 @@ import org.apache.sis.internal.coverage.j2d.RasterFactory;
import org.apache.sis.internal.geotiff.Compression;
import org.apache.sis.internal.geotiff.Predictor;
import org.apache.sis.internal.geotiff.Inflater;
-import org.apache.sis.internal.geotiff.Resources;
import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.image.DataType;
import static java.lang.Math.toIntExact;
@@ -45,16 +43,6 @@ import static org.apache.sis.internal.jdk9.JDK9.multiplyFull;
*/
final class CompressedSubset extends DataSubset {
/**
- * The compression method.
- */
- private final Compression compression;
-
- /**
- * The mathematical operator that is applied to the image data before an encoding scheme is applied.
- */
- private final Predictor predictor;
-
- /**
* Number of sample values to skip for moving to the next row of a tile in the GeoTIFF file.
* This is not necessarily the same scanline stride than for the tiles created by this class.
*/
@@ -111,20 +99,15 @@ final class CompressedSubset extends DataSubset {
* by {@link ImageFileDirectory#validateMandatoryTags()} before this call.
* This constructor should be invoked inside a synchronized block.
*
- * @param source the resource which contain this {@code DataSubset}.
- * @param subset description of the {@code owner} subset to cover.
- * @param rasters potentially shared cache of rasters read by this {@code DataSubset}.
- * @param compression the compression method.
- * @param predictor the mathematical operator applied to image data before compression.
+ * @param source the resource which contain this {@code DataSubset}.
+ * @param subset description of the {@code owner} subset to cover.
+ * @param rasters potentially shared cache of rasters read by this {@code DataSubset}.
* @throws ArithmeticException if the number of tiles overflows 32 bits integer arithmetic.
*/
- CompressedSubset(final DataCube source, final TiledGridResource.Subset subset,
- final Compression compression, final Predictor predictor)
+ CompressedSubset(final DataCube source, final TiledGridResource.Subset subset)
throws DataStoreException
{
super(source, subset);
- this.compression = compression;
- this.predictor = predictor;
scanlineStride = multiplyFull(getTileSize(0), sourcePixelStride);
final int between = sourcePixelStride * (getSubsampling(0) - 1);
int afterLastBand = sourcePixelStride * (getTileSize(0) - 1);
@@ -229,27 +212,20 @@ final class CompressedSubset extends DataSubset {
*/
final int pixelsPerElement = getPixelsPerElement(); // Always ≥ 1 and usually = 1.
assert (head % pixelsPerElement) == 0 : head;
- final int capacity = getBankCapacity(pixelsPerElement);
- final Buffer[] banks = new Buffer[numBanks];
- final ChannelDataInput input = input();
+ final int capacity = getBankCapacity(pixelsPerElement);
+ final Buffer[] banks = new Buffer[numBanks];
+ final ChannelDataInput input = input();
+ final Compression compression = source.getCompression();
+ final Predictor predictor = source.getPredictor();
for (int b=0; b<numBanks; b++) {
/*
* Prepare the object which will perform the actual decompression row-by-row,
* optionally skipping chunks if a subsampling is applied.
*/
final Buffer bank = RasterFactory.createBuffer(type, capacity);
- final Inflater algo = Inflater.create(compression, input, offsets[b], byteCounts[b],
- getTileSize(0), chunksPerRow, samplesPerChunk, skipAfterChunks,
- pixelsPerElement, bank);
- if (algo == null) {
- throw new DataStoreContentException(reader().resources().getString(
- Resources.Keys.UnsupportedCompressionMethod_1, compression));
- }
- // TODO: Add predictor handling here.
- if (predictor != Predictor.NONE) {
- throw new DataStoreContentException(reader().resources().getString(
- Resources.Keys.UnsupportedPredictor_1, predictor));
- }
+ final Inflater algo = Inflater.create(compression, predictor, input, offsets[b], byteCounts[b],
+ sourcePixelStride, getTileSize(0), chunksPerRow, samplesPerChunk, skipAfterChunks,
+ pixelsPerElement, bank, this);
for (long y = lower[1]; --y >= 0;) {
algo.skip(scanlineStride); // `skip(…)` may round to next element boundary.
}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
index 463d6ea..bcbaa7a 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
@@ -184,16 +184,15 @@ abstract class DataCube extends TiledGridResource implements ResourceOnFileSyste
throw new DataStoreContentException(reader.resources().getString(
Resources.Keys.MissingValue_2, Tags.name(Tags.Compression)));
}
- final Predictor predictor = getPredictor();
/*
* The `DataSubset` parent class is the most efficient but has many limitations
* documented in the javadoc of its `readSlice(…)` method. If any pre-condition
* is not met, we need to fallback on the less direct `CompressedSubset` class.
*/
- if (compression == Compression.NONE && predictor == Predictor.NONE && canReadDirect(subset)) {
+ if (compression == Compression.NONE && getPredictor() == Predictor.NONE && canReadDirect(subset)) {
coverage = new DataSubset(this, subset);
} else {
- coverage = new CompressedSubset(this, subset, compression, predictor);
+ coverage = new CompressedSubset(this, subset);
}
coverage = preload(coverage);
}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
index 9454d55..82f15e2 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
@@ -72,7 +72,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
* The resource which contain this {@code DataSubset}.
* Used for fetching information like the input channel and where to report warnings.
*/
- private final DataCube source;
+ final DataCube source;
/**
* For each tile, the byte offset of that tile as compressed and stored on disk.
@@ -208,18 +208,11 @@ class DataSubset extends TiledGridCoverage implements Localized {
}
/**
- * Returns the GeoTIFF reader which contains this subset.
- */
- final Reader reader() {
- return source.reader;
- }
-
- /**
* Returns the input of bytes for compressed raster data. If the TIFF tag {@code FillOrder} is 2
* (which should be very rare), the input stream reverse the order of all bits in each byte.
*/
final ChannelDataInput input() throws IOException {
- ChannelDataInput input = reader().input;
+ ChannelDataInput input = source.reader.input;
if (source.isBitOrderReversed()) {
input = ReversedBitsChannel.wrap(input);
}
@@ -342,7 +335,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
tile.copyTileInfo(tileOffsets, offsets, includedBanks, numTiles);
tile.copyTileInfo(tileByteCounts, byteCounts, includedBanks, numTiles);
for (int b=0; b<offsets.length; b++) {
- offsets[b] = addExact(offsets[b], reader().origin);
+ offsets[b] = addExact(offsets[b], source.reader.origin);
}
WritableRaster r = readSlice(offsets, byteCounts, lower, upper, subsampling, origin);
result[tile.indexInResultArray] = tile.cache(r);
@@ -435,7 +428,7 @@ class DataSubset extends TiledGridCoverage implements Localized {
final Buffer[] banks = new Buffer[numBanks];
for (int b=0; b<numBanks; b++) {
if (b < byteCounts.length && length > byteCounts[b]) {
- throw new DataStoreContentException(reader().resources().getString(
+ throw new DataStoreContentException(source.reader.resources().getString(
Resources.Keys.UnexpectedTileLength_2, length, byteCounts[b]));
}
hr.setOrigin(offsets[b]);
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedEncodingException.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedEncodingException.java
new file mode 100644
index 0000000..29f3f55
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/UnsupportedEncodingException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage;
+
+
+/**
+ * Thrown when a storage uses some encoding options not supported by current implementation.
+ * For example it may be a compression method not implemented by the reader.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public class UnsupportedEncodingException extends DataStoreContentException {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 4998668012290557156L;
+
+ /**
+ * Creates an exception with the specified details message.
+ *
+ * @param message the detail message.
+ */
+ public UnsupportedEncodingException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Creates an exception with the specified cause and no details message.
+ *
+ * @param cause the cause for this exception.
+ */
+ public UnsupportedEncodingException(final Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates an exception with the specified details message and cause.
+ *
+ * @param message the detail message.
+ * @param cause the cause for this exception.
+ */
+ public UnsupportedEncodingException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}