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

在 Java 2D 的这一部分中,我们讨论透明性。 我们提供一些基本定义和一些有趣的透明效果。

透明度说明

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 alpha 合成实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 alpha 通道。 Alpha 通道是图形文件格式的 8 位层,用于表达半透明性(透明度)。 每个像素的额外八位用作掩码,表示 256 级半透明。
(answers.com,wikipedia.org)

AlphaComposite类用于 Java 2D 中的透明性。 它实现了基本的 alpha 合成规则,用于合并源像素和目标像素,以实现图形和图像的融合和透明效果。 要创建AlphaComposite,我们提供两个值:规则指示符和 alpha 值。 该规则指定了我们如何组合源像素和目标像素。 最常见的是AlphaComposite.SRC_OVER。 alpha 值的范围可以从0.0f(完全透明)到1.0f(完全不透明)。

透明矩形

第一个示例绘制了十个具有不同透明度级别的矩形。

TransparentRectanglesEx.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 javax.swing.JFrame;
  8. import javax.swing.JPanel;
  9. class Surface extends JPanel {
  10. private void doDrawing(Graphics g) {
  11. Graphics2D g2d = (Graphics2D) g.create();
  12. g2d.setPaint(Color.blue);
  13. for (int i = 1; i <= 10; i++) {
  14. float alpha = i * 0.1f;
  15. AlphaComposite alcom = AlphaComposite.getInstance(
  16. AlphaComposite.SRC_OVER, alpha);
  17. g2d.setComposite(alcom);
  18. g2d.fillRect(50 * i, 20, 40, 40);
  19. }
  20. g2d.dispose();
  21. }
  22. @Override
  23. public void paintComponent(Graphics g) {
  24. super.paintComponent(g);
  25. doDrawing(g);
  26. }
  27. }
  28. public class TransparentRectanglesEx extends JFrame {
  29. public TransparentRectanglesEx() {
  30. initUI();
  31. }
  32. private void initUI() {
  33. add(new Surface());
  34. setTitle("Transparent rectangles");
  35. setSize(590, 120);
  36. setLocationRelativeTo(null);
  37. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  38. }
  39. public static void main(String[] args) {
  40. EventQueue.invokeLater(new Runnable() {
  41. @Override
  42. public void run() {
  43. TransparentRectanglesEx ex = new TransparentRectanglesEx();
  44. ex.setVisible(true);
  45. }
  46. });
  47. }
  48. }

在我们的示例中,我们绘制了 10 个具有不同透明度级别的蓝色矩形。

  1. float alpha = i * 0.1f;

alpha 值在for循环中动态变化。

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

AlphaComposite.getInstance()方法使用指定的规则和常数 alpha 来创建AlphaComposite对象,以与源的 alpha 相乘。

  1. g2d.setComposite(alcom);

setComposite()方法设置Graphics2D对象的复合属性。

透明度 - 图1

图:透明矩形

淡出演示

在下一个示例中,我们将淡出图像。 图像将逐渐变得更加透明,直到完全不可见为止。

FadeOutEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.Dimension;
  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.ActionEvent;
  9. import java.awt.event.ActionListener;
  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
  15. implements ActionListener {
  16. private Image img;
  17. private Timer timer;
  18. private float alpha = 1f;
  19. private final int DELAY = 40;
  20. private final int INITIAL_DELAY = 500;
  21. public Surface() {
  22. loadImage();
  23. setSurfaceSize();
  24. initTimer();
  25. }
  26. private void loadImage() {
  27. img = new ImageIcon("mushrooms.jpg").getImage();
  28. }
  29. private void setSurfaceSize() {
  30. int h = img.getHeight(this);
  31. int w = img.getWidth(this);
  32. setPreferredSize(new Dimension(w, h));
  33. }
  34. private void initTimer() {
  35. timer = new Timer(DELAY, this);
  36. timer.setInitialDelay(INITIAL_DELAY);
  37. timer.start();
  38. }
  39. private void doDrawing(Graphics g) {
  40. Graphics2D g2d = (Graphics2D) g.create();
  41. AlphaComposite acomp = AlphaComposite.getInstance(
  42. AlphaComposite.SRC_OVER, alpha);
  43. g2d.setComposite(acomp);
  44. g2d.drawImage(img, 0, 0, null);
  45. g2d.dispose();
  46. }
  47. @Override
  48. public void paintComponent(Graphics g) {
  49. super.paintComponent(g);
  50. doDrawing(g);
  51. }
  52. private void step() {
  53. alpha += -0.01f;
  54. if (alpha <= 0) {
  55. alpha = 0;
  56. timer.stop();
  57. }
  58. }
  59. @Override
  60. public void actionPerformed(ActionEvent e) {
  61. step();
  62. repaint();
  63. }
  64. }
  65. public class FadeOutEx extends JFrame {
  66. public FadeOutEx() {
  67. initUI();
  68. }
  69. private void initUI() {
  70. add(new Surface());
  71. pack();
  72. setTitle("Fade out");
  73. setLocationRelativeTo(null);
  74. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  75. }
  76. public static void main(String[] args) {
  77. EventQueue.invokeLater(new Runnable() {
  78. @Override
  79. public void run() {
  80. FadeOutEx ex = new FadeOutEx();
  81. ex.setVisible(true);
  82. }
  83. });
  84. }
  85. }

使用AlphaComposite,我们逐渐淡出面板上的图像。

  1. private void setSurfaceSize() {
  2. int h = img.getHeight(this);
  3. int w = img.getWidth(this);
  4. setPreferredSize(new Dimension(w, h));
  5. }

setSurfaceSize()方法找出图像的大小并为面板设置首选大小。 首选大小与pack()方法的组合将显示恰好足以显示整个图像的窗口。

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

initTimer()方法启动一个计时器。 计时器在指定的初始延迟后触发操作事件。 在事件之间延迟之后会生成连续的动作事件。 为了响应动作事件,我们将更改 Alpha 值并重新绘制面板。

  1. AlphaComposite acomp = AlphaComposite.getInstance(
  2. AlphaComposite.SRC_OVER, alpha);
  3. g2d.setComposite(acomp);
  4. g2d.drawImage(img, 0, 0, null);

此代码在面板上绘制具有增加的透明度的图像。

  1. private void step() {
  2. alpha += -0.01f;
  3. if (alpha <= 0) {
  4. alpha = 0;
  5. timer.stop();
  6. }
  7. }

step()方法代表淡出周期。 alpha逐渐降低。 请注意,alpha 值不得为负。 当达到零时,计时器停止。

  1. repaint();

repaint()方法重新绘制组件。 它调用面板组件的paint()方法,然后又调用paintComponent()方法。

等待演示

在此示例中,我们使用透明效果创建一个等待演示。 我们绘制了 8 条逐渐消失的线,从而产生了一条线在移动的错觉。 此类效果通常用于通知用户幕后正在进行繁重的任务。 例如,通过互联网流式传输视频时。

WaitingEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.BasicStroke;
  4. import java.awt.EventQueue;
  5. import java.awt.Graphics;
  6. import java.awt.Graphics2D;
  7. import java.awt.RenderingHints;
  8. import java.awt.event.ActionEvent;
  9. import java.awt.event.ActionListener;
  10. import javax.swing.JFrame;
  11. import javax.swing.JPanel;
  12. import javax.swing.Timer;
  13. class Surface extends JPanel
  14. implements ActionListener {
  15. private Timer timer;
  16. private int count;
  17. private final int INITIAL_DELAY = 200;
  18. private final int DELAY = 80;
  19. private final int NUMBER_OF_LINES = 8;
  20. private final int STROKE_WIDTH = 3;
  21. private final double[][] trs = {
  22. {0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0},
  23. {1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9},
  24. {0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8},
  25. {0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65},
  26. {0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5},
  27. {0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3},
  28. {0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15},
  29. {0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0}
  30. };
  31. public Surface() {
  32. initTimer();
  33. }
  34. private void initTimer() {
  35. timer = new Timer(DELAY, this);
  36. timer.setInitialDelay(INITIAL_DELAY);
  37. timer.start();
  38. }
  39. private void doDrawing(Graphics g) {
  40. Graphics2D g2d = (Graphics2D) g.create();
  41. g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  42. RenderingHints.VALUE_ANTIALIAS_ON);
  43. g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
  44. RenderingHints.VALUE_RENDER_QUALITY);
  45. int width = getWidth();
  46. int height = getHeight();
  47. g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
  48. BasicStroke.JOIN_ROUND));
  49. g2d.translate(width / 2, height / 2);
  50. for (int i = 0; i < NUMBER_OF_LINES; i++) {
  51. float alpha = (float) trs[count % NUMBER_OF_LINES][i];
  52. AlphaComposite acomp = AlphaComposite.getInstance(
  53. AlphaComposite.SRC_OVER, alpha);
  54. g2d.setComposite(acomp);
  55. g2d.rotate(Math.PI / 4f);
  56. g2d.drawLine(0, -10, 0, -40);
  57. }
  58. g2d.dispose();
  59. }
  60. @Override
  61. public void paintComponent(Graphics g) {
  62. super.paintComponent(g);
  63. doDrawing(g);
  64. }
  65. @Override
  66. public void actionPerformed(ActionEvent e) {
  67. repaint();
  68. count++;
  69. }
  70. }
  71. public class WaitingEx extends JFrame {
  72. public WaitingEx() {
  73. initUI();
  74. }
  75. private void initUI() {
  76. add(new Surface());
  77. setTitle("Waiting");
  78. setSize(300, 200);
  79. setLocationRelativeTo(null);
  80. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  81. }
  82. public static void main(String[] args) {
  83. EventQueue.invokeLater(new Runnable() {
  84. @Override
  85. public void run() {
  86. WaitingEx ex = new WaitingEx();
  87. ex.setVisible(true);
  88. }
  89. });
  90. }
  91. }

我们用八个不同的 alpha 值绘制八条线。

  1. private final double[][] trs = {
  2. ...
  3. };

这是此演示中使用的透明度值的二维数组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。

  1. g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
  2. BasicStroke.JOIN_ROUND));

我们使线条更粗一些,以便更好地显示它们。 我们用圆帽画线。

  1. g2d.rotate(Math.PI/4f);
  2. g2d.drawLine(0, -10, 0, -40);

这段代码绘制了八行。 rotate()方法用于沿直线旋转线。

透明度 - 图2

图:等待

在 Java 2D 教程的这一部分中,我们讨论了透明性。