ImageIO read()和write()操作后GIF图片出错 [英] GIF image becomes wrong after ImageIO read() and write() operations

查看:57
本文介绍了ImageIO read()和write()操作后GIF图片出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个代码.它只是读取一个 GIF 文件,用背景重新绘制它并输出到一个新的 GIF 文件.

I have this code. It simply reads a GIF file, redraws it with background and output to a new GIF file.

问题是结果文件变得奇怪.我不知道为什么它变得质量差.该问题不会发生在 JPG 文件上.如何解决?

The problem is the result file becomes strange. I have no idea why it becomes poor quality. The problem doesn't happen on JPG files. How to fix it?

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ImageTest {

    public static void main(String[] args) {
        f();
    }

    private static final String EXTENSION = "gif";
    private static final String FILENAME = "pinkHeart";
    private static final String PATH = "/Users/hieugioi/Downloads/";

    public static void f() {
        File file = new File(PATH + FILENAME + "." + EXTENSION);

        try {
            final BufferedImage originalImage = ImageIO.read(file);

            int imageType = getImageType(originalImage);
            final BufferedImage buff = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), imageType);
            final Graphics2D g = buff.createGraphics();

            Color backgroundColor = Color.GRAY;
            g.setColor(backgroundColor);
            g.fill(new Rectangle(0, 0, buff.getWidth(), buff.getHeight()));
            g.drawImage(originalImage, null, 0, 0);

            File out = new File(PATH + FILENAME + "Out." + EXTENSION);
            ImageIO.write(buff, EXTENSION, out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static int getImageType(BufferedImage img) {
        int imageType = img.getType();
        if (imageType == BufferedImage.TYPE_CUSTOM) {
            if (img.getAlphaRaster() != null) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            } else {
                imageType = BufferedImage.TYPE_INT_RGB;
            }
        } else if (imageType == BufferedImage.TYPE_BYTE_INDEXED && img.getColorModel().hasAlpha()) {
            imageType = BufferedImage.TYPE_INT_ARGB_PRE;
        }
        return imageType;
    }
}

输入图片(pinkHeart.gif):

Input image (pinkHeart.gif):

输出图像(pinkHeartOut.gif):

Output image (pinkHeartOut.gif):

更新案例 2

输入图像(example.gif):

Input image (example.gif):

输出图像(exampleOut.gif):输出的黄色完全消失!

Output image (exampleOut.gif): The output's yellow totally disappears!

推荐答案

这里有两个不同的问题.

There are two separate problems here.

首先假设您的输入图像具有透明度.据我所知,他们没有.因此,背景不会变为灰色,而是在两种情况下都保持纯白色.这没有任何问题,但可能不是您想要/预期的.

The first is the assumption that your input images have transparency. They do not, as far as I can see. Because of this, the background will not change to gray, but stay solid white in both cases. There's nothing wrong with this, but perhaps not what you intended/expected.

另一个(真正的"问题)是 getImageType(..) 的代码没有针对没有 alpha 的 BufferedImage.TYPE_BYTE_INDEXED 的特殊分支.因此,图像类型将按原样返回.当一个 BufferedImage 被创建为 BufferedImage.TYPE_BYTE_INDEXED 类型时,它会有一个带有固定的默认调色板的颜色模型(事实上,这是旧的 256 色网络安全"调色板).您原稿中的粉红色与此调色板中的粉红色不完全匹配,因此使用粉红色和白色进行抖动处理.

The other (the "real" problem) is that the code for getImageType(..) has no special branch for BufferedImage.TYPE_BYTE_INDEXED without alpha. Because of this, the image type will just be returned as is. And when a BufferedImage is created with the BufferedImage.TYPE_BYTE_INDEXED type, it will have a color model with a fixed, default palette (in fact, it's the good old 256 color "web safe" palette). The pink color in your origininal does not exactly fit the pink in this palette, and is thus dithered using pink and white.

第二个输入图像的问题"在于它根本不是 TYPE_BYTE_INDEXED,而是 TYPE_BYTE_BINARY.这种类型用于每像素有 1-4 位的图像,并且多个像素打包"到一个字节中.如上所述,当使用 BufferedImage.TYPE_BYTE_BINARY 类型创建 BufferedImage 时,它将具有一个带有固定的、默认的 2 色黑/白调色板的颜色模型(这就是黄色消失的原因).

The "issue" with your second input image, is that it is not TYPE_BYTE_INDEXED at all, it is TYPE_BYTE_BINARY. This type is used for images that have 1-4 bits per pixel, and multiple pixels "packed" into one byte. As above, when a BufferedImage is created with the BufferedImage.TYPE_BYTE_BINARY type, it will have a color model with a fixed, default 2 color black/white palette (that's why the yellow disappear).

通过在返回 TYPE_INT_RGBgetImageType(..) 方法中为上述类型添加分支,我得到与原始输出相同的输出(这就是我期望,只要您的图像没有透明背景):

By adding branches for the above types in the getImageType(..) method that returns TYPE_INT_RGB instead, I get the same output as your original (which is what I expect, as long as your image has no transparent background):

public static int getImageType(BufferedImage img) {
    int imageType = img.getType();
    switch (imageType) {
        case BufferedImage.TYPE_CUSTOM:
            if (img.getAlphaRaster() != null) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            }
            else {
                imageType = BufferedImage.TYPE_INT_RGB;
            }
            break;
        case BufferedImage.TYPE_BYTE_BINARY:
            // Handle both BYTE_BINARY (1-4 bit/pixel) and BYTE_INDEXED (8 bit/pixel)
        case BufferedImage.TYPE_BYTE_INDEXED:
            if (img.getColorModel().hasAlpha()) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            }
            else {
                // Handle non-alpha variant
                imageType = BufferedImage.TYPE_INT_RGB;
            }
            break;
    }

    return imageType;
}

<小时>

PS:这是一种替代方法,它完全避免了创建原始图像副本的问题,而且速度更快并且节省了内存作为奖励.它应该与您上面的代码完全相同:


PS: Here's an alternate approach that avoids the problem of creating a copy of the original image altogether, and is both faster and saves memory as a bonus. It should do exactly the same as your code above intends:

public class ImageTest2 {

    public static void main(String[] args) throws IOException {
        f(new File(args[0]));
    }

    static void f(File file) throws IOException {
        BufferedImage image = ImageIO.read(file);

        // TODO: Test if image has transparency before doing anything else,
        // otherwise just copy the original as-is, for even better performance

        Graphics2D g = image.createGraphics();

        try {
            // Here's the trick, with DstOver we'll paint "behind" the original image
            g.setComposite(AlphaComposite.DstOver); 
            g.setColor(Color.GRAY);
            g.fill(new Rectangle(0, 0, image.getWidth(), image.getHeight()));
        }
        finally {
            g.dispose();
        }

        File out = new File(file.getParent() + File.separator + file.getName().replace('.', '_') + "_out.gif");
        ImageIO.write(image, "GIF", out);
    }
}

这篇关于ImageIO read()和write()操作后GIF图片出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆