diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java
index 26209b5feffe6ed86d760dd5dd9e9356fc7dfa5a..52521107b7437e7f3021674922efaa7e8e8be5e7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/ImageUtil.java
@@ -160,14 +160,19 @@ public class ImageUtil
      */
     public static boolean isImageFile(File file)
     {
-        String name = file.getName();
+        String fileName = file.getName();
+        String fileType = tryGetFileExtension(fileName);
+        return fileType != null && FILE_TYPES.contains(fileType);
+    }
+
+    private static String tryGetFileExtension(String name)
+    {
         int lastIndexOfDot = name.lastIndexOf('.');
         if (lastIndexOfDot < 0)
         {
-            return false;
+            return null;
         }
-        String fileType = name.substring(lastIndexOfDot + 1).toLowerCase();
-        return FILE_TYPES.contains(fileType);
+        return name.substring(lastIndexOfDot + 1).toLowerCase();
     }
 
     /**
@@ -267,7 +272,7 @@ public class ImageUtil
      */
     public static BufferedImage createThumbnail(BufferedImage image, int maxWidth, int maxHeight)
     {
-        return rescale(image, maxWidth, maxHeight, true, false);
+        return rescale(image, maxWidth, maxHeight, true);
     }
 
     /**
@@ -279,11 +284,9 @@ public class ImageUtil
      * @param maxHeight Maximum height of the result image.
      * @param enlargeIfNecessary if false and the image has smaller width and height than the
      *            specified limit, then the image is not changed.
-     * @param preserveAlpha if true alpha channel will be not ignored (and image will take more
-     *            space)
      */
     public static BufferedImage rescale(BufferedImage image, int maxWidth, int maxHeight,
-            boolean enlargeIfNecessary, boolean preserveAlpha)
+            boolean enlargeIfNecessary)
     {
         int width = image.getWidth();
         int height = image.getHeight();
@@ -304,7 +307,10 @@ public class ImageUtil
         int thumbnailWidth = (int) (scale * width + 0.5);
         int thumbnailHeight = (int) (scale * height + 0.5);
 
-        int imageType = preserveAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
+        // preserve alpha channel if it was present before
+        int imageType =
+                image.getColorModel().hasAlpha() ? BufferedImage.TYPE_INT_ARGB
+                        : BufferedImage.TYPE_INT_RGB;
         BufferedImage thumbnail = new BufferedImage(thumbnailWidth, thumbnailHeight, imageType);
         Graphics2D graphics2D = thumbnail.createGraphics();
         graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java
index 1997be2239d431af55526c429e64cfbbcf41f5b8..1fd91d69d5709699e3614f5e81fe8772bd25459a 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java
@@ -72,7 +72,7 @@ class Hdf5ThumbnailGenerator implements IHdf5WriterClient
             File img = new File(imagesInStoreFolder, imagePath);
             BufferedImage image = ImageUtil.loadImage(img);
             BufferedImage thumbnail =
-                    ImageUtil.rescale(image, thumbnailMaxWidth, thumbnailMaxHeight, false, true);
+                    ImageUtil.rescale(image, thumbnailMaxWidth, thumbnailMaxHeight, false);
             ByteArrayOutputStream output = new ByteArrayOutputStream();
             try
             {
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
index b16f7d65062f0fbcd4f0b6c9ceb844e6e59a892b..4e4ab5dc8979dc19a9c4139486f7a74937f62d4b 100644
--- 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
@@ -115,24 +115,24 @@ public class ImageChannelsUtils
         {
             // NOTE: never merges the overlays, draws each channel separately (merging looses
             // transparency and is slower)
-            List<BufferedImage> overlayImages =
+            List<ImageWithReference> overlayImages =
                     getSingleImagesSkipNonExisting(overlayChannels, overlaySize,
                             datasetDirectoryProvider);
-            for (BufferedImage overlayImage : overlayImages)
+            for (ImageWithReference overlayImage : overlayImages)
             {
                 if (image != null)
                 {
                     drawOverlay(image, overlayImage);
                 } else
                 {
-                    image = overlayImage;
+                    image = overlayImage.getBufferedImage();
                 }
             }
         }
         return createResponseContentStream(image, null);
     }
 
-    private static List<BufferedImage> getSingleImagesSkipNonExisting(
+    private static List<ImageWithReference> getSingleImagesSkipNonExisting(
             DatasetAcquiredImagesReference imagesReference, RequestedImageSize imageSize,
             IDatasetDirectoryProvider datasetDirectoryProvider)
     {
@@ -445,7 +445,7 @@ public class ImageChannelsUtils
             start = operationLog.isDebugEnabled() ? System.currentTimeMillis() : 0;
             image =
                     ImageUtil.rescale(image, size.getWidth(), size.getHeight(),
-                            requestedSize.enlargeIfNecessary(), true);
+                            requestedSize.enlargeIfNecessary());
             if (operationLog.isDebugEnabled())
             {
                 operationLog.debug("Create thumbnail: " + (System.currentTimeMillis() - start));
@@ -492,8 +492,8 @@ public class ImageChannelsUtils
             return calculateAndTransformSingleImage(imageReference, transform, allChannelsMerged);
         } else
         {
-            List<BufferedImage> images = calculateSingleImages(imageReferences);
-            BufferedImage mergedImage = mergeImages(images, imageReferences);
+            List<ImageWithReference> images = calculateSingleImages(imageReferences);
+            BufferedImage mergedImage = mergeImages(images);
             // NOTE: even if we are not merging all the channels but just few of them we use the
             // merged-channel transformation
             IImageTransformerFactory transformerFactory =
@@ -513,13 +513,37 @@ public class ImageChannelsUtils
         return factoryOrNull.createTransformer().transform(input);
     }
 
-    private static List<BufferedImage> calculateSingleImages(
+    private static class ImageWithReference
+    {
+        private final BufferedImage image;
+
+        private final AbsoluteImageReference reference;
+
+        public ImageWithReference(BufferedImage image, AbsoluteImageReference reference)
+        {
+            this.image = image;
+            this.reference = reference;
+        }
+
+        public BufferedImage getBufferedImage()
+        {
+            return image;
+        }
+
+        public AbsoluteImageReference getReference()
+        {
+            return reference;
+        }
+    }
+
+    private static List<ImageWithReference> calculateSingleImages(
             List<AbsoluteImageReference> imageReferences)
     {
-        List<BufferedImage> images = new ArrayList<BufferedImage>();
+        List<ImageWithReference> images = new ArrayList<ImageWithReference>();
         for (AbsoluteImageReference imageRef : imageReferences)
         {
-            images.add(calculateSingleImage(imageRef));
+            BufferedImage image = calculateSingleImage(imageRef);
+            images.add(new ImageWithReference(image, imageRef));
         }
         return images;
     }
@@ -559,13 +583,11 @@ public class ImageChannelsUtils
         return (i1OrNull == null) ? (i2OrNull == null) : i1OrNull.equals(i2OrNull);
     }
 
-    private static BufferedImage mergeImages(List<BufferedImage> images,
-            List<AbsoluteImageReference> imageReferences)
+    private static BufferedImage mergeImages(List<ImageWithReference> images)
     {
         assert images.size() > 1 : "more than 1 image expected, but found: " + images.size();
-        assert images.size() == imageReferences.size() : "images.size() != imageReferences.size()";
 
-        BufferedImage newImage = createNewImage(images.get(0));
+        BufferedImage newImage = createNewImage(images.get(0).getBufferedImage());
         int width = newImage.getWidth();
         int height = newImage.getHeight();
         int colorBuffer[] = new int[4];
@@ -573,30 +595,89 @@ public class ImageChannelsUtils
         {
             for (int y = 0; y < height; y++)
             {
-                int mergedRGB = mergeRGBColor(images, imageReferences, x, y, colorBuffer);
+                int mergedRGB = mergeRGBColor(images, x, y, colorBuffer);
                 newImage.setRGB(x, y, mergedRGB);
             }
         }
         return newImage;
     }
 
-    private static void drawOverlay(BufferedImage image, BufferedImage overlayImage)
+    private static void drawOverlay(BufferedImage image, ImageWithReference overlayImage)
+    {
+        BufferedImage overlayBufferedImage = overlayImage.getBufferedImage();
+        if (supportsTransparency(overlayImage))
+        {
+            drawTransparentOverlayFast(image, overlayBufferedImage);
+        } else
+        {
+            drawOverlaySlow(image, overlayBufferedImage);
+        }
+    }
+
+    private static void drawTransparentOverlayFast(BufferedImage image,
+            BufferedImage overlayBufferedImage)
     {
         Graphics2D graphics = image.createGraphics();
         AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
         graphics.setComposite(ac);
-        graphics.drawImage(overlayImage, null, null);
+        graphics.drawImage(overlayBufferedImage, null, null);
+    }
+
+    /**
+     * Draws overlay by computing the maximal color components (r,g,b) for each pixel. In this way
+     * both transparent and black pixels are not drawn. Useful when overlays are saved in a format
+     * which does not support transparency.
+     */
+    private static void drawOverlaySlow(BufferedImage image, BufferedImage overlayImage)
+    {
+        int width = Math.min(image.getWidth(), overlayImage.getWidth());
+        int height = Math.min(image.getHeight(), overlayImage.getHeight());
+
+        for (int x = 0; x < width; x++)
+        {
+            for (int y = 0; y < height; y++)
+            {
+                int imageRGB = image.getRGB(x, y);
+                int overlayRGB = overlayImage.getRGB(x, y);
+                int overlayedRGB = overlayRGBColor(imageRGB, overlayRGB);
+                image.setRGB(x, y, overlayedRGB);
+            }
+        }
+    }
+
+    // creates a color with a maximum value of each component
+    private static int overlayRGBColor(int imageRGB, int overlayRGB)
+    {
+        Color imageColor = new Color(imageRGB);
+        Color overlayColor = new Color(overlayRGB, true);
+
+        if (overlayColor.getAlpha() == 0)
+        {
+            return imageRGB; // overlay is transparent, return the original pixel
+        } else
+        {
+            int r = Math.max(imageColor.getRed(), overlayColor.getRed());
+            int g = Math.max(imageColor.getGreen(), overlayColor.getGreen());
+            int b = Math.max(imageColor.getBlue(), overlayColor.getBlue());
+            return new Color(r, g, b).getRGB();
+        }
+    }
+
+    private static boolean supportsTransparency(ImageWithReference image)
+    {
+        return image.getBufferedImage().getColorModel().hasAlpha();
     }
 
-    private static int mergeRGBColor(List<BufferedImage> images,
-            List<AbsoluteImageReference> imageReferences, int x, int y, int colorBuffer[])
+    private static int mergeRGBColor(List<ImageWithReference> images, int x, int y,
+            int colorBuffer[])
     {
         Arrays.fill(colorBuffer, 0);
         for (int index = 0; index < images.size(); index++)
         {
-            int rgb = images.get(index).getRGB(x, y);
+            ImageWithReference image = images.get(index);
+            int rgb = image.getBufferedImage().getRGB(x, y);
             Color singleColor = new Color(rgb, true);
-            int channelIndex = imageReferences.get(index).getChannelIndex();
+            int channelIndex = image.getReference().getChannelIndex();
             for (int i : getRGBColorIndexes(channelIndex))
             {
                 colorBuffer[i] = Math.max(colorBuffer[i], extractMaxColorIngredient(singleColor));