背景:私は、「無効」な外観の画像を作成できる必要があります。一般的に示唆されている方法は、画像をグレースケールに変換し、グレースケール画像を表示することです。欠点は、画像でのみ機能するため、無効状態の画像にすぐにアクセスできないグラフィックを表示するのが面倒です。 これはjava.awt.Compositeを使って即座に実行できると思いました(アイコンを無効にするためにアイコンがどのように実装されているか知る必要はありません)。グレイスケールに変換する実装がないように思われるので、自分で作成する必要がありました。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);
}
}
}
BlendCompositeは役に立つツールのように見えますが、ヒントのおかげです。見た目からは、compose()メソッド内のすべての可能なColorModelで動作しないかのように見えますが、おそらく他のColorModelは実際には関係ありません。 "あなたはいつもBufferedImageを作成することができます"に関しては、compose()メソッドのインプリメンテーションでどのようにできるのか分かりません。 paintComponent()のどこかで行うことは可能ですが、Compositeを設定してから通常のレンダリングチェーンを続けるよりもはるかに柔軟性がありません。 – Durandal
@Durandalすべてのカラーモデルに適しているかどうかはわかりません(私は推測できるものの、このコンテキストでどのColorModelが使用されているのか分かりません)、私が知っていること、 SwingXは公式のSwingチームからの入力を受けているので、一般的な用途に柔軟に対応する必要があります。私の最初の提案では、Compositeを使用せず、paintComponentでやっていました。これは、柔軟性があり、ちょっとした作業(図面のサイズに合わせて画像を作成し、描画し、フィルタリングし、ペイントします。 ....同じサイズの場合に最後に使用するもの).... – Tom
@Durandal BufferedImageの 'Graphics'を作成し、そのグラフィックスインスタンスで' paintComponent'を呼び出すことができます。 – Tom