Home  Contents

Drawing with Cairo II

نستكمل الرسم مع مكتبة كايرو

Donut

فى هذا المثال سننشئ شكل معقد بإستخدام مجموعة من القطع الناقصة

donut.cs
 
using Gtk;
using Cairo;
using System;
 
class SharpApp : Window {
 

    public SharpApp() : base("Donut")
    {
        SetDefaultSize(350, 250);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };
        
        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);
                
        cr.LineWidth = 0.5;

        int width, height;
        width = Allocation.Width;
        height = Allocation.Height;
       
        cr.Translate(width/2, height/2);
        cr.Arc(0, 0, 120, 0, 2*Math.PI);
        cr.Stroke();
         
        cr.Save();

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

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }


    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

فى هذا المثال ننشئ donut “دونة"



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

فى البداية هناك قطع

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

بعد عدة rotation نجد قطعة الحلوى :D


Donut

Figure: Donut



Gradients

In computer graphics, gradient is a smooth blending of shades from light to dark or from one color to another. In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. (answers.com)

gradients.cs
 
using Gtk;
using Cairo;
using System;
 
class SharpApp : Window {
 

    public SharpApp() : base("Gradients")
    {
        SetDefaultSize(340, 390);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };
        
        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);
                
               
        LinearGradient lg1 = new LinearGradient(0.0, 0.0, 350.0, 350.0);
       
        int count = 1;

        for (double j=0.1; j<1.0; j+= 0.1) {
            if (Convert.ToBoolean(count % 2)) {
                lg1.AddColorStop(j, new Color(0, 0, 0, 1));
            } else {
                lg1.AddColorStop(j, new Color(1, 0, 0, 1));
            }
        count++;
        }

        cr.Rectangle(20, 20, 300, 100);
        cr.Pattern = lg1;
        cr.Fill();

        LinearGradient lg2 = new LinearGradient(0.0, 0.0, 350.0, 0);
       
        count = 1;

        for (double i=0.05; i<0.95; i+= 0.025) {
            if (Convert.ToBoolean(count % 2)) {
                lg2.AddColorStop(i, new Color(0, 0, 0, 1));
            } else {
                lg2.AddColorStop(i, new Color(0, 0, 1, 1));
            }
        count++;
        }

        cr.Rectangle(20, 140, 300, 100);
        cr.Pattern = lg2;
        cr.Fill();

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

        cr.Rectangle(20, 260, 300, 100);
        cr.Pattern = lg3;
        cr.Fill();
        

        lg1.Destroy();
        lg2.Destroy();
        lg3.Destroy();        

        ((IDisposable) cr.Target).Dispose ();                                      
        ((IDisposable) cr).Dispose ();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

فى مثالنا لدينا 3 مستطيلات بجرادينتات مختلفة

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

هنا ننشئ نمط لجرادينت خطى.. والمعاملات هى السطر اللذى عليه نرسم الجرادينت.. فى حالتنا هنا هو خط رأسى

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

نحدد موقفات للون لإنشاء نمط الجرادينت وهنا يجمع بين الأسود والأصفر.. وبإضافة لونين اسود وتوقف اصفر ننشئ نمط جرادينت افقى.. ماذا تعنى تلك التوقفات حقيقة ؟فى حالتنا هذه نبدأ باللون الأسود اللذى يتوقف عند عشر مساحته ثم يبدأ برسم الأصفر تصاعديا التى ستتوج مركز الشكل.. ويتوقف الأصفر عند 9 من 10 المساحة حيث نعود نرسم الأسود مجددا وهكذا




Gradients

Figure: Gradients



Puff

فى المثال التالى سننشئ تأثير puff.. فى المثال نعرض نص فى المنتصف يكبر حتى حد معين ثم يتلاشى



puff.cs
 
using Gtk;
using Cairo;
using System;
 
class SharpApp : Window {
 

    private bool timer = true;
    private double alpha = 1.0;
    private double size = 1.0;
    private DrawingArea darea;
   
    
    public SharpApp() : base("Puff")
    {
        SetDefaultSize(350, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };
        
        GLib.Timeout.Add(14, new GLib.TimeoutHandler(OnTimer));
        
        darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }
    
    bool OnTimer() 
    { 
        if (!timer) return false;

        darea.QueueDraw();
        return true;
    }      

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);
              
        int x = Allocation.Width / 2;
        int y = Allocation.Height / 2;

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

        cr.SelectFontFace("Courier", FontSlant.Normal, FontWeight.Bold);

        size += 0.8;

        if (size > 20) {
            alpha -= 0.01;
        }

        cr.SetFontSize(size);
        cr.SetSourceRGB(1, 1, 1); 
        
        TextExtents extents = cr.TextExtents("ZetCode");

        cr.MoveTo(x - extents.Width/2, y);
        cr.TextPath("ZetCode");
        cr.Clip();
        cr.Stroke();
        cr.PaintWithAlpha(alpha);

        if (alpha <= 0) {
            timer = false;
        }
        
        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }


    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

فى هذا المثال سننشئ نص يكبر ويتلاشى على النافذة.

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

يتم استدعاء التايمر -المؤقت- كل 14 ميلى ثانية

 bool OnTimer() 
 { 
     if (!timer) return false;
     
     darea.QueueDraw();
     return true;
 }      

فى الطريقة OnTimer نستدعى الطريقة QueueDraw خلال الرسم فى منطقة الرسم مما يطلق الحدث ExposeEvent

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

احداثيات المركز

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

نجعل الخلفية احمر غامق

 size += 0.8;

مع كل دورة يزيد حجم الخط بمقدار 0.8

 if (size > 20) {
     alpha -= 0.01;
 }

الشحوب يبدأ عندما يزيد حجم الخط عن 20

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

نحصل على مقاييس النص

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

نستخدم مقاييس النص لسنترته -وضعه بالمنتصف- على النافذة

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

نحصل على مسار النص ونحدد منطقة القصاصة الحالية



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

نرسم المسار مع استخدام قيمة الفا


Puff

Figure: Puff



Reflection

فى المثال التالى سنعرض صورة منعكسة.. هذا التأثير الجميل يخلق وهم ان الصورة منعكسة فى الماء



reflection.cs

using Gtk;
using Cairo;
using System;
 
class SharpApp : Window {
 
    private ImageSurface surface;
    private int imageWidth;
    private int imageHeight;
    private int gap;
    private int border;

    public SharpApp() : base("Reflection")
    {
        
        try {
            surface = new ImageSurface("slanec.png");
        } catch {
            Console.WriteLine("File not found");
            Environment.Exit(1);
        } 
        
        imageWidth = surface.Width;
        imageHeight = surface.Height;
        gap = 40;
        border = 20;

        SetDefaultSize(300, 350);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };
        
        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);
                   
        int width = Allocation.Width;
        int height = Allocation.Height;
          
        LinearGradient lg = new LinearGradient(width/2, 0, width/2, height*3);
        lg.AddColorStop(0, new Color(0, 0, 0, 1));
        lg.AddColorStop(height, new Color(0.2, 0.2, 0.2, 1));

        cr.Pattern = lg;
        cr.Paint();
        
        cr.SetSourceSurface(surface, border, border);
        cr.Paint();

        double alpha = 0.7;
        double step = 1.0 / imageHeight;
      
        cr.Translate(0, 2 * imageHeight + gap);
        cr.Scale(1, -1);
        
        int i = 0;
        
       
        while(i < imageHeight) {
            cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));

            i++;
            
            cr.Clip();
            cr.SetSource(surface, border, border);

            cr.PaintWithAlpha(alpha-=step);
            cr.ResetClip();
        }
        
         
        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

هذا المثال يظهر قلعة منعكسة

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

 cr.Pattern = lg;
 cr.Paint();

الخلفية ممتلئة بجرادينت يمزج بين الأسود والرمادى الغامق

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

هذا الكود يعكس الصورة ويترجمها اسفل الصورة الأصلية.. عملية الترجمة مهمة لأن عملية الscaling تعكس الصورة اسفل لأعلى



 cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));

 i++;
            
 cr.Clip();
 cr.SetSource(surface, border, border);

 cr.PaintWithAlpha(alpha-=step);
 cr.ResetClip();

الجزء الحاسم من الكود نجعل فيه الصورة الثانية شفافة -الشفافية ليست ثابته- حيث تتلاشى الصورة تصاعديا ويتم ذلك من خلال GradientPaint




Reflection

Figure: Reflection



Waiting

فى هذا المثال سنستخدم الشفافية لإنشاء "انتظار" فيه سنرسم 8 اسطر التى تشحب لخلق وهم بأن الخط يتحرك.. مثل هذه المؤثرات تستخدم لإعلام المستخدمين بأن مهمة طويلة تحدث فى الكواليس :)

waiting.cs

using Gtk;
using Cairo;
using System;
 
class SharpApp : Window {
 

    private double [,] trs = new double[,] {
        { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
        { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
        { 0.9, 1.0,  0.0,  0.15, 0.3, 0.5, 0.65, 0.8 },
        { 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5, 0.65},
        { 0.65, 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5 },
        { 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15, 0.3 },
        { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15 },
        { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0, }
    };

    private short count = 0;
    private DrawingArea darea;

    public SharpApp() : base("Waiting")
    {
        SetDefaultSize(250, 150);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };
      
        GLib.Timeout.Add(100, new GLib.TimeoutHandler(OnTimer));
      
        darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    bool OnTimer() 
    { 
        count += 1;
        darea.QueueDraw();
        return true;
    }        

      
    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);
        
        cr.LineWidth = 3;
        cr.LineCap = LineCap.Round;

        int width, height;
        width = Allocation.Width;
        height = Allocation.Height;

        cr.Translate(width/2, height/2);

        for (int i = 0; i < 8; i++) {
            cr.SetSourceRGBA(0, 0, 0, trs[count%8, i]);
            cr.MoveTo(0.0, -10.0);
            cr.LineTo(0.0, -40.0);
            cr.Rotate(Math.PI/4);
            cr.Stroke();
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

نرسم ال 8 اسطر بقيم الفا مختلفة

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

نستخدم التايمر لعمل الأنيميشن

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

هذه مصفوفة ثنائية الأبعاد مستخدمه فى هذا العرض.. هناك 8 صفوف كل منها لحالة.. كل من ال 8 اسطر يستخدمها



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

نجعل الأسطر اسمك، ليصبحو اكثر وضوحا.. نرسم

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

هنا نحدد الشفافية لسطر

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

هذا الكود سيقوم برسم ال 8 سطور


Waiting

Figure: Waiting

فى هذا الفصل عملنا بعض الرسم المتقدم بإستخدام كايرو



Home ‡ Contents ‡ Top of Page