15

AWT Graphics2DにSWT GCのような xor モード描画を実装しようとしています。組み込みのXORCompositeの使用は、SWT のように xor モードの描画を実装していないため、オプションではありません。

SWT の xor モード描画は、バイナリの排他的論理和を介してソースと宛先の色を組み合わせます。AWT XORComposite ( 経由で使用可能g2d.setXORMode(Color)) は、一定の xor-color を使用します。この xor-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 は例外をスローしませんが、CompositeContext に渡したい Raster オブジェクトを作成しようとすると、AWT 内部が例外をスローします。

残念ながら、PixelToParallelogramConverter は、アンチエイリアシングが有効になっている場合にのみ、カスタム コンポジットに使用されます。たとえば、組み込みの XORComposite はネイティブ メソッドを使用して描画します。AWT のバグだと思いますが、よくわかりません。

助けていただければ幸いです:)

アップデート:

Durandal が提案したように、java-6-sun と java-1.6.0-openjdk でコードをテストしました。OpenJDK は例外をスローし、Sun-JDK はスローしないことがわかりました。そのため、 OpenJDK バグトラッカーでバグを報告しました。

問題が解決したら、この質問を更新します。現在の進行状況については、対応する OpenJDK バグを参照してください。

よろしく、マティアス

以下にサンプル プログラムを示します。ローカルでテストできます。

/*******************************************************************************
 * 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));
    }

}
4

2 に答える 2