组合
最后修改于 2023 年 7 月 17 日
在 Java 2D 编程教程的这一部分,我们定义了组合操作。
组合是将来自不同源的视觉元素合并成单个图像。它们用于创造一种错觉,即所有这些元素都属于同一个场景。组合技术广泛应用于电影行业,用于创建人群、全新的世界,这些世界如果用其他方式创建将会非常昂贵或不可能。(wikipedia.org)
操作
有几种组合操作。我们在下一个代码示例中展示了其中的一些。AlphaComposite
类实现了基本的 alpha 组合规则,用于组合源颜色和目标颜色,以实现图形和图像的混合和透明效果。
假设我们要在一个面板上绘制两个对象。第一个绘制的对象称为目标,第二个称为源。AlphaComposite
类决定了这两个对象将如何混合在一起。如果我们有一个 AlphaComposite.SRC_OVER
规则,那么在两个对象重叠的地方,将绘制源对象的像素。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private final int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT }; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); for (int x = 20, y = 20, i = 0; i < rules.length; x += 60, i++) { AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f); BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); gbi.setPaint(Color.blue); gbi.fillRect(0, 0, 40, 40); gbi.setComposite(ac); gbi.setPaint(Color.green); gbi.fillRect(5, 5, 40, 40); g2d.drawImage(buffImg, x, y, null); gbi.dispose(); } g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class CompositionEx extends JFrame { public CompositionEx() { add(new Surface()); setTitle("Composition"); setSize(400, 120); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { CompositionEx ex = new CompositionEx(); ex.setVisible(true); } }); } }
我们绘制两个矩形,并用六种不同的组合操作将它们组合起来。
private final int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT };
这里我们有六种不同的组合规则。
AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);
这里我们获取 AlphaComposite
类。
BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB);
我们使用一个缓冲图像来执行组合操作。
Graphics2D gbi = buffImg.createGraphics();
使用 createGraphics
方法从缓冲图像创建 Graphics2D
对象。
gbi.setComposite(ac);
setComposite
方法设置 Graphics2D
上下文的组合器。
g2d.drawImage(buffImg, x, y, null);
使用 drawImage
方法将缓冲图像绘制到面板上。
gbi.dispose();
创建的图形对象必须被释放。

太阳和云
在下一个示例中,我们将展示太阳从云后面出来。我们在动画中使用组合技术。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Image sun; private Image cloud; private Timer timer; private float alpha = 1f; private final int DELAY = 600; public Surface() { loadImages(); initTimer(); } private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage(); } private void initTimer() { timer = new Timer(DELAY, this); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); BufferedImage buffImg = new BufferedImage(220, 140, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); AlphaComposite ac = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha); gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null); gbi.dispose(); g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void step() { alpha -= 0.1; if (alpha <= 0) { alpha = 0; timer.stop(); } } @Override public void actionPerformed(ActionEvent e) { step(); repaint(); } } public class SunAndCloudEx extends JFrame { public SunAndCloudEx() { initUI(); } private void initUI() { add(new Surface()); setTitle("Sun and cloud"); setSize(300, 210); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { SunAndCloudEx ex = new SunAndCloudEx(); ex.setVisible(true); } }); } }
太阳从云后面出来。云最终消失。
private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage(); }
我们从磁盘加载两张图像。
private void initTimer() { timer = new Timer(DELAY, this); timer.start(); }
在 initTimer
方法中,计时器被激活。
AlphaComposite ac = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha);
我们使用 AlphaComposite.SRC_OVER
规则——源与目标混合并覆盖空白像素。
gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null);
这些图像被渲染到一个 BufferedImage
中,然后被复制到屏幕上。setComposite
指定了在渲染过程中,新的像素如何与图形设备上现有的像素相结合。

聚光灯
聚光灯是一种强光束,只照亮一小块区域,尤其用于将注意力集中在舞台表演者身上。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private final int RADIUS = 50; private Image image; private int iw; private int ih; private int x; private int y; private boolean mouseIn; public Surface() { initUI(); } private void initUI() { loadImage(); iw = image.getWidth(null); ih = image.getHeight(null); addMouseMotionListener(new MyMouseAdapter()); addMouseListener(new MyMouseAdapter()); } private void loadImage() { image = new ImageIcon("penguin.png").getImage(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); int midX = (getWidth() - iw) / 2; int midY = (getHeight() - ih) / 2; BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D bigr = bi.createGraphics(); if (mouseIn) { bigr.setPaint(Color.white); bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2); bigr.setComposite(AlphaComposite.SrcAtop); bigr.drawImage(image, midX, midY, iw, ih, this); } bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f)); bigr.drawImage(image, midX, midY, iw, ih, this); bigr.dispose(); g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this); g2d.dispose(); } private class MyMouseAdapter extends MouseAdapter { @Override public void mouseExited(MouseEvent e) { mouseIn = false; repaint(); } @Override public void mouseEntered(MouseEvent e) { mouseIn = true; } @Override public void mouseMoved(MouseEvent e) { x = e.getX(); y = e.getY(); repaint(); } } } public class SpotlightEx extends JFrame { public SpotlightEx() { initUI(); } private void initUI() { add(new Surface()); setSize(350, 300); setTitle("Spotlight"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { SpotlightEx ex = new SpotlightEx(); ex.setVisible(true); } }); } }
聚光灯效果是通过组合规则和 alpha 透明度值创建的。同样重要的是要注意,我们的图像具有透明背景。
BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
创建一个 BufferedImage
。其尺寸等于面板的尺寸。我们的 PNG 文件具有透明背景;因此,我们使用 BufferedImage.TYPE_INT_ARGB
图像类型。
if (mouseIn) { bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2); bigr.setComposite(AlphaComposite.SrcAtop); bigr.drawImage(image, midX, midY, iw, ih, this); }
如果鼠标在面板区域内,将使用 AlphaComposite.SrcAtop
规则在鼠标指针周围绘制一个完全不透明的圆。
bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f)); bigr.drawImage(image, midX, midY, iw, ih, this);
这两行绘制了图像的其余部分。AlphaComposite.SrcOver
规则用于创建高度透明的图像,该图像与其背景混合。
g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);
最后一步,将缓冲图像绘制在整个面板区域之上。

在本篇 Java 2D 教程中,我们讨论了图像组合。