在Java中调整索引图像的大小而不会丢失透明度

时间:2011-06-03 15:45:05

标签: java image png java-2d

这是我调整图像大小的功能。 质量不是Photoshop,但它是可以接受的。

索引png上的行为是不可接受的 我们希望如果我们缩小具有透明索引的256色调色板的图像,我们将获得具有相同透明度的调整大小的图像,但事实并非如此。

因此我们对新的ARGB图像进行了调整,然后将其缩小为256色。问题是如何“重新引入”透明像素索引。

private static BufferedImage internalResize(BufferedImage source, int destWidth, int destHeight) {
    int sourceWidth = source.getWidth();
    int sourceHeight = source.getHeight();
    double xScale = ((double) destWidth) / (double) sourceWidth;
    double yScale = ((double) destHeight) / (double) sourceHeight;
    Graphics2D g2d = null;

    BufferedImage resizedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TRANSLUCENT);

    log.debug("resizing image to  w:" + destWidth + " h:" + destHeight);
    try {

        g2d = resizedImage.createGraphics();

        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

        AffineTransform at = AffineTransform.getScaleInstance(xScale, yScale);

        g2d.drawRenderedImage(source, at);

    } finally {
        if (g2d != null)
            g2d.dispose();
    }

//doesn't keep the transparency
    if (source.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
        log.debug("reducing to color-indexed image");

        BufferedImage indexedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_BYTE_INDEXED);

        try {
            Graphics g = indexedImage.createGraphics();
            g.drawImage(resizedImage, 0, 0, null);
        } finally {
            if (g != null)
                g.dispose();
        }
        System.err.println("source" + ((IndexColorModel) source.getColorModel()).getTransparentPixel()
                         + "   " + ((IndexColorModel) indexedImage.getColorModel()).getTransparentPixel());

        return indexedImage;
    }

    return resizedImage;

}

2 个答案:

答案 0 :(得分:3)

尝试更改

BufferedImage indexedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_BYTE_INDEXED);

    BufferedImage indexedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_BYTE_INDEXED, (IndexColorModel) source.getColorModel());

即使具体对您没有帮助(如果调整大小,无论出于何种原因,也可能不会改变索引的特定颜色值),您可以创建一个给定的新BufferedImage IndexColorModel对您来说可能非常有用。

http://download.oracle.com/javase/6/docs/api/java/awt/image/BufferedImage.html#BufferedImage%28int,%20int,%20int,%20java.awt.image.IndexColorModel%29

编辑:注意到您的resizedImage构造函数应该使用BufferedImage.TYPE_INT_ARGB而不是BufferedImage.TRANSLUCENT。不确定这是否会改变它的工作方式,但BufferedImage.TRANSLUCENT不应该传递给构造函数的那种形式。 http://download.oracle.com/javase/1,5.0/docs/api/java/awt/image/BufferedImage.html#BufferedImage%28int,%20int,%20int%29

无论如何,也许尝试这样的事情:

DirectColorModel resizedModel = (DirectColorModel) resizedImage.getColorModel();
int numPixels = resizedImage.getWidth() * resizedImage.getHeight();

byte[numPixels] reds;
byte[numPixels] blues;
byte[numPixels] greens;
byte[numPixels] alphas;
int curIndex = 0;
int curPixel;

for (int i = 0; i < resizedImage.getWidth(); i++)
{
    for (int j = 0; j < resizedImage.getHeight(); j++)
    {
        curPixel = resizedImage.getRGB(i, j);
        reds[curIndex] = resizedModel.getRed(curPixel);
        blues[curIndex]= resizedModel.getBlue(curPixel);
        greens[curIndex] = resizedModel.getGreen(curPixel);
        alphas[curIndex] = resizedModel.getAlpha(curPixel);
        curIndex++;
    }
}

BufferedImage indexedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_BYTE_INDEXED, new IndexColorModel(resizedModel.pixel_bits, numPixels, reds, blues, greens, alphas));

不知道这是否真的有效。

答案 1 :(得分:0)

具有透明度的索引图像是一种黑客行为。它们只在某些条件下工作,并且调整大小不是其中之一。

具有透明度的图像不仅具有完全不透明且完全透明的像素。特别是在不规则形状的边界处,存在许多具有部分透明度的像素。如果将其保存为具有索引颜色的格式,其中单个颜色用于透明像素,则必须确定背景将具有的颜色。然后,所有具有部分透明度的像素在它们的颜色和背景颜色之间混合(根据它们的透明度)并变得完全不透明。仅为完全透​​明的像素指定透明伪色。

如果这种图像显示在具有不同颜色的背景上,则丑陋的边框将变得明显。这是透明度处理不足的一件神器。

调整图像大小时,会引入更多工件。通常从几个相邻像素混合新像素的颜色。如果一些是透明的而一些是不透明的,则结果是部分透明的像素。保存时,部分透明像素与背景颜色混合并变为不透明。因此,不透明区域(以及相关的工件)随着每个调整大小(或大多数其他图像处理)而增长。

无论使用何种编程语言或图形库,工件都会增长,结果会变得更糟。我建议你使用ARGB缓冲区并将图像保存为非索引的PNG文件。