使用Java调整动画GIF的大小并保持其动画

9

我正在使用Java中的Graphics2D来调整图像大小,对于jpg、png和其他格式的图像效果非常好。

但是我的问题在于动态GIF图像,在调整大小后动画消失了!

这是我正在使用的方法:

    private BufferedImage doResize(int newWidth, int newHeight, double scaleX,
        double scaleY, BufferedImage source) {

    GraphicsConfiguration gc = getDefaultConfiguration();
    BufferedImage result = gc.createCompatibleImage(newWidth, newHeight, source.getColorModel().getTransparency());
    Graphics2D g2d = null;

    try {
        g2d = result.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.scale(scaleX, scaleY);
        g2d.drawImage(source, 0, 0, null);
    } finally {
        if (g2d != null) {
            g2d.dispose();
        }
    }

    return result;
}

那么,有什么办法可以在重新调整大小后保持动画 GIF 的连续播放吗?谢谢。

4个回答

12

所以我知道这很老了,但我找到了一个解决方案,我正在使用Java 8,不确定它是否适用于其他版本。

    ImageIcon image = ? (whatever/wherever your gif is)
    int width = 100;
    int height = 100;
    image.setImage(image.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT));

你可以将SCALE_DEFAULT更改为此处列出的其他选项,但对于SCALE_SMOOTH和SCALE_AREA_AVERAGING,它们对我没有效果,图片会是空的。 https://docs.oracle.com/javase/7/docs/api/java/awt/Image.html


1
这是我使用的方法。SCALE_SMOOTH和SCALE_AREA_AVERAGING在底层下是相同的算法,如果我将动画GIF输入它,我可以确认我会得到一个空白图像输出。在这种情况下,最好使用SCALE_DEFAULT以获得最佳结果。对于其他图像,SCALE_SMOOTH会产生更好的结果。 - JakeRobb
非常好的建议,这真的很有效。当然,平滑处理可能更理想,但为了快速获得一些结果,这也可以。 - Stefan Reich

8
我找到了两个资源,它们结合起来可以在保持动画的同时调整图像大小。
在这个问题(将每个动态GIF帧转换为单独的BufferedImage)中,查找Alex Orzechowski的答案。他的代码将gif文件转换为ImageFrames数组(这是他自己制作的一个类,包装了BufferedImage)。然后看看这段代码,它将一系列的BufferedImages转换为gif文件(http://elliot.kroo.net/software/java/GifSequenceWriter/)。
你可能已经猜到了,你只需要上传gif文件,使用Alex的代码将其转换为ImageFiles/BufferedImages数组,使用你的Graphics2D代码调整每个帧的大小(你需要向Alex的ImageFrame类添加一个setImage方法),然后使用Elliot的代码将该数组转换为gif!这是我的样子:
public static void main( String[] args )
{
  try {
     File imageFile = new File( "InputFile" );
     FileInputStream fiStream = new FileInputStream( imageFile );

     ImageFrame[] frames = readGif( fiStream );
     for( int i = 0; i < frames.length; i++ ){
        //code to resize the image
        BufferedImage image = ImageUtilities.resizeImage( frames[ i ].getImage(), newWidth, newHeight);
        frames[ i ].setImage( image );
   }

     ImageOutputStream output =
       new FileImageOutputStream( new File( "OutputFile" ) );

     GifSequenceWriter writer =
       new GifSequenceWriter( output, frames[0].getImage().getType(), frames[0].getDelay(), true );

     writer.writeToSequence( frames[0].getImage() );
     for ( int i = 1; i < frames.length; i++ ) {
        BufferedImage nextImage = frames[i].getImage();
        writer.writeToSequence( nextImage );
     }

     writer.close();
     output.close();
  }
  catch ( FileNotFoundException e ) {
     System.out.println( "File not found" );
  }
  catch ( IOException e ) {
     System.out.println( "IO Exception" );
  }
}

然而,这段代码没有考虑到GIF图像帧之间时间间隔不同的情况。

2
太棒了,非常感谢。问题完全解决了,现在我可以调整 GIF 大小并保持动画效果。唯一需要注意的是,你应该将 frames[0].getDelay() 乘以 10,因为在 GifSequenceWriter 中它会除以 10(以调整为毫秒)。 - Erich
1
它的功能很好,但对于大小为1MB且包含222帧的gif图像,需要大约200MB的内存。有没有办法减少内存使用? - Vipul
谢谢您提供有关序列的提示! - drdrej
Elliot网站已经崩溃了。GifSequenceWriter的代码可以在这里找到:https://github.com/Deep-Symmetry/wayang/blob/main/src/main/java/org/deepsymmetry/GifSequenceWriter.java(或使用网络存档)。 - chill appreciator
Elliot网站崩溃了。GifSequenceWriter的代码可以在这里找到:https://github.com/Deep-Symmetry/wayang/blob/main/src/main/java/org/deepsymmetry/GifSequenceWriter.java(或使用网络存档)。 - undefined

1

Jonny March的解决方案对我没有用,因为它只处理和输出GIF文件的第一张图像。

这是我的解决方案,它在调整大小的同时保留了动画效果。

File f = new File("path of your animated gif");
URL img = f.toURL();
ImageIcon icon = new ImageIcon(img);
//You have to convert it to URL because ImageIO just ruins the animation

int width = 100;
int height = 100;
icon.setImage(icon.getImage().getScaledInstance(width, height,Image.SCALE_DEFAULT));

0

BufferedImage origBuffImg = ImageIO.read(orignalImage); int type = origBuffImg.getType() == 0? BufferedImage.TYPE_INT_ARGB : origBuffImg.getType();

缓冲图像origBuffImg = ImageIO.read(orignalImage); int类型= origBuffImg.getType() == 0? BufferedImage.TYPE_INT_ARGB : origBuffImg.getType();

          BufferedImage resizedBuffImg = new BufferedImage(width, height, type);
          Graphics2D g = resizedBuffImg.createGraphics();
          g.drawImage(origBuffImg, 0, 0, width, height, null);
          g.dispose();
         
          String newFile = orignalImage.getAbsolutePath().substring(0,orignalImage.getAbsolutePath().lastIndexOf("."))+"_"+width+"x"+height+"."+extension;
          ImageIO.write(resizedBuffImg, extension, new File(newFile));
         
          System.out.println("File created : "+newFile);

1
请务必为您的代码编写一些解释并正确缩进。 - surajs1n

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接