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 1556a92 Add the possibility to substitute a tile placeholder in
"prefetch" operation if an error handler has been specified.
1556a92 is described below
commit 1556a92d9d574678a3a541c11232db251bed8667
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Feb 5 23:54:19 2021 +0100
Add the possibility to substitute a tile placeholder in "prefetch"
operation if an error handler has been specified.
---
.../apache/sis/gui/coverage/CoverageCanvasApp.java | 15 ++-
.../org/apache/sis/gui/coverage/GridViewApp.java | 2 +-
.../java/org/apache/sis/image/ImageProcessor.java | 16 +--
.../java/org/apache/sis/image/PrefetchedImage.java | 108 ++++++++++++++++++++-
.../sis/internal/coverage/j2d/ImageUtilities.java | 24 +++++
.../sis/internal/coverage/j2d/RasterFactory.java | 6 +-
.../apache/sis/image/StatisticsCalculatorTest.java | 4 +-
.../java/org/apache/sis/image/TiledImageMock.java | 75 +++++++++-----
8 files changed, 210 insertions(+), 40 deletions(-)
diff --git
a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
index ab7964a..bcba2d9 100644
---
a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
+++
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/CoverageCanvasApp.java
@@ -52,7 +52,7 @@ public class CoverageCanvasApp extends Application implements
ChangeListener<Thr
* Width and height should be different in order to increase the chance to
see bugs
* if some code confuse them.
*/
- private static final int TILE_WIDTH = 60, TILE_HEIGHT = 50;
+ private static final int TILE_WIDTH = 200, TILE_HEIGHT = 300;
/**
@@ -101,9 +101,14 @@ public class CoverageCanvasApp extends Application
implements ChangeListener<Thr
* have artificial errors in order to see the error controls.
*/
private static GridCoverage2D createImage() {
- final Random random = new Random();
- final int width = TILE_WIDTH * 15;
- final int height = TILE_HEIGHT * 12;
+ /*
+ * A few interresting seeds for debugging:
+ * -638852012230008460L, 987724905110811687L,
+ * 4120510106559901474L, 4533692522112080642L, 2518316107261433588L
+ */
+ final Random random = new Random(987724905110811687L);
+ final int width = TILE_WIDTH * 4;
+ final int height = TILE_HEIGHT * 2;
final TiledImageMock image = new TiledImageMock(
DataBuffer.TYPE_BYTE, 1,
random.nextInt(50) - 25, // minX
@@ -125,7 +130,7 @@ public class CoverageCanvasApp extends Application
implements ChangeListener<Thr
}
it.setSample(0, value);
}
-// image.failRandomly(random);
+ image.failRandomly(random, false);
return new GridCoverage2D(new GridGeometry(null,
PixelInCell.CELL_CORNER,
MathTransforms.identity(2),
CommonCRS.Engineering.DISPLAY.crs()), null, image);
}
diff --git
a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
index bc457ce..d3438e3 100644
---
a/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
+++
b/application/sis-javafx/src/test/java/org/apache/sis/gui/coverage/GridViewApp.java
@@ -99,7 +99,7 @@ public final strictfp class GridViewApp extends Application {
false);
image.validate();
image.initializeAllTiles(0);
- image.failRandomly(new Random());
+ image.failRandomly(new Random(), true);
return image;
}
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index b786f97..146ba81 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -860,17 +860,19 @@ public class ImageProcessor implements Cloneable {
* Computes immediately all tiles in the given region of interest, then
return an image will those tiles ready.
* Computations will use many threads if {@linkplain #getExecutionMode()
execution mode} is parallel.
*
- * <div class="note"><b>Note:</b>
- * current implementation ignores the {@linkplain #getErrorHandler() error
handler} because we do not yet
- * have a mechanism for specifying which tile to produce in replacement of
tiles that can not be computed.
- * This behavior may be changed in a future version.</div>
- *
* <h4>Properties used</h4>
* This operation uses the following properties in addition to method
parameters:
* <ul>
* <li>{@linkplain #getExecutionMode() Execution mode} (parallel or
sequential).</li>
+ * <li>{@linkplain #getErrorHandler() Error handler} (whether to fail if
an exception is thrown).</li>
* </ul>
*
+ * <h4>Limitation</h4>
+ * Current implementation ignores the {@linkplain #getErrorHandler() error
handler} in sequential execution
+ * (i.e. error handler is used only during parallel execution). In
addition, there is not yet a mechanism
+ * for specifying which tile to produce in replacement of tiles that can
not be computed.
+ * Those limitations may be addressed in a future version.
+ *
* @param source the image to compute immediately (may be {@code
null}).
* @param areaOfInterest pixel coordinates of the region to prefetch, or
{@code null} for the whole image.
* @return image with all tiles intersecting the AOI computed, or {@code
null} if the given image was null.
@@ -885,10 +887,12 @@ public class ImageProcessor implements Cloneable {
source = ((PrefetchedImage) source).source;
}
final boolean parallel;
+ final ErrorHandler errorListener;
synchronized (this) {
parallel = parallel(source);
+ errorListener = errorHandler;
}
- final PrefetchedImage image = new PrefetchedImage(source,
areaOfInterest, parallel);
+ final PrefetchedImage image = new PrefetchedImage(source,
areaOfInterest, errorListener, parallel);
return image.isEmpty() ? source : image;
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
index 6436786..3dea68e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
@@ -16,15 +16,20 @@
*/
package org.apache.sis.image;
+import java.awt.Point;
+import java.util.Arrays;
import java.util.Vector;
import java.awt.Rectangle;
+import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.awt.image.RenderedImage;
import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
import java.awt.image.RasterFormatException;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import org.apache.sis.internal.coverage.j2d.TileOpExecutor;
+import org.apache.sis.internal.util.Numerics;
import org.apache.sis.util.resources.Errors;
@@ -61,6 +66,8 @@ final class PrefetchedImage extends PlanarImage {
/**
* The prefetched tiles. This array contains only the tiles in the area of
interest (AOI)
* specified at construction time; it does not necessarily contains all
tiles in the image.
+ * Some element may be {@code null} if an error occurred while computing
that tile,
+ * in which case a placeholder will be computed by {@link #getTile(int,
int)}.
*/
private final Raster[] tiles;
@@ -69,9 +76,12 @@ final class PrefetchedImage extends PlanarImage {
*
* @param source the image to compute immediately (may be {@code
null}).
* @param areaOfInterest pixel coordinates of the region to prefetch, or
{@code null} for the whole image.
+ * @param errorHandler action to execute (throw an exception or log a
warning) if an error occurs.
* @param parallel whether to execute computation in parallel.
*/
- PrefetchedImage(final RenderedImage source, Rectangle areaOfInterest,
final boolean parallel) {
+ PrefetchedImage(final RenderedImage source, Rectangle areaOfInterest,
+ final ErrorHandler errorHandler, final boolean parallel)
+ {
this.source = source;
if (areaOfInterest != null) {
areaOfInterest = new Rectangle(areaOfInterest);
@@ -92,11 +102,25 @@ final class PrefetchedImage extends PlanarImage {
numXTiles = ti.width;
numYTiles = ti.height;
tiles = new Raster[Math.multiplyExact(numYTiles, numXTiles)];
+ worker.setErrorHandler(errorHandler);
if (parallel) {
worker.parallelReadFrom(source);
} else {
worker.readFrom(source);
}
+ /*
+ * If an error occurred during a tile computation, the array element
corresponding
+ * to that tile still null. Replace them by a placeholder. Note that
it may happen
+ * only if the error handler is not `ErrorHandler.THROW`.
+ */
+ Raster previous = null;
+ for (int i=0; i<tiles.length; i++) {
+ if (tiles[i] == null) {
+ final int tileX = (i % numXTiles) + minTileX;
+ final int tileY = (i / numXTiles) + minTileY;
+ tiles[i] = previous = createPlaceholder(tileX, tileY,
previous);
+ }
+ }
}
/**
@@ -192,4 +216,86 @@ final class PrefetchedImage extends PlanarImage {
}
return source.getTile(tileX, tileY);
}
+
+ /**
+ * Creates a tile to use as a placeholder when a tile can not be computed.
+ *
+ * @param tileX column index of the tile for which to create a
placeholder.
+ * @param tileY row index of the tile for which to create a
placeholder.
+ * @param previous tile previously created by this method, or {@code
null} if none.
+ * @return placeholder for the tile at given indices.
+ */
+ private Raster createPlaceholder(final int tileX, final int tileY, final
Raster previous) {
+ final SampleModel model = getSampleModel();
+ final Point location = new Point(ImageUtilities.tileToPixelX(source,
tileX),
+ ImageUtilities.tileToPixelY(source,
tileY));
+ if (previous != null) {
+ // Reuse same `DataBuffer` with only a different location.
+ return Raster.createRaster(model, previous.getDataBuffer(),
location);
+ }
+ final double[] samples = new double[model.getNumBands()];
+ if (ImageUtilities.isIntegerType(model)) {
+ final boolean isUnsigned = ImageUtilities.isUnsignedType(model);
+ for (int i=0; i<samples.length; i++) {
+ int size = model.getSampleSize(i);
+ if (!isUnsigned) size--;
+ samples[i] = Numerics.bitmask(size) - 1;
+ }
+ } else {
+ final ColorSpace cs;
+ final ColorModel cm = getColorModel();
+ if (cm != null && (cs = cm.getColorSpace()) != null) {
+ for (int i = Math.min(cs.getNumComponents(), samples.length);
--i >=0;) {
+ samples[i] = cs.getMaxValue(i);
+ }
+ } else {
+ Arrays.fill(samples, 1);
+ }
+ }
+ /*
+ * Draw borders around the tile as dotted lines. The left border will
have (usually) white pixels
+ * at even coordinates relative to upper-left corner, while right
border will have same pixels at
+ * odd coordinates. The same pattern applies to top and bottom borders.
+ */
+ final WritableRaster tile = WritableRaster.createWritableRaster(model,
model.createDataBuffer(), location);
+ final int width = tile.getWidth();
+ final int height = tile.getHeight();
+ final int xmin = tile.getMinX();
+ final int ymin = tile.getMinY();
+ final int xmax = width + xmin - 1;
+ final int ymax = height + ymin - 1;
+ int x = xmin;
+ while (x < xmax) {
+ tile.setPixel(x++, ymin, samples);
+ tile.setPixel(x++, ymax, samples);
+ }
+ int y = ymin;
+ while (y < ymax) {
+ tile.setPixel(xmin, y++, samples);
+ tile.setPixel(xmax, y++, samples);
+ }
+ if (x == xmax) tile.setPixel(xmax, ymin, samples);
+ if (y == ymax) tile.setPixel(xmin, ymax, samples);
+ /*
+ * Add a cross (X) inside the tile.
+ */
+ if (width >= height) {
+ final double step = height / (double) width;
+ for (int i=0; i<width; i++) {
+ x = xmin + i;
+ y = (int) (i*step);
+ tile.setPixel(x, ymin + y, samples);
+ tile.setPixel(x, ymax - y, samples);
+ }
+ } else {
+ final double step = width / (double) height;
+ for (int i=0; i<height; i++) {
+ y = ymin + i;
+ x = (int) (i*step);
+ tile.setPixel(xmin + x, y, samples);
+ tile.setPixel(xmax - x, y, samples);
+ }
+ }
+ return tile;
+ }
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
index d1d7eea..a0a2e21 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
@@ -494,6 +494,30 @@ public final class ImageUtilities extends Static {
}
/**
+ * Converts a tile column index to smallest <var>x</var> pixel coordinate
inside the tile.
+ * The returned value is a coordinate of the pixel in upper-left corner.
+ *
+ * @param image the image containing tiles.
+ * @param tileX the tile coordinate for which to get pixel coordinate.
+ * @return smallest <var>x</var> pixel coordinate inside the tile.
+ */
+ public static int tileToPixelX(final RenderedImage image, final int tileX)
{
+ return Math.addExact(Math.multiplyExact(tileX, image.getTileWidth()),
image.getTileGridXOffset());
+ }
+
+ /**
+ * Converts a tile row index to smallest <var>y</var> pixel coordinate
inside the tile.
+ * The returned value is a coordinate of the pixel in upper-left corner.
+ *
+ * @param image the image containing tiles.
+ * @param tileY the tile coordinate for which to get pixel coordinate.
+ * @return smallest <var>y</var> pixel coordinate inside the tile.
+ */
+ public static int tileToPixelY(final RenderedImage image, final int tileY)
{
+ return Math.addExact(Math.multiplyExact(tileY, image.getTileHeight()),
image.getTileGridYOffset());
+ }
+
+ /**
* Suggests the height of a transfer region for a tile of the given size.
The given region should be
* contained inside {@link Raster#getBounds()}. This method modifies
{@link Rectangle#height} in-place.
* The {@link Rectangle#width} value is never modified, so caller can
iterate on all raster rows without
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java
index 0823dd5..a73c7dc 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RasterFactory.java
@@ -137,8 +137,8 @@ public final class RasterFactory extends Static {
final int dataType = buffer.getDataType();
/*
* This SampleModel variable is a workaround for WritableRaster static
methods not supporting all data types.
- * If 'dataType' is unsupported, then we create a SampleModel
ourselves in the 'switch' statements below and
- * use it for creating a WritableRaster at the end of this method.
This variable, together with the 'switch'
+ * If `dataType` is unsupported, then we create a SampleModel
ourselves in the `switch` statements below and
+ * use it for creating a WritableRaster at the end of this method.
This variable, together with the `switch`
* statements, may be removed in a future SIS version if all types
become supported by the JDK.
*/
@Workaround(library = "JDK", version = "10")
@@ -152,7 +152,7 @@ public final class RasterFactory extends Static {
switch (dataType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT: {
- // 'scanlineStride' and 'pixelStride' really interchanged
in that method signature.
+ // `scanlineStride` and `pixelStride` really interchanged
in that method signature.
return WritableRaster.createInterleavedRaster(buffer,
width, height, scanlineStride, pixelStride, bandOffsets, location);
}
case DataBuffer.TYPE_INT: {
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
b/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
index 9b881f9..135799d 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/image/StatisticsCalculatorTest.java
@@ -115,7 +115,7 @@ public final strictfp class StatisticsCalculatorTest
extends TestCase {
final ImageProcessor operations = new ImageProcessor();
operations.setExecutionMode(ImageProcessor.Mode.PARALLEL);
final TiledImageMock image = createImage();
- image.failRandomly(new Random(-8739538736973900203L));
+ image.failRandomly(new Random(-8739538736973900203L), true);
try {
operations.valueOfStatistics(image, null);
fail("Expected ImagingOpException.");
@@ -136,7 +136,7 @@ public final strictfp class StatisticsCalculatorTest
extends TestCase {
operations.setExecutionMode(ImageProcessor.Mode.PARALLEL);
operations.setErrorHandler(ErrorHandler.LOG);
final TiledImageMock image = createImage();
- image.failRandomly(new Random(8004277484984714811L));
+ image.failRandomly(new Random(8004277484984714811L), true);
final Statistics[] stats = operations.valueOfStatistics(image, null);
for (final Statistics a : stats) {
assertTrue(a.count() > 0);
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
b/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
index 8e93266..4ef472d 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/TiledImageMock.java
@@ -55,6 +55,12 @@ import static org.junit.Assert.*;
*/
public final strictfp class TiledImageMock extends PlanarImage implements
WritableRenderedImage {
/**
+ * Inverse of the probability that a tile has failure.
+ * This is used only if {@link #failRandomly(Random, boolean)} is invoked.
+ */
+ private static final int FAILURE_PROBABILITY = 10;
+
+ /**
* Location of the upper-left pixel of the image. Should be a non-trivial
* value for increasing the chances to detect error in index calculation.
*/
@@ -107,22 +113,31 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* {@link #getTile(int, int)} invocation should fail. Note that two
consecutive invocations
* of {@code getTile(…)} may give different result.
*
- * @see #failRandomly(Random)
+ * @see #failRandomly(Random, boolean)
*/
private Random randomFailures;
/**
+ * Tiles to keep in error on all invocations of {@link #getTile(int,
int)}. This is an alternative
+ * to {@link #randomFailures} for creating artificial errors. Contrarily
to {@link #randomFailures},
+ * all calls to {@link #getTile(int, int)} with the same indices have the
same behavior.
+ */
+ private boolean[] constantFailures;
+
+ /**
* Sequential number for use in production of error messages, to
differentiate them.
* Since {@link #getTile(int, int)} may be invoked in background threads,
this field
* should be safe to concurrent threads.
*
- * @see #failRandomly(Random)
+ * @see #failRandomly(Random, boolean)
*/
private AtomicInteger errorSequence;
/**
* The color model, created only if requested.
* This is needed only for visualizing the image on screen; most tests do
not need it.
+ *
+ * @see #getColorModel()
*/
private ColorModel colorModel;
@@ -169,7 +184,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* More color models may be supported in future versions if there is a
need for them.
*/
@Override
- public ColorModel getColorModel() {
+ public synchronized ColorModel getColorModel() {
if (colorModel == null && sampleModel instanceof ComponentSampleModel
&& sampleModel.getNumBands() == 1) {
final int dataType = sampleModel.getDataType();
if (dataType <= DataBuffer.TYPE_USHORT) {
@@ -205,19 +220,33 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
*
* @see #verify()
*/
- public void validate() {
+ public synchronized void validate() {
assertNull(verify());
}
/**
* Causes some {@link #getTile(int, int)} and {@link #getWritableTile(int,
int)} calls to fail.
- * Note that two consecutive invocations of {@code getTile(…)} may give
different result.
+ * if {@code onEachCall} is {@code true}, then two consecutive invocations
of {@code getTile(…)}
+ * may give different result.
*
- * @param random the random number generator to use for deciding if a
tile request should fail.
+ * @param random the random number generator to use for deciding if
a tile request should fail.
+ * @param onEachCall {@code true} if failure may happen on any {@code
getTile(…)}, or {@code false}
+ * for constant behavior when {@code getTile(…)} is
invoked with same tile indices.
*/
- public void failRandomly(final Random random) {
- randomFailures = random;
- errorSequence = new AtomicInteger();
+ public synchronized void failRandomly(final Random random, final boolean
onEachCall) {
+ if (onEachCall) {
+ randomFailures = random;
+ constantFailures = null;
+ } else {
+ randomFailures = null;
+ constantFailures = new boolean[tiles.length];
+ for (int i=0; i<constantFailures.length; i++) {
+ constantFailures[i] = (random.nextInt(FAILURE_PROBABILITY) ==
0);
+ }
+ }
+ if (errorSequence == null) {
+ errorSequence = new AtomicInteger();
+ }
}
/**
@@ -230,7 +259,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* @param b band index of the sample value to set.
* @param value the new value.
*/
- public void setSample(final int x, final int y, final int b, final double
value) {
+ public synchronized void setSample(final int x, final int y, final int b,
final double value) {
final int ox = x - minX;
final int oy = y - minY;
if (ox < 0 || ox >= width || oy < 0 || oy >= height) {
@@ -254,7 +283,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
*
* @param band band index where to set values. Other bands will be
unmodified.
*/
- public void initializeAllTiles(final int band) {
+ public synchronized void initializeAllTiles(final int band) {
int ti = 0;
for (int ty=0; ty<numYTiles; ty++) {
for (int tx=0; tx<numXTiles; tx++) {
@@ -281,7 +310,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* @param generator the random number generator to use for obtaining
values.
* @param upper upper limit (exclusive) of random numbers to
generate.
*/
- public void setRandomValues(final int band, final Random generator, final
int upper) {
+ public synchronized void setRandomValues(final int band, final Random
generator, final int upper) {
for (final WritableRaster raster : tiles) {
final int x = raster.getMinX();
final int y = raster.getMinY();
@@ -302,7 +331,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* any future Apache SIS version.
*/
@Override
- public Raster getTile(final int tileX, final int tileY) {
+ public synchronized Raster getTile(final int tileX, final int tileY) {
assertFalse("isTileAcquired", isTileAcquired); // See
javadoc.
return tile(tileX, tileY, false);
}
@@ -311,7 +340,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* Returns the tile at the given location tile coordinates.
*/
@Override
- public WritableRaster getWritableTile(final int tileX, final int tileY) {
+ public synchronized WritableRaster getWritableTile(final int tileX, final
int tileY) {
assertFalse("isTileAcquired", isTileAcquired);
final WritableRaster raster = tile(tileX, tileY, true);
isTileAcquired = true;
@@ -330,11 +359,13 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
{
throw new IndexOutOfBoundsException();
}
- if (randomFailures != null && randomFailures.nextInt(10) == 0) {
+ final int i = tileY * numXTiles + tileX;
+ if ((constantFailures != null && constantFailures[i]) ||
+ (randomFailures != null &&
randomFailures.nextInt(FAILURE_PROBABILITY) == 0))
+ {
throw new ImagingOpException("Artificial error #" +
errorSequence.incrementAndGet()
+ " on tile (" + tileX + ", " + tileY +
").");
}
- final int i = tileY * numXTiles + tileX;
WritableRaster raster = tiles[i];
if (raster == null) {
if (!allowCreate) {
@@ -351,7 +382,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* Verifies that the given tile has been acquired.
*/
@Override
- public void releaseWritableTile(final int tileX, final int tileY) {
+ public synchronized void releaseWritableTile(final int tileX, final int
tileY) {
assertTrue("isTileAcquired", isTileAcquired);
assertEquals("tileX", acquiredTileX, tileX);
assertEquals("tileY", acquiredTileY, tileY);
@@ -363,7 +394,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* {@link #getWritableTile(int, int)} and that tile has not yet been
released.
*/
@Override
- public boolean isTileWritable(final int tileX, final int tileY) {
+ public synchronized boolean isTileWritable(final int tileX, final int
tileY) {
return isTileAcquired && (tileX == acquiredTileX) && (tileY ==
acquiredTileY);
}
@@ -371,7 +402,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* Returns {@code false} since we do not keep track of who called {@link
#getWritableTile(int,int)}.
*/
@Override
- public boolean hasTileWriters() {
+ public synchronized boolean hasTileWriters() {
return isTileAcquired;
}
@@ -379,7 +410,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* Returns the indices of acquired tile, or {@code null} if none.
*/
@Override
- public Point[] getWritableTileIndices() {
+ public synchronized Point[] getWritableTileIndices() {
return isTileAcquired ? new Point[] {new Point(acquiredTileX,
acquiredTileY)} : null;
}
@@ -403,7 +434,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
* Current implementation can set raster covering only one tile.
*/
@Override
- public void setData(final Raster r) {
+ public synchronized void setData(final Raster r) {
final int minX = r.getMinX();
final int minY = r.getMinY();
final int tx = ImageUtilities.pixelToTileX(this, minX);
@@ -419,7 +450,7 @@ public final strictfp class TiledImageMock extends
PlanarImage implements Writab
*
* @return this image as a more complete implementation.
*/
- public WritableTiledImage toWritableTiledImage() {
+ public synchronized WritableTiledImage toWritableTiledImage() {
return new WritableTiledImage(null, null, width, height, minTileX,
minTileY, tiles);
}
}