diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
index dbc613dda65e9cdc5270ac77a4734a2844583820..dc7dfbb46460f146566ffe723741858016d2549e 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
@@ -16,8 +16,6 @@
 
 package ch.systemsx.cisd.openbis.dss.generic.server;
 
-import java.awt.Color;
-import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
 
@@ -26,11 +24,10 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
 import ch.systemsx.cisd.bds.hcs.Location;
-import ch.systemsx.cisd.bds.storage.INode;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.server.images.TileImageReference;
 
 /**
  * ABstract class for servlets which allow to download screening images in a chosen size for a
@@ -45,10 +42,10 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
     /**
      * @throw EnvironmentFailureException if image does not exist
      */
-    protected abstract ResponseContentStream createImageResponse(RequestParams params,
+    protected abstract ResponseContentStream createImageResponse(TileImageReference params,
             File datasetRoot) throws IOException, EnvironmentFailureException;
 
-    protected static class RequestParams
+    protected static class RequestParams extends TileImageReference
     {
         // -- optional servlet parameters
 
@@ -68,36 +65,31 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
 
         private final static String CHANNEL_PARAM = "channel";
 
-        private final String sessionId;
-
-        private final String displayMode;
-
-        private final String datasetCode;
-
-        private final Location wellLocation;
+        public static TileImageReference createTileImageReference(HttpServletRequest request)
+        {
+            return new RequestParams(request);
+        }
 
-        private final Location tileLocation;
+        private RequestParams(HttpServletRequest request)
+        {
+            this.sessionId = getParam(request, SESSION_ID_PARAM);
 
-        private boolean mergeAllChannels;
+            String displayModeText = request.getParameter(DISPLAY_MODE_PARAM);
+            String displayMode = displayModeText == null ? "" : displayModeText;
+            this.thumbnailSizeOrNull = tryAsThumbnailDisplayMode(displayMode);
 
-        // contains the channel number or the number of all channels if all of them should be merged
-        private int channel;
+            this.datasetCode = getParam(request, DATASET_CODE_PARAM);
 
-        public RequestParams(HttpServletRequest request)
-        {
-            sessionId = getParam(request, SESSION_ID_PARAM);
-            String displayModeText = request.getParameter(DISPLAY_MODE_PARAM);
-            displayMode = displayModeText == null ? "" : displayModeText;
-            datasetCode = getParam(request, DATASET_CODE_PARAM);
             int wellRow = getIntParam(request, WELL_ROW_PARAM);
             int wellCol = getIntParam(request, WELL_COLUMN_PARAM);
             int tileRow = getIntParam(request, TILE_ROW_PARAM);
             int tileCol = getIntParam(request, TILE_COL_PARAM);
             this.wellLocation = new Location(wellCol, wellRow);
             this.tileLocation = new Location(tileCol, tileRow);
-            channel = getIntParam(request, CHANNEL_PARAM);
+
+            this.channel = getIntParam(request, CHANNEL_PARAM);
             String mergeChannelsText = request.getParameter(MERGE_CHANNELS_PARAM);
-            mergeAllChannels =
+            this.mergeAllChannels =
                     (mergeChannelsText == null) ? false : mergeChannelsText
                             .equalsIgnoreCase("true");
         }
@@ -125,41 +117,6 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
             }
             return value;
         }
-
-        public String getSessionId()
-        {
-            return sessionId;
-        }
-
-        public String getDatasetCode()
-        {
-            return datasetCode;
-        }
-
-        public String getDisplayMode()
-        {
-            return displayMode;
-        }
-
-        public boolean isMergeAllChannels()
-        {
-            return mergeAllChannels;
-        }
-
-        public int getChannel()
-        {
-            return channel;
-        }
-
-        public Location getWellLocation()
-        {
-            return wellLocation;
-        }
-
-        public Location getTileLocation()
-        {
-            return tileLocation;
-        }
     }
 
     @Override
@@ -168,7 +125,7 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
     {
         try
         {
-            RequestParams params = new RequestParams(request);
+            TileImageReference params = RequestParams.createTileImageReference(request);
             HttpSession session = tryGetOrCreateSession(request, params.getSessionId());
             if (session == null)
             {
@@ -185,7 +142,7 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
 
     }
 
-    protected void deliverFile(HttpServletResponse response, RequestParams params,
+    protected void deliverFile(HttpServletResponse response, TileImageReference params,
             HttpSession session) throws IOException
     {
         ensureDatasetAccessible(params.getDatasetCode(), session, params.getSessionId());
@@ -205,25 +162,7 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
         writeResponseContent(responseStream, response);
     }
 
-    /** throws {@link EnvironmentFailureException} when image does not exist */
-    protected final static File getPath(HCSDatasetLoader imageAccessor, RequestParams params,
-            int chosenChannel)
-    {
-        INode image =
-                imageAccessor.tryGetStandardNodeAt(chosenChannel, params.getWellLocation(), params
-                        .getTileLocation());
-        if (image != null)
-        {
-            return new File(image.getPath());
-        } else
-        {
-            throw EnvironmentFailureException.fromTemplate(
-                    "No image found for well %s, tile %s and channel %d", params.getWellLocation(),
-                    params.getTileLocation(), chosenChannel);
-        }
-    }
-
-    protected final static void logImageDelivery(RequestParams params,
+    protected final static void logImageDelivery(TileImageReference params,
             ResponseContentStream responseStream)
     {
         if (operationLog.isInfoEnabled())
@@ -232,82 +171,4 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
                     + responseStream.getSize() + " bytes)");
         }
     }
-
-    protected final static BufferedImage asThumbnailIfRequested(RequestParams params,
-            BufferedImage image)
-    {
-        Size thumbnailSizeOrNull = tryAsThumbnailDisplayMode(params.getDisplayMode());
-        if (thumbnailSizeOrNull != null)
-        {
-            return createThumbnail(image, thumbnailSizeOrNull);
-        } else
-        {
-            return image;
-        }
-    }
-
-    protected final static BufferedImage transformToChannel(BufferedImage bufferedImage,
-            int channelNumber)
-    {
-        BufferedImage newImage = createNewImage(bufferedImage);
-        int width = bufferedImage.getWidth();
-        int height = bufferedImage.getHeight();
-        for (int x = 0; x < width; x++)
-        {
-            for (int y = 0; y < height; y++)
-            {
-                int rgb = bufferedImage.getRGB(x, y);
-                int channelColor = getGrayscaleAsChannel(rgb, channelNumber);
-                newImage.setRGB(x, y, channelColor);
-            }
-        }
-        return newImage;
-    }
-
-    protected final static BufferedImage createNewImage(BufferedImage bufferedImage)
-    {
-        BufferedImage newImage =
-                new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(),
-                        BufferedImage.TYPE_INT_RGB);
-        return newImage;
-    }
-
-    protected final static int getRGBColorIndex(int channel)
-    {
-        assert channel <= 3 : "to many channels: " + channel;
-        return 3 - channel;
-    }
-
-    protected final static int getGrayscaleAsChannel(int rgb, int channelNumber)
-    {
-        // NOTE: we handle only 3 channels until we know that more channels can be used and what
-        // kind of color manipulation makes sense
-        if (channelNumber <= 3)
-        {
-            // we assume that the color was in a grayscale
-            // we reset all ingredients besides the one which should be shown
-            int newColor[] = new int[]
-                { 0, 0, 0 };
-            newColor[getRGBColorIndex(channelNumber)] =
-                    extractChannelColorIngredient(rgb, channelNumber);
-            return asRGB(newColor);
-        } else
-        {
-            return rgb;
-        }
-    }
-
-    // returns the ingredient for the specified channel
-    protected final static int extractChannelColorIngredient(int rgb, int channelNumber)
-    {
-        Color c = new Color(rgb);
-        int channelColors[] = new int[]
-            { c.getBlue(), c.getGreen(), c.getRed() };
-        return channelColors[channelNumber - 1];
-    }
-
-    protected final static int asRGB(int[] rgb)
-    {
-        return new Color(rgb[0], rgb[1], rgb[2]).getRGB();
-    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
index dfac20074d94a45a8970b4a89b32df6b48fc48c0..a2f4ee4a544f5164a057981ebd5f8cde4d1509cd 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
@@ -19,12 +19,11 @@ package ch.systemsx.cisd.openbis.dss.generic.server;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
 
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
-import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
+import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelsUtils;
+import ch.systemsx.cisd.openbis.dss.generic.server.images.TileImageReference;
 
 /**
  * Allows to download screening images in a chosen size for a specified channels or with all
@@ -38,97 +37,17 @@ public class MergingImagesDownloadServlet extends AbstractImagesDownloadServlet
 {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * @throws EnvironmentFailureException if image does not exist
+     **/
     @Override
-    protected final ResponseContentStream createImageResponse(RequestParams params, File datasetRoot)
-            throws IOException, EnvironmentFailureException /* if image does not exist */
+    protected final ResponseContentStream createImageResponse(TileImageReference params,
+            File datasetRoot) throws IOException, EnvironmentFailureException
     {
-        List<File> imageFiles = getImagePaths(datasetRoot, params);
-        BufferedImage image = mergeImages(imageFiles, params);
+        List<File> imageFiles = ImageChannelsUtils.getImagePaths(datasetRoot, params);
+        BufferedImage image = ImageChannelsUtils.mergeImageChannels(params, imageFiles);
         File singleFileOrNull = imageFiles.size() == 1 ? imageFiles.get(0) : null;
         return createResponseContentStream(image, singleFileOrNull);
     }
 
-    /** throws {@link EnvironmentFailureException} when image does not exist */
-    private static List<File> getImagePaths(File datasetRoot, RequestParams params)
-    {
-        HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
-        List<File> paths = new ArrayList<File>();
-
-        if (params.isMergeAllChannels())
-        {
-            for (int chosenChannel = 1; chosenChannel <= params.getChannel(); chosenChannel++)
-            {
-                File path = getPath(imageAccessor, params, chosenChannel);
-                paths.add(path);
-            }
-        } else
-        {
-            File path = getPath(imageAccessor, params, params.getChannel());
-            paths.add(path);
-        }
-        imageAccessor.close();
-
-        return paths;
-    }
-
-    private static BufferedImage mergeImages(List<File> imageFiles, RequestParams params)
-    {
-        List<BufferedImage> images = loadImages(imageFiles, params);
-
-        BufferedImage resultImage;
-        if (images.size() == 1)
-        {
-            resultImage = transformToChannel(images.get(0), params.getChannel());
-        } else
-        {
-            resultImage = mergeChannels(images);
-        }
-        resultImage = asThumbnailIfRequested(params, resultImage);
-        return resultImage;
-    }
-
-    private static BufferedImage mergeChannels(List<BufferedImage> images)
-    {
-        assert images.size() > 1 : "more than 1 image expected, but found: " + images.size();
-        BufferedImage newImage = createNewImage(images.get(0));
-        int width = newImage.getWidth();
-        int height = newImage.getHeight();
-        for (int x = 0; x < width; x++)
-        {
-            for (int y = 0; y < height; y++)
-            {
-                int mergedRGB = mergeRGBColor(images, x, y);
-                newImage.setRGB(x, y, mergedRGB);
-            }
-        }
-        return newImage;
-    }
-
-    // NOTE: we handle only 3 channels until we know that more channels can be used and
-    // what
-    // kind of color manipulation makes sense
-    private static int mergeRGBColor(List<BufferedImage> images, int x, int y)
-    {
-        int color[] = new int[]
-            { 0, 0, 0 };
-        for (int channel = 1; channel <= Math.min(3, images.size()); channel++)
-        {
-            int rgb = images.get(channel - 1).getRGB(x, y);
-            color[getRGBColorIndex(channel)] = extractChannelColorIngredient(rgb, channel);
-        }
-        int mergedRGB = asRGB(color);
-        return mergedRGB;
-    }
-
-    private static List<BufferedImage> loadImages(List<File> imageFiles, RequestParams params)
-    {
-        List<BufferedImage> images = new ArrayList<BufferedImage>();
-        for (File imageFile : imageFiles)
-        {
-            BufferedImage image = ImageUtil.loadImage(imageFile);
-            images.add(image);
-        }
-        return images;
-    }
-
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
index 0393510f280faebdf6b21f39d758c1926db03c69..82739794b59e9f341e52130b3620bbb8bc563b67 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
@@ -21,8 +21,10 @@ import java.io.File;
 import java.io.IOException;
 
 import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
+import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
-import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
+import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelsUtils;
+import ch.systemsx.cisd.openbis.dss.generic.server.images.TileImageReference;
 
 /**
  * Allows to download screening images in a chosen size for a specified channels or with all
@@ -36,28 +38,28 @@ public class SplittingImagesDownloadServlet extends AbstractImagesDownloadServle
     private static final long serialVersionUID = 1L;
 
     /** throws {@link EnvironmentFailureException} when image does not exist */
-    private File getImagePath(File datasetRoot, RequestParams params)
+    private File getImagePath(File datasetRoot, TileImageReference params)
+            throws EnvironmentFailureException
     {
         HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
+        Location wellLocation = params.getWellLocation();
+        Location tileLocation = params.getTileLocation();
         int channel = 1; // NOTE: we assume that there is only one channel
-        File path = getPath(imageAccessor, params, channel);
+        File path =
+                ImageChannelsUtils.getImagePath(imageAccessor, wellLocation, tileLocation, channel);
         imageAccessor.close();
         return path;
     }
 
+    /**
+     * @throws EnvironmentFailureException if image does not exist
+     **/
     @Override
-    protected final ResponseContentStream createImageResponse(RequestParams params, File datasetRoot)
-            throws IOException, EnvironmentFailureException /* if image does not exist */
+    protected final ResponseContentStream createImageResponse(TileImageReference params,
+            File datasetRoot) throws IOException, EnvironmentFailureException
     {
         File imageFile = getImagePath(datasetRoot, params);
-        BufferedImage image = ImageUtil.loadImage(imageFile);
-        if (params.isMergeAllChannels() == false)
-        {
-            image = transformToChannel(image, params.getChannel());
-        }
-        image = asThumbnailIfRequested(params, image);
-
+        BufferedImage image = ImageChannelsUtils.mergeImageChannels(params, imageFile);
         return createResponseContentStream(image, imageFile);
     }
-
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1813dcd0f0eea08ceded3d2ce6fa062845d41d42
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed 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 ch.systemsx.cisd.openbis.dss.generic.server.images;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
+import ch.systemsx.cisd.bds.hcs.Location;
+import ch.systemsx.cisd.bds.storage.INode;
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDatasetDownloadServlet.Size;
+import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
+
+/**
+ * Utility classes to create an image of a specified size containing one channel or a subset of
+ * all channels.
+ * 
+ * @author Tomasz Pylak
+ */
+public class ImageChannelsUtils
+{
+
+    /**
+     * Creates an images of the specified tile and with requested channels merged.<br>
+     * Assumes that originally all channels are merged on one image.
+     */
+    public static BufferedImage mergeImageChannels(TileImageReference params, File imageFile)
+    {
+        BufferedImage image = ImageUtil.loadImage(imageFile);
+        if (params.isMergeAllChannels() == false)
+        {
+            image = transformToChannel(image, params.getChannel());
+        }
+        image = asThumbnailIfRequested(params, image);
+        return image;
+    }
+
+    /**
+     * @return file with the image for the chosen channel or images for all channels if they should
+     *         be merged.
+     * @throw {@link EnvironmentFailureException} when image does not exist
+     */
+    public static List<File> getImagePaths(File datasetRoot, TileImageReference params)
+    {
+        HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
+        Location wellLocation = params.getWellLocation();
+        Location tileLocation = params.getTileLocation();
+        List<File> paths = new ArrayList<File>();
+
+        if (params.isMergeAllChannels())
+        {
+            for (int chosenChannel = 1; chosenChannel <= params.getChannel(); chosenChannel++)
+            {
+                File path = getImagePath(imageAccessor, wellLocation, tileLocation, chosenChannel);
+                paths.add(path);
+            }
+        } else
+        {
+            File path =
+                    getImagePath(imageAccessor, wellLocation, tileLocation, params.getChannel());
+            paths.add(path);
+        }
+        imageAccessor.close();
+
+        return paths;
+    }
+
+    /**
+     * Creates an images of the specified tile and with requested channels merged.<br>
+     * Assumes that different channels are on separate images.
+     */
+    public static BufferedImage mergeImageChannels(TileImageReference params, List<File> imageFiles)
+    {
+        List<BufferedImage> images = loadImages(imageFiles);
+
+        BufferedImage resultImage;
+        if (images.size() == 1)
+        {
+            resultImage = transformToChannel(images.get(0), params.getChannel());
+        } else
+        {
+            resultImage = mergeChannels(images);
+        }
+        resultImage = asThumbnailIfRequested(params, resultImage);
+        return resultImage;
+    }
+
+    private static BufferedImage mergeChannels(List<BufferedImage> images)
+    {
+        assert images.size() > 1 : "more than 1 image expected, but found: " + images.size();
+        BufferedImage newImage = createNewImage(images.get(0));
+        int width = newImage.getWidth();
+        int height = newImage.getHeight();
+        for (int x = 0; x < width; x++)
+        {
+            for (int y = 0; y < height; y++)
+            {
+                int mergedRGB = mergeRGBColor(images, x, y);
+                newImage.setRGB(x, y, mergedRGB);
+            }
+        }
+        return newImage;
+    }
+
+    // NOTE: we handle only 3 channels until we know that more channels can be used and
+    // what
+    // kind of color manipulation makes sense
+    private static int mergeRGBColor(List<BufferedImage> images, int x, int y)
+    {
+        int color[] = new int[]
+            { 0, 0, 0 };
+        for (int channel = 1; channel <= Math.min(3, images.size()); channel++)
+        {
+            int rgb = images.get(channel - 1).getRGB(x, y);
+            color[getRGBColorIndex(channel)] = extractChannelColorIngredient(rgb, channel);
+        }
+        int mergedRGB = asRGB(color);
+        return mergedRGB;
+    }
+
+    private static List<BufferedImage> loadImages(List<File> imageFiles)
+    {
+        List<BufferedImage> images = new ArrayList<BufferedImage>();
+        for (File imageFile : imageFiles)
+        {
+            BufferedImage image = ImageUtil.loadImage(imageFile);
+            images.add(image);
+        }
+        return images;
+    }
+
+    // --------- common
+
+    /** throws {@link EnvironmentFailureException} when image does not exist */
+    public static File getImagePath(HCSDatasetLoader imageAccessor, Location wellLocation,
+            Location tileLocation, int chosenChannel)
+    {
+        INode image = imageAccessor.tryGetStandardNodeAt(chosenChannel, wellLocation, tileLocation);
+        if (image != null)
+        {
+            return new File(image.getPath());
+        } else
+        {
+            throw EnvironmentFailureException.fromTemplate(
+                    "No image found for well %s, tile %s and channel %d", wellLocation,
+                    tileLocation, chosenChannel);
+        }
+    }
+
+    private static BufferedImage asThumbnailIfRequested(TileImageReference params,
+            BufferedImage image)
+    {
+        Size thumbnailSizeOrNull = params.tryGetThumbnailSize();
+        if (thumbnailSizeOrNull != null)
+        {
+            int width = thumbnailSizeOrNull.getWidth();
+            int height = thumbnailSizeOrNull.getHeight();
+            return ImageUtil.createThumbnail(image, width, height);
+        } else
+        {
+            return image;
+        }
+    }
+
+    private static BufferedImage transformToChannel(BufferedImage bufferedImage, int channelNumber)
+    {
+        BufferedImage newImage = createNewImage(bufferedImage);
+        int width = bufferedImage.getWidth();
+        int height = bufferedImage.getHeight();
+        for (int x = 0; x < width; x++)
+        {
+            for (int y = 0; y < height; y++)
+            {
+                int rgb = bufferedImage.getRGB(x, y);
+                int channelColor = getGrayscaleAsChannel(rgb, channelNumber);
+                newImage.setRGB(x, y, channelColor);
+            }
+        }
+        return newImage;
+    }
+
+    private static BufferedImage createNewImage(BufferedImage bufferedImage)
+    {
+        BufferedImage newImage =
+                new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(),
+                        BufferedImage.TYPE_INT_RGB);
+        return newImage;
+    }
+
+    private static int getRGBColorIndex(int channel)
+    {
+        assert channel <= 3 : "to many channels: " + channel;
+        return 3 - channel;
+    }
+
+    private static int getGrayscaleAsChannel(int rgb, int channelNumber)
+    {
+        // NOTE: we handle only 3 channels until we know that more channels can be used and what
+        // kind of color manipulation makes sense
+        if (channelNumber <= 3)
+        {
+            // we assume that the color was in a grayscale
+            // we reset all ingredients besides the one which should be shown
+            int newColor[] = new int[]
+                { 0, 0, 0 };
+            newColor[getRGBColorIndex(channelNumber)] =
+                    extractChannelColorIngredient(rgb, channelNumber);
+            return asRGB(newColor);
+        } else
+        {
+            return rgb;
+        }
+    }
+
+    // returns the ingredient for the specified channel
+    private static int extractChannelColorIngredient(int rgb, int channelNumber)
+    {
+        Color c = new Color(rgb);
+        int channelColors[] = new int[]
+            { c.getBlue(), c.getGreen(), c.getRed() };
+        return channelColors[channelNumber - 1];
+    }
+
+    private static int asRGB(int[] rgb)
+    {
+        return new Color(rgb[0], rgb[1], rgb[2]).getRGB();
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/TileImageReference.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/TileImageReference.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea1b42507f49ca5e409f3d02f3d926f33dd54ad6
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/TileImageReference.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed 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 ch.systemsx.cisd.openbis.dss.generic.server.images;
+
+import ch.systemsx.cisd.bds.hcs.Location;
+import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDatasetDownloadServlet.Size;
+
+/**
+ * DTO to point to a screening image or its thumbnail, optionally with all channels merged.
+ * 
+ * @author Tomasz Pylak
+ */
+public class TileImageReference
+{
+    protected String sessionId;
+
+    // original size if null
+    protected Size thumbnailSizeOrNull;
+
+    protected String datasetCode;
+
+    protected Location wellLocation;
+
+    protected Location tileLocation;
+
+    protected boolean mergeAllChannels;
+
+    // contains the channel number or the number of all channels if all of them should be merged
+    protected int channel;
+
+    public String getSessionId()
+    {
+        return sessionId;
+    }
+
+    public String getDatasetCode()
+    {
+        return datasetCode;
+    }
+
+    public Size tryGetThumbnailSize()
+    {
+        return thumbnailSizeOrNull;
+    }
+
+    public boolean isMergeAllChannels()
+    {
+        return mergeAllChannels;
+    }
+
+    public int getChannel()
+    {
+        return channel;
+    }
+
+    public Location getWellLocation()
+    {
+        return wellLocation;
+    }
+
+    public Location getTileLocation()
+    {
+        return tileLocation;
+    }
+}
\ No newline at end of file