我正在开发一个Java应用程序,在JPanel上绘制了许多颜色为红色,绿色和蓝色的地理形状。
现在,我想重叠形状,让它们的重叠区域用添加这两种颜色的颜色绘制,如右图所示。
下图是使用透明色的绘图颜色的当前真实截图,但这不起作用。接下来是所需结果的编辑图像。我怎样才能做到这一点?
使用以下方式绘制当前图形:
public class DrawPanel extends JPanel
Color red, green, blue;
DrawPanel(){
red = new Color(255, 0, 0, 150);
green = new Color(0, 255, 0, 150);
blue = new Color(0, 0, 255, 150);
this.setBackground(Color.WHITE);
this.setSize(400, 300);
this.setFocusable(true);
this.requestFocus();
this.setVisible(true);
}
@Override
protected void paintComponent(java.awt.Graphics g) {
g.setColor(red);
g.fillRect(50, 50, 100, 100);
g.setColor(blue);
g.fillRect(75, 75, 100, 100);
g.setColor(green);
g.fillRect(100, 100, 100, 100);
}
}
编辑:我发现使用
g.setXORMode(Color.Black)
使颜色按预期混合:
无论其!这样做似乎非常耗费资源。该程序会冻结几秒钟甚至崩溃我的电脑!
答案 0 :(得分:2)
Java Graphics2D
有一个Composite
,它与CompositeContext
一起决定了颜色的合成方式。不幸的是,AlphaComposite
提供的模式都没有做你想要的。可以创建一个将颜色添加到一起的自定义合成器,但这并不简单。有了这样的合成器后,请调用Graphics2D::setComposite
将其设置为用于绘图的合成器。
我找到了一个已经创建an additive compositor的人。我已经在这里复制了一些代码并进行了一些修改,以防帖子变得不可用。特别是,我将其更改为使用int
而不是float
作为样本,这更有可能与支持SampleModel
的实际Rasters
匹配。
添加剂组合物存在一个问题,即JPanel的背景颜色也包括在添加剂中。要获得与您正在寻找的结果类似的结果,您需要将“绿色屏幕”用作chroma-key的方式排除此背景颜色。我把它添加到了JuddMann的添加剂合成器中:
AdditiveComposite.java:
import java.awt.*;
import java.awt.image.*;
import java.util.Objects;
public class AdditiveComposite implements Composite {
private final Color chromaKey;
public AdditiveComposite(final Color chromaKey) {
this.chromaKey = Objects.requireNonNull(chromaKey);
}
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel, RenderingHints hints) {
return new AdditiveCompositeContext(chromaKey);
}
}
AdditiveCompositeContext.java:
import java.awt.*;
import java.awt.image.*;
import java.util.Objects;
public class AdditiveCompositeContext implements CompositeContext {
private final Color chromaKey;
public AdditiveCompositeContext(final Color chromaKey) {
this.chromaKey = Objects.requireNonNull(chromaKey);
}
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
int r = chromaKey.getRed(), g = chromaKey.getGreen(), b = chromaKey.getBlue();
int[] pxSrc = new int[src.getNumBands()];
int[] pxDst = new int[dstIn.getNumBands()];
int chans = Math.min(src.getNumBands(), dstIn.getNumBands());
for (int x = 0; x < dstIn.getWidth(); x++) {
for (int y = 0; y < dstIn.getHeight(); y++) {
pxSrc = src.getPixel(x, y, pxSrc);
pxDst = dstIn.getPixel(x, y, pxDst);
int alpha = pxSrc.length > 3? alpha = pxSrc[3] : 255;
if (pxDst[0] == r && pxDst[1] == g && pxDst[2] == b) {
pxDst[0] = 0; pxDst[1] = 0; pxDst[2] = 0;
}
for (int i = 0; i < 3 && i < chans; i++) {
pxDst[i] = Math.min(255, (pxSrc[i] * alpha / 255) + (pxDst[i]));
dstOut.setPixel(x, y, pxDst);
}
}
}
}
@Override public void dispose() { }
}
(我还进行了一些格式更改并删除了未使用的变量。)
作者JuddMann在评论中提到了一些性能考虑因素。对于要合成的区域中的每个像素,代码调用getPixel
两次,setPixel
调用一次。可以调用getPixels
和setPixels
来一次获取整个扫描线或像素块,但在某些测试中,我这样做似乎对所花费的时间几乎没有影响,在下面的演示中,绘制方法需要几十毫秒的时间。
这是一个快速演示。 Compo.java:
import java.awt.*;
import javax.swing.*;
public class Compo extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame("Compo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.setLocation(400, 100);
JPanel buttons = new JPanel();
frame.add(buttons, BorderLayout.NORTH);
frame.add(new Compo(), BorderLayout.CENTER);
frame.setVisible(true);
}
@Override public void paintComponent(Graphics g1) {
super.paintComponent(g1);
Graphics2D g = (Graphics2D) g1;
g.setComposite(new AdditiveComposite(getBackground()));
int x = getWidth() / 5;
int y = getHeight() / 5;
g.setColor(Color.RED);
g.fillOval(3*x/2, y, 2*x, 2*y);
g.setColor(Color.BLUE);
g.fillOval(2*x, 2*y, 2*x, 2*y);
g.setColor(Color.GREEN);
g.fillOval(x, 2*y, 2*x, 2*y);
}
}