原文: http://zetcode.com/gui/javagnome/drawingII/

在 Java Gnome 编程教程的这一部分中,我们将继续使用 Cairo 库进行绘制。

甜甜圈

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。

donut.java

  1. package com.zetcode;
  2. import org.freedesktop.cairo.Context;
  3. import org.freedesktop.cairo.Matrix;
  4. import org.gnome.gdk.Event;
  5. import org.gnome.gdk.EventExpose;
  6. import org.gnome.gtk.DrawingArea;
  7. import org.gnome.gtk.Gtk;
  8. import org.gnome.gtk.Widget;
  9. import org.gnome.gtk.Window;
  10. import org.gnome.gtk.WindowPosition;
  11. /**
  12. * ZetCode Java Gnome tutorial
  13. *
  14. * This program draws a Donut
  15. * shape on the drawing area.
  16. *
  17. * @author jan bodnar
  18. * website zetcode.com
  19. * last modified March 2009
  20. */
  21. public class GDonut extends Window
  22. implements Widget.ExposeEvent {
  23. public GDonut() {
  24. setTitle("Donut");
  25. initUI();
  26. connect(new Window.DeleteEvent() {
  27. public boolean onDeleteEvent(Widget source, Event event) {
  28. Gtk.mainQuit();
  29. return false;
  30. }
  31. });
  32. setDefaultSize(300, 260);
  33. setPosition(WindowPosition.CENTER);
  34. showAll();
  35. }
  36. public void initUI() {
  37. DrawingArea darea = new DrawingArea();
  38. darea.connect(this);
  39. add(darea);
  40. }
  41. public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
  42. final Context cr;
  43. cr = new Context(widget.getWindow());
  44. drawDonut(cr);
  45. return false;
  46. }
  47. public void drawDonut(Context cr) {
  48. int width = this.getWindow().getWidth();
  49. int height = this.getWindow().getHeight();
  50. cr.setLineWidth(0.5);
  51. cr.translate(width/2, height/2);
  52. cr.arc( 0, 0, 120, 0, 2 * Math.PI);
  53. cr.stroke();
  54. cr.save();
  55. for ( int i = 0; i < 36; i++) {
  56. Matrix mat = new Matrix();
  57. mat.rotate(i*Math.PI/36);
  58. mat.scale(0.3, 1);
  59. cr.transform(mat);
  60. cr.arc(0, 0, 120, 0, 2 * Math.PI);
  61. cr.restore();
  62. cr.stroke();
  63. cr.save();
  64. }
  65. }
  66. public static void main(String[] args) {
  67. Gtk.init(args);
  68. new GDonut();
  69. Gtk.main();
  70. }
  71. }

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

  1. cr.translate(width/2, height/2);
  2. cr.arc( 0, 0, 120, 0, 2 * Math.PI);
  3. cr.stroke();

刚开始时有一个椭圆。

  1. Matrix mat = new Matrix();
  2. mat.rotate(i*Math.PI/36);
  3. mat.scale(0.3, 1);
  4. cr.transform(mat);
  5. cr.arc(0, 0, 120, 0, 2 * Math.PI);
  6. cr.restore();
  7. cr.stroke();
  8. cr.save();

旋转几圈后,有一个甜甜圈。

Cario 绘图 II - 图1

图:多纳圈

渐变

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)

gradients.java

  1. package com.zetcode;
  2. import org.freedesktop.cairo.Context;
  3. import org.freedesktop.cairo.LinearPattern;
  4. import org.gnome.gdk.Color;
  5. import org.gnome.gdk.EventExpose;
  6. import org.gnome.gtk.DrawingArea;
  7. import org.gnome.gtk.Gtk;
  8. import org.gnome.gtk.Widget;
  9. import org.gnome.gtk.Window;
  10. import org.gnome.gtk.WindowPosition;
  11. /**
  12. * ZetCode Java Gnome tutorial
  13. *
  14. * This program draws gradients.
  15. *
  16. * @author jan bodnar
  17. * website zetcode.com
  18. * last modified March 2009
  19. */
  20. public class GGradients extends Window implements Widget.ExposeEvent {
  21. public GGradients() {
  22. setTitle("Gradients");
  23. initUI();
  24. connect(new Window.DeleteEvent() {
  25. public boolean onDeleteEvent(Widget source, Event event) {
  26. Gtk.mainQuit();
  27. return false;
  28. }
  29. });
  30. setDefaultSize(340, 390);
  31. setPosition(WindowPosition.CENTER);
  32. showAll();
  33. }
  34. public void initUI() {
  35. DrawingArea darea = new DrawingArea();
  36. darea.connect(this);
  37. add(darea);
  38. }
  39. public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
  40. final Context cr;
  41. cr = new Context(widget.getWindow());
  42. drawGradients(cr);
  43. return false;
  44. }
  45. public void drawGradients(Context cr) {
  46. LinearPattern lg1 = new LinearPattern(0.0, 0.0, 350.0, 350.0);
  47. int count = 1;
  48. for (double j=0.1; j<1.0; j+= 0.1) {
  49. if (count % 2 != 0) {
  50. lg1.addColorStopRGB(j, 0, 0, 0);
  51. } else {
  52. lg1.addColorStopRGB(j, 1, 0, 0);
  53. }
  54. count++;
  55. }
  56. cr.rectangle(20, 20, 300, 100);
  57. cr.setSource(lg1);
  58. cr.fill();
  59. LinearPattern lg2 = new LinearPattern(0.0, 0.0, 350.0, 0);
  60. count = 1;
  61. for (double i=0.05; i<0.95; i+= 0.025) {
  62. if (count % 2 != 0) {
  63. lg2.addColorStopRGB(i, 0, 0, 0);
  64. } else {
  65. lg2.addColorStopRGB(i, 0, 0, 1);
  66. }
  67. count++;
  68. }
  69. cr.rectangle(20, 140, 300, 100);
  70. cr.setSource(lg2);
  71. cr.fill();
  72. LinearPattern lg3 = new LinearPattern(20.0, 260.0, 20.0, 360.0);
  73. lg3.addColorStopRGB(0.1, 0, 0, 0 );
  74. lg3.addColorStopRGB(0.5, 1, 1, 0);
  75. lg3.addColorStopRGB(0.9, 0, 0, 0 );
  76. cr.rectangle(20, 260, 300, 100);
  77. cr.setSource(lg3);
  78. cr.fill();
  79. }
  80. public static void main(String[] args) {
  81. Gtk.init(args);
  82. new GGradients();
  83. Gtk.main();
  84. }
  85. }

在我们的示例中,我们绘制了三个具有三个不同渐变的矩形。

  1. LinearPattern lg1 = new LinearPattern(0.0, 0.0, 350.0, 350.0);

在这里,我们创建一个线性渐变图案。 参数指定直线,沿着该直线绘制渐变。 在我们的情况下,它是一条对角线。

  1. LinearPattern lg3 = new LinearPattern(20.0, 260.0, 20.0, 360.0);
  2. lg3.addColorStopRGB(0.1, 0, 0, 0 );
  3. lg3.addColorStopRGB(0.5, 1, 1, 0);
  4. lg3.addColorStopRGB(0.9, 0, 0, 0 );

我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该颜色将以大小的 1/10 停止。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘图,直到结束。

Cario 绘图 II - 图2

图:渐变

星形

在下面的示例中,我们创建了一颗移动的星星。星星移动,旋转并成长/收缩。

star.java

  1. package com.zetcode;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import org.freedesktop.cairo.Context;
  5. import org.freedesktop.cairo.Matrix;
  6. import org.gnome.gdk.Event;
  7. import org.gnome.gdk.EventExpose;
  8. import org.gnome.gtk.DrawingArea;
  9. import org.gnome.gtk.Gtk;
  10. import org.gnome.gtk.Widget;
  11. import org.gnome.gtk.Window;
  12. import org.gnome.gtk.WindowPosition;
  13. /**
  14. * ZetCode Java Gnome tutorial
  15. *
  16. * This program shows an animated star. Rotate,
  17. * translate and scale operations are
  18. * applied on the star.
  19. *
  20. * @author jan bodnar
  21. * website zetcode.com
  22. * last modified March 2009
  23. */
  24. public class GStar extends Window
  25. implements Widget.ExposeEvent {
  26. private static Timer timer;
  27. private int count;
  28. private double angle = 0;
  29. private double scale = 1;
  30. private double delta = 0.01;
  31. double points[][] = {
  32. { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 },
  33. { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 },
  34. { 40, 190 }, { 50, 125 }, { 0, 85 }
  35. };
  36. public GStar() {
  37. setTitle("Star");
  38. timer = new Timer();
  39. timer.scheduleAtFixedRate(new ScheduleTask(), 100, 20);
  40. count = 0;
  41. initUI();
  42. connect(new Window.DeleteEvent() {
  43. public boolean onDeleteEvent(Widget source, Event event) {
  44. timer.cancel();
  45. Gtk.mainQuit();
  46. return false;
  47. }
  48. });
  49. setSizeRequest(350, 250);
  50. setPosition(WindowPosition.CENTER);
  51. showAll();
  52. }
  53. public void initUI() {
  54. DrawingArea darea = new DrawingArea();
  55. darea.connect(this);
  56. add(darea);
  57. }
  58. public void drawStar(Context cr) {
  59. int width = this.getWindow().getWidth();
  60. int height = this.getWindow().getHeight();
  61. cr.setSource(0, 0.44, 0.7);
  62. cr.setLineWidth(1);
  63. Matrix mat = new Matrix();
  64. mat.translate(width/2, height/2);
  65. mat.rotate(angle);
  66. mat.scale(scale, scale);
  67. cr.transform(mat);
  68. for ( int i = 0; i < 10; i++ ) {
  69. cr.lineTo(points[i][0], points[i][1]);
  70. }
  71. cr.fill();
  72. cr.stroke();
  73. if ( scale < 0.01 ) {
  74. delta = -delta;
  75. } else if (scale > 0.99) {
  76. delta = -delta;
  77. }
  78. scale += delta;
  79. angle += 0.01;
  80. }
  81. public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
  82. Context cr = new Context(widget.getWindow());
  83. drawStar(cr);
  84. return false;
  85. }
  86. class ScheduleTask extends TimerTask {
  87. public void run() {
  88. count++;
  89. queueDraw();
  90. }
  91. }
  92. public static void main(String[] args) {
  93. Gtk.init(args);
  94. new GStar();
  95. Gtk.main();
  96. }
  97. }

我们对星形应用平移,缩放和旋转操作。

  1. public boolean onDeleteEvent(Widget source, Event event) {
  2. timer.cancel();
  3. Gtk.mainQuit();
  4. return false;
  5. }

为了干净出口,我们一定不要忘记停止计时器。 Timer是 Java util库的对象,并且不被Gtk.mainQuit()方法停止。

  1. double points[][] = {
  2. { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 },
  3. { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 },
  4. { 40, 190 }, { 50, 125 }, { 0, 85 }
  5. };

这些点用于构建星形。

  1. Matrix mat = new Matrix();
  2. mat.translate(width/2, height/2);
  3. mat.rotate(angle);
  4. mat.scale(scale, scale);
  5. cr.transform(mat);

在这里,我们对星形应用平移,旋转和缩放操作。

  1. for ( int i = 0; i < 10; i++ ) {
  2. cr.lineTo(points[i][0], points[i][1]);
  3. }

在这里,我们画星星。

等待

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

waiting.java

  1. package com.zetcode;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import org.freedesktop.cairo.Context;
  5. import org.gnome.gdk.Event;
  6. import org.gnome.gdk.EventExpose;
  7. import org.gnome.gtk.DrawingArea;
  8. import org.gnome.gtk.Gtk;
  9. import org.gnome.gtk.Widget;
  10. import org.gnome.gtk.Window;
  11. import org.gnome.gtk.WindowPosition;
  12. /**
  13. * ZetCode Java Gnome tutorial
  14. *
  15. * This program creates a waiting
  16. * effect.
  17. *
  18. * @author jan bodnar
  19. * website zetcode.com
  20. * last modified March 2009
  21. */
  22. public class GWaiting extends Window
  23. implements Widget.ExposeEvent {
  24. private static Timer timer;
  25. private int count;
  26. private final double[][] trs = {
  27. { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
  28. { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
  29. { 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 },
  30. { 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 },
  31. { 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 },
  32. { 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 },
  33. { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 },
  34. { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, }
  35. };
  36. public GWaiting() {
  37. setPosition(WindowPosition.CENTER);
  38. timer = new Timer();
  39. timer.scheduleAtFixedRate(new ScheduleTask(), 100, 80);
  40. count = 0;
  41. initUI();
  42. connect(new Window.DeleteEvent() {
  43. public boolean onDeleteEvent(Widget source, Event event) {
  44. timer.cancel();
  45. Gtk.mainQuit();
  46. return false;
  47. }
  48. });
  49. resize(250, 150);
  50. setTitle("Waiting");
  51. showAll();
  52. }
  53. public void initUI() {
  54. DrawingArea darea = new DrawingArea();
  55. add(darea);
  56. darea.connect(this);
  57. }
  58. public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
  59. Context cr = new Context(widget.getWindow());
  60. drawWaiting(cr);
  61. return false;
  62. }
  63. private void drawWaiting(Context cr) {
  64. int w = this.getWidth();
  65. int h = this.getHeight();
  66. cr.translate(w/2, h/2);
  67. for (int i = 0; i < 8; i++) {
  68. cr.setLineWidth(3);
  69. cr.setSource(0, 0, 0, trs[count%8][i]);
  70. cr.moveTo(0, -10);
  71. cr.lineTo(0, -40);
  72. cr.rotate(Math.PI/4f);
  73. cr.stroke();
  74. }
  75. }
  76. class ScheduleTask extends TimerTask {
  77. public void run() {
  78. count++;
  79. queueDraw();
  80. }
  81. }
  82. public static void main(String[] args) {
  83. Gtk.init(args);
  84. new GWaiting();
  85. Gtk.main();
  86. }
  87. }

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

  1. private final double[][] trs = {
  2. { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
  3. ....
  4. };

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

  1. cr.setLineWidth(3);

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

  1. cr.setSource(0, 0, 0, trs[count%8][i]);

在这里,我们定义了一条线的透明度值。

  1. cr.moveTo(0, -10);
  2. cr.lineTo(0, -40);
  3. cr.rotate(Math.PI/4f);
  4. cr.stroke();

这些代码行将绘制八行中的每行。

Cario 绘图 II - 图3

图:等待

在 Java Gnome 编程库的这一章中,我们使用 Cairo 库进行了一些更高级的绘制。