7

背景: 「無効」な外観の画像を作成できるようにする必要があります。一般的に推奨される方法は、画像をグレースケールに変換し、グレースケールの画像を表示することです。欠点は、画像でしか機能しないため、無効な状態の画像にすぐにアクセスできない場合にグラフィックを表示するのが面倒なことです。これは、java.awt.Composite を使用してその場で実行できると考えました (たとえば、アイコンを無効にするために実装する方法を知る必要はありません)。グレースケールに変換する実装がないように見えるだけなので、独自に作成する必要がありました...

そうは言っても、私は実装を一緒にハックしました (そして、それは私が期待するものをレンダリングします)。しかし、すべてのケースで本当に正しく機能するかどうかはわかりません (Composite/CompositeContext の Javadoc は、このような複雑な操作に対して非常に薄いようです)。そして、私の実装からわかるように、関連するラスターによって指示されない形式でピクセルを一括して読み書きする簡単な方法がないように見えるため、ピクセルごとに処理するための回り道をします。

より広範なドキュメント/例/ヒントへのポインタは大歓迎です。

SSCCE は、DisabledComposite を介して (色付きの) GradientPaint をレンダリングし、グラデーションをグレースケールに変換します。現実の世界では、何を呼び出して何をレンダリングするかはわからないことに注意してください。グラデーションは実際には単なる例です (申し訳ありませんが、あまりにも多くの人がそれを理解していないため、今回は明確にします)。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class CompositeSSCE implements Runnable {

    static class DisabledComposite implements Composite {
        @Override
        public CompositeContext createContext(
            final ColorModel srcColorModel,
            final ColorModel dstColorModel,
            final RenderingHints hints) {
            return new DisabledCompositeContext(srcColorModel, dstColorModel);
        }
    } 

    static class DisabledCompositeContext implements CompositeContext {

        private final ColorModel srcCM;
        private final ColorModel dstCM;

        final static int PRECBITS = 22;
        final static int WEIGHT_R = (int) ((1 << PRECBITS) * 0.299); 
        final static int WEIGHT_G = (int) ((1 << PRECBITS) * 0.578);
        final static int WEIGHT_B = (int) ((1 << PRECBITS) * 0.114);
        final static int SRCALPHA = (int) ((1 << PRECBITS) * 0.667);

        DisabledCompositeContext(final ColorModel srcCM, final ColorModel dstCM) {
            this.srcCM = srcCM;
            this.dstCM = dstCM;
        }

        public void compose(final Raster src, final Raster dstIn, final WritableRaster dstOut) {
            final int w = Math.min(src.getWidth(), dstIn.getWidth());
            final int h = Math.min(src.getHeight(), dstIn.getHeight());
            for (int y = 0; y < h; ++y) {
                for (int x = 0; x < w; ++x) {
                    int rgb1 = srcCM.getRGB(src.getDataElements(x, y, null));
                    int a1 = ((rgb1 >>> 24) * SRCALPHA) >> PRECBITS;
                    int gray = (
                        ((rgb1 >> 16) & 0xFF) * WEIGHT_R +
                        ((rgb1 >>  8) & 0xFF) * WEIGHT_G +
                        ((rgb1      ) & 0xFF) * WEIGHT_B
                        ) >> PRECBITS;
                    int rgb2 = dstCM.getRGB(dstIn.getDataElements(x, y, null));
                    int a2 = rgb2 >>> 24;
                    int r2 = (rgb2 >> 16) & 0xFF;
                    int g2 = (rgb2 >>  8) & 0xFF;
                    int b2 = (rgb2      ) & 0xFF;
                    // mix the two pixels
                    gray = gray * a1 / 255;
                    final int ta = a2 * (255 - a1);
                    r2 = gray + (r2 * ta / (255*255));
                    g2 = gray + (g2 * ta / (255*255));
                    b2 = gray + (b2 * ta / (255*255));
                    a2 = a1 + (ta / 255);
                    rgb2 = (a2 << 24) | (r2 << 16) | (g2 << 8) | b2; 
                    Object data = dstCM.getDataElements(rgb2, null);
                    dstOut.setDataElements(x, y, data);
                }
            }
        }

        @Override
        public void dispose() {
            // nothing for this implementation
        }
    }

    // from here on out its only the fluff to make this a runnable example
    public static void main(String[] argv) {
        Runnable r = new CompositeSSCE();
        SwingUtilities.invokeLater(r);
    }

    // simple component to use composite to render
    static class DemoComponent extends JComponent {
        // demonstrate rendering an icon in grayscale with the composite
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            GradientPaint p = new GradientPaint(0, 0, Color.GREEN, 127, 127, Color.BLUE, true);
            g2d.setComposite(new DisabledComposite());
            g2d.setPaint(p);
            g2d.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    // Fluff to use the Composite in Swing 
    public void run() {
        try {
            JFrame f = new JFrame("Test grayscale composite");
            DemoComponent c = new DemoComponent();
            c.setPreferredSize(new Dimension(500, 300));
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLayout(new BorderLayout());
            f.add(c, BorderLayout.CENTER);
            f.pack();
            f.setVisible(true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
4

1 に答える 1

4

いつでも を作成し、を使用してその画像のオブジェクトにBufferedImageグレースケールする必要があるものを描画し、 を使用して画像のグレースケールのコピーを作成できます。GraphicsbufferedImage.createGraphics()javax.swing.GreyFilter

このページでは、 の使用方法を説明しますFilter

また、あなたのアプローチを SwingX の と比較するBlendCompositeこともできます。これは、あなたが行っていることと同じことを期待しています。BlendComposite には、グレースケールを可能にする彩度モードがあります。(他にもモード​​があります。)

このページには BlendComposite のデモがあります。

効率については、中間のステップがあり、私が見る限り、両方で画像データの完全なコピーがあるため、それらの間に重要な違いはないと思います。しかし、私が提案した最初の方法を使用してグレースケールの画像を保持すると、非動的コントロールの再計算を防ぐことができます。

上記のいずれかを実行すると、パフォーマンスが正しくなり、それが本当に必要なことだと思います。

あなたのコメントから、エフェクトをコンポーネントに適用したいだけかもしれません。これにはJLayer、Java 7 でのみ使用可能な を使用できます。Javadocには、半透明の緑を重ねる例があります。これを灰色に置き換えることができます。以前のバージョンの Java で JLayer を使用する場合JXLayerは、SwingX プロジェクトの一部である を使用できます。

于 2012-12-03T15:58:06.307 に答える