在 Java 中将灰度 BufferedImage 乘以自定义颜色
Multiply greyscale BufferedImage by custom Color in Java
到处搜索,找到了一些方法。然而,在逐像素方法中(从 Raster
中提取 byte
数据,位移位/乘以 ARGB 结果的值),我在弄清楚如何将我的 Color
应用于该像素时遇到了一些困难.
图片如下:
这是我当前的方法和代码,但 foiled by a bug 将在后续的 JRE / JDK 版本中修复,但目前还没有 (8u66):
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = new Float(Integer.toString(shader.getRed()));
float blue = new Float(Integer.toString(shader.getBlue()));
float green = new Float(Integer.toString(shader.getGreen()));
if(red > 0.0f) red = 255.0f / red * 100f;
if(blue > 0.0f) blue = 255.0f / blue * 100f;
if(green > 0.0f) green = 255.0f / green * 100f;
System.out.println(red + ", " + blue + ", " + green);
float[] factors = new float[] {
1.0f, 1.0f, 1.0f, 1.0f
};
float[] offsets = new float[] {
red, blue, green
};
RescaleOp op = new RescaleOp(factors, offsets, null);
output = op.filter(input, null);
return output;
}
这是我之前的方法,希望能正确输入颜色值 (Stack Overflow source),但它会无限期地挂起(好吧,在我杀死它之前挂了几分钟)。可能是我的代码,但性能也可能需要考虑:
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = new Float(Integer.toString(shader.getRed()));
float blue = new Float(Integer.toString(shader.getBlue()));
float green = new Float(Integer.toString(shader.getGreen()));
int red1 = shader.getRed();
int blue1 = shader.getBlue();
int green1 = shader.getGreen();
final byte[] pixels = ((DataBufferByte) input.getRaster().getDataBuffer()).getData();
final int width = input.getWidth();
final int height = input.getHeight();
final boolean hasAlphaChannel = input.getAlphaRaster() != null;
int[][] result = new int[height][width];
if(hasAlphaChannel) {
final int pixelLength = 4;
for(int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
argb += ((int) (pixels[pixel + 1] + blue1) & 0xff); // blue
argb += (((int) (pixels[pixel + 2] + green1) & 0xff) << 8); // green
argb += (((int) (pixels[pixel + 3] + red1) & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for(int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) (pixels[pixel] + blue1) & 0xff); // blue
argb += (((int) (pixels[pixel + 1] + green1) & 0xff) << 8); // green
argb += (((int) (pixels[pixel + 2] + red1) & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
}
System.out.println(input.getRaster().getWidth() + " / " + input.getRaster().getHeight());
WritableRaster raster = Raster.createWritableRaster(input.getSampleModel(), new Point(0, 0));
for(int i = 0; i < input.getRaster().getWidth(); i++) {
for(int j = 0; j < input.getRaster().getHeight(); j++) {
int k = result[i][j];
raster.setSample(i, j, 0, k);
}
}
output.setData(raster);
return output;
}
有任何建议或链接吗?我是否在 byte
到 ARGB 方法的替代方法中遗漏了一些简单的东西?
This is the closest question I've found,但令人惊讶的是连一票都没有,没关系回答。
根据我上面的评论,使用 4 个因子和 4 个偏移量,使用第一个创建的 output
并将 RBG 顺序重新排列为 RGB,我有以下代码并且工作正常:
public class ShaderTest {
private final Color shader;
public ShaderTest(final Color shader) {
this.shader = shader;
}
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = shader.getRed();
float green = shader.getGreen();
float blue = shader.getBlue();
if(red > 0.0f) red = 255.0f / red * 100f;
if(green > 0.0f) green = 255.0f / green * 100f;
if(blue > 0.0f) blue = 255.0f / blue * 100f;
System.out.println(red + ", " + green + ", " + blue);
float[] factors = new float[] {
1.0f, 1.0f, 1.0f, 1.0f
};
float[] offsets = new float[] {
red, green, blue, 0
};
RescaleOp op = new RescaleOp(factors, offsets, null);
return op.filter(input, output);
}
// Test code
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
final BufferedImage shaded = new ShaderTest(Color.ORANGE).applyShader(image);
// Show shaded image
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("ShaderTest");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(shaded)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
到处搜索,找到了一些方法。然而,在逐像素方法中(从 Raster
中提取 byte
数据,位移位/乘以 ARGB 结果的值),我在弄清楚如何将我的 Color
应用于该像素时遇到了一些困难.
图片如下:
这是我当前的方法和代码,但 foiled by a bug 将在后续的 JRE / JDK 版本中修复,但目前还没有 (8u66):
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = new Float(Integer.toString(shader.getRed()));
float blue = new Float(Integer.toString(shader.getBlue()));
float green = new Float(Integer.toString(shader.getGreen()));
if(red > 0.0f) red = 255.0f / red * 100f;
if(blue > 0.0f) blue = 255.0f / blue * 100f;
if(green > 0.0f) green = 255.0f / green * 100f;
System.out.println(red + ", " + blue + ", " + green);
float[] factors = new float[] {
1.0f, 1.0f, 1.0f, 1.0f
};
float[] offsets = new float[] {
red, blue, green
};
RescaleOp op = new RescaleOp(factors, offsets, null);
output = op.filter(input, null);
return output;
}
这是我之前的方法,希望能正确输入颜色值 (Stack Overflow source),但它会无限期地挂起(好吧,在我杀死它之前挂了几分钟)。可能是我的代码,但性能也可能需要考虑:
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = new Float(Integer.toString(shader.getRed()));
float blue = new Float(Integer.toString(shader.getBlue()));
float green = new Float(Integer.toString(shader.getGreen()));
int red1 = shader.getRed();
int blue1 = shader.getBlue();
int green1 = shader.getGreen();
final byte[] pixels = ((DataBufferByte) input.getRaster().getDataBuffer()).getData();
final int width = input.getWidth();
final int height = input.getHeight();
final boolean hasAlphaChannel = input.getAlphaRaster() != null;
int[][] result = new int[height][width];
if(hasAlphaChannel) {
final int pixelLength = 4;
for(int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
argb += ((int) (pixels[pixel + 1] + blue1) & 0xff); // blue
argb += (((int) (pixels[pixel + 2] + green1) & 0xff) << 8); // green
argb += (((int) (pixels[pixel + 3] + red1) & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for(int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) (pixels[pixel] + blue1) & 0xff); // blue
argb += (((int) (pixels[pixel + 1] + green1) & 0xff) << 8); // green
argb += (((int) (pixels[pixel + 2] + red1) & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
}
System.out.println(input.getRaster().getWidth() + " / " + input.getRaster().getHeight());
WritableRaster raster = Raster.createWritableRaster(input.getSampleModel(), new Point(0, 0));
for(int i = 0; i < input.getRaster().getWidth(); i++) {
for(int j = 0; j < input.getRaster().getHeight(); j++) {
int k = result[i][j];
raster.setSample(i, j, 0, k);
}
}
output.setData(raster);
return output;
}
有任何建议或链接吗?我是否在 byte
到 ARGB 方法的替代方法中遗漏了一些简单的东西?
This is the closest question I've found,但令人惊讶的是连一票都没有,没关系回答。
根据我上面的评论,使用 4 个因子和 4 个偏移量,使用第一个创建的 output
并将 RBG 顺序重新排列为 RGB,我有以下代码并且工作正常:
public class ShaderTest {
private final Color shader;
public ShaderTest(final Color shader) {
this.shader = shader;
}
public BufferedImage applyShader(BufferedImage input) {
BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
float red = shader.getRed();
float green = shader.getGreen();
float blue = shader.getBlue();
if(red > 0.0f) red = 255.0f / red * 100f;
if(green > 0.0f) green = 255.0f / green * 100f;
if(blue > 0.0f) blue = 255.0f / blue * 100f;
System.out.println(red + ", " + green + ", " + blue);
float[] factors = new float[] {
1.0f, 1.0f, 1.0f, 1.0f
};
float[] offsets = new float[] {
red, green, blue, 0
};
RescaleOp op = new RescaleOp(factors, offsets, null);
return op.filter(input, output);
}
// Test code
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
final BufferedImage shaded = new ShaderTest(Color.ORANGE).applyShader(image);
// Show shaded image
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("ShaderTest");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(shaded)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}