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

在Java中调整索引图像的大小而不丢失透明度,java,image,png,java-2d,Java,Image,Png,Java 2d,这是我调整图像大小的功能。 质量不是photoshop,但可以接受 不可接受的是索引png上的行为。 我们希望,如果我们缩小一个256色调色板和一个透明索引的图像,我们将得到一个具有相同透明度的调整大小的图像,但事实并非如此 因此,我们对一个新的ARGB图像进行了调整,然后将其减少到256色。问题是如何“重新引入”透明像素索引 private static BufferedImage internalResize(BufferedImage source, int destWidth, int

这是我调整图像大小的功能。 质量不是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;

}
试着改变

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

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

编辑:刚刚注意到您的
resizedImage
构造函数可能应该使用
buffereImage.TYPE_INT\u ARGB
而不是
buffereImage.transparent
。不确定这是否会改变它的工作方式,但
BufferedImage.transparent
不应该传递给该构造函数形式

不管怎样,不妨试试这样:

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));
DirectColorModel resizedModel=(DirectColorModel)resizedImage.getColorModel();
int numPixels=resizedImage.getWidth()*resizedImage.getHeight();
字节[numPixels]红色;
字节[numPixels]蓝色;
字节[numPixels]绿色;
字节[numPixels]字母;
int-curIndex=0;
整数像素;
对于(int i=0;i

不过,我不知道这是否真的有效。

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

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

如果在不同颜色的背景下显示这样的图像,那么丑陋的边框将变得明显。这是透明度处理不足的产物

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


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

我已经尝试过,但这更糟糕,因为它使用原始图像的透明索引,而这不是正确的索引。好吧,正确的解决方案可能是创建一组新的索引颜色,但是我需要一种更聪明的方法,我还需要标记透明的一个。假设在转换后至少有一个像素保持完全透明,
getTransparentPixel()
应该告诉您该像素的索引。如果没有,那么图像中就没有任何纯透明的像素了,你已经得到了部分透明度。我想。@JAB,那不是真的。在最终图像getTransparentPixel()中,应为透明的原始颜色仍然存在。我说的是一个简单的图像,背景是透明的,上面写着黑色。@Uberto:首先,使用
getTransparency()
检查索引颜色模型是否确实是透明的(即返回值不是
Transparency.不透明的)。然后,如果不是这样,则迭代结果图像中的所有像素,并检查每个像素的alpha值。如果它们的alpha值都不为0,那么当然
getTransparentPixel()
将返回-1,因为如果至少有一个像素的alpha值为0或指定为trans-pixel,则它只返回像素的索引。如果其中一个的alpha值为0,那么在某个地方就有错误。当然有限制,但我说的是缩小图像,而不是放大图像。将256色调色板的图像缩小20%通常会得到更小但仍然可以接受的图像。Codo,显然Java库允许您对某些索引颜色模型使用部分透明度。我觉得这很有趣。
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));