我正在尝试为 AWT Graphics2D 实现类似 SWT GC 的异或模式绘图。使用内置的 XORComposite 不是一个选项,因为它不能像 SWT 那样实现异或模式绘图。
SWT 的异或模式绘图通过二进制异或方式结合源颜色和目标颜色。AWT XORComposite(可通过
我所想到的唯一选择是编写自己的Composite和CompositeContext实现,以适当地结合源和目标。在阅读了一些资料后,我想出了这个简单的实现:(是的,我知道getPixel(...),setPixel(...)开销很大。在优化之前,我希望它能正常工作。)
值得注意的是,我的Composite/CompositeContext没有抛出异常,但当尝试创建Raster对象并将其传递给我的CompositeContext时,AWT内部会抛出异常。不幸的是,当启用反锯齿时,PixelToParallelogramConverter只用于自定义合成操作。例如,内置的XORComposite使用本地方法进行绘制。我认为这是一个AWT bug,但我不确定。非常感谢您的帮助 :)
更新:正如Durandal所建议的那样,我使用java-6-sun和java-1.6.0-openjdk测试了代码。我发现OpenJDK会抛出异常,而Sun-JDK则不会。因此,我在OpenJDK bug跟踪器上报告了该bug。问题解决后,我将更新此问题。请访问相应的OpenJDK bug以获取有关当前进展的信息。
敬礼, Matthias
以下是一个示例程序,您可以在本地测试它:
SWT 的异或模式绘图通过二进制异或方式结合源颜色和目标颜色。AWT XORComposite(可通过
g2d.setXORMode(Color)
使用)使用一个恒定的异或颜色,该颜色与源颜色通过二进制异或结合,即目标颜色不影响最终颜色。我所想到的唯一选择是编写自己的Composite和CompositeContext实现,以适当地结合源和目标。在阅读了一些资料后,我想出了这个简单的实现:(是的,我知道getPixel(...),setPixel(...)开销很大。在优化之前,我希望它能正常工作。)
private static class XorComposite implements Composite {
public static XorComposite INSTANCE = new XorComposite();
private XorContext context = new XorContext();
@Override
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel, RenderingHints hints) {
return context;
}
}
private static class XorContext implements CompositeContext {
public XorContext() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
int w = Math.min(src.getWidth(), dstIn.getWidth());
int h = Math.min(src.getHeight(), dstIn.getHeight());
int[] srcRgba = new int[4];
int[] dstRgba = new int[4];
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
src.getPixel(x, y, srcRgba);
dstIn.getPixel(x, y, dstRgba);
for (int i = 0; i < 3; i++) {
dstRgba[i] ^= srcRgba[i];
}
dstOut.setPixel(x, y, dstRgba);
}
}
}
@Override
public void dispose() {
}
}
当关闭抗锯齿时,此实现可以正常工作。如果启用了抗锯齿,只要我的绘图完全可见,即在我绘制的JPanel内部,它就可以工作。但是,如果一幅图跨越了JPanel的边界,则会引发RasterFormatException异常:
Exception in thread "AWT-EventQueue-0" java.awt.image.RasterFormatException: (y + height) is outside raster
at sun.awt.image.IntegerInterleavedRaster.createWritableChild(IntegerInterleavedRaster.java:470)
at sun.awt.image.IntegerInterleavedRaster.createChild(IntegerInterleavedRaster.java:514)
at sun.java2d.pipe.GeneralCompositePipe.renderPathTile(GeneralCompositePipe.java:106)
at sun.java2d.pipe.AAShapePipe.renderTiles(AAShapePipe.java:201)
at sun.java2d.pipe.AAShapePipe.fillParallelogram(AAShapePipe.java:102)
at sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(PixelToParallelogramConverter.java:322)
at sun.java2d.pipe.PixelToParallelogramConverter.fill(PixelToParallelogramConverter.java:159)
at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:160)
at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2422)
at org.eclipse.gef4.graphics.examples.AwtXorTestPanel.paint(AwtXorTest.java:117)
... (irrelevant)
值得注意的是,我的Composite/CompositeContext没有抛出异常,但当尝试创建Raster对象并将其传递给我的CompositeContext时,AWT内部会抛出异常。不幸的是,当启用反锯齿时,PixelToParallelogramConverter只用于自定义合成操作。例如,内置的XORComposite使用本地方法进行绘制。我认为这是一个AWT bug,但我不确定。非常感谢您的帮助 :)
更新:正如Durandal所建议的那样,我使用java-6-sun和java-1.6.0-openjdk测试了代码。我发现OpenJDK会抛出异常,而Sun-JDK则不会。因此,我在OpenJDK bug跟踪器上报告了该bug。问题解决后,我将更新此问题。请访问相应的OpenJDK bug以获取有关当前进展的信息。
敬礼, Matthias
以下是一个示例程序,您可以在本地测试它:
/*******************************************************************************
* Copyright (c) 2013 itemis AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef4.graphics.examples;
import java.awt.Color;
public class AwtXorTest extends JApplet {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("AWT XorMode Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet applet = new AwtXorTest();
applet.init();
frame.getContentPane().add(applet);
frame.pack();
frame.setVisible(true);
}
@Override
public void init() {
JPanel panel = new AwtXorTestPanel();
getContentPane().add(panel);
}
}
class AwtXorTestPanel extends JPanel {
private static class XorComposite implements Composite {
public static XorComposite INSTANCE = new XorComposite();
private XorContext context = new XorContext();
@Override
public CompositeContext createContext(ColorModel srcColorModel,
ColorModel dstColorModel, RenderingHints hints) {
return context;
}
}
private static class XorContext implements CompositeContext {
public XorContext() {
}
@Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
int w = Math.min(src.getWidth(), dstIn.getWidth());
int h = Math.min(src.getHeight(), dstIn.getHeight());
int[] srcRgba = new int[4];
int[] dstRgba = new int[4];
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
src.getPixel(x, y, srcRgba);
dstIn.getPixel(x, y, dstRgba);
for (int i = 0; i < 3; i++) {
dstRgba[i] ^= srcRgba[i];
}
dstOut.setPixel(x, y, dstRgba);
}
}
}
@Override
public void dispose() {
}
}
private static final long serialVersionUID = 1L;
public AwtXorTestPanel() {
setPreferredSize(new Dimension(640, 480));
}
@Override
public void paint(Graphics graphics) {
Graphics2D g2d = (Graphics2D) graphics;
// comment out to see it working:
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(XorComposite.INSTANCE);
g2d.setColor(new Color(0, 255, 255)); // resulting color should be red
g2d.fill(new Rectangle(100, 100, 500, 500));
}
}