sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Add the possibility to substitute a tile placeholder in "prefetch" operation if an error handler has been specified.
Date Fri, 05 Feb 2021 22:55:06 GMT
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 <martin.desruisseaux@geomatys.com>
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);
     }
 }


Mime
View raw message