Merlin の魔術

Porter-Duff のルール!

Java 2D が残りの 4 つのルールを追加

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Merlin の魔術

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Merlin の魔術

このシリーズの続きに乞うご期待。

かつて 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) には何も描画されません。

AlphaComposite.CLEAR
AlphaComposite.CLEAR
AlphaComposite.CLEAR

ルール 2.AlphaComposite.SRC: SRC はソースを意味します。ソース・イメージ (source image) だけが結合イメージに含まれます。

AlphaComposite.SRC
AlphaComposite.SRC
AlphaComposite.SRC

ルール 3.AlphaComposite.DST*: DST は宛先を意味します。宛先イメージ (destination image) だけが、結合イメージに含まれます。

AlphaComposite.DST
AlphaComposite.DST
AlphaComposite.DST

ルール 4.AlphaComposite.SRC_OVER: ソース・イメージが宛先イメージの上に描画されます。

AlphaComposite.SRC_OVER
AlphaComposite.SRC_OVER
AlphaComposite.SRC_OVER

ルール 5.AlphaComposite.DST_OVER: 宛先イメージがソース・イメージの上に描画されます。

AlphaComposite.DST_OVER
AlphaComposite.DST_OVER
AlphaComposite.DST_OVER

ルール 6.AlphaComposite.SRC_IN: ソース・イメージのうちの宛先イメージと重なる部分だけが描画されます。

AlphaComposite.SRC_IN
AlphaComposite.SRC_IN
AlphaComposite.SRC_IN

ルール 7.AlphaComposite.DST_IN: 宛先イメージのうちのソース・イメージと重なる部分だけが描画されます。

AlphaComposite.DST_IN
AlphaComposite.DST_IN
AlphaComposite.DST_IN

ルール 8.AlphaComposite.SRC_OUT: ソース・イメージのうちの宛先イメージと重ならない部分だけが描画されます。

AlphaComposite.SRC_OUT
AlphaComposite.SRC_OUT
AlphaComposite.SRC_OUT

ルール 9.AlphaComposite.DST_OUT: 宛先イメージのうちのソース・イメージと重ならない部分だけが描画されます。

AlphaComposite.DST_OUT
AlphaComposite.DST_OUT
AlphaComposite.DST_OUT

ルール 10.AlphaComposite.SRC_ATOP*: ソース・イメージのうちのソース・イメージと重なる部分が、宛先イメージのうちのソース・イメージと重ならない部分とともに描画されます。

AlphaComposite.SRC_ATOP
AlphaComposite.SRC_ATOP
AlphaComposite.SRC_ATOP

ルール 11.AlphaComposite.DST_ATOP*: 宛先イメージのうちのソース・イメージと重なる部分が、ソース・イメージのうちの宛先イメージと重ならない部分とともに描画されます。

AlphaComposite.DST_ATOP
AlphaComposite.DST_ATOP
AlphaComposite.DST_ATOP

ルール 12.AlphaComposite.XOR*: ソース・イメージのうちの宛先イメージと重ならない部分が、宛先イメージのうちのソース・イメージと重ならない部分とともに描画されます。

AlphaComposite.XOR
AlphaComposite.XOR
AlphaComposite.XOR

完結した例

次に示すプログラムは、アルファ合成ルールのインタラクティブ・デモンストレーションです。各トライアングルの不透過度を変更し、イメージの合成による効果を確かめるために使うルールを選んでみてください。

リスト 1. インタラクティブな例
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.lang.reflect.*;
import javax.swing.*;
import javax.swing.event.*;
import 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);
    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(
          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;
    }
  }
}

ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218844
ArticleTitle=Merlin の魔術: Porter-Duff のルール!
publish-date=09012001