Merlin の魔術: Porter-Duff のルール! Java 2D が残りの 4 つのルールを追加 John Zukowski President JZ Ventures, Inc. 2001年 9月 01日 Java 言語による二次元グラフィックス・プログラミングがちょっとした進化を遂げています。 これまでディジタル・イメージ合成に関する 12 の Porter-Duff ルールのうち 8 項目しかサポー トしていなかった AlphaComposite クラスは、12 項目すべてをサポートするようになりまし た。Merlin の魔術の今回の記事で、John Zukowski は 12 の全ルールに言及し、インタラクティ ブ・プログラムを通じてそのはたらきを紹介しています。 このシリーズの他の記事を見る かつて 1984年に、Thomas Porter と Tom Duff は、「Compositing Digital Images」というテーマの 論文を著し、2 つのイメージを合成する場合の 12 のルールについて論じました。こうした合成の ルールに対するサポートは、Java 言語のバージョン 1.2 で最初に導入された AlphaComposite クラ スにみられます。現在ベータ 2 となっているバージョン 1.4 は、12 のルールすべてをサポートし ます。 合成に対するサポートは、背景イメージやプレーヤー・キャラクター・イメージなどの複数のイ メージ・タイプを含むゲームのようなアプリケーションで必要になります。常に背景の上にプ レーヤーを描くことは簡単ですが、プレーヤーが木の向こうにジャンプするような状況では、 キャラクター・イメージが木のイメージの後ろに姿を消すような効果が欲しくなることでしょ う。こんな時こそ、合成が役立ちます。 Porter と Duff の 12 のルール AlphaComposite クラスには 12 の定数があり、それぞれが各ルールに対応しています。ルールが どのように適用されるのかを説明すると、込み入ったことになるおそれもあるので、稼動中の各 ルールを図解して説明します。イメージが不透明でない場合には、実際の組み合わせも異なって きます。リスト 1 に示されているデモンストレーション・プログラムは、参考文献のセクション でダウンロードできるので、さまざまな透過レベルで試してみることができます。 注: Merlin リリースで新たに加えられたルールには、アスタリスク (*) を付けてあります。 ルール 1.AlphaComposite.CLEAR: 結合イメージ (combined image) には何も描画されません。 © Copyright IBM Corporation 2001 Merlin の魔術: Porter-Duff のルール! 商標 ページ 1 / 11 developerWorks® ibm.com/developerWorks/jp/ AlphaComposite.CLEAR ルール 2.AlphaComposite.SRC: SRC はソースを意味します。ソース・イメージ (source image) だけ が結合イメージに含まれます。 AlphaComposite.SRC ルール 3.AlphaComposite.DST*: DST は宛先を意味します。宛先イメージ (destination image) だけ が、結合イメージに含まれます。 AlphaComposite.DST ルール 4.AlphaComposite.SRC_OVER: ソース・イメージが宛先イメージの上に描画されます。 Merlin の魔術: Porter-Duff のルール! ページ 2 / 11 ibm.com/developerWorks/jp/ developerWorks® AlphaComposite.SRC_OVER ルール 5.AlphaComposite.DST_OVER: 宛先イメージがソース・イメージの上に描画されます。 AlphaComposite.DST_OVER ルール 6.AlphaComposite.SRC_IN: ソース・イメージのうちの宛先イメージと重なる部分だけが描 画されます。 AlphaComposite.SRC_IN ルール 7.AlphaComposite.DST_IN: 宛先イメージのうちのソース・イメージと重なる部分だけが描 画されます。 Merlin の魔術: Porter-Duff のルール! ページ 3 / 11 developerWorks® ibm.com/developerWorks/jp/ AlphaComposite.DST_IN ルール 8.AlphaComposite.SRC_OUT: ソース・イメージのうちの宛先イメージと重ならない部分だけ が描画されます。 AlphaComposite.SRC_OUT ルール 9.AlphaComposite.DST_OUT: 宛先イメージのうちのソース・イメージと重ならない部分だけ が描画されます。 AlphaComposite.DST_OUT ルール 10.AlphaComposite.SRC_ATOP*: ソース・イメージのうちのソース・イメージと重なる部分 が、宛先イメージのうちのソース・イメージと重ならない部分とともに描画されます。 Merlin の魔術: Porter-Duff のルール! ページ 4 / 11 ibm.com/developerWorks/jp/ developerWorks® AlphaComposite.SRC_ATOP ルール 11.AlphaComposite.DST_ATOP*: 宛先イメージのうちのソース・イメージと重なる部分が、 ソース・イメージのうちの宛先イメージと重ならない部分とともに描画されます。 AlphaComposite.DST_ATOP ルール 12.AlphaComposite.XOR*: ソース・イメージのうちの宛先イメージと重ならない部分が、宛 先イメージのうちのソース・イメージと重ならない部分とともに描画されます。 AlphaComposite.XOR Merlin の魔術: Porter-Duff のルール! ページ 5 / 11 developerWorks® ibm.com/developerWorks/jp/ 完結した例 次に示すプログラムは、アルファ合成ルールのインタラクティブ・デモンストレーションです。 各トライアングルの不透過度を変更し、イメージの合成による効果を確かめるために使うルール を選んでみてください。 リスト 1. インタラクティブな例 import import import import import import import import java.awt.*; java.awt.event.*; java.awt.geom.*; java.awt.image.*; java.lang.reflect.*; javax.swing.*; javax.swing.event.*; java.util.*; public class CompositeIt extends JFrame { JSlider sourcePercentage = new JSlider(); JSlider destinationPercentage = new JSlider(); JComboBox alphaComposites = new JComboBox(); DrawingPanel drawingPanel = new DrawingPanel(); public CompositeIt() { super("Porter-Duff"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel contentPane = (JPanel) this.getContentPane(); Dictionary labels = new Hashtable(); labels.put(new Integer(0), new JLabel("0.0")); labels.put(new Integer(25), new JLabel("0.25")); labels.put(new Integer(33), new JLabel("0.33")); labels.put(new Integer(50), new JLabel("0.50")); labels.put(new Integer(67), new JLabel("0.67")); labels.put(new Integer(75), new JLabel("0.75")); labels.put(new Integer(100), new JLabel("1.00")); sourcePercentage.setOrientation(JSlider.VERTICAL); sourcePercentage.setLabelTable(labels); sourcePercentage.setPaintTicks(true); sourcePercentage.setPaintLabels(true); sourcePercentage.setValue(100); sourcePercentage.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int sourceValue = sourcePercentage.getValue(); drawingPanel.setSourcePercentage(sourceValue/100.0f); } }); destinationPercentage.setOrientation(JSlider.VERTICAL); destinationPercentage.setLabelTable(labels); destinationPercentage.setPaintTicks(true); destinationPercentage.setPaintLabels(true); destinationPercentage.setValue(100); destinationPercentage.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int destinationValue = destinationPercentage.getValue(); drawingPanel.setDestinationPercentage(destinationValue/100.0f); } }); String rules[] = { "CLEAR", "DST", "DST_ATOP", "DST_IN", "DST_OUT", "DST_OVER", "SRC", "SRC_ATOP", "SRC_IN", "SRC_OUT", "SRC_OVER", "XOR"}; ComboBoxModel model = new DefaultComboBoxModel(rules); alphaComposites.setModel(model); Merlin の魔術: Porter-Duff のルール! ページ 6 / 11 ibm.com/developerWorks/jp/ developerWorks® alphaComposites.setSelectedItem("XOR"); alphaComposites.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String alphaValue = alphaComposites.getSelectedItem().toString(); Class alphaClass = AlphaComposite.class; try { Field field = alphaClass.getDeclaredField(alphaValue); int rule = ((Integer)field.get(AlphaComposite.Clear)).intValue(); drawingPanel.setCompositeRule(rule); } catch (Exception exception) { System.err.println("Unable to find field"); } } }); contentPane.add(sourcePercentage, BorderLayout.WEST); contentPane.add(destinationPercentage, BorderLayout.EAST); contentPane.add(alphaComposites, BorderLayout.SOUTH); contentPane.add(drawingPanel, BorderLayout.CENTER); pack(); } public static void main(String args[]) { new CompositeIt().show(); } class DrawingPanel extends JPanel { GeneralPath sourcePath, destPath; BufferedImage source, dest; float sourcePercentage = 1, destinationPercentage = 1; int compositeRule = AlphaComposite.XOR; Dimension dimension = new Dimension(200, 200); public DrawingPanel() { sourcePath = new GeneralPath(); sourcePath.moveTo(0, 0); sourcePath.lineTo(150, 0); sourcePath.lineTo(0, 200); sourcePath.closePath(); source = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB); destPath = new GeneralPath(); destPath.moveTo(200, 0); destPath.lineTo(50, 0); destPath.lineTo(200, 200); destPath.closePath(); dest = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB); } public void setSourcePercentage(float value) { sourcePercentage = value; repaint(); } public void setDestinationPercentage(float value) { destinationPercentage = value; repaint(); } public void setCompositeRule(int value) { compositeRule = value; repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; Graphics2D sourceG = source.createGraphics(); Graphics2D destG = dest.createGraphics(); destG.setComposite(AlphaComposite.Clear); destG.fillRect(0, 0, 200, 200); destG.setComposite(AlphaComposite.getInstance( Merlin の魔術: Porter-Duff のルール! ページ 7 / 11 developerWorks® ibm.com/developerWorks/jp/ AlphaComposite.XOR, destinationPercentage)); destG.setPaint(Color.magenta); destG.fill(destPath); sourceG.setComposite(AlphaComposite.Clear); sourceG.fillRect(0, 0, 200, 200); sourceG.setComposite(AlphaComposite.getInstance( AlphaComposite.XOR, sourcePercentage)); sourceG.setPaint(Color.green); sourceG.fill(sourcePath); destG.setComposite(AlphaComposite.getInstance(compositeRule)); destG.drawImage(source, 0, 0, null); g2d.drawImage(dest, 0, 0, this); } public Dimension getPreferredSize() { return dimension; } } } Merlin の魔術: Porter-Duff のルール! ページ 8 / 11 ibm.com/developerWorks/jp/ developerWorks® ダウンロード 内容 j-mer0918.zip Merlin の魔術: Porter-Duff のルール! ファイル名 j-mer0918.zip サイズ ページ 9 / 11 developerWorks® ibm.com/developerWorks/jp/ 参考文献 • シリーズ「Merlin の魔術」のその他の記事もお読み下さい。 • さまざまな Porter-Duff ルールについては、原典の SigGraph paper をお読みください。 • Java 2 SDK, 1.4 の新たな Java 2D features について理解を深めてください。 • 「The Java 2 user interface」(developerWorks、2001年 7月) で Java UI の発展過程についてお読 みください。 • IBM ではグラフィックスとビジュアリゼーションの分野で幅広い研究を行っています。現在 進行中のプロジェクトをご覧ください。 • John Zukowski による、以下の Merlin のヒント集もご一読ください。 • Swing の新たな Spinner コンポーネント: JSpinner を使用して、ユーザーがピック・リス トから素早く日付、数値および選択肢を選べるようにする • もう 1 つのシンプルなフレーム: AWT Frame を最大化し、その装飾を取り外す • 長期の永続性: JavaBean コンポーネントの状態を XML にシリアライズする • 挿入順序を保持する: 新たにリンクされた HashSet と HashMap の実装を使ってみる • タブ付きペインのスクロール: 大き過ぎて収まりきらない場合にどうするか • developerWorks の Java ゾーンで他の Java 参考文献をご覧ください。 Merlin の魔術: Porter-Duff のルール! ページ 10 / 11 ibm.com/developerWorks/jp/ developerWorks® 著者について John Zukowski John Zukowski は、JZ Ventures, Inc. の戦略的 Java コンサルティングを推進し、数多く の jGuru のコミュニティー主導の Java FAQs で常任指導者として活躍しています。最 新の著書には、Apress から出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第 2 版) があります。 © Copyright IBM Corporation 2001 (www.ibm.com/legal/copytrade.shtml) 商標 (www.ibm.com/developerworks/jp/ibm/trademarks/) Merlin の魔術: Porter-Duff のルール! ページ 11 / 11