views:

143

answers:

3

Hi,

I'm having problems generating thumbnails of images with an Alpha channel (transparency). The code I use is this:

public void saveThumbnail(File file, String imageType) {
    if (bufferedThumb == null) {
        return;
    }

    if(bufferedImage.getColorModel().hasAlpha()) {
        logger.debug("Original image has Alpha channel");
    }

    BufferedImage bi = new BufferedImage(bufferedThumb.getWidth(null), bufferedThumb.getHeight(null), BufferedImage.TYPE_INT_ARGB);
    Graphics g = bi.getGraphics();
    g.drawImage(bufferedThumb, 0, 0, null);
    try {
        ImageIO.write(bi, imageType, file);
    } catch (IOException ioe) {
        ioe.printStackTrace();
        System.out.println("Error occured saving thumbnail");
    }
}

However, if I supply for example a GIF image with a transparent background I always end up with a black or colored background.

EDIT:

This is how it's called from the class using the thumbnail, I missed the two-parameter version of the getThuimbnail()-method the last time:

Thumbnail th = new Thumbnail(file.getPath());
th.getThumbnail(100);

Added methods used for getting the images:

public Thumbnail(String fileName) {
    try {
        this.bufferedImage = ImageIO.read(new File(fileName));
    } catch (IOException ex) {
        logger.error("Failed to read image file: " + ex.getMessage());
    }
}

public Image getThumbnail(int size) {
    int dir = VERTICAL;
    if (bufferedImage.getHeight() < bufferedImage.getWidth()) {
        dir = HORIZONTAL;
    }
    return getThumbnail(size, dir);
}

/**
 * Creates image with specifed max sized to a specified direction.
 * Will use Image.SCALE_SMOOTH for scaling.
 * @param size Maximum size
 * @param dir Direction of maximum size - 0 = vertical, 1 = height.
 * @return Resized image.
 */
public Image getThumbnail(int size, int dir) {
    return getThumbnail(size, dir, Image.SCALE_SMOOTH);
}

/**
 * Creates image with specified size.
 * @param size Maximum size
 * @param dir Direction of maximum size - 0 = vertical, 1 = height.
 * @param scale Image.Scale to use for conversion.
 * @return Resized image.
 */
public Image getThumbnail(int size, int dir, int scale) {
    if (dir == HORIZONTAL) {
        bufferedThumb = bufferedImage.getScaledInstance(size, -1, scale);
    } else {
        bufferedThumb = bufferedImage.getScaledInstance(-1, size, scale);
    }
    return bufferedThumb;
}

Thanks!

A: 

The code you're using for writing the file is correct, at least if you use a format which supports transparency (e.g. PNG). Most probably, bufferedThumb is already corrupted, so to help you, it would be of more help if you stated how you get bufferedImage and how you create bufferedThumb.

jarnbjo
Thank you, I've added the methods used for getting the images to the question.
Malakim
No, you haven't. If you are using `getThumbnail(int size)` to generate the thumbnail, you have not included the 2-parameter implementation of `getThumbnail` which you are invoking. If the 3-parameter implementation of `getThumbnail` is relevant, you do not show how you are using it.
jarnbjo
Added the missing info, thanks!
Malakim
+1  A: 

Scaling down GIF images with a transparent color should work fine. I agree with jarnbjo, that the problem is most likely in the bufferedThumb generation.

Maybe the following hints help:

1) When creating the thumb copy the image type from the source e.g.:

BufferedImage thumb = new BufferedImage(fit, fit, image.getType());

2) Use the 2D approach:

Graphics2D g = thumb.createGraphics();

Here some sample code for simple thumb creation (tested and works; transparency GIF in, thumb keeps transparency):

  public static BufferedImage thumb(BufferedImage image, int fit) {

    //image = blur(image);
    BufferedImage thumb = new BufferedImage(fit, fit, image.getType());
    Graphics2D g = thumb.createGraphics();

    try {
      int width = image.getWidth();
      int height = image.getHeight();
      int sx1;
      int sy1;
      int sx2;
      int sy2;
      int tmp;

      if (height > width) {
        tmp = height - width;
        sx1 = 0;
        sy1 = tmp / 2;
        sx2 = width;
        sy2 = height - sy1;
      } else if (width > height) {
        tmp = width - height;
        sx1 = tmp / 2;
        sy1 = 0;
        sx2 = width - sx1;
        sy2 = height;
      } else {
        sx1 = 0;
        sy1 = 0;
        sx2 = width;
        sy2 = height;
      }

      g.drawImage(
          image,
          0, 0,
          fit, fit,
          sx1, sy1,
          sx2, sy2,
          null
      );

    } finally {
      g.dispose();
    }
    return thumb;
  }//thumb

Note: with simple, I mean that it won't produce high quality results if you are trying to scale too much in a single step (e.g. 2048 px in, 100px out). You may need to take a multi-step approach, and probably you should resort to an AffineTransformOp with hints instead of using the Graphics device.

Dieter
Thank you, I've added the methods used for getting the images to the question.Image quality is not too much of a concern, the thumbs will be pretty small and only for the user to get an idea of which original image it is.
Malakim
Malakim, did you try the thumb method i posted instead of your thumbnail code? It really should work. Just pass the image and the box size in pixel for the thumbnail (the code takes care of horizontal and vertical, fitting it into the box).
Dieter
Just got in to the office, trying it now.- Thanks!
Malakim
I does not work for me. I stil get the black background when using a transparent GIF. I tried adding if(image.getColorModel().hasAlpha()) { logger.debug("Orignal image has Alpha channel");}if(thumb.getColorModel().hasAlpha()) { logger.debug("Thumbnail image has Alpha channel");}The first one hits, but not the second. I also tried setting it after the image has benn rendered into the new thumb (last thing in your method), but it still won't hit the debug statement.-Thanks!
Malakim
+1  A: 

Solved it by using an AffineTransform scale operation:

AffineTransform af = new AffineTransform();
af.scale(scaleFactor, scaleFactor);

AffineTransformOp operation = new AffineTransformOp(af, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
bufferedThumb = operation.filter(bufferedImage, null);
return bufferedThumb;

This seems to preserve any transparency in the image.

Malakim