原文: https://zetcode.com/gfx/java2d/composition/

在 Java 2D 编程教程的这一部分中,我们定义了合成操作。

合成是将来自不同来源的视觉元素组合成单个图像。 它们被用来创建一种幻觉,即所有这些元素都是同一场景的一部分。 合成在电影行业中被广泛使用来创造人群,否则将是昂贵或不可能创造的整个新世界。 (wikipedia.org)

工作方式

有几种合成操作。 我们在下一个代码示例中展示其中的一些。 AlphaComposite类实现基本的 alpha 合成规则,用于组合源色和目标色,以实现图形和图像的混合和透明效果。

假设我们要在面板上绘制两个对象。 绘制的第一个对象称为目标,第二个称为源。 AlphaComposite类确定如何将这两个对象混合在一起。 如果我们有AlphaComposite.SRC_OVER规则,则将在两个对象重叠的位置绘制源对象的像素。

CompositionEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.Color;
  4. import java.awt.EventQueue;
  5. import java.awt.Graphics;
  6. import java.awt.Graphics2D;
  7. import java.awt.image.BufferedImage;
  8. import javax.swing.JFrame;
  9. import javax.swing.JPanel;
  10. class Surface extends JPanel {
  11. private final int rules[] = {
  12. AlphaComposite.DST,
  13. AlphaComposite.DST_ATOP,
  14. AlphaComposite.DST_OUT,
  15. AlphaComposite.SRC,
  16. AlphaComposite.SRC_ATOP,
  17. AlphaComposite.SRC_OUT
  18. };
  19. private void doDrawing(Graphics g) {
  20. Graphics2D g2d = (Graphics2D) g.create();
  21. for (int x = 20, y = 20, i = 0; i < rules.length; x += 60, i++) {
  22. AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);
  23. BufferedImage buffImg = new BufferedImage(60, 60,
  24. BufferedImage.TYPE_INT_ARGB);
  25. Graphics2D gbi = buffImg.createGraphics();
  26. gbi.setPaint(Color.blue);
  27. gbi.fillRect(0, 0, 40, 40);
  28. gbi.setComposite(ac);
  29. gbi.setPaint(Color.green);
  30. gbi.fillRect(5, 5, 40, 40);
  31. g2d.drawImage(buffImg, x, y, null);
  32. gbi.dispose();
  33. }
  34. g2d.dispose();
  35. }
  36. @Override
  37. public void paintComponent(Graphics g) {
  38. super.paintComponent(g);
  39. doDrawing(g);
  40. }
  41. }
  42. public class CompositionEx extends JFrame {
  43. public CompositionEx() {
  44. add(new Surface());
  45. setTitle("Composition");
  46. setSize(400, 120);
  47. setLocationRelativeTo(null);
  48. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  49. }
  50. public static void main(String[] args) {
  51. EventQueue.invokeLater(new Runnable() {
  52. @Override
  53. public void run() {
  54. CompositionEx ex = new CompositionEx();
  55. ex.setVisible(true);
  56. }
  57. });
  58. }
  59. }

我们绘制两个矩形,并将它们与六个不同的合成操作合并。

  1. private final int rules[] = {
  2. AlphaComposite.DST,
  3. AlphaComposite.DST_ATOP,
  4. AlphaComposite.DST_OUT,
  5. AlphaComposite.SRC,
  6. AlphaComposite.SRC_ATOP,
  7. AlphaComposite.SRC_OUT
  8. };

在这里,我们有六个不同的合成规则。

  1. AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);

在这里,我们得到AlphaComposite类。

  1. BufferedImage buffImg = new BufferedImage(60, 60,
  2. BufferedImage.TYPE_INT_ARGB);

我们使用缓冲图像执行合成操作。

  1. Graphics2D gbi = buffImg.createGraphics();

使用createGraphics()方法从缓冲的图像创建Graphics2D对象。

  1. gbi.setComposite(ac);

setComposite()方法为Graphics2D上下文设置组合。

  1. g2d.drawImage(buffImg, x, y, null);

使用drawImage()方法在面板上绘制缓冲图像。

  1. gbi.dispose();

必须处理创建的图形对象。

合成 - 图1

图:组合

太阳和云

在下一个示例中,我们显示太阳来自云层后面。 我们将在此动画中使用合成技术。

SunAndCloudEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Image;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. import java.awt.image.BufferedImage;
  10. import javax.swing.ImageIcon;
  11. import javax.swing.JFrame;
  12. import javax.swing.JPanel;
  13. import javax.swing.Timer;
  14. class Surface extends JPanel implements ActionListener {
  15. private Image sun;
  16. private Image cloud;
  17. private Timer timer;
  18. private float alpha = 1f;
  19. private final int DELAY = 600;
  20. public Surface() {
  21. loadImages();
  22. initTimer();
  23. }
  24. private void loadImages() {
  25. sun = new ImageIcon("sun.png").getImage();
  26. cloud = new ImageIcon("cloud.png").getImage();
  27. }
  28. private void initTimer() {
  29. timer = new Timer(DELAY, this);
  30. timer.start();
  31. }
  32. private void doDrawing(Graphics g) {
  33. Graphics2D g2d = (Graphics2D) g.create();
  34. BufferedImage buffImg = new BufferedImage(220, 140,
  35. BufferedImage.TYPE_INT_ARGB);
  36. Graphics2D gbi = buffImg.createGraphics();
  37. AlphaComposite ac = AlphaComposite.getInstance(
  38. AlphaComposite.SRC_OVER, alpha);
  39. gbi.drawImage(sun, 40, 30, null);
  40. gbi.setComposite(ac);
  41. gbi.drawImage(cloud, 0, 0, null);
  42. g2d.drawImage(buffImg, 20, 20, null);
  43. gbi.dispose();
  44. g2d.dispose();
  45. }
  46. @Override
  47. public void paintComponent(Graphics g) {
  48. super.paintComponent(g);
  49. doDrawing(g);
  50. }
  51. private void step() {
  52. alpha -= 0.1;
  53. if (alpha <= 0) {
  54. alpha = 0;
  55. timer.stop();
  56. }
  57. }
  58. @Override
  59. public void actionPerformed(ActionEvent e) {
  60. step();
  61. repaint();
  62. }
  63. }
  64. public class SunAndCloudEx extends JFrame {
  65. public SunAndCloudEx() {
  66. initUI();
  67. }
  68. private void initUI() {
  69. add(new Surface());
  70. setTitle("Sun and cloud");
  71. setSize(300, 210);
  72. setLocationRelativeTo(null);
  73. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  74. }
  75. public static void main(String[] args) {
  76. EventQueue.invokeLater(new Runnable() {
  77. @Override
  78. public void run() {
  79. SunAndCloudEx ex = new SunAndCloudEx();
  80. ex.setVisible(true);
  81. }
  82. });
  83. }
  84. }

太阳来自云层背后。 云终于消失了。

  1. private void loadImages() {
  2. sun = new ImageIcon("sun.png").getImage();
  3. cloud = new ImageIcon("cloud.png").getImage();
  4. }

我们从磁盘加载两个映像。

  1. private void initTimer() {
  2. timer = new Timer(DELAY, this);
  3. timer.start();
  4. }

initTimer()方法内部,计时器被激活。

  1. AlphaComposite ac = AlphaComposite.getInstance(
  2. AlphaComposite.SRC_OVER, alpha);

我们使用AlphaComposite.SRC_OVER规则-源与目标混合并覆盖空白像素。

  1. gbi.drawImage(sun, 40, 30, null);
  2. gbi.setComposite(ac);
  3. gbi.drawImage(cloud, 0, 0, null);
  4. g2d.drawImage(buffImg, 20, 20, null);

图像被渲染到BufferedImage中,随后被复制到屏幕上。 setComposite()指定在渲染过程中如何将新像素与图形设备上的现有像素合并。

合成 - 图2

图:太阳和云

聚光灯

聚光灯是只照亮很小区域的强光束,特别用于将注意力集中在舞台表演者身上。

SpotlightEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.Color;
  4. import java.awt.EventQueue;
  5. import java.awt.Graphics;
  6. import java.awt.Graphics2D;
  7. import java.awt.Image;
  8. import java.awt.event.MouseAdapter;
  9. import java.awt.event.MouseEvent;
  10. import java.awt.image.BufferedImage;
  11. import javax.swing.ImageIcon;
  12. import javax.swing.JFrame;
  13. import javax.swing.JPanel;
  14. class Surface extends JPanel {
  15. private final int RADIUS = 50;
  16. private Image image;
  17. private int iw;
  18. private int ih;
  19. private int x;
  20. private int y;
  21. private boolean mouseIn;
  22. public Surface() {
  23. initUI();
  24. }
  25. private void initUI() {
  26. loadImage();
  27. iw = image.getWidth(null);
  28. ih = image.getHeight(null);
  29. addMouseMotionListener(new MyMouseAdapter());
  30. addMouseListener(new MyMouseAdapter());
  31. }
  32. private void loadImage() {
  33. image = new ImageIcon("penguin.png").getImage();
  34. }
  35. @Override
  36. protected void paintComponent(Graphics g) {
  37. super.paintComponent(g);
  38. doDrawing(g);
  39. }
  40. private void doDrawing(Graphics g) {
  41. Graphics2D g2d = (Graphics2D) g.create();
  42. int midX = (getWidth() - iw) / 2;
  43. int midY = (getHeight() - ih) / 2;
  44. BufferedImage bi = new BufferedImage(getWidth(),
  45. getHeight(), BufferedImage.TYPE_INT_ARGB);
  46. Graphics2D bigr = bi.createGraphics();
  47. if (mouseIn) {
  48. bigr.setPaint(Color.white);
  49. bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2,
  50. RADIUS * 2);
  51. bigr.setComposite(AlphaComposite.SrcAtop);
  52. bigr.drawImage(image, midX, midY, iw, ih, this);
  53. }
  54. bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f));
  55. bigr.drawImage(image, midX, midY, iw, ih, this);
  56. bigr.dispose();
  57. g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);
  58. g2d.dispose();
  59. }
  60. private class MyMouseAdapter extends MouseAdapter {
  61. @Override
  62. public void mouseExited(MouseEvent e) {
  63. mouseIn = false;
  64. repaint();
  65. }
  66. @Override
  67. public void mouseEntered(MouseEvent e) {
  68. mouseIn = true;
  69. }
  70. @Override
  71. public void mouseMoved(MouseEvent e) {
  72. x = e.getX();
  73. y = e.getY();
  74. repaint();
  75. }
  76. }
  77. }
  78. public class SpotlightEx extends JFrame {
  79. public SpotlightEx() {
  80. initUI();
  81. }
  82. private void initUI() {
  83. add(new Surface());
  84. setSize(350, 300);
  85. setTitle("Spotlight");
  86. setLocationRelativeTo(null);
  87. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  88. }
  89. public static void main(String[] args) {
  90. EventQueue.invokeLater(new Runnable() {
  91. @Override
  92. public void run() {
  93. SpotlightEx ex = new SpotlightEx();
  94. ex.setVisible(true);
  95. }
  96. });
  97. }
  98. }

使用构图规则和 Alpha 透明度值创建聚光灯效果。 还必须注意,我们的图像具有透明背景。

  1. BufferedImage bi = new BufferedImage(getWidth(),
  2. getHeight(), BufferedImage.TYPE_INT_ARGB);

创建了BufferedImage。 它的大小等于面板的大小。 我们的 PNG 文件具有透明背景; 因此,我们使用BufferedImage.TYPE_INT_ARGB图像类型。

  1. if (mouseIn) {
  2. bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2,
  3. RADIUS * 2);
  4. bigr.setComposite(AlphaComposite.SrcAtop);
  5. bigr.drawImage(image, midX, midY, iw, ih, this);
  6. }

如果鼠标在面板区域中,则AlphaComposite.SrcAtop规则用于在鼠标指针周围绘制一个完全不透明的圆圈。

  1. bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f));
  2. bigr.drawImage(image, midX, midY, iw, ih, this);

这两行描绘了图像的其余部分。 AlphaComposite.SrcOver规则用于创建高度透明的图像,并将其与背景混合。

  1. g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);

在最后一步中,缓冲的图像将在面板的整个区域上绘制。

合成 - 图3

图:聚光灯

在 Java 2D 教程的这一部分中,我们讨论了图像合成。