如何在Java中使用加色混合制作重叠的绘制形状?

时间:2017-02-25 23:47:10

标签: java graphics colors

我正在开发一个Java应用程序,在JPanel上绘制了许多颜色为红色,绿色和蓝色的地理形状。

现在,我想重叠形状,让它们的重叠区域用添加这两种颜色的颜色绘制,如右图所示。

下图是使用透明色的绘图颜色的当前真实截图,但这不起作用。接下来是所需结果的编辑图像。我怎样才能做到这一点?

enter image description here enter image description here

使用以下方式绘制当前图形:

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)

使颜色按预期混合:

enter image description here

无论其!这样做似乎非常耗费资源。该程序会冻结几秒钟甚至崩溃我的电脑!

1 个答案:

答案 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调用一次。可以调用getPixelssetPixels来一次获取整个扫描线或像素块,但在某些测试中,我这样做似乎对所花费的时间几乎没有影响,在下面的演示中,绘制方法需要几十毫秒的时间。

这是一个快速演示。 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);
    }
}