Применение оттенка к изображению в Java
Я пытаюсь создать несколько похожих визуальных стилей для своих программ, каждая из которых имеет свою цветовую тему. Чтобы сделать это, я реализовал использование значков для представления различных состояний JCheckBox
с и JRadioButton
s. Вместо того, чтобы делать один полный набор иконок для каждого возможного цвета, могу ли я просто взять один набор и изменить оттенок / насыщенность / яркость / альфа изображения перед его отображением?
7 ответов
Есть способ, но вам придется использовать некоторые преобразования BufferedImage. После того, как вы их создадите, сохраните их в кэш-памяти или сохраните для последующего использования По сути, вы хотите начать с черного изображения (исходный цвет #000000), в котором альфа-слой используется только для отключения пикселей (также обеспечивая плавное сглаживание). Например, в исходном изображении каждый пиксель черный, но альфа-канал отличается от пикселя к пикселю.
Во-первых, прочтите эту статью для получения дополнительной справочной информации: http://www.javalobby.org/articles/ultimate-image/
Как только вы закончите с этим учебником, вам нужно загрузить изображение в BufferedImage:
BufferedImage loadImg = ImageUtil.loadImage("C:/Images/myimg.png");
Затем вам нужно создать новый BufferedImage, чтобы преобразовать в:
public BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue) {
BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(),
BufferedImage.TRANSLUCENT);
Graphics2D graphics = img.createGraphics();
Color newColor = new Color(red, green, blue, 0 /* alpha needs to be zero */);
graphics.setXORMode(newColor);
graphics.drawImage(loadImg, null, 0, 0);
graphics.dispose();
return img;
}
По сути, setXORMode будет XOR цвета, который вы предоставляете с цветом в исходном изображении. Если исходное изображение чёрное, то любой цвет, который вы предоставите, будет написан так, как вы его укажете. С новым цветом, использующим "0" для альфа-канала, будут сохранены исходные значения альфа-канала. Конечный результат - это композит, который вы ищете.
Редактировать:
Вы можете загрузить начальный BufferedImage одним из двух способов. Проще всего использовать более новый Java ImageIO API: http://download.oracle.com/javase/6/docs/api/javax/imageio/ImageIO.html чтобы загрузить файл непосредственно в BufferedImage. Вызов будет выглядеть примерно так:
BufferedImage img = ImageIO.read(url);
Кроме того, вы можете создать метод для чтения изображения с помощью набора инструментов.
public BufferedImage loadImage(String url) {
ImageIcon icon = new ImageIcon(url);
Image image = icon.getImage();
// Create empty BufferedImage, sized to Image
BufferedImage buffImage =
new BufferedImage(
image.getWidth(null),
image.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
// Draw Image into BufferedImage
Graphics g = buffImage.getGraphics();
g.drawImage(image, 0, 0, null);
return buffImage;
}
Конечно, если вы обратите внимание, мы должны сделать то же самое, чтобы прочитать изображение в буферизованное изображение, как мы делаем, чтобы подкрасить его. Короче говоря, если вы изменили подпись colorImage
способ принять Image
объект, вам нужно всего лишь внести пару изменений в методы getWidth() и getHeight(), чтобы заставить его работать.
Чтобы рассчитать среднее значение для каждого цветового компонента и сохранить исходную альфа:
public static void tint(BufferedImage image, Color color) {
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
Color pixelColor = new Color(image.getRGB(x, y), true);
int r = (pixelColor.getRed() + color.getRed()) / 2;
int g = (pixelColor.getGreen() + color.getGreen()) / 2;
int b = (pixelColor.getBlue() + color.getBlue()) / 2;
int a = pixelColor.getAlpha();
int rgba = (a << 24) | (r << 16) | (g << 8) | b;
image.setRGB(x, y, rgba);
}
}
}
Это работает лучше всего для моего случая.
public static void tint(BufferedImage img) {
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
Color color = new Color(img.getRGB(x, y));
// do something with the color :) (change the hue, saturation and/or brightness)
// float[] hsb = new float[3];
// Color.RGBtoHSB(color.getRed(), old.getGreen(), old.getBlue(), hsb);
// or just call brighter to just tint it
Color brighter = color.brighter();
img.setRGB(x, y, brighter.getRGB());
}
}
}
Самый простой способ сделать это - использовать фильтры изображений от JH Labs. Вы можете просто настроить HSB, позвонив,
public BufferedImage setHSB(BufferedImage source, float hValue, float sValue, float bValue) {
com.jhlabs.image.HSBAdjustFilter hsb hsb = new HSBAdjustFilter();
BufferedImage destination = hsb.createCompatibleDestImage(source, null);
hsb.setHFactor(hValue);
hsb.setSFactor(sValue);
hsb.setBFactor(bValue);
BufferedImage result = hsb.filter(bi, destination);
return result;
}
Я попробовал каждое решение на этой странице, не повезло. Xor one (принятый ответ) не сработал для меня - он окрашивал странный желтый цвет вместо цвета, который я приводил в качестве аргумента, независимо от того, какой аргумент. Я наконец нашел подход, который работает для меня, хотя это немного грязно. Думаю, я бы добавил его на тот случай, если у кого-то возникнут те же проблемы, что и с другими решениями Ура!
/** Tints the given image with the given color.
* @param loadImg - the image to paint and tint
* @param color - the color to tint. Alpha value of input color isn't used.
* @return A tinted version of loadImg */
public static BufferedImage tint(BufferedImage loadImg, Color color) {
BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(),
BufferedImage.TRANSLUCENT);
final float tintOpacity = 0.45f;
Graphics2D g2d = img.createGraphics();
//Draw the base image
g2d.drawImage(loadImg, null, 0, 0);
//Set the color to a transparent version of the input color
g2d.setColor(new Color(color.getRed() / 255f, color.getGreen() / 255f,
color.getBlue() / 255f, tintOpacity));
//Iterate over every pixel, if it isn't transparent paint over it
Raster data = loadImg.getData();
for(int x = data.getMinX(); x < data.getWidth(); x++){
for(int y = data.getMinY(); y < data.getHeight(); y++){
int[] pixel = data.getPixel(x, y, new int[4]);
if(pixel[3] > 0){ //If pixel isn't full alpha. Could also be pixel[3]==255
g2d.fillRect(x, y, 1, 1);
}
}
}
g2d.dispose();
return img;
}
Это не совсем тонировка, это больше похоже на нанесение на него другого слоя, но это работает для меня:
public static BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue, int alpha /*Also the intesity*/) {
Graphics g = loadImg.getGraphics();
g.setColor(new Color(red, green, blue, alpha));
g.fillRect(0, 0, loadImg.getWidth(), loadImg.getHeight());
g.dispose();
return loadImg;
}
Поскольку все методы, которые я нашел, не работали для меня по любой причине, вот простой способ приблизиться к этому (никаких дополнительных библиотек не требуется):
/**
* Colors an image with specified color.
* @param r Red value. Between 0 and 1
* @param g Green value. Between 0 and 1
* @param b Blue value. Between 0 and 1
* @param src The image to color
* @return The colored image
*/
protected BufferedImage color(float r, float g, float b, BufferedImage src) {
// Copy image ( who made that so complicated :< )
BufferedImage newImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TRANSLUCENT);
Graphics2D graphics = newImage.createGraphics();
graphics.drawImage(src, 0, 0, null);
graphics.dispose();
// Color image
for (int i = 0; i < newImage.getWidth(); i++) {
for (int j = 0; j < newImage.getHeight(); j++) {
int ax = newImage.getColorModel().getAlpha(newImage.getRaster().getDataElements(i, j, null));
int rx = newImage.getColorModel().getRed(newImage.getRaster().getDataElements(i, j, null));
int gx = newImage.getColorModel().getGreen(newImage.getRaster().getDataElements(i, j, null));
int bx = newImage.getColorModel().getBlue(newImage.getRaster().getDataElements(i, j, null));
rx *= r;
gx *= g;
bx *= b;
newImage.setRGB(i, j, (ax << 24) | (rx << 16) | (gx << 8) | (bx << 0));
}
}
return newImage;
}
Черное изображение всегда будет оставаться черным, но белое изображение будет тем цветом, который вы укажете. Этот метод проходит через каждый пиксель и умножает значения красного, зеленого и синего изображения на параметры. Это точное поведение метода OpenGL glColor3f (). Параметры R, G и B должны быть от 0,0F до 1,0F.
Этот метод не имеет проблем с альфа-значениями.