Изменение размера индексированного изображения в Java без потери прозрачности
Это моя функция для изменения размера изображений. Качество не фотошоп, но это приемлемо.
Что неприемлемо, так это поведение на индексированном 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 ответа:
Попробуйте изменить
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
, вероятно, будет весьма полезным для ты.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));
Индексированные изображения с прозрачностью-это хак. Они работают только при определенных условиях, и изменение размера не входит в их число.
Изображение с прозрачностью не просто имеет полностью непрозрачные и полностью прозрачные пиксели. В частности, на границах неправильной формы имеется много пикселей с частичной прозрачностью. Если вы сохраняете его в формате с индексированными цветами, где один цвет используется для прозрачных пикселей, вы должны решить, какой цвет будет иметь фон. Все пиксели с частичным прозрачность затем смешивается между их цветом и цветом фона (в соответствии с их прозрачностью) и становится полностью непрозрачной. Только полностью прозрачному пикселю присваивается прозрачный псевдокрасок.
Если такое изображение отображается на фоне с другим цветом, уродливая граница станет очевидной. Это артефакт неадекватной обработки прозрачности.
При изменении размера изображения появляется больше артефактов. Цвет нового пикселя обычно равен смешивается с несколькими соседними пикселями. Если некоторые из них прозрачны, а некоторые непрозрачны, то в результате получается частично прозрачный пиксель. Когда вы сохраняете его, частично прозрачный пиксель смешивается с фоновым цветом и становится непрозрачным. В результате непрозрачная область (и связанные с ней артефакты) увеличиваются с каждым изменением размера (или большинством других манипуляций с изображением).
Какой бы язык программирования или графическую библиотеку вы ни использовали, артефакты будут расти, а результат будет ухудшаться. Я рекомендуется использовать буфер ARGB и сохранить изображение в виде неиндексированного файла PNG.