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

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

甜甜圈

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

donut.cs

  1. using Gtk;
  2. using Cairo;
  3. using System;
  4. class SharpApp : Window {
  5. public SharpApp() : base("Donut")
  6. {
  7. SetDefaultSize(350, 250);
  8. SetPosition(WindowPosition.Center);
  9. DeleteEvent += delegate { Application.Quit(); };
  10. DrawingArea darea = new DrawingArea();
  11. darea.ExposeEvent += OnExpose;
  12. Add(darea);
  13. ShowAll();
  14. }
  15. void OnExpose(object sender, ExposeEventArgs args)
  16. {
  17. DrawingArea area = (DrawingArea) sender;
  18. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  19. cr.LineWidth = 0.5;
  20. int width, height;
  21. width = Allocation.Width;
  22. height = Allocation.Height;
  23. cr.Translate(width/2, height/2);
  24. cr.Arc(0, 0, 120, 0, 2*Math.PI);
  25. cr.Stroke();
  26. cr.Save();
  27. for (int i = 0; i < 36; i++) {
  28. cr.Rotate( i*Math.PI/36);
  29. cr.Scale(0.3, 1);
  30. cr.Arc(0, 0, 120, 0, 2*Math.PI);
  31. cr.Restore();
  32. cr.Stroke();
  33. cr.Save();
  34. }
  35. ((IDisposable) cr.Target).Dispose();
  36. ((IDisposable) cr).Dispose();
  37. }
  38. public static void Main()
  39. {
  40. Application.Init();
  41. new SharpApp();
  42. Application.Run();
  43. }
  44. }

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此称为甜甜圈。

  1. cr.Translate(width/2, height/2);
  2. cr.Arc(0, 0, 120, 0, 2*Math.PI);
  3. cr.Stroke();

刚开始时有一个椭圆。

  1. for (int i = 0; i < 36; i++) {
  2. cr.Rotate( i*Math.PI/36);
  3. cr.Scale(0.3, 1);
  4. cr.Arc(0, 0, 120, 0, 2*Math.PI);
  5. cr.Restore();
  6. cr.Stroke();
  7. cr.Save();
  8. }

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

GTK# 中的 Cario 绘图 II - 图1

图:多纳圈

渐变

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

gradients.cs

  1. using Gtk;
  2. using Cairo;
  3. using System;
  4. class SharpApp : Window {
  5. public SharpApp() : base("Gradients")
  6. {
  7. SetDefaultSize(340, 390);
  8. SetPosition(WindowPosition.Center);
  9. DeleteEvent += delegate { Application.Quit(); };
  10. DrawingArea darea = new DrawingArea();
  11. darea.ExposeEvent += OnExpose;
  12. Add(darea);
  13. ShowAll();
  14. }
  15. void OnExpose(object sender, ExposeEventArgs args)
  16. {
  17. DrawingArea area = (DrawingArea) sender;
  18. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  19. LinearGradient lg1 = new LinearGradient(0.0, 0.0, 350.0, 350.0);
  20. int count = 1;
  21. for (double j=0.1; j<1.0; j+= 0.1) {
  22. if (Convert.ToBoolean(count % 2)) {
  23. lg1.AddColorStop(j, new Color(0, 0, 0, 1));
  24. } else {
  25. lg1.AddColorStop(j, new Color(1, 0, 0, 1));
  26. }
  27. count++;
  28. }
  29. cr.Rectangle(20, 20, 300, 100);
  30. cr.Pattern = lg1;
  31. cr.Fill();
  32. LinearGradient lg2 = new LinearGradient(0.0, 0.0, 350.0, 0);
  33. count = 1;
  34. for (double i=0.05; i<0.95; i+= 0.025) {
  35. if (Convert.ToBoolean(count % 2)) {
  36. lg2.AddColorStop(i, new Color(0, 0, 0, 1));
  37. } else {
  38. lg2.AddColorStop(i, new Color(0, 0, 1, 1));
  39. }
  40. count++;
  41. }
  42. cr.Rectangle(20, 140, 300, 100);
  43. cr.Pattern = lg2;
  44. cr.Fill();
  45. LinearGradient lg3 = new LinearGradient(20.0, 260.0, 20.0, 360.0);
  46. lg3.AddColorStop(0.1, new Color (0, 0, 0, 1) );
  47. lg3.AddColorStop(0.5, new Color (1, 1, 0, 1) );
  48. lg3.AddColorStop(0.9, new Color (0, 0, 0, 1) );
  49. cr.Rectangle(20, 260, 300, 100);
  50. cr.Pattern = lg3;
  51. cr.Fill();
  52. lg1.Destroy();
  53. lg2.Destroy();
  54. lg3.Destroy();
  55. ((IDisposable) cr.Target).Dispose ();
  56. ((IDisposable) cr).Dispose ();
  57. }
  58. public static void Main()
  59. {
  60. Application.Init();
  61. new SharpApp();
  62. Application.Run();
  63. }
  64. }

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

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

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

  1. LinearGradient lg3 = new LinearGradient(20.0, 260.0, 20.0, 360.0);
  2. lg3.AddColorStop(0.1, new Color (0, 0, 0, 1) );
  3. lg3.AddColorStop(0.5, new Color (1, 1, 0, 1) );
  4. lg3.AddColorStop(0.9, new Color (0, 0, 0, 1) );

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

GTK# 中的 Cario 绘图 II - 图2

图:渐变

泡泡

在以下示例中,我们创建一个粉扑效果。 该示例将显示一个不断增长的居中文本,该文本将从某个点逐渐淡出。 这是一个非常常见的效果,您经常可以在 Flash 动画中看到它。

puff.cs

  1. using Gtk;
  2. using Cairo;
  3. using System;
  4. class SharpApp : Window {
  5. private bool timer = true;
  6. private double alpha = 1.0;
  7. private double size = 1.0;
  8. private DrawingArea darea;
  9. public SharpApp() : base("Puff")
  10. {
  11. SetDefaultSize(350, 200);
  12. SetPosition(WindowPosition.Center);
  13. DeleteEvent += delegate { Application.Quit(); };
  14. GLib.Timeout.Add(14, new GLib.TimeoutHandler(OnTimer));
  15. darea = new DrawingArea();
  16. darea.ExposeEvent += OnExpose;
  17. Add(darea);
  18. ShowAll();
  19. }
  20. bool OnTimer()
  21. {
  22. if (!timer) return false;
  23. darea.QueueDraw();
  24. return true;
  25. }
  26. void OnExpose(object sender, ExposeEventArgs args)
  27. {
  28. DrawingArea area = (DrawingArea) sender;
  29. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  30. int x = Allocation.Width / 2;
  31. int y = Allocation.Height / 2;
  32. cr.SetSourceRGB(0.5, 0, 0);
  33. cr.Paint();
  34. cr.SelectFontFace("Courier", FontSlant.Normal, FontWeight.Bold);
  35. size += 0.8;
  36. if (size > 20) {
  37. alpha -= 0.01;
  38. }
  39. cr.SetFontSize(size);
  40. cr.SetSourceRGB(1, 1, 1);
  41. TextExtents extents = cr.TextExtents("ZetCode");
  42. cr.MoveTo(x - extents.Width/2, y);
  43. cr.TextPath("ZetCode");
  44. cr.Clip();
  45. cr.Stroke();
  46. cr.PaintWithAlpha(alpha);
  47. if (alpha <= 0) {
  48. timer = false;
  49. }
  50. ((IDisposable) cr.Target).Dispose();
  51. ((IDisposable) cr).Dispose();
  52. }
  53. public static void Main()
  54. {
  55. Application.Init();
  56. new SharpApp();
  57. Application.Run();
  58. }
  59. }

该示例在窗口上创建一个逐渐增长和褪色的文本。

  1. GLib.Timeout.Add(14, new GLib.TimeoutHandler(OnTimer));

每隔 14 毫秒调用一次OnTimer()方法。

  1. bool OnTimer()
  2. {
  3. if (!timer) return false;
  4. darea.QueueDraw();
  5. return true;
  6. }

OnTimer()方法中,我们在绘图区域上调用QueueDraw()方法,该方法会触发ExposeEvent

  1. int x = Allocation.Width / 2;
  2. int y = Allocation.Height / 2;

中间点的坐标。

  1. cr.SetSourceRGB(0.5, 0, 0);
  2. cr.Paint();

我们将背景色设置为深红色。

  1. size += 0.8;

每个周期,字体大小将增加 0.8 个单位。

  1. if (size > 20) {
  2. alpha -= 0.01;
  3. }

字体大小大于 20 后开始淡出。

  1. TextExtents extents = cr.TextExtents("ZetCode");

我们得到了文本指标。

  1. cr.MoveTo(x - extents.Width/2, y);

我们使用文本指标将文本放在窗口的中心。

  1. cr.TextPath("ZetCode");
  2. cr.Clip();

我们获取文本的路径,并为其设置当前的片段区域。

  1. cr.Stroke();
  2. cr.PaintWithAlpha(alpha);

我们绘制当前路径并考虑 alpha 值。

GTK# 中的 Cario 绘图 II - 图3

图:粉扑

反射

在下一个示例中,我们显示反射图像。 这种美丽的效果使人产生幻觉,好像图像在水中被反射一样。

reflection.cs

  1. using Gtk;
  2. using Cairo;
  3. using System;
  4. class SharpApp : Window {
  5. private ImageSurface surface;
  6. private int imageWidth;
  7. private int imageHeight;
  8. private int gap;
  9. private int border;
  10. public SharpApp() : base("Reflection")
  11. {
  12. try {
  13. surface = new ImageSurface("slanec.png");
  14. } catch {
  15. Console.WriteLine("File not found");
  16. Environment.Exit(1);
  17. }
  18. imageWidth = surface.Width;
  19. imageHeight = surface.Height;
  20. gap = 40;
  21. border = 20;
  22. SetDefaultSize(300, 350);
  23. SetPosition(WindowPosition.Center);
  24. DeleteEvent += delegate { Application.Quit(); };
  25. DrawingArea darea = new DrawingArea();
  26. darea.ExposeEvent += OnExpose;
  27. Add(darea);
  28. ShowAll();
  29. }
  30. void OnExpose(object sender, ExposeEventArgs args)
  31. {
  32. DrawingArea area = (DrawingArea) sender;
  33. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  34. int width = Allocation.Width;
  35. int height = Allocation.Height;
  36. LinearGradient lg = new LinearGradient(width/2, 0, width/2, height*3);
  37. lg.AddColorStop(0, new Color(0, 0, 0, 1));
  38. lg.AddColorStop(height, new Color(0.2, 0.2, 0.2, 1));
  39. cr.Pattern = lg;
  40. cr.Paint();
  41. cr.SetSourceSurface(surface, border, border);
  42. cr.Paint();
  43. double alpha = 0.7;
  44. double step = 1.0 / imageHeight;
  45. cr.Translate(0, 2 * imageHeight + gap);
  46. cr.Scale(1, -1);
  47. int i = 0;
  48. while(i < imageHeight) {
  49. cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));
  50. i++;
  51. cr.Clip();
  52. cr.SetSource(surface, border, border);
  53. cr.PaintWithAlpha(alpha-=step);
  54. cr.ResetClip();
  55. }
  56. ((IDisposable) cr.Target).Dispose();
  57. ((IDisposable) cr).Dispose();
  58. }
  59. public static void Main()
  60. {
  61. Application.Init();
  62. new SharpApp();
  63. Application.Run();
  64. }
  65. }

该示例显示了一个反射的城堡。

  1. LinearGradient lg = new LinearGradient(width/2, 0, width/2, height*3);
  2. lg.AddColorStop(0, new Color(0, 0, 0, 1));
  3. lg.AddColorStop(height, new Color(0.2, 0.2, 0.2, 1));
  4. cr.Pattern = lg;
  5. cr.Paint();

背景充满了渐变的油漆。 涂料是从黑色到深灰色的平滑混合。

  1. cr.Translate(0, 2 * imageHeight + gap);
  2. cr.Scale(1, -1);

此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄一张照片并将其放在桌子上即可。 现在翻转它。

  1. cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));
  2. i++;
  3. cr.Clip();
  4. cr.SetSource(surface, border, border);
  5. cr.PaintWithAlpha(alpha-=step);
  6. cr.ResetClip();

代码的关键部分。 我们使第二个图像透明。 但是透明度不是恒定的。 图像逐渐淡出。 这是通过GradientPaint实现的。

GTK# 中的 Cario 绘图 II - 图4

图:反射

等待

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

waiting.cs

  1. using Gtk;
  2. using Cairo;
  3. using System;
  4. class SharpApp : Window {
  5. private double [,] trs = new double[,] {
  6. { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
  7. { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
  8. { 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 },
  9. { 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65},
  10. { 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 },
  11. { 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 },
  12. { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 },
  13. { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, }
  14. };
  15. private short count = 0;
  16. private DrawingArea darea;
  17. public SharpApp() : base("Waiting")
  18. {
  19. SetDefaultSize(250, 150);
  20. SetPosition(WindowPosition.Center);
  21. DeleteEvent += delegate { Application.Quit(); };
  22. GLib.Timeout.Add(100, new GLib.TimeoutHandler(OnTimer));
  23. darea = new DrawingArea();
  24. darea.ExposeEvent += OnExpose;
  25. Add(darea);
  26. ShowAll();
  27. }
  28. bool OnTimer()
  29. {
  30. count += 1;
  31. darea.QueueDraw();
  32. return true;
  33. }
  34. void OnExpose(object sender, ExposeEventArgs args)
  35. {
  36. DrawingArea area = (DrawingArea) sender;
  37. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  38. cr.LineWidth = 3;
  39. cr.LineCap = LineCap.Round;
  40. int width, height;
  41. width = Allocation.Width;
  42. height = Allocation.Height;
  43. cr.Translate(width/2, height/2);
  44. for (int i = 0; i < 8; i++) {
  45. cr.SetSourceRGBA(0, 0, 0, trs[count%8, i]);
  46. cr.MoveTo(0.0, -10.0);
  47. cr.LineTo(0.0, -40.0);
  48. cr.Rotate(Math.PI/4);
  49. cr.Stroke();
  50. }
  51. ((IDisposable) cr.Target).Dispose();
  52. ((IDisposable) cr).Dispose();
  53. }
  54. public static void Main()
  55. {
  56. Application.Init();
  57. new SharpApp();
  58. Application.Run();
  59. }
  60. }

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

  1. GLib.Timeout.Add(100, new GLib.TimeoutHandler(OnTimer));

我们使用计时器函数来创建动画。

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

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

  1. cr.LineWidth = 3;
  2. cr.LineCap = LineCap.Round;

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

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

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

  1. cr.MoveTo(0.0, -10.0);
  2. cr.LineTo(0.0, -40.0);
  3. cr.Rotate(Math.PI/4);
  4. cr.Stroke();

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

GTK# 中的 Cario 绘图 II - 图5

图:等待

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