ОБНОВЛЕННОЕ РЕШЕНИЕ (альтернатива без необходимости использования Raster и ColorModel)
Меня действительно беспокоило, что мое старое решение (см. Ниже) по-прежнему требовало растров и ColorModels. У меня возникла проблема с моим решением, поэтому я потратил больше времени на поиск альтернатив. Итак, лучшее, что я мог придумать сейчас, - это следующее:
try {
final FileInputStream fileInputStream = new FileInputStream("dice.png");
final BufferedImage image = ImageIO.read(fileInputStream);
fileInputStream.close(); // ImageIO.read does not close the input stream
final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
fileOutputStream.close(); // ImageIO.write does not close the output stream
if (!canWrite) {
throw new IllegalStateException("Failed to write image.");
}
} catch (IOException e) {
e.printStackTrace();
}
Я получил копию BufferedImage, как и раньше. Он делает более или менее то же самое, но на самом деле вы можете более легко повторно использовать ColorModel и Raster.
drawImage()
, похоже, заботится о большей части того, что я делал раньше, вручную. И поскольку это стандартный код библиотеки java, он действительно кажется лучшим способом.
Обратите внимание, что в итоге вы получаете изображение типа BufferedImage.TYPE_INT_RGB
. Хотя кажется, что это работает для типов jpg
, png
и gif
, я не уверен, что произойдет с другими форматами файлов или файлами с другим хранилищем ColorModel - информация может быть потеряна (например, 4 цветовых канала к 3). Для упомянутых типов нам не нужен альфа-канал, даже если мы конвертируем из gif
или jpg
в png
(это будет Color.WHITE).
СТАРОЕ РЕШЕНИЕ Мне не понравился мой первый дизайн, и он не совсем работал так, как должен.
Поэтому я создал его с нуля. В итоге я получил небольшой конвертер для файлов sRGB
. Вы можете конвертировать из png
в jpg
и наоборот (Edit: добавлена поддержка gif
). Если вы хотите обрабатывать другие типы, не стесняйтесь расширять это дальше. Вы можете более или менее добавить его таким же образом. Это может работать и для других типов файлов, но я их еще не тестировал. К счастью, похоже, что sRGB
встречается довольно часто.
Tbh. Я понятия не имею, сколько комбинаций и вариантов (цветовые палитры, точность, качество, ч / б и т. Д. c.) Вы можете произвести или какие общие свойства они имеют.
Может быть, вам этого достаточно . Может быть нет. По крайней мере, для меня это было приятным упражнением.
Это решение отнюдь не идеальное. Результаты выглядели нормально - я sh. Преобразование типа файла сработало, и размер файла также меньше, чем png
.
try {
final String fileName = "dice.png";
final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
final String outputFormat = "gif";
if (!isSRGB) {
throw new IllegalArgumentException("Please provide an image that supports sRGB.");
}
final WritableRaster raster = createRaster(inputImage);
final ColorModel colorModel = createColorModel(inputImage);
final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
final String outputFileName = fileName + "-converted." + outputFormat;
final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
if (!writeResult) {
throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
}
System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
e.printStackTrace();
}
@NotNull
public static ColorModel createColorModel(@NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
boolean isAlphaPremultiplied = false;
int transparency = Transparency.OPAQUE;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
isAlphaPremultiplied = true;
}
return new ComponentColorModel(
ColorModel.getRGBdefault().getColorSpace(),
false, isAlphaPremultiplied, transparency,
bufferedImage.getData().getDataBuffer().getDataType()
);
}
@NotNull
public static WritableRaster createRaster(@NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
final int width = bufferedImage.getWidth();
final int height = bufferedImage.getHeight();
final int pixelStride = 3;
int[] offset = new int[]{0, 1, 2};
DataBufferByte dataBufferByte;
if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
int dataIndex = 0;
final byte[] data = new byte[height * width * pixelStride];
final int bitmask = 0xff;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int rgb = bufferedImage.getRGB(x, y);
final int blue = bitmask & rgb;
final int green = bitmask & (rgb >> 8);
final int red = bitmask & (rgb >> 16);
if (rgb == 0) {
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
} else {
data[dataIndex++] = (byte) red;
data[dataIndex++] = (byte) green;
data[dataIndex++] = (byte) blue;
}
}
}
dataBufferByte = new DataBufferByte(data, data.length);
} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
offset = new int[]{2, 1, 0};
} else {
throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
}
return Raster.createInterleavedRaster(
dataBufferByte, width, height,
pixelStride * width, pixelStride,
offset,
null
);
}
Примеры преобразования изображений
EDIT : добавлена поддержка gif.