Изменение размера проиндексированного изображения в Java без потери прозрачности - PullRequest
3 голосов
/ 03 июня 2011

Это моя функция для изменения размера изображений.Качество не в фотошопе, но оно приемлемое.

Что не приемлемо, так это поведение на индексированном 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 ]

3 голосов
/ 03 июня 2011

Попробуйте изменить

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

EDIT: только что заметил, что ваш 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));

Не знаю, сработает ли это на самом деле.

0 голосов
/ 03 июня 2011

Индексированные изображения с прозрачностью - это взлом. Они работают только при определенных условиях, и изменение размера не входит в их число.

Изображение с прозрачностью не просто имеет полностью непрозрачные и полностью прозрачные пиксели. В частности, на границах неправильной формы, есть много пикселей с частичной прозрачностью. Если вы сохраните его в формате с индексированными цветами, где для прозрачных пикселей используется один цвет, вы должны решить, какой цвет будет иметь фон. Все пиксели с частичной прозрачностью затем смешиваются между своим цветом и цветом фона (в соответствии с их прозрачностью) и становятся полностью непрозрачными. Только полностью прозрачному пикселю назначается прозрачный псевдоцвет.

Если такое изображение отображается на фоне с другим цветом, станет видна уродливая рамка. Это артефакт неправильной обработки прозрачности.

Когда вы изменяете размер изображения, вы вводите больше артефактов. Цвет новых пикселей обычно смешивается с несколькими соседними пикселями. Если некоторые являются прозрачными, а некоторые непрозрачными, в результате получается частично прозрачный пиксель. При сохранении частично прозрачный пиксель смешивается с цветом фона и становится непрозрачным. В результате непрозрачная область (и связанные с ней артефакты) растут с каждым изменением размера (или большинством других манипуляций с изображением).

Какой бы язык программирования или графическую библиотеку вы не использовали, артефакты будут расти, а результат ухудшаться. Я рекомендую вам использовать буфер ARGB и сохранить изображение как неиндексированный файл PNG.

...