Преобразование прозрачного gif/png в jpeg с помощью java
Я хотел бы преобразовать gif-изображения в jpeg с помощью Java. Он отлично работает для большинства изображений, но у меня есть простой прозрачный gif-образ:
Входное изображение gif http://img292.imageshack.us/img292/2103/indexedtestal7.gif
[В случае отсутствия изображения: синий круг с прозрачными пикселями вокруг него]
Когда я конвертирую это изображение, используя следующий код:
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);
Этот код работает, не вызывая исключение, но приводит к недопустимому jpeg-изображению:
Выходное изображение jpeg http://img297.imageshack.us/img297/3493/indexedtest1qe5.jpg
[В случае отсутствия изображения: IE не может показать jpeg, Firefox показывает изображение с недопустимыми цветами.]
Я использую Java 1.5.
Я также попытался преобразовать образец gif в png с gimp и использовать png в качестве ввода для Java-кода. Результат тот же.
Это ошибка в JDK? Как правильно конвертировать изображения предпочтительно без сторонних библиотек?
UPDATE:
Ответы показывают, что jpeg-преобразование не может корректно обрабатывать прозрачность (я все еще думаю, что это ошибка) и предлагает обходное решение для замены прозрачных пикселей с предопределенным цветом. Оба предложенных метода довольно сложны, поэтому я реализовал более простой (опубликует как ответ). Я принимаю первый опубликованный ответ с этим обходным путем (Маркус). Я не знаю, какая реализация лучше. Я иду на самый простой, но я нашел gif, где он не работает.
Ответы
Ответ 1
Для Java 6 (и, я думаю, 5):
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
Ответ 2
Как уже упоминалось в UPDATE вопроса, я реализовал более простой способ замены прозрачных пикселей с предопределенным цветом:
public static BufferedImage fillTransparentPixels( BufferedImage image,
Color fillColor ) {
int w = image.getWidth();
int h = image.getHeight();
BufferedImage image2 = new BufferedImage(w, h,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = image2.createGraphics();
g.setColor(fillColor);
g.fillRect(0,0,w,h);
g.drawRenderedImage(image, null);
g.dispose();
return image2;
}
и я вызываю этот метод перед преобразованием jpeg следующим образом:
if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}
Ответ 3
Проблема (по крайней мере, с преобразованием png в jpg) заключается в том, что цветовая схема не то же самое, потому что jpg не поддерживает прозрачность.
То, что мы успешно выполнили, это что-то в этом роде (это извлекается из разных битов кода, поэтому, пожалуйста, прощайте грубость форматирования):
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;
//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
if (image.getType() == BufferedImage.TYPE_CUSTOM) {
//coerce it to TYPE_INT_ARGB and cross fingers -- PNGs give a TYPE_CUSTOM and that doesn't work with
//trying to create a new BufferedImage
jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
} else {
jpgImage = new BufferedImage(width, height, image.getType());
}
} else {
jgpImage = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().
createCompatibleImage(width, height, image.getTransparency());
}
//copy the original to the new image
Graphics2D g2 = null;
try {
g2 = jpg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(image, 0, 0, width, height, null);
}
finally {
if (g2 != null) {
g2.dispose();
}
}
File f = new File("indexed_test.jpg");
ImageIO.write(jpgImage, "jpg", f);
Это работает для png для jpg и gif для jpg. И у вас будет белый фон, где были прозрачные биты. Вы можете изменить это, если g2 заполнит изображение другим цветом перед вызовом drawImage.
Ответ 4
3 месяца поздно, но у меня очень похожая проблема (хотя и не загружаю gif, а просто создаю прозрачное изображение - скажем, нет фона, цветной формы), где при сохранении в jpeg все цвета перепутаны, а не только фон)
Нашел этот бит кода в этот довольно старый поток в списке интересов java2d, подумал, что я поделюсь, потому что после быстрого тест, он намного более эффективен, чем ваше решение:
final WritableRaster raster = img.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});
// create a ColorModel that represents the one of the ARGB except the alpha channel
final DirectColorModel cm = (DirectColorModel) img.getColorModel();
final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that we'll use to write the image
return new BufferedImage(newCM, newRaster, false, null);
К сожалению, я не могу сказать, что я точно понимаю, что он делает;)
Ответ 5
Если вы создадите BufferedImage типа BufferedImage.TYPE_INT_ARGB и сохраните его в JPEG, это приведет к появлению странных вещей. В моем случае цвета помечены оранжевым цветом. В других случаях созданное изображение может быть недействительным, а другие читатели откажутся его загрузить.
Но если вы создаете образ типа BufferedImage.TYPE_INT_RGB, то сохранение его в JPEG отлично работает.
Я думаю, что это, следовательно, ошибка в Java JPEG image writer - он должен писать только то, что он может без прозрачности (например, что делает .NET GDI +). Или в худшем случае выбрасывается исключение со значимым сообщением, например. msgstr "невозможно записать изображение с прозрачностью".
Ответ 6
JPEG не поддерживает прозрачность. Таким образом, даже если вы правильно установили цвет круга, у вас все равно будет черный или белый фон, в зависимости от вашего кодировщика и/или рендерера.
Ответ 7
BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
for (int x = 0; x < originalImage.getWidth(); x++) {
for (int y = 0; y < originalImage.getHeight(); y++) {
newImage.setRGB(x, y, originalImage.getRGB(x, y));
}
}