Páginas

13 de abril de 2011

Gerador de thumbnails em Java

Resolvi compartilhar um gerador de thumbnail de imagens muito útil que sempre utilizo.
Basicamente o código recebe o arquivo de origem, o destino, a largura e a altura máxima do thumbnail a ser gerado.

É tudo muito simples e requer apenas a biblioteca padrão awt do Java.

Primeiro vamos ler o arquivo de origem:
FileInputStream input = new FileInputStream(source);
BufferedImage bufImage = ImageIO.read(input);

Agora precisamos calcular o scale, que será o índice para redimensionar a área da imagem. Se a altura original da imagem for maior, o scale será o resultado da divisão da altura do thumb pela altura original. E vice- versa.
double scale1 = (double) width / (double) bufImg.getWidth();
double scale2 = (double) height / (double) bufImg.getHeight(); 
double resultScale = scale1 > scale2 ? scale2 : scale1;

Utilizando o resultado obtido para o cálculo do redimensionamento:
int realWidth = bufImg.getWidth();
int realHeigth = bufImg.getHeight();
BufferedImage imageScaled = new BufferedImage((int) (
    w * resultScale), (int) (h * resultScale), 
    BufferedImage.TYPE_INT_RGB);

Só o BufferedImage gerado não basta. É necessário, por fim, desenhar os gráficos da imagem novamente com os novos parâmetros calculados:
Graphics2D g2 = scaled.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.drawImage(img.getScaledInstance((int) (realWidth * resultScale), 
    (int) (realHeigth * resultScale), Image.SCALE_SMOOTH),
    0, 0, (int) (w * resultScale), (int) (h * resultScale), null);
g2.dispose();

// Escrevendo no arquivo de sáida
ImageIO.write(imageScaled, "JPG", output);

Código completo:
public class ThumbGenerator {

    public static boolean generate (File source, File dest, 
                   int height, int width) throws IOException {
        if (source != null && dest != null) {
     FileInputStream input = new FileInputStream(source);
     try {
                BufferedImage bufImage = ImageIO.read(input);
  if(bufImage != null) {
      double scale = calcScale(bufImage, width, height);
      BufferedImage imageScaled = scale(bufImage, scale);
      FileOutputStream fos = new FileOutputStream(dest);
      ImageIO.write(imageScaled, "JPG", fos);
      fos.close();
                    input.close();
      return true;
  }

     } catch (Exception e) {
         e.printStackTrace();
     }
 }
 return false;
    }

    private static double calcScale(BufferedImage img, 
                                      int width, int height) {
        double scale1 = (double) width / (double) img.getWidth();
        double scale2 = (double) height / (double) img.getHeight();
        double scale = scale1 > scale2 ? scale2 : scale1;
 return scale;
    }

    private static BufferedImage scale(BufferedImage img, 
                                               double scale) {
        int w = img.getWidth();
        int h = img.getHeight();
        BufferedImage scaled = new BufferedImage((int) (w * scale), 
                (int) (h * scale), BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = scaled.createGraphics();
        g2.setComposite(AlphaComposite.Src);
        g2.drawImage(img.getScaledInstance((int) (w * scale), 
                (int) (h * scale), Image.SCALE_SMOOTH),
                0, 0, (int) (w * scale), (int) (h * scale), null);
        g2.dispose();
        return scaled;
    }
}

Daí é só chamar:
ThumbGenerator.generate(imgSource, output, 400, 400); 
// Ver resultado no arquivo output

ThumbGenerator.generate(imgSource, output, 400, 600); 
ThumbGenerator.generate(imgSource, output, 600, 400); 
ThumbGenerator.generate(imgSource, output, 100, 100); 

3 comentários:

Anônimo disse...

Interessante, farei um teste para ver se funciona bem!

Anônimo disse...

Muito bom!
Para quem faz upload e não quer salvar o arquivo enviado uma dica interessante seria passar os bytes[] ao invés do "File source". Será que dá erro ou vai certinho?

Leandro Maríngolo disse...

Opa, ótima dica. Dá sim, só ia precisar trocar a chamada que cria o inputStream.
Em vez de ser FileInputStream recebendo um file, poderia ser um ByteArrayInputStream recebendo um array de bytes.

O método read do ImageIO recebe um objeto do tipo InputStream, então isso daria sim!