我正在尝试将 Cairo 与 GTK# 一起使用来创建一个简单的绘画应用程序。给我带来麻烦的功能是在两点之间画一条线。单击绘图区域后,应出现一条线,并跟随光标直到释放鼠标按钮,此时绘图区域更新,可以绘制另一条线。它应该是 MS Paint 等程序中熟悉的功能。
现在,我被告知在开罗执行此类更新的方法是使用开罗上下文.Save();
和.Restore()
方法来处理状态。问题是,除了创建它的事件处理程序之外,我找不到一种方法来继续在事件处理程序中引用相同的上下文,因此我在画线后恢复状态的所有尝试都没有成果。
在下面的最小(但仍然是一种巨大的,抱歉)工作代码示例中,非解决方案是在每个 Draw 上创建一个新的上下文。这给出了如下结果:
笔(左侧)表现良好。这条线显然没有,因为移动鼠标后无法恢复到以前的状态,所以它们都留在屏幕上以形成如图所示的扇形图案。
using Gtk;
using Cairo;
using System;
public class Paint : Gtk.Window
{
bool isDrawing = false;
bool isDrawingLine = false;
bool isDrawingWithPen = false;
double Point_l1x;
double Point_l1y;
double Point_l2x;
double Point_l2y;
double Point_p1x;
double Point_p1y;
Button line;
Button pen;
static DrawingArea area;
Cairo.Context ctx;
void OnDrawingAreaExposed (object source, ExposeEventArgs args)
{
DrawingArea area = (DrawingArea) source;
ctx = Gdk.CairoHelper.Create (area.GdkWindow);
((IDisposable) ctx.Target).Dispose();
((IDisposable) ctx).Dispose ();
}
public void DrawImage ()
{
//Shouldn't this be referencing an external context?
using (Cairo.Context ctx = Gdk.CairoHelper.Create (area.GdkWindow))
{
ctx.Color = new Cairo.Color (0, 0, 0);
if(isDrawingLine)
{
ctx.MoveTo (new PointD (Point_l1x, Point_l1y));
ctx.LineTo (new PointD (Point_l2x, Point_l2y));
ctx.Stroke ();
}
else if(isDrawingWithPen)
{
ctx.Rectangle(Point_p1x, Point_p1y, 1, 1);
ctx.Stroke();
}
}
}
public void LineClicked(object sender, EventArgs args)
{
isDrawingLine = true;
isDrawingWithPen = false;
}
public void PenClicked(object sender, EventArgs args)
{
isDrawingLine = false;
isDrawingWithPen = true;
}
void OnMousePress (object source, ButtonPressEventArgs args)
{
isDrawing = true;
if (isDrawingLine)
{
Point_l1x = args.Event.X;
Point_l1y = args.Event.Y;
}
else if (isDrawingWithPen)
{
Point_p1x = args.Event.X;
Point_p1y = args.Event.Y;
}
}
void OnMouseRelease (object source, ButtonReleaseEventArgs args)
{
isDrawing = false;
DrawImage ();
}
void OnMouseMotion (object source, MotionNotifyEventArgs args)
{
if (!isDrawing)
{
return;
}
if (isDrawingLine)
{
Point_l2x = args.Event.X;
Point_l2y = args.Event.Y;
}
else if (isDrawingWithPen)
{
Point_p1x = args.Event.X;
Point_p1y = args.Event.Y;
}
DrawImage();
}
public Paint () : base("Painting application")
{
area = new DrawingArea ();
area.ExposeEvent += OnDrawingAreaExposed;
area.AddEvents (
(int)Gdk.EventMask.PointerMotionMask
| (int)Gdk.EventMask.ButtonPressMask
| (int)Gdk.EventMask.ButtonReleaseMask);
area.ButtonPressEvent += OnMousePress;
area.ButtonReleaseEvent += OnMouseRelease;
area.MotionNotifyEvent += OnMouseMotion;
DeleteEvent += delegate { Application.Quit(); };
SetDefaultSize(500, 500);
SetPosition(WindowPosition.Center);
VBox vbox = new VBox();
vbox.Add(area);
HBox hbox = new HBox();
line = new Button("Line");
pen = new Button("Pen");
hbox.Add(line);
hbox.Add(pen);
Alignment halign = new Alignment(1, 0, 0, 0);
halign.Add(hbox);
vbox.Add(hbox);
vbox.PackStart(halign, false, false, 3);
line.Clicked += LineClicked;
pen.Clicked += PenClicked;
Add(vbox);
ShowAll();
}
public static void Main()
{
Application.Init();
new Paint();
Application.Run();
}
}
如果我修改 DrawImage 方法以引用 OnDrawingAreaExposed 中定义的 Context,整个事情就会崩溃,并提供我无法真正理解的堆栈跟踪:
Stacktrace:
at (wrapper managed-to-native) Cairo.NativeMethods.cairo_set_source_rgba (intptr,double,double,double,double) <0xffffffff>
at Cairo.Context.set_Color (Cairo.Color) <0x0002b>
at Paint.DrawImage () <0x000a3>
at Paint.OnMouseMotion (object,Gtk.MotionNotifyEventArgs) <0x001af>
at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object_object (object,intptr,intptr,intptr) <0xffffffff>
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <0xffffffff>
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <0x0018b>
at System.Reflection.MethodBase.Invoke (object,object[]) <0x0002a>
at System.Delegate.DynamicInvokeImpl (object[]) <0x001a3>
at System.MulticastDelegate.DynamicInvokeImpl (object[]) <0x0003b>
at System.Delegate.DynamicInvoke (object[]) <0x00018>
at GLib.Signal.ClosureInvokedCB (object,GLib.ClosureInvokedArgs) <0x0014f>
at GLib.SignalClosure.Invoke (GLib.ClosureInvokedArgs) <0x0002f>
at GLib.SignalClosure.MarshalCallback (intptr,intptr,uint,intptr,intptr,intptr) <0x0050b>
at (wrapper native-to-managed) GLib.SignalClosure.MarshalCallback (intptr,intptr,uint,intptr,intptr,intptr) <0xffffffff>
at (wrapper managed-to-native) Gtk.Application.gtk_main () <0xffffffff>
at Gtk.Application.Run () <0x0000b>
at Paint.Main () <0x00027>
at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff>
Native stacktrace:
mono() [0x4961e9]
mono() [0x4e6d1f]
mono() [0x41dcb7]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xfcb0) [0x7f3fd2f07cb0]
/usr/lib/x86_64-linux-gnu/libcairo.so.2(cairo_set_source_rgba+0x1) [0x7f3fcaaccc71]
[0x41a28dbd]
我是否在正确的轨道上,试图引用该上下文?Cairo Contexts 甚至可以这样工作吗?如果没有,我怎样才能让这条线不断地重新渲染?