أعجوبة

البرمجيات الحُرة والمفتوحة المصدر

أدوات المستخدم

أدوات الموقع


docs:cairographics

~~ODT~~

مكتبة الرسم المتجهي Cairo

هذه دروس عن كايرو تم كتابتها بلغة سي، ومناسبة للمطورين مبتدأين ومتوسطين. لاتنس الإطلاع على دروس GTK+

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

مرحبا بك فى دروس كايرو، فى هذه الدروس ستتعلم الأساسيات وبعض المواضيع المتقدمة فى مكتبة كايرو للرسم المتجهى. لمعظم الأمثلة سنستخدم مكتبة GTK+ ولغة السى

الرسم المتجهى

هنا نوعان من جرافيك الكمبيوتر متجهى و raster ال Raster يمثل الصور على انها مجموعة من البكسلز بينما المتجهى يعتمد على اساسيات الأشكال من نقاط وخطوط ومنحنيات ليعبر عن الصور. ويتم عملها بناء على معادلات رياضية. كلا النوعين لهما ميزات وعيوب.. من مميزات المتجهى على ال raster التالى:

  • مساحة اصغر
  • امكانية التكبير -zoom- لانهائى
  • النقل والتوسيع والملأ او الدوران لا تقلل من جودة الصورة

كايرو

كايرو هى مكتبة للرسم المتجهى ثنائى الأبعاد. مكتوبة بلغة C ويمكن استخدامها عن طريق لغات اخرى مثل Python, Perl, C++, Java,..الخ وايضا متعددة المنصات، فتعمل على لينكس وبى اس دى وماك وغيرهم

تتدعم كايرو backends مختلفة مثلا

  • X Window System
  • Win32 GDI
  • Mac OS X Quartz
  • PNG
  • PDF
  • PostScript
  • SVG

مما يعنى اننا نستطيع ان نرسم على ويندوز، على لينكس او غيرهم ونستطيع استخدامها لعمل صور PNG وملفات PDF وملفات PostScript وايضا ملفات SVG

نستطيع انا نقارن مكتبة كايرو بال GDI فى ويندوز او Quartz 2D فى ماك. كايرو مكتبة مفتوحة المصدر. ومنذ الإصدار 2.8 اصبحت جزء من GTK+

ترجمة الأمثلة

الأمثلة مكتوبة بلغة C .فسنستخدم GCC لترجمتهم

 gcc -o example `pkg-config --cflags --libs gtk+-2.0` example.c

تعريفات كايرو

فى هذه الجزئية سنقدم بعض التعريفات المفيدة لفهم افضل مع نظام الرسم فى كايرو

السياق

لعمل بعض الرسم فى كايرو، يجب اولا انشاء سياق، ليحمل كل معاملات حالة الرسم. وهذا يشمل معلومات مثل سمك الخط واللون. السطح: هو المكان للرسم اليه وبعض الأشياء الأخرى. فهو يسمح لدوال الرسم الأصلية بأخذ معاملات اقل لتسهيل ال Interface مثلا gdk_cairo_create() تنشئ سياق للرسم الى المرسوم

To do some drawing in Cairo, we must first create a Cairo context. The Cairo context holds all of the graphics state parameters that describe how drawing is to be done. This includes information such as line width, color, the surface to draw to, and many other things. This allows the actual drawing functions to take fewer arguments to simplify the interface. The gdk_cairo_create() function call creates a cairo context for drawing to drawable.

  cairo_t *cr;
  cr = gdk_cairo_create(widget->window);

السطرين السابقين انشأو سياق. لاحظ ان السياق مرتبط ب GdkDrawable البناء cairo_t يشمل حالة المٌرندر واحداثيات الأشكال اللتى سترسم. الكائنات المنشئة من البناء cairo_t تدعى سياقات كايرو

كل الرسم يتم دائما الى كائن cairo_t .السياق مرتبط بسطح ما، PDF, SVG, PNG, GtkDrawable .. الخ

GDK لاتغلف Cairo. تسمح بإنشاء سياق ليستخدم فى الرسم على اى سطح قابل للرسم عليه. الدوال الإضافية تسمح بتحويل المستطيلات والمناطق الخاصة بGDK الى مسارات و pixbufs كمصدر لعملية الرسم

المسار

المسار يتكون من خط او اكثر، كل من الخطوط مرتبط بنقطتين او اكتر. المسارات ممكن ان تتكون من خطوط مستقيمة او منحنيات. هناك نوعين من المسارات: مفتوح ومغلق. فى المسار المغلق تلتقى نقطتى البداية والنهاية معا بعكس المسار المفتوح

فى كايرو نبدأ بمسار خالى. اولا نقوم بتعريف المسار ثم نجعله ظاهرة بال stroking والملء. ملحوظة مهمة: بعد كل استدعاء ل cairo_stroke() او cairo_fill() يتم تفريغ المسار. فيجب علينا اعادة تعريف مسار جديد. المسار يتكون من عدة مسارات فرعية

المصدر

هو الدهان او الطلاء الذى سنستخدمه فى الرسم. نستطيع ان نشبه بالحبر. وسنستخدمه لعملية التحديد الخارجى وملء الأشكال. هناك 4 انواع من المصادر: الألوان ، التدرجات ، الأنماط ، الصور

السطح

السطح هو الوجهة اللتى سنرسم اليها. نستطيع “رندرة” الوثائق بإستخدام اسطح PDF او PostScript مباشرة الى المنصة عبر اسطح Xlib او Win32

الوثائق ذكرت الأسطح التالية:

typedef enum _cairo_surface_type {
  CAIRO_SURFACE_TYPE_IMAGE,
  CAIRO_SURFACE_TYPE_PDF,
  CAIRO_SURFACE_TYPE_PS,
  CAIRO_SURFACE_TYPE_XLIB,
  CAIRO_SURFACE_TYPE_XCB,
  CAIRO_SURFACE_TYPE_GLITZ,
  CAIRO_SURFACE_TYPE_QUARTZ,
  CAIRO_SURFACE_TYPE_WIN32,
  CAIRO_SURFACE_TYPE_BEOS,
  CAIRO_SURFACE_TYPE_DIRECTFB,
  CAIRO_SURFACE_TYPE_SVG,
  CAIRO_SURFACE_TYPE_OS2
} cairo_surface_type_t;

القناع

قبل ان يتم تطبيق المصدر على السطح يتم ترشيحه -فلترته- اولا. فيستخدم القناع كمرشح -فلتر-. يحدد اي الأماكن سيتم تطبيق المصدر

الأجزاء الغير شفافة فى القناع تتيح نسخ المصدر بعكس الأجزاء الشفافة Before the source is applied to the surface, it is filtered first. The mask is used as a filter. The mask determines, where the sourse is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.

النمط

النمط فى كايرو يعبر عن المصدر عند الرسم، فى كايرو: النمط هو شئ تستطيع ان تقرا منه يستخدم كمصدر او مرشح لعملية الرسم وقد يكون مصمت او متدرج او غيره

A cairo pattern represents a source when drawing onto a surface. In cairo, a pattern is something that you can read from, that is used as the source or mask of a drawing operation. Patterns in cairo can be solid, surface-based, or even gradients patterns.

Cairo backends

The Cairo library supports various backends. In this section of the Cairo graphics tutorial, we will use Cairo to create a PNG image, PDF file, SVG file and we will draw on a GTK window.

PNG image

فى اول مثال سننشئ صورة PNG

#include <cairo.h>
 
int main (int argc, char *argv[])
{
  cairo_surface_t *surface;
  cairo_t *cr;
 
  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60);
  cr = cairo_create(surface);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, 40.0);
 
  cairo_move_to(cr, 10.0, 50.0);
  cairo_show_text(cr, "Disziplin ist Macht.");
 
  cairo_surface_write_to_png(surface, "image.png");
 
  cairo_destroy(cr);
  cairo_surface_destroy(surface);
 
  return 0;
}

المثال تطبيق console لإنشاء صورة PNG

 #include <cairo.h>

فى الملف الرأسى نجد التعريفات للدوال والثوابت الخاصة بنا.

 cairo_surface_t *surface;
 cairo_t *cr;

هنا نعرف السطح وسياق كايرو

 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60);
 cr = cairo_create(surface);

هنا انشئنا السطح والسياق. السطح 390*60 بكسل

 cairo_set_source_rgb(cr, 0, 0, 0);

سنرسم بالحبر الإسود

 cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
 cairo_set_font_size(cr, 40.0);

نختار نوع الخط ونحدد الحجم

 cairo_move_to(cr, 10.0, 50.0);
 cairo_show_text(cr, "Disziplin ist Macht.");

ننتقل الى الموضع a (10.0, 50.0) فى الصورة ونرسم النص

 cairo_surface_write_to_png(surface, "image.png");

الدالة السابقة تنشئ صورة ال PNG

 cairo_destroy(cr);
 cairo_surface_destroy(surface);

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

PNG image

PDF file

فى المثال التالى، سنستخدم مكتبة Cairo لعمل ملف PDF بسيط

#include <cairo/cairo.h>
#include <cairo/cairo-pdf.h>
 
int main() {
 
  cairo_surface_t *surface;
  cairo_t *cr;
 
  surface = cairo_pdf_surface_create("pdffile.pdf", 504, 648);
  cr = cairo_create(surface);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size (cr, 40.0);
 
  cairo_move_to(cr, 10.0, 50.0);
  cairo_show_text(cr, "Disziplin ist Macht.");
 
  cairo_show_page(cr);
 
  cairo_surface_destroy(surface);
  cairo_destroy(cr);
 
  return 0;
}

يجب فتح الملف فى عارض PDF .مستخدمى لينكس يستطيعو استخدام KPDF او Evnice

We must open the pdf file in a pdf viewer. Linux users can use KPDF or Evince viewers.

 surface = cairo_pdf_surface_create("pdffile.pdf", 504, 648);

ل“رندرة” ملف ال PDF يجب انشاء سطح PDF عن طريق الدالة cairo_pdf_surface_create() ثم تحديد المساحة بصورة نقط

 cairo_show_page(cr);

الدالة cairo_show_page() تنهى ال “رندرة” لملف ال PDF

PDF file in Evince

SVG file

فى المثال التالى سننشئ ملف SVG (Scalable Vector Graphics) بسيط

#include <cairo.h>
#include <cairo-svg.h> 
 
int main() {
 
  cairo_surface_t *surface;
  cairo_t *cr;
 
  surface = cairo_svg_surface_create("svgfile.svg", 390, 60);
  cr = cairo_create(surface);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size (cr, 40.0);
 
  cairo_move_to(cr, 10.0, 50.0);
  cairo_show_text(cr, "Disziplin ist Macht.");
 
  cairo_surface_destroy(surface);
  cairo_destroy(cr);
 
  return 0;
}

نستخدم فيرفكس ، اوبرا او انكسكيب لعرض ملفات ال svg

 surface = cairo_svg_surface_create("svgfile.svg", 390, 60);

لإنشاء ملف SVG يجب اولا انشاء سطح SVG عن طريق cairo_svg_surface_create()

باقى الكود مشابة للسابق

SVG file in Opera

نافذة GTK

فى المثال الأخير سنقوم بالرسم على نافذة GTK وسنقوم بإستخدامها على مدار باقى الدروس

#include <cairo.h>
#include <gtk/gtk.h>
 
static gboolean
on_expose_event(GtkWidget      *widget,
		 GdkEventExpose *event,
		 gpointer        data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, 40.0);
 
  cairo_move_to(cr, 10.0, 50.0);
  cairo_show_text(cr, "Disziplin ist Macht.");
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
		    G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
		    G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 90); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

المثال يعرض نافذة فى منتصف الشاشة -متسنترة- نرسم عليها النص “Disziplin ist Macht”

#include <cairo.h>
#include <gtk/gtk.h>

We include the necessary Cairo and GTK headers.

 g_signal_connect(window, "expose-event",
     G_CALLBACK(on_expose_event), NULL);

عند اعادة رسم النافذة يتم بعث الإشارة expose-event فنقوم بربطها بمعالج الإشارة on_expose_event()

تقوم اداة التطوير بإعادة رسم المناطق المكشوفة بالنسبة للنوافذ العادية فستقوم بتصفية -محو- الخلفية. واذا اردت ان تقوم برسوماتك الخاصة يجب ان تعلم GTK عن ذلك عن طريق الدالة gtk_widget_set_app_paintable

 gtk_widget_set_app_paintable(window, TRUE)

اذا اردت الرسم فى GTK+ فيمكننا الرسم على ويدجت GtkDrawingArea او على GtkWindow بسيطة. انا اخترت الأخير. للرسم على النافذة لابد ان نبلغ GTK+ عبر استدعاء gtk_widget_set_app_paintable فتقوم هذه الدالة بكتم -اخفاء- او كبح الرسم الإفتراضى فى خلفية الويدجت. فى حال استخدام GtkDrawingArea فإننا لانحتاج لإستدعاء هذه الدالة.

 cairo_t *cr;
 
 cr = gdk_cairo_create(widget->window);

يتم الرسم فى معالج الإشارة on_expose_event ، فيه نقوم بإنشاء سياق كايرو ونرسم النص كالمعتاد.

GTK window

اساسيات الرسم فى كايرو

فى هذه الجزئية، سنقوم برسم بعض الأساسيات مثل الخطوط سنقوم بعمليتى الملء والتحديد وسنتكلم عن الداشات -dashes- وربط الخطوط

الخطوط

الخطوط هى كائنات اساسية فى المتجهات. لرسم سطر نستدعى دالتين.لتحديد نقطة البداية ب cairo_move_to() بينما نقطة النهاية تحدد ب cairo_line_to()

#include <cairo.h>
#include <gtk/gtk.h>
 
 
double coordx[100];
double coordy[100];
 
int count = 0;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width (cr, 0.5);
 
  int i, j;
  for ( i = 0; i <= count - 1; i++ ) {
      for ( j  = 0; j <= count -1; j++ ) {
          cairo_move_to(cr, coordx[i], coordy[i]);
          cairo_line_to(cr, coordx[j], coordy[j]);
      }
  }
 
  count = 0;
  cairo_stroke(cr);
  cairo_destroy(cr);
 
  return FALSE;
}
 
gboolean clicked(GtkWidget *widget, GdkEventButton *event,
    gpointer user_data)
{
    if (event->button == 1) {
        coordx[count] = event->x;
        coordy[count++] = event->y;
    }
 
    if (event->button == 3) {
        gtk_widget_queue_draw(widget);
    }
 
    return TRUE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
  g_signal_connect(window, "button-press-event", 
      G_CALLBACK(clicked), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_title(GTK_WINDOW(window), "lines");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال، نضغط عشوائيا على النافذة بالزر الأيسر للفأرة، يتم تخزين كل ضغطه فى مصفوفة وعندما نضغط بالزر الأيمن يتم دمج جميع النقاط فى المصفوفة. بهذه الطريقة نستطيع عمل كائنات مثيرة للإهتمام. الضغط بالزر الأيمن يقوم بتصفية -تنظيف- النافذة ، ونستطيع الضغط لكائن اخر

 cairo_set_source_rgb(cr, 0, 0, 0);
 cairo_set_line_width (cr, 0.5);

هذه السطور سترسم بالحبر الإسود مع سمك 0.5

 int i, j;
 for ( i = 0; i <= count - 1; i++ ) {
     for ( j  = 0; j <= count -1; j++ ) {
         cairo_move_to(cr, coordx[i], coordy[i]);
         cairo_line_to(cr, coordx[j], coordy[j]);
     }
 }

نربط كل النقاط مع النقط الأخرى

 cairo_stroke(cr);

الدالة cairo_stroke() تقوم برسم الخطوط

 g_signal_connect(window, "button-press-event", 
     G_CALLBACK(clicked), NULL);

نربط button-press-event بالمعالج clicked

We connect the button-press-event to the clicked callback.

 if (event->button == 1) {
     coordx[count] = event->x;
     coordy[count++] = event->y;
 }

داخل المعالج clicked نقوم بتحديد هل كانت الضغطة على الفأرة بالزر الأيمن او الأيسر، إذا كان الأيسر نقوم بتخزين احداثيات x,y

 if (event->button == 3) {
     gtk_widget_queue_draw(widget);
 }

بالضغط على الزر الأيمن نعيد رسم النافذة

Lines

الملء والتحديد

عملية ال stroke تقوم برسم الحدود الخارجية للشكل عملية ال fill تقوم بملء ماداخل الشكل

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
static gboolean
on_expose_event (GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
  cairo_set_line_width(cr, 9);
 
  cairo_set_source_rgb(cr, 0.69, 0.19, 0);
  cairo_arc(cr, width/2, height/2, 
      (width < height ? width : height) / 2 - 10, 0, 2 * M_PI);
  cairo_stroke_preserve(cr);
 
  cairo_set_source_rgb(cr, 0.3, 0.4, 0.6); 
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 150); 
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال سنقوم برسم دائرة

 #include &lt;math.h&gt;

نحتاج هذا الملف الرأسى للثابت M_PI

 int width, height;
 gtk_window_get_size(GTK_WINDOW(widget), &width, &height);

نحصل على عرض وارتفاع النافذة، نحتاج هذه القيم ليتم اعادة تحجيم الدائرة عند اعادة تحجيم النافذة

 cairo_set_source_rgb(cr, 0.69, 0.19, 0);
 cairo_arc(cr, width/2, height/2, 
     (width < height ? width : height) / 2 - 10, 0, 2 * M_PI);
 cairo_stroke_preserve(cr);

نرسم الحدود الخارجية للدائرة

 cairo_set_source_rgb(cr, 0.3, 0.4, 0.6); 
 cairo_fill(cr);

نملئها بلون ازرق

Fill and stroke

<!–

 

–>

Dash

كل سطر يمكن ان يرسم ب داش مختلف. مما يحدد ستايل الخط. يستخدم الداش فى الدالة cairo_stroke()

نمط الداش يتم تحديده بالدالة cairo_set_dash() .ويتم تحديده من خلال مصفوفة الداش -مصفوفة من القيم الموجبة لتحديد الأماكن اللتى سيتم فيها رسم الخط داخل السطر. ايضا نقوم بتحديد طول المصفوفة وال offset . فى حال كان الطول للمصفوفة 0 يتم الغاء الداشينج. واذا كانت 1 يعتبر متماثل. ويحدد ال offset قيمة البداية -او بكلمات اخرى المساحة الفارغة عند البداية

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgba(cr, 0, 0, 0, 1);
 
  static const double dashed1[] = {4.0, 1.0};
  static int len1  = sizeof(dashed1) / sizeof(dashed1[0]);
 
  static const double dashed2[] = {4.0, 1.0, 4.0};
  static int len2  = sizeof(dashed2) / sizeof(dashed2[0]);
 
  static const double dashed3[] = {1.0};
 
  cairo_set_line_width(cr, 1.5);
 
  cairo_set_dash(cr, dashed1, len1, 0);
 
  cairo_move_to(cr, 40, 30);  
  cairo_line_to(cr, 200, 30);
  cairo_stroke(cr);
 
  cairo_set_dash(cr, dashed2, len2, 1);
 
  cairo_move_to(cr, 40, 50);  
  cairo_line_to(cr, 200, 50);
  cairo_stroke(cr);
 
  cairo_set_dash(cr, dashed3, 1, 0);
 
  cairo_move_to(cr, 40, 70);  
  cairo_line_to(cr, 200, 70);
  cairo_stroke(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;  
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 100); 
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال، سنرسم 3 خطوط مع انماط داش مختلفة

 static const double dashed1[] = {4.0, 1.0};

هنا نحدد اول نمط. سنرسم نمط من 4 بكسل ونقطة فارغة

 static int len1  = sizeof(dashed1) / sizeof(dashed1[0]);

نحصل على حجم ال array

 cairo_set_dash(cr, dashed1, len1, 0);

نحدد الداش

 darea = gtk_drawing_area_new();
 gtk_container_add(GTK_CONTAINER(window), darea);

فى المثال لانقوم بالرسم مباشرة ولكن عبر ال drawing area

Dashes

Line caps

ال Line caps هى نقط النهاية للخط

  • CAIRO_LINE_CAP_SQUARE
  • CAIRO_LINE_CAP_ROUND
  • CAIRO_LINE_CAP_BUTT

هناك 3 انواع مختلفة فى كايرو

Line caps

الخط ب CAIRO_LINE_CAP_SQUARE سيكون لديه حجم مختلف عن خط ب CAIRO_LINE_CAP_BUTT لو كان خط بعرض 1 بكسل فسيكون الخط ب CAIRO_LINE_CAP_SQUARE سيكون نفس العرض بكسل زيادة فى الحجم. العرض/2 بكسل فى البداية و العرض/2 بكسل فى النهاية ##FIXME

A line with a CAIRO_LINE_CAP_SQUARE cap will have a different size, than a line with a CAIRO_LINE_CAP_BUTT cap. If a line is width px wide, the line with a CAIRO_LINE_CAP_SQUARE cap will be exactly width px greater in size. width/2 px at the beginning and width/2 px at the end.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgba(cr, 0, 0, 0, 1);
  cairo_set_line_width(cr, 10);
 
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); 
  cairo_move_to(cr, 30, 50); 
  cairo_line_to(cr, 150, 50);
  cairo_stroke(cr);
 
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); 
  cairo_move_to(cr, 30, 90); 
  cairo_line_to(cr, 150, 90);
  cairo_stroke(cr);
 
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); 
  cairo_move_to(cr, 30, 130); 
  cairo_line_to(cr, 150, 130);
  cairo_stroke(cr);
 
  cairo_set_line_width(cr, 1.5);
 
  cairo_move_to(cr, 30, 40);  
  cairo_line_to(cr, 30, 140);
  cairo_stroke(cr);
 
  cairo_move_to(cr, 150, 40);  
  cairo_line_to(cr, 150, 140);
  cairo_stroke(cr);
 
  cairo_move_to(cr, 155, 40);  
  cairo_line_to(cr, 155, 140);
  cairo_stroke(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;  
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new ();
  gtk_container_add(GTK_CONTAINER (window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); 
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال نرسم 3 خطوط ب 3 caps مختلفة. سيعرضلنا ايضا الإختلاف فى المساحة للخطوط The example draws three lines with three different caps. It will also graphically demonstrate the differences is size of the lines.

 cairo_set_line_width(cr, 10);

خطوطنا ستكون بعرض 10 بكسل

 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); 
 cairo_move_to(cr, 30, 90); 
 cairo_line_to(cr, 150, 90);
 cairo_stroke(cr);

هنا نرسم خط افقى ب CAIRO_LINE_CAP_ROUND

 cairo_set_line_width(cr, 1.5);
 
 cairo_move_to(cr, 30, 40);  
 cairo_line_to(cr, 30, 140);
 cairo_stroke(cr);

هذا الخط من ال 3 خطوط الرأسية يستخدم لعرض الفرق فى المساحة This is one of the three vertical lines used to demostrate the differences in size.

Line caps

رابطات الخطوط

نستطيع ربط الخطوط ب3 ستايلات

  • CAIRO_LINE_JOIN_MITER
  • CAIRO_LINE_JOIN_BEVEL
  • CAIRO_LINE_JOIN_ROUND

Bevel, Round, Miter line joins

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgb(cr, 0.1, 0, 0);
 
  cairo_rectangle(cr, 30, 30, 100, 100);
  cairo_set_line_width(cr, 14);
  cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); 
  cairo_stroke(cr);
 
  cairo_rectangle(cr, 160, 30, 100, 100);
  cairo_set_line_width(cr, 14);
  cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); 
  cairo_stroke(cr);
 
  cairo_rectangle(cr, 100, 160, 100, 100);
  cairo_set_line_width(cr, 14);
  cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); 
  cairo_stroke(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
int
main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;  
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 280); 
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
 
}

فى المثال نرسم 3 مستطيلات سميكة مع رابطات خطوط متنوعة.

 cairo_rectangle(cr, 30, 30, 100, 100);
 cairo_set_line_width(cr, 14);
 cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); 
 cairo_stroke(cr);

فى هذا الكود، رسمنا مستطيل برابط CAIRO_LINE_JOIN_MITER والخطوط عرضها 14 بكسل

Line joins

الأشكال والملء فى كايرو

فى هذه الجزئية سننشئ بعض الأشكال البسيطة والمتقدمة وسنملئهم بصورة بألوان مصمته، وانماط،وتدرجات

الأشكال الأساسية

تقدم لنا كايرو العديد من الدوال لإنشاء الأشكال الأساسية.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width(cr, 1);
 
  cairo_rectangle(cr, 20, 20, 120, 80);
  cairo_rectangle(cr, 180, 20, 80, 80);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_arc(cr, 90, 160, 40, M_PI/4, M_PI);
  cairo_close_path(cr);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_translate(cr, 220, 180);
  cairo_scale(cr, 1, 0.7);
  cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;  
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 390, 240); 
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال نرسم مستطيل، مربع، دائرة، قوس و قطع ناقص

 cairo_rectangle(cr, 20, 20, 120, 80);
 cairo_rectangle(cr, 180, 20, 80, 80);

الدالة cairo_rectangle() تستخدم لعمل مربعات ومستطيلات -المربع هو نوع خاص من المستطيلات فيه الطول=العرض-

 cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);

هذا السطر ينشئ الدائرة

 cairo_scale(cr, 1, 0.7);
 cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);

نستخدم cairo_scale() لإنشاء القطع الناقص

Shapes الأشكال الأخرى -المعقد منها خصوصا- نستطيع انشاءها بتجميع الأساسيات

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
int points[11][2] = { 
    { 0, 85 }, 
    { 75, 75 }, 
    { 100, 10 }, 
    { 125, 75 }, 
    { 200, 85 },
    { 150, 125 }, 
    { 160, 190 },
    { 100, 150 }, 
    { 40, 190 },
    { 50, 125 },
    { 0, 85 } 
};
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width(cr, 1);
 
 
  gint i;
  for ( i = 0; i < 10; i++ ) {
      cairo_line_to(cr, points[i][0], points[i][1]);
  }
 
  cairo_close_path(cr);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
 
  cairo_move_to(cr, 240, 40);
  cairo_line_to(cr, 240, 160);
  cairo_line_to(cr, 350, 160);
  cairo_close_path(cr);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_move_to(cr, 380, 40);
  cairo_line_to(cr, 380, 160);
  cairo_line_to(cr, 450, 160);
  cairo_curve_to(cr, 440, 155, 380, 145, 380, 40);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 460, 240); 
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى هذا المثال ننشئ نجمة، مثلث -هذه الأشكال تنشئ عن طريق الخطوط ومنحنى

 gint i;
 for ( i = 0; i < 10; i++ ) {
     cairo_line_to(cr, points[i][0], points[i][1]);
 }
 
 cairo_close_path(cr);

‪ النجمة ترسم عن طريق جمع كل النقاط -الموجودة فى مصفوفة النقط-ويتم انهاء النجمة بإستدعاء cairo_close_path() التى تقوم بربط اخر نقطتين

 cairo_move_to(cr, 380, 40);
 cairo_line_to(cr, 380, 160);
 cairo_line_to(cr, 450, 160);
 cairo_curve_to(cr, 440, 155, 380, 145, 380, 40);

المستطيل المعدل هو عبارة عن دمج لخطين ومنحنى.

Shapes

Fills

المالئات ؟ #FIXME : مش الأصل fillers ?

Fills fill the interiors of shapes. Fills can be solid colors, patters or gradients.

Solid colors

اللون هو كائن يمثل تجميعة من الأحمر والأخضر والأزرق RGB القيم المسموحة بين الفترة 0 و 1

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);  
 
 
  cairo_set_source_rgb(cr, 0.5, 0.5, 1);
  cairo_rectangle(cr, 20, 20, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 150, 20, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 0, 0.3, 0);
  cairo_rectangle(cr, 20, 140, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source_rgb(cr, 1, 0, 0.5);
  cairo_rectangle(cr, 150, 140, 100, 100);
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 270, 260); 
  gtk_window_set_title(GTK_WINDOW(window), "colors");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main(); 
 
  return 0;
}

فى المثال نرسم 4 مستطيلات ملونة.

 cairo_set_source_rgb(cr, 0.5, 0.5, 1);
 cairo_rectangle(cr, 20, 20, 100, 100);
 cairo_fill(cr);

الدالة cairo_set_source_rgb تقوم بتحديد المصدر الى لون غير شفاف ومعاملاتها هى قيم الأحمر والأخضر والأزرق. المصدر سيستخدم لملء داخل المستطيل ويتم ذلك عن طريق الدالة cairo_fill

Solid colors

الأنماط

الأنماط هى كائنات رسومية معقدة يمكن ان تملء الأشكال.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
cairo_surface_t *surface1;
cairo_surface_t *surface2;
cairo_surface_t *surface3;
cairo_surface_t *surface4;
 
static void create_surfaces() {
  surface1 = cairo_image_surface_create_from_png("blueweb.png");
  surface2 = cairo_image_surface_create_from_png("maple.png");
  surface3 = cairo_image_surface_create_from_png("crack.png");
  surface4 = cairo_image_surface_create_from_png("chocolate.png");
}
 
static void destroy_surfaces() {
  g_print("destroying surfaces");
  cairo_surface_destroy(surface1);
  cairo_surface_destroy(surface2);
  cairo_surface_destroy(surface3);
  cairo_surface_destroy(surface4);
}
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cairo_pattern_t *pattern1;
  cairo_pattern_t *pattern2;
  cairo_pattern_t *pattern3;
  cairo_pattern_t *pattern4;
 
  cr = gdk_cairo_create(widget->window);
 
  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);  
 
 
  pattern1 = cairo_pattern_create_for_surface(surface1);
  pattern2 = cairo_pattern_create_for_surface(surface2);
  pattern3 = cairo_pattern_create_for_surface(surface3);
  pattern4 = cairo_pattern_create_for_surface(surface4);
 
 
  cairo_set_source(cr, pattern1);
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
  cairo_rectangle(cr, 20, 20, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source(cr, pattern2); 
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); 
  cairo_rectangle(cr, 150, 20, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source(cr, pattern3);
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
  cairo_rectangle(cr, 20, 140, 100, 100);
  cairo_fill(cr);
 
  cairo_set_source(cr, pattern4);
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
  cairo_rectangle(cr, 150, 140, 100, 100);
  cairo_fill(cr);
 
  cairo_pattern_destroy(pattern1);
  cairo_pattern_destroy(pattern2);
  cairo_pattern_destroy(pattern3);
  cairo_pattern_destroy(pattern4);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  create_surfaces();
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 270, 260); 
  gtk_window_set_title(GTK_WINDOW(window), "patterns");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  destroy_surfaces();
 
  return 0;
}

فى هذا المثال نرسم 4 مستطيلات مرة اخرى، ولكن هذه المرة نملئهم بإستخدام الأنماط -patterns-. نستخدم 4 انماط من برنامج Gimp لمعالجة الصور. يجب ان نحتفظ بالحجم الأساسى للأنماط.. لأننا سوف ن tile هم ؟ FIXME: هل تترجم على اننا سوف نبلطهم ؟

ننشئ الأسطح خارج الدالة on_expose_event لأنا لانريد قرائتها من القرص الصلب كل مرة تريد فيها النافذة اعادة رسم نفسها.

 pattern1 = cairo_pattern_create_for_surface(surface1);

We create a pattern from the surface by calling the cairo_pattern_create_for_surface() function.

  cairo_set_source(cr, pattern1);
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
  cairo_rectangle(cr, 20, 20, 100, 100);
  cairo_fill(cr);

cairo_set_source تخبر سياق كايرو ان تستخدم نمط كمصدر للرسم، ربما لاتتناسب الأنماط مع الشكل لذا نقوم بتحديدال mode الى CAIRO_EXTEND_REPEAT مما يقوم بتكرير النمط! الدالة cairo_rectangle تنشئ مسار مستطيلى، واخيرا cairo_fill تملء ذلك المسار بالمصدر

Patterns

Gradients

#FIXME: التعبير المناسب لل Gradient ؟

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)

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_pattern_t *pat1;
  cairo_pattern_t *pat2;
  cairo_pattern_t *pat3;
 
  cr = gdk_cairo_create(widget->window);
 
  pat1 = cairo_pattern_create_linear(0.0, 0.0,  350.0, 350.0);
 
  gdouble j;
  gint count = 1;
  for ( j = 0.1; j < 1; j += 0.1 ) {
      if (( count % 2 ))  {
          cairo_pattern_add_color_stop_rgb(pat1, j, 0, 0, 0);
      } else { 
          cairo_pattern_add_color_stop_rgb(pat1, j, 1, 0, 0);
      }
   count++;
  }
 
  cairo_rectangle(cr, 20, 20, 300, 100);
  cairo_set_source(cr, pat1);
  cairo_fill(cr);
 
 
  pat2 = cairo_pattern_create_linear(0.0, 0.0,  350.0, 0.0);
 
  gdouble i;
  count = 1;
  for ( i = 0.05; i < 0.95; i += 0.025 ) {
      if (( count % 2 ))  {
          cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 0);
      } else { 
          cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 1);
      }
   count++;
  }
 
  cairo_rectangle(cr, 20, 140, 300, 100);
  cairo_set_source(cr, pat2);
  cairo_fill(cr);
 
 
  pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);
 
  cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0);
  cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0);
  cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);
 
  cairo_rectangle(cr, 20, 260, 300, 100);
  cairo_set_source(cr, pat3);
  cairo_fill(cr);
 
  cairo_pattern_destroy(pat1);
  cairo_pattern_destroy(pat2);
  cairo_pattern_destroy(pat3);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 340, 390); 
  gtk_window_set_title(GTK_WINDOW(window), "gradients");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

نقوم فى المثال برسم 3 مستطيلات ب 3 (تدرجات؟) مختلفة In our example, we draw three rectangles with three different gradients.

 pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);

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

 cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0);
 cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0);
 cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);

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

Gradients

الشفافية

فى هذه الجزئية سنتخدث عن الشفافية وبعض التعريفات الأساسية وتأثيرين مثيرين للإهتمام

الشفافية هى الجودة للقدرة على الرؤية عبر المادة. ابسط طريقة لفهمها هى تخيل قطع من الزجاج او الماء، تقنيا اشعة الضوء تعبر سطح الزجاج او الماء لذا نستطيع ان نرى الأشياء خلفها فى الحاسب، نستطيع الوصول لتأثيرات الشفافية عن طريق alpha compositing.

alpha compositing هى عملية لدمج صورة مع خلفية لجعل المظهر شفاف جزئيا. عملية ال composition تستخدم alpha channel.

alpha channel هى طبقة من 8 بت فى ملف جرافيك تستخدم للتعبير عن الشفافية. ال8 بت الإضافية لكل بكسل تعمل كقناع تعبر عن 256 مستوى للشفافية

In computer graphics, we can achieve transparency effects using Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency.

The composition process uses an alpha channel. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency). The extra eight bits per pixel serves as a mask and represents 256 levels of translucency.

(answers.com, wikipedia.org)

المستطيلات الشفافة

فى المثال سنرسم 10 مستطيلات ذات مستويات مختلفة من الشفافية

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  gint i;
  for ( i = 1; i <= 10; i++) {
      cairo_set_source_rgba(cr, 0, 0, 1, i*0.1);
      cairo_rectangle(cr, 50*i, 20, 40, 40);
      cairo_fill(cr);  
  }
 
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 590, 90); 
  gtk_window_set_title(GTK_WINDOW(window), "transparency");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

cairo_set_source_rgba() لديها معامل اختيارى لقيمة الألفا للشفافية

 gint i;
 for ( i = 1; i <= 10; i++) {
     cairo_set_source_rgba(cr, 0, 0, 1, i*0.1);
     cairo_rectangle(cr, 50*i, 20, 40, 40);
     cairo_fill(cr);  
 }

هذا الكود ينشئ 10 مستطيلات ب قيم الفا تتدرج من 0.1 الى 1

Transparency

تأثير التلاشى

فى المثال سنقوم بملاشاة صورة، ستزداد شفافيتها حتى تصبح مخفية تماما

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
cairo_surface_t *image;
gboolean timer = TRUE;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  static double alpha = 1;
  double const delta = 0.01;
 
  cairo_set_source_surface(cr, image, 10, 10);
  cairo_paint_with_alpha(cr, alpha);
 
  alpha -= delta; 
 
  if (alpha <= 0) timer = FALSE;
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;
 
  gint width, height;
 
  image = cairo_image_surface_create_from_png("turnacastle.png");
  width = cairo_image_surface_get_width(image);
  height = cairo_image_surface_get_height(image);
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER (window), darea);
 
  g_signal_connect(darea, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), width+20, height+20);
 
  gtk_window_set_title(GTK_WINDOW(window), "fade out");
  g_timeout_add(50, (GSourceFunc) time_handler, (gpointer) window);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

فى المثال سنقوم بملاشاة صورة، ستزداد شفافيتها حتى تصبح مخفية تماما استخدمنا صورة بقايا قلعة Turna فى شرق سلوفاكيا تستطيع تحميلها <a href=“../images/turnacastle.png”>هنا</a>.

 image = cairo_image_surface_create_from_png("turnacastle.png");

لأسباب خاصة بالكفاءة، انشاء سطح الصورة نجعله فى الدالة main

 cairo_set_source_surface(cr, image, 10, 10);

هنا نحدد المصدر

 cairo_paint_with_alpha(cr, alpha);

تأثير التلاشى ينشئ بإستخدام الدالة cairo_paint_with_alpha() تلك الدالة تستخدم الشفافية كقناع

 alpha -= delta;

هنا قللنا قيمة الفا كل مرة يتم فيها تنفيذ on_expose_event()

 if (alpha <= 0) timer = FALSE;

اذا كانت قيمة الفا اقل من او تساوى 0 ننهى تأثير التلاشى -بجعل قيمة المؤقت الى FALSE -لاننا لانحتاجه بعد ذلك–

 g_timeout_add(50, (GSourceFunc) time_handler, (gpointer) window);

ننشئ دالة مؤقت تستدعى time_handler كل 50 ms

We create a timer function. This function will call time_handler every 50 ms.

 static gboolean
 time_handler (GtkWidget *widget)
 {
   if (widget->window == NULL) return FALSE;
 
   if (!timer) return FALSE;
 
   gtk_widget_queue_draw(widget);
   return TRUE;
 }

الهدف الأساسى ل time_handler لإعادة رسم النافذة بصورة منتظمة حتى تصبح قيمته FALSE

 if (widget->window == NULL) return FALSE;

قد تحتاج لماذا هذا الكود هنا؟ هنا نقوم بعمل اختبار بسيط اذا كانت قيمة timeout صغيرة جدا مثلا 5ms فيحدث عند غلق التطبيق يكون تم تدمير النافذة بالفعل ويتم تنفيذ ال timeout function. فيمنع هذا السطر معالجة النافذة فى مثل تلك الحالات.

Fade out

Waiting demo

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

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
static gushort count = 0;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  static gdouble const trs[8][8] = {
      { 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, }
  };
 
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
 
  cairo_translate(cr, width / 2, height /2);
 
  gint i = 0;
  for (i = 0; i < 8; i++) {
      cairo_set_line_width(cr, 3);
      cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
      cairo_set_source_rgba(cr, 0, 0, 0, trs[count%8][i]);
 
      cairo_move_to(cr, 0.0, -10.0);
      cairo_line_to(cr, 0.0, -40.0);
      cairo_rotate(cr, M_PI/4);
 
      cairo_stroke(cr);
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  count += 1;
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *darea;  
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 150); 
 
  gtk_window_set_title(GTK_WINDOW(window), "waiting demo");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
  g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_main();
 
  return 0;
}

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

 static gdouble const trs[8][8] = {
     { 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, }
 };

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

 cairo_set_line_width(cr, 3);
 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);

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

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

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

 cairo_move_to(cr, 0.0, -10.0);
 cairo_line_to(cr, 0.0, -40.0);
 cairo_rotate(cr, M_PI/4);

هذا الكود سيرسم كل من ال 8 خطوط

 g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);

نستخدم دالة مؤقت لعمل الأنيميشن.

Waiting demo

التركيب

فى هذه الجزئية سنقوم بتعريف عمليات التركيب.

Compositing هو دمج اكثر من عنصر مرئى من مصادر مختلفة الى صورة واحدة. يستخدمو لإيهام ان كل تلك العناصر هى جزء من نفس المشهد. يستخدم التركيب بكثرة فى صناعة الأفلام لإنشاء الحشود، عوالم جديدة.. الخ

العمليات

يوجد العديد من عمليات التركيب، تقدم كايرو 14 عملية تركيب مختلفة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static void draw(cairo_t *cr, gint x, gint w,
    gint h, cairo_operator_t op)
{
 
  cairo_t *first_cr, *second_cr;
  cairo_surface_t *first, *second;
 
  first = cairo_surface_create_similar(cairo_get_target(cr),
      CAIRO_CONTENT_COLOR_ALPHA, w, h);
 
  second = cairo_surface_create_similar(cairo_get_target(cr),
      CAIRO_CONTENT_COLOR_ALPHA, w, h);
 
  first_cr = cairo_create(first);
  cairo_set_source_rgb(first_cr, 0, 0, 0.4);
  cairo_rectangle(first_cr, x, 20, 50, 50);
  cairo_fill(first_cr);
 
  second_cr = cairo_create(second);
  cairo_set_source_rgb(second_cr, 0.5, 0.5, 0);
  cairo_rectangle(second_cr, x+10, 40, 50, 50);
  cairo_fill(second_cr);
 
  cairo_set_operator(first_cr, op);
  cairo_set_source_surface(first_cr, second, 0, 0);
  cairo_paint(first_cr);
 
  cairo_set_source_surface(cr, first, 0, 0);
  cairo_paint(cr);
 
  cairo_surface_destroy(first);
  cairo_surface_destroy(second);
 
  cairo_destroy(first_cr);
  cairo_destroy(second_cr);
 
}
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  gint w, h;
  gint x, y;
 
  cairo_operator_t oper[] = {
    CAIRO_OPERATOR_DEST_OVER, 
    CAIRO_OPERATOR_DEST_IN, 
    CAIRO_OPERATOR_OUT,
    CAIRO_OPERATOR_ADD, 
    CAIRO_OPERATOR_ATOP,
    CAIRO_OPERATOR_DEST_ATOP,
  };
 
  gtk_window_get_size(GTK_WINDOW(widget), &w, &h);
 
  cr = gdk_cairo_create(widget->window); 
 
  gint i;
  for(x=20, y=20, i=0; i < 6; x+=80, i++) {
      draw(cr, x, w, h, oper[i] );
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 510, 120);
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال نعرض 6 عمليات تركيب على مربعين.

 first = cairo_surface_create_similar(cairo_get_target(cr),
     CAIRO_CONTENT_COLOR_ALPHA, w, h);
 
 second = cairo_surface_create_similar(cairo_get_target(cr),
     CAIRO_CONTENT_COLOR_ALPHA, w, h);

ننشئ سطحين.

 first_cr = cairo_create(first);
 cairo_set_source_rgb(first_cr, 0, 0, 0.4);
 cairo_rectangle(first_cr, x, 20, 50, 50);
 cairo_fill(first_cr);

نرسم مستطيل على السطح.

 cairo_set_operator(first_cr, op);
 cairo_set_source_surface(first_cr, second, 0, 0);
 cairo_paint(first_cr);

نقوم بتطبيق عملية التركيب على السطحين.

 cairo_set_source_surface(cr, first, 0, 0);
 cairo_paint(cr);

اخيرانقوم بالرسم على النافذة.

 cairo_operator_t oper[] = {
   CAIRO_OPERATOR_DEST_OVER, 
   CAIRO_OPERATOR_DEST_IN, 
   CAIRO_OPERATOR_OUT,
   CAIRO_OPERATOR_ADD, 
   CAIRO_OPERATOR_ATOP,
   CAIRO_OPERATOR_DEST_ATOP,
 };

فى مثالنا استخدمنا هذه الستة عمليات تركيب

In our example, we use these six compositing operations.

Compositing operations

Clipping and masking

فى هذه الجزئية سنتحدث عن ال clipping -القصر- وال masking -التقنيع- In this part of the Cairo tutorial, we will talk about clipping and masking.

Clipping

ال clipping هى عملية قصر للرسم فى منطقة محددة ويستخدم لأسباب متعلقة بالكفاءة ولإنشاء تأثيرات مثيرة.

–سنستخدم كلمة قصاصة للتعبير عن clip

تقصيص صورة

فى المثال سنوضح كيف نقوم بعمل تقصيص لصورة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
cairo_surface_t *image;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  static gint pos_x = 128;
  static gint pos_y = 128;
  gint radius = 40;  
 
  static gint delta[] = { 3, 3 };
 
  cr = gdk_cairo_create(widget->window);
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
 
  if (pos_x < 0 + radius) {
      delta[0] = rand() % 4 + 5;
  } else if (pos_x > width - radius) {
      delta[0] = -(rand() % 4 + 5);
  }
 
  if (pos_y < 0 + radius) {
      delta[1] = rand() % 4 + 5;
  } else if (pos_y > height - radius) {
      delta[1] = -(rand() % 4 + 5);
  }
 
  pos_x += delta[0];
  pos_y += delta[1];
 
  cairo_set_source_surface(cr, image, 1, 1);
  cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
  cairo_clip(cr);
  cairo_paint(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
  gint width, height;  
 
  image = cairo_image_surface_create_from_png("turnacastle.png");
  width = cairo_image_surface_get_width(image);
  height = cairo_image_surface_get_height(image); 
 
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), width+2, height+2); 
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
  g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

فى المثال سنقوم بعمل قصاصة لصورة. دائرة تتحرك على الشاشة وتعرض الجزء الظاهر تحتها كأننا ننظر من ثقب.

 if (pos_x < 0 + radius) {
     delta[0] = rand() % 4 + 5;
 } else if (pos_x > width - radius) {
     delta[0] = -(rand() % 4 + 5);
 }

اذا مرت الدائرة بالجانب الأيسر او الأيمن للنافذة يتم تغيير اتجاة حركة الدائرة عشوائيا ونفس الشئ للجانب الأعلى والأسفل.

 cairo_set_source_surface(cr, image, 1, 1);
 cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);

هنا نرسم الصورة والدائرة -لاحظ اننا لانرسم على النافذة فى هذه اللحظة.. فقط فى الذاكرة.

 cairo_clip(cr);

الدالة cairo_clip تقوم بتقصيص منطقة- منقطة القصاصة هى المسار الحالى المستخدم اللذى انشئ بواسطة الدالة cairo_arc

 cairo_paint(cr);

The cairo_paint() paints the current source everywhere within the current clip region.

Clipping image

تقصيص مستطيل

المثال التالى مستوحى من مثال وجدته فى امثلة Java2D

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cr = gdk_cairo_create(widget->window);
 
  static gboolean xdirection = TRUE;
  static gint counter = 0;
 
  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
 
  static gdouble rotate = 0;
 
  static gint bigx = 20;
  static gint bigy = 200;
  static gint delta = 1;
 
  counter += 1;  
 
 
  if (bigx > width) {
      xdirection = FALSE;
      delta = -delta;
      bigx = width;
  }
 
  if (bigx < 1) {
      bigx = 1;
      delta = -delta; 
  }
 
  if (bigy > height) {
      xdirection = TRUE;
      delta = -delta;
      bigy = height;
  }
 
  if (bigy < 1) {
      delta = -delta; 
      bigy = 1;
  }
 
  if (xdirection) {
      bigx += delta;
  } else {
      bigy += delta;
  }
 
  cairo_translate(cr, width / 2, height /2);
 
  cairo_rectangle(cr, -bigx/2, -bigy/2, bigx-2, bigy-2);
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width(cr, 1);  
  cairo_stroke(cr);
 
  cairo_rotate(cr, rotate);
  rotate += 0.01;
 
  cairo_rectangle(cr, -50, -25, 100, 50);
  cairo_stroke(cr);
 
  GdkRectangle bigrect;
  GdkRectangle rect;
  GdkRectangle intersect;
 
  bigrect.x = -bigx/2;
  bigrect.y = -bigy/2;
  bigrect.width = bigx -2;
  bigrect.height = bigy -2;
 
  rect.x = -50;
  rect.y = -25;
  rect.width = 100;
  rect.height = 50;
 
  gdk_rectangle_intersect(&bigrect, &rect, &intersect);
  cairo_rectangle(cr, intersect.x, intersect.y, intersect.width, intersect.height);
  cairo_fill(cr); 
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200); 
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
  g_timeout_add(5, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_main();
 
  return 0;
}

فى المثال لدينا مستطيلين (واحد كبير واخر يدور- الكبير يتقلص وينمو بصورة مستمرة. والصغير بدور. هنا نقوم بتطبيق عملية التقاطع على الإثنين. فيتم تلوين منقطة التقاطع بينهم باللون الأسود. لاحظ ان التقاطع ليس مستطيل تام ولكن للتسهيل سنقوم بتقريب المساحة لمستطيل

 static gboolean xdirection = TRUE;

هذا المتغير يحدد اتجاه حركة المستطيل الكبير.

 if (bigx > width) {
     xdirection = FALSE;
     delta = -delta;
     bigx = width;
 }

المستطيل الكبير بنفس عرض النافذة ونقوم بتغير الإتجاه، فيبدأ تقلص المستطيل فى اتجاه ص

 cairo_rotate(cr, rotate);

الدالة cairo_rotate تدير المستطيل الأصغر.

 GdkRectangle bigrect;
 GdkRectangle rect;
 GdkRectangle intersect;

هنا نقوم بتعريف 3 مستطيلات. مستطيل التقاطع هو ناتج تقاطع المستطيلين.

 gdk_rectangle_intersect(&bigrect, &rect, &intersect);

هنا نقوم بتطبيق عملية التقاطع.

 cairo_rectangle(cr, intersect.x, intersect.y, intersect.width, intersect.height);
 cairo_fill(cr); 

نرسم مساحة التقاطع.

Clipping a rectangle

قناع

قبل ان يتم تطبيق المصدر على السطح يتم ترشيحه اولا، يتم استخدام القناع كمرشح. ويقوم القناع بتحديد اى الأماكن اللتى سيتم تطبيق المصدر عليها.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_surface_t *surface;
  cairo_set_source_rgb(cr, 0, 0, 0);
 
  cr = gdk_cairo_create(widget->window);
  surface = cairo_image_surface_create_from_png("omen.png");
  cairo_mask_surface(cr, surface, 0, 0);
  cairo_fill(cr);
 
  cairo_surface_destroy(surface);
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 305, 100); 
 
  gtk_window_set_title(GTK_WINDOW(window), "mask");
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى هذا المثال السيط نوضح الفكرة الأساسية وراء القناع. يحدد القناع اي الأماكن يتم فيها الرسم

 surface = cairo_image_surface_create_from_png("omen.png");
 cairo_mask_surface(cr, surface, 0, 0);
 cairo_fill(cr);

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

Applying a mask

التحويلات

فى هذه الجزئية سنتخدث عن التحويلات

affine transform “التحول القريب” يتكون من 0 او اكثر من تحول (دوران -rotation- او scaling -توسيع- او shear -قص- ) والترجمة -shift-

عدة تحويلات خطية يمكن ان دمج سويا لمصفوفة واحدة. rotation او الدوران يقوم بتدوير الجسم حول نقطة محددة. scaling او التوسع يقوم بتكيير ابعاد الكائنات. ويكون معامل التوسيع واحد فى جميع الإتجاهات translation او الترجمة هى تحويل يقوم بإزاحة كل النقاط الى مسافة ثابتة فى اتجاة ثابت shear او القص هو تحويل يقوم بإزاحة الكائن بصورة عمودية على محور معين مع قيمة اعلى اعلى فى محور عن الآخر #FIXME: توضيح افضل للقص

An affine transform is composed of zero or more linear transformations (rotation, scaling or shear) and translation (shift). Several linear transformations can be combined into a single matrix. A rotation is a transformation that moves a rigid body around a fixed point. A scaling is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions. A translation is a transformation that moves every point a constant distance in a specified direction. A shear is a transformation that moves an object perpendicular to a given axis, with greater value on one side of the axis than the other.

sources: (wikipedia.org, freedictionary.com)

الترجمة

المثال التالى يوصف ترجمة بسيطة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_translate(cr, 100, 100);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 230); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

المثال يرسم مستطيل، ثم نقوم بعمل ترجمة ونرسم نفس المستطيل مرة اخرى.

 cairo_translate(cr, 100, 100);

cairo_translate() تعدل مصفوفة التحويل عن طريق ترجمة نقطة اصل مساحة المستخدم.. فى حالتنا هذه قمنا بتحويل نقطة الأصل 100 وحدة فى الإتجاهين.

Translate

الدواران

المثال التالى يوضح لنا مفهوم الدوران

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_translate(cr, 150, 100);
  cairo_rotate(cr, M_PI/2);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 230); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

المثال نرسم فيه مستطيل، ونقوم بعمل ترجمة ودوران ثم نرسم نفس المستطيل مرة اخرى

 cairo_translate(cr, 150, 100);
 cairo_rotate(cr, M_PI/2);

اولا نقوم بإزاحة نقطة الأصل للمستخدم ونقوم بتدويرها بمقدار 180درجة -لاحظ ان الزاوية بالتقدير الدائرى-

2 ط = 360

Rotate

Scale

المثال التالى يتعرض لعملية ال scaling -التوسيع-

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_save(cr);
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 30, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_save(cr);
  cairo_translate(cr, 130, 30);
  cairo_scale(cr, 0.7, 0.7);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_save(cr);
  cairo_translate(cr, 220, 30);
  cairo_scale(cr, 1.5, 1.5);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 360, 140); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

هذه المرة نجعل المستطيل الأصلى اصغر ثم اكبر بإستخدام معامل توسيع محدد

 cairo_save(cr);
 ...
 cairo_restore(cr);

We want to perform two scaling operations on the initial rectangle. Therefore, we need to save the initial transformation matrix. This is done by a pair of cairo_save() and cairo_restore() functions.

 cairo_translate(cr, 130, 30);
 cairo_scale(cr, 0.7, 0.7);

ننقل نقطة الأصل ونقوم بالتوسيع بنسبة 0.7

Scale

القص

فى المثال التالى سنقوم بعمل قص shearing

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_matrix_t matrix;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_save(cr);
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 30, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_save(cr);
  cairo_translate(cr, 130, 30);
  cairo_matrix_init(&matrix,
      1.0, 0.5,
      0.0, 1.0,
      0.0, 0.0);
 
  cairo_transform (cr, &matrix);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_save(cr);
  cairo_translate(cr, 220, 30);
  cairo_matrix_init(&matrix,
      1.0, 0.0,
      0.7, 1.0,
      0.0, 0.0);
 
  cairo_transform(cr, &matrix);
 
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 360, 140); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

In this code example, we perform two shear transformations. For a shear transformation, we do not have a special function. We must use matrices.

 cairo_matrix_t matrix;

cairo_matrix_t هى هيكل يحمل تحول قريب #FIXME: اعادة صياغة

The cairo_matrix_t is a structure that holds an affine transformation.

 cairo_matrix_init(&matrix,
    1.0, 0.5,
    0.0, 1.0,
    0.0, 0.0);
 
 cairo_transform (cr, &matrix);

هذا التحول يقص قيم ص بمقدار 0.5 من قيم س

 cairo_matrix_init(&matrix,
     1.0, 0.0,
     0.7, 1.0,
     0.0, 0.0);
 
 cairo_transform(cr, &matrix);

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

And this transformation multiplies the x value of each coordinate by 0.7 of the y.

Shear

Ellipses

فى المثال التالى سننشئ شكل معقد عبر الدوران ومجموعة قطاعات.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height); 
 
  cairo_set_line_width(cr, 0.5);
  cairo_translate(cr, width/2, height/2);
  cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
  cairo_stroke(cr);
 
  gint i;
 
  cairo_save(cr);
  for ( i = 0; i < 36; i++) {
      cairo_rotate(cr, i*M_PI/36);
      cairo_scale(cr, 0.3, 1);
      cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
      cairo_restore(cr);
      cairo_stroke(cr);
      cairo_save(cr);
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); 
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}
 cairo_translate(cr, width/2, height/2);
 cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
 cairo_stroke(cr);

فى منتصف النافذة ننشئ دائرة In the middle of the GTK+ window, we create a circle. This will be a bounding circle for our ellipses.

 cairo_save(cr);
 for ( i = 0; i < 36; i++) {
     cairo_rotate(cr, i*M_PI/36);
     cairo_scale(cr, 0.3, 1);
     cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
     cairo_restore(cr);
     cairo_stroke(cr);
     cairo_save(cr);
 }

ننشئ 36 قطع ناقص على طول الدائرة ونقوم بتدويرهم لننشئ شكل مثير جدا :)

Ellipse rotate

نجمة

المثال التالى يعرض تدوير وتوسيع نجمة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;math.h&gt;
 
int points[11][2] = { 
    { 0, 85 }, 
    { 75, 75 }, 
    { 100, 10 }, 
    { 125, 75 }, 
    { 200, 85 },
    { 150, 125 }, 
    { 160, 190 },
    { 100, 150 }, 
    { 40, 190 },
    { 50, 125 },
    { 0, 85 } 
};
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  static gdouble angle = 0;
  static gdouble scale = 1;
  static gdouble delta = 0.01;
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_set_source_rgb(cr, 0, 0.44, 0.7);
  cairo_set_line_width(cr, 1);
 
  cairo_translate(cr, width / 2, height / 2 );
  cairo_rotate(cr, angle);
  cairo_scale(cr, scale, scale);
 
  gint i;
 
  for ( i = 0; i < 10; i++ ) {
      cairo_line_to(cr, points[i][0], points[i][1]);
  }
 
  cairo_close_path(cr);
  cairo_fill(cr);
  cairo_stroke(cr);
 
  if ( scale < 0.01 ) {
      delta = -delta;
  } else if (scale > 0.99) {
      delta = -delta;
  }
 
  scale += delta;
  angle += 0.01;
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
 
int main(int argc, char *argv[])
{
 
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_title(GTK_WINDOW(window), "star");
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  g_timeout_add(10, (GSourceFunc) time_handler, (gpointer) window);  
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

فى المثال ننشئ كائن نجمة، ونقوم بترجمته وتدويره ثم توسيعه.

 cairo_translate(cr, width / 2, height / 2 );
 cairo_rotate(cr, angle);
 cairo_scale(cr, scale, scale 

نقوم بإزاحة النجمة الى منتصف النافذة وتدويرها وتوسيعها.

  for ( i = 0; i < 10; i++ ) {
      cairo_line_to(cr, points[i][0], points[i][1]);
  }
 
  cairo_close_path(cr);
  cairo_fill(cr);
  cairo_stroke(cr);

هنا نرسم كائن النجمة.

 if ( scale < 0.01 ) {
     delta = -delta;
 } else if (scale > 0.99) {
     delta = -delta;
 }

هذه الأسطر تتحكم فى نمو او تقلص النجمة.

Star

النص فى كايرو

فى هذه الجزئية سنتعامل مع النصوص.

Soulmate

فى التالى سنرسم بعض النص على النافذة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); 
 
  cairo_select_font_face(cr, "Purisa",
      CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_BOLD);
 
  cairo_set_font_size(cr, 13);
 
  cairo_move_to(cr, 20, 30);
  cairo_show_text(cr, "Most relationships seem so transitory");  
  cairo_move_to(cr, 20, 60);
  cairo_show_text(cr, "They're all good but not the permanent one");
 
  cairo_move_to(cr, 20, 120);
  cairo_show_text(cr, "Who doesn't long for someone to hold");
 
  cairo_move_to(cr, 20, 150);
  cairo_show_text(cr, "Who knows how to love you without being told");
  cairo_move_to(cr, 20, 180);
  cairo_show_text(cr, "Somebody tell me why I'm on my own");
  cairo_move_to(cr, 20, 210);
  cairo_show_text(cr, "If there's a soulmate for everyone");
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
 
int main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 420, 250); 
  gtk_window_set_title(GTK_WINDOW(window), "Soulmate");
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

هنا عرضنا جزء كلمات اغنية soulmate ل Natasha Bedingfields

 cairo_select_font_face(cr, "Purisa",
    CAIRO_FONT_SLANT_NORMAL,
    CAIRO_FONT_WEIGHT_BOLD);

هنا حددنا الخط واستخدمنا Pursia bold

 cairo_set_font_size(cr, 13);

نحدد حجم الخط

 cairo_move_to(cr, 20, 30);
 cairo_show_text(cr, "Most relationships seem so transitory"); 

ننتقل الى النقطة حيث نرسم النص بإستخدام cairo_show_text()

Soulmate

حرف حرف

فى هذا التأثير، سنعرض النص حرف حرف مع بعض التأخير.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
gpointer text[7] = { "Z", "e", "t", "C", "o", "d", "e" };
gboolean timer = TRUE;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_text_extents_t extents;
  static gint count = 0;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_select_font_face(cr, "Courier",
      CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_BOLD);
 
  cairo_set_font_size(cr, 35);	
  cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); 
 
  gint i;
  gint x = 0;
 
  for (i = 0; i < count; i++) {
      cairo_text_extents(cr, text[i], &extents);
      x += extents.width + 2;
      cairo_move_to(cr, x + 30, 50);
      cairo_show_text(cr, text[i]);  
  }
 
  count++;
 
  if (count == 8) {
      timer = FALSE;
      count = 0;
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
 
int main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 300, 90); 
  gtk_window_set_title(GTK_WINDOW(window), "ZetCode");
  gtk_widget_set_app_paintable(window, TRUE);
 
  g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_widget_show_all(window);
 
 
  gtk_main();
 
  return 0;
}

فى هذا المثال، سنقوم برسم النص “ZetCode” على النافذة حرف حرف مع بعض التأخير.

 gpointer text[7] = { "Z", "e", "t", "C", "o", "d", "e" };

ننشئ مصفوفة من السلاسل الحرفية

 cairo_select_font_face(cr, "Courier",
    CAIRO_FONT_SLANT_NORMAL,
    CAIRO_FONT_WEIGHT_BOLD);

نختار الخط Courier

 for (i = 0; i < count; i++) {
    cairo_text_extents(cr, text[i], &extents);
    x += extents.width + 2;
    cairo_move_to(cr, x + 30, 50);
    cairo_show_text(cr, text[i]);  
 }

نرسم النص حرف حرف. extents.width تعطينا العرض للحرف الحالى.

ZetCode

Puff

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

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
gpointer text[7] = { "Z", "e", "t", "C", "o", "d", "e" };
gboolean timer = TRUE;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_text_extents_t extents;
 
  static gdouble alpha = 1.0;
  static gdouble size = 1;
 
 
  gint x = widget->allocation.width / 2;
  gint y = widget->allocation.height / 2;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_set_source_rgb(cr, 0.5, 0, 0); 
  cairo_paint(cr);
 
  cairo_select_font_face(cr, "Courier",
      CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_BOLD);
 
  size += 0.8;
 
  if (size > 20) {
      alpha -= 0.01;
  }
 
  cairo_set_font_size(cr, size);
 
  cairo_set_source_rgb(cr, 1, 1, 1); 
 
  cairo_text_extents(cr, "ZetCode", &extents);
  cairo_move_to(cr, x - extents.width/2, y);
  cairo_text_path(cr, "ZetCode");
  cairo_clip(cr);
  cairo_stroke(cr);
  cairo_paint_with_alpha(cr, alpha);
 
  if (alpha <= 0) {
      timer = FALSE;
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
 
  return TRUE;
}
 
 
int main (int argc, char *argv[])
{
  GtkWidget *window;
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 350, 200); 
  gtk_window_set_title(GTK_WINDOW(window), "puff");
  gtk_widget_set_app_paintable(window, TRUE);
 
  g_timeout_add(14, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

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

 gint x = widget->allocation.width / 2;
 gint y = widget->allocation.height / 2;

Coordinates of the middle point.

 cairo_set_source_rgb(cr, 0.5, 0, 0); 
 cairo_paint(cr);

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

 size += 0.8;

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

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

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

 cairo_text_extents(cr, "ZetCode", &extents);

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

 cairo_move_to(cr, x - extents.width/2, y);

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

 cairo_text_path(cr, "ZetCode");
 cairo_clip(cr);

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

 cairo_stroke(cr);
 cairo_paint_with_alpha(cr, alpha);

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

Puff

الصور فى كايرو

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

عرض صورة

فى اول مثال سنقوم بعرض صورة

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
cairo_surface_t *image;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create (widget->window);
 
  cairo_set_source_surface(cr, image, 10, 10);
  cairo_paint(cr);
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  image = cairo_image_surface_create_from_png("plaveckycastle.png");
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(window, "expose-event",
      G_CALLBACK (on_expose_event), NULL);
  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 320, 250); 
  gtk_widget_set_app_paintable(window, TRUE);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

المثال يعرض صورة 300×225 .. تستطيع تحميلها من <a href=“../images/plaveckycastle.png”>هنا</a>. هذه قلعة medieval فى Palvecke Podharadie فى غرب سلوفاكيا

 image = cairo_image_surface_create_from_png("plaveckycastle.png");

ننشئ سطح صورة من صورة PNG ولأسباب متعلفة بالكفاءة يتم استدعائها فى الدالة الرئيسية

 cairo_set_source_surface(cr, image, 10, 10);

نحدد المصدر للرسم من سطح الصورة المنشئ سابقا

 cairo_paint(cr);

نرسم على النافذة

Image

تأثير الستارة

فى هذا المثال سنقوم بتأثير كاننا نرفع ستارة من على صورة من اعلى لأسفل حتى تظهر الصورة كاملة.

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
gboolean timer = TRUE;
cairo_surface_t *image;
 
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_t *ic;
 
  cairo_surface_t *surface;
 
  static gdouble angle = 0;
  static gint image_width = 0;
  static gint image_height = 0;
 
  static gint w = 0;
  static gint h = 0;
 
  cr = gdk_cairo_create(widget->window);
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height); 
 
  image_width = cairo_image_surface_get_width(image);
  image_height = cairo_image_surface_get_height(image);
  w = image_width;
 
  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_width, image_height);
  ic = cairo_create(surface);
 
  cairo_rectangle(ic, 0, 0, w, h);
  cairo_fill(ic);
 
  h += 1;
  if ( h == image_height) timer = FALSE;
 
  cairo_set_source_surface(cr, image, 10, 10);
  cairo_mask_surface(cr, surface, 10, 10);
 
  cairo_surface_destroy(surface);
 
  cairo_destroy(cr);
  cairo_destroy(ic);
  return FALSE;
}
 
static gboolean
time_handler(GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  image = cairo_image_surface_create_from_png("plaveckycastle.png");
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 325, 250); 
  gtk_window_set_title(GTK_WINDOW(window), "blind down");
 
  gtk_widget_set_app_paintable(window, TRUE);
  g_timeout_add(15, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

الفكرة وراء هذا التأثير بسيطة جدا اذا كانت الصورة بإرتفاع م بكسل فإننا نقوم برسم الخطوط من 0 الى م بإرتفاع 1 بكسل حتى تظهر الصورة كاملة.

 cairo_t *cr;
 cairo_t *ic;

هنا نقوم بتعريف سياقين كايرو، واحد ليربط بالنافذة والآخر بالصورة.

 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_width, image_height);
 ic = cairo_create(surface);

ننشئ سطح صورة ومنه ننشئ سياق كايرو.

 cairo_rectangle(ic, 0, 0, w, h);
 cairo_fill(ic);

هنا نرسم ابتدائيا مستطيل فارغ وفى كل دورة يزداد 1 بكسل والصورة المنشئة بهذه الطريقة سوق تخدم كقناع لاحقا.

  h += 1;
  if ( h == image_height) timer = FALSE;

هنا نوقف المؤقت عندما يتم رسم الصورة كاملة -الإرتفاع الحالى = ارتفاع الصورة.

 cairo_set_source_surface(cr, image, 10, 10);
 cairo_mask_surface(cr, surface, 10, 10);

صورة القلعة يتم استخدامها كمصدر للرسم والدالة cairo_mask_surface ترسم المصدر الحالى عبر قناة الفا للسطح كقناع.

تأثير الطيف

لم استطع ان افكر فى اسم افضل من “تأثير الطيف”. ربما يتذكر بعضكم الأيام الخوالى مع حاسوب ZX Spectrum. فى المثال التالى سنقوم بتحميل صورة تظهر تدريجيا على الشاشة.

#FIXME :تنفيذ المثال

#include &lt;cairo.h&gt;
#include &lt;gtk/gtk.h&gt;
 
 
gboolean timer = TRUE;
cairo_surface_t *image;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cairo_t *ic;
 
  cairo_surface_t *surface;
 
  static gdouble angle = 0;
  static gint w = 0;
  static gint h = 0;
 
  static gint image_width = 0;
  static gint image_height = 0;
 
  static gint count = 0;
 
  cr = gdk_cairo_create(widget->window);
 
  gint width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height); 
 
  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, image_width, image_height);  
 
  image_width = cairo_image_surface_get_width(image);
  image_height = cairo_image_surface_get_height(image);
  w = image_width;  
 
  ic = cairo_create(surface);
 
  gint i, j;
  for (i = 0; i <= image_height; i+=7) {
      for (j=0 ; j < count; j++) {
          cairo_move_to(ic, 0, i+j);
          cairo_line_to(ic, w, i+j);
      }
  }
 
  count++;
  if ( count == 8) timer = FALSE;
 
  cairo_stroke(ic);
 
  cairo_set_source_surface(cr, image, 10, 10);
  cairo_mask_surface(cr, surface, 10, 10);
 
  cairo_surface_destroy(surface);
 
  cairo_destroy(cr);
  cairo_destroy(ic);
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int main(int argc, char *argv[])
{
  GtkWidget *window;
 
  image = cairo_image_surface_create_from_png("plaveckycastle.png");
 
  gtk_init(&argc, &argv);
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
  g_signal_connect(G_OBJECT(window), "expose-event",
      G_CALLBACK(on_expose_event), NULL);
  g_signal_connect(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);
 
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 325, 250); 
 
  gtk_widget_set_app_paintable(window, TRUE);
  g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window);
 
  gtk_widget_show_all(window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

المثال مشابه للمثال السابق ولكن هنا نقوم بتقسيم الصورة الى 8 خطوط كل قطعة يزداد حجمها بمقدار 1 بكسل وتعمل كقناع لعرض صورة القلعة

In most details, the example is similar to the previous one. This time, we divide the image into n parts consisting of 8 lines. Each cycle each part of the image will get bigger by one pixel. The created image will serve again as a mask for displaying the image of the castle.

 gint i, j;
 for (i = 0; i <= image_height; i+=7) {
     for (j=0 ; j < count; j++) {
         cairo_move_to(ic, 0, i+j);
         cairo_line_to(ic, w, i+j);
     }
 }

هنا المنطق وراء هذا المثال، نرسم الخطوط تدريجيا الى كل جزء من الأجزاء

Spectrum

ودجت مخصص

فى هذه الجزئية سنقوم بعمل ودجت مخصص بإستخدام كايرو.

ودجت CPU

فى المثال سننشئ ودجت لل CPU

/* cpu.h */
 
#ifndef __CPU_H
#define __CPU_H
 
#include &lt;gtk/gtk.h&gt;
#include &lt;cairo.h&gt;
 
G_BEGIN_DECLS
 
 
#define GTK_CPU(obj) GTK_CHECK_CAST(obj, gtk_cpu_get_type (), GtkCpu)
#define GTK_CPU_CLASS(klass) GTK_CHECK_CLASS_CAST(klass, gtk_cpu_get_type(), GtkCpuClass)
#define GTK_IS_CPU(obj) GTK_CHECK_TYPE(obj, gtk_cpu_get_type())
 
 
typedef struct _GtkCpu GtkCpu;
typedef struct _GtkCpuClass GtkCpuClass;
 
 
struct _GtkCpu {
  GtkWidget widget;
 
  gint sel;
};
 
struct _GtkCpuClass {
  GtkWidgetClass parent_class;
};
 
 
GtkType gtk_cpu_get_type(void);
void gtk_cpu_set_sel(GtkCpu *cpu, gint sel);
GtkWidget * gtk_cpu_new();
 
 
G_END_DECLS
 
#endif /* __CPU_H */
/* cpu.c */
 
#include "cpu.h"
 
 
static void gtk_cpu_class_init(GtkCpuClass *klass);
static void gtk_cpu_init(GtkCpu *cpu);
static void gtk_cpu_size_request(GtkWidget *widget,
    GtkRequisition *requisition);
static void gtk_cpu_size_allocate(GtkWidget *widget,
    GtkAllocation *allocation);
static void gtk_cpu_realize(GtkWidget *widget);
static gboolean gtk_cpu_expose(GtkWidget *widget,
    GdkEventExpose *event);
static void gtk_cpu_paint(GtkWidget *widget);
static void gtk_cpu_destroy(GtkObject *object);
 
 
GtkType
gtk_cpu_get_type(void)
{
  static GtkType gtk_cpu_type = 0;
 
 
  if (!gtk_cpu_type) {
      static const GtkTypeInfo gtk_cpu_info = {
          "GtkCpu",
          sizeof(GtkCpu),
          sizeof(GtkCpuClass),
          (GtkClassInitFunc) gtk_cpu_class_init,
          (GtkObjectInitFunc) gtk_cpu_init,
          NULL,
          NULL,
          (GtkClassInitFunc) NULL
      };
      gtk_cpu_type = gtk_type_unique(GTK_TYPE_WIDGET, &gtk_cpu_info);
  }
 
 
  return gtk_cpu_type;
}
 
void
gtk_cpu_set_state(GtkCpu *cpu, gint num)
{
   cpu->sel = num;
   gtk_cpu_paint(GTK_WIDGET(cpu));
}
 
 
GtkWidget * gtk_cpu_new()
{
   return GTK_WIDGET(gtk_type_new(gtk_cpu_get_type()));
}
 
 
static void
gtk_cpu_class_init(GtkCpuClass *klass)
{
  GtkWidgetClass *widget_class;
  GtkObjectClass *object_class;
 
 
  widget_class = (GtkWidgetClass *) klass;
  object_class = (GtkObjectClass *) klass;
 
  widget_class->realize = gtk_cpu_realize;
  widget_class->size_request = gtk_cpu_size_request;
  widget_class->size_allocate = gtk_cpu_size_allocate;
  widget_class->expose_event = gtk_cpu_expose;
 
  object_class->destroy = gtk_cpu_destroy;
}
 
 
static void
gtk_cpu_init(GtkCpu *cpu)
{
   cpu->sel = 0;
}
 
 
static void
gtk_cpu_size_request(GtkWidget *widget,
    GtkRequisition *requisition)
{
  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));
  g_return_if_fail(requisition != NULL);
 
  requisition->width = 80;
  requisition->height = 100;
}
 
 
static void
gtk_cpu_size_allocate(GtkWidget *widget,
    GtkAllocation *allocation)
{
  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));
  g_return_if_fail(allocation != NULL);
 
  widget->allocation = *allocation;
 
  if (GTK_WIDGET_REALIZED(widget)) {
     gdk_window_move_resize(
         widget->window,
         allocation->x, allocation->y,
         allocation->width, allocation->height
     );
   }
}
 
 
static void
gtk_cpu_realize(GtkWidget *widget)
{
  GdkWindowAttr attributes;
  guint attributes_mask;
 
  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));
 
  GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
 
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = 80;
  attributes.height = 100;
 
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
 
  attributes_mask = GDK_WA_X | GDK_WA_Y;
 
  widget->window = gdk_window_new(
     gtk_widget_get_parent_window (widget),
     & attributes, attributes_mask
  );
 
  gdk_window_set_user_data(widget->window, widget);
 
  widget->style = gtk_style_attach(widget->style, widget->window);
  gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
}
 
 
static gboolean
gtk_cpu_expose(GtkWidget *widget,
    GdkEventExpose *event)
{
  g_return_val_if_fail(widget != NULL, FALSE);
  g_return_val_if_fail(GTK_IS_CPU(widget), FALSE);
  g_return_val_if_fail(event != NULL, FALSE);
 
  gtk_cpu_paint(widget);
 
  return FALSE;
}
 
 
static void
gtk_cpu_paint(GtkWidget *widget)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_translate(cr, 0, 7);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_paint(cr);
 
  gint pos = GTK_CPU(widget)->sel;
  gint rect = pos / 5;
 
  cairo_set_source_rgb(cr, 0.2, 0.4, 0);
 
  gint i;
  for ( i = 1; i <= 20; i++) {
      if (i > 20 - rect) {
          cairo_set_source_rgb(cr, 0.6, 1.0, 0);
      } else {
          cairo_set_source_rgb(cr, 0.2, 0.4, 0);
      }
      cairo_rectangle(cr, 8, i*4, 30, 3);
      cairo_rectangle(cr, 42, i*4, 30, 3);
      cairo_fill(cr);
  }
 
  cairo_destroy(cr);
}
 
 
static void
gtk_cpu_destroy(GtkObject *object)
{
  GtkCpu *cpu;
  GtkCpuClass *klass;
 
  g_return_if_fail(object != NULL);
  g_return_if_fail(GTK_IS_CPU(object));
 
  cpu = GTK_CPU(object);
 
  klass = gtk_type_class(gtk_widget_get_type());
 
  if (GTK_OBJECT_CLASS(klass)->destroy) {
     (* GTK_OBJECT_CLASS(klass)->destroy) (object);
  }
}
/* main.c */
 
#include "cpu.h"
 
 
static void set_value(GtkWidget * widget, gpointer data)
{
  GdkRegion *region;
 
  GtkRange *range = (GtkRange *) widget;
  GtkWidget *cpu = (GtkWidget *) data;
  GTK_CPU(cpu)->sel = gtk_range_get_value(range);
 
  region = gdk_drawable_get_clip_region(cpu->window);
  gdk_window_invalidate_region(cpu->window, region, TRUE);
  gdk_window_process_updates(cpu->window, TRUE);
}
 
 
int main (int argc, char ** argv)
{
  GtkWidget *window;
  GtkWidget *cpu;
  GtkWidget *fixed;
  GtkWidget *scale;
 
  gtk_init(&argc, &argv);
 
 
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "CPU widget");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 180);
 
 
  g_signal_connect(G_OBJECT(window), "destroy", 
       G_CALLBACK(gtk_main_quit), NULL);
 
  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);
 
  cpu = gtk_cpu_new();
  gtk_fixed_put(GTK_FIXED(fixed), cpu, 30, 40);
 
 
  scale = gtk_vscale_new_with_range(0.0, 100.0, 1.0);
  gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
  gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
  gtk_widget_set_size_request(scale, 50, 120);
  gtk_fixed_put(GTK_FIXED(fixed), scale, 130, 20);
 
  g_signal_connect(G_OBJECT(scale), "value_changed", G_CALLBACK(set_value), (gpointer) cpu);
 
  gtk_widget_show(cpu);
  gtk_widget_show(fixed);
  gtk_widget_show_all(window);
  gtk_main();
 
  return 0;
}

‪ ‪ ‪

 ودجت ال CPU هو **GtkWidget** اللذى سنرسمه من خلال كايرو، نرسم خلفية سوداء و 40 مستطيل، هذه المستطيلات ترسم بلونين الأخضر الغامق والفاقع
 وودجت **GtkVScale** يتحكم عدد المستطيلات الفاقعة المرسومة على الودجت
 
 المثال ربما يبدو صعبا من الوهلة الأولى، ولكنه ليس صعبا على الإطلاق.
 معظم هذه الأكواد نمطى ومتكرر عند انشاء اى ويدجت جديد.
 يتم الرسم فى الدالة **gtk_cpu_paint()**
  ‬  ‬  ‬ 
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  cairo_translate(cr, 0, 7);
 
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_paint(cr);

كالمعتاد، ننشئ سياق كايرو ونزيج نقطة الأصل 7 وحدات ونقوم برسم الخلفية باللون الأسود.

 gint pos = GTK_CPU(widget)->sel;
 gint rect = pos / 5;

هنا نحصل على sel -الرقم الذى حصلنا عليه من ويدجت المقياس -scale- المنزلق يحوى 100 رقم والمعامل rect يقوم بالتحويل من قيم المنزلق الى المستطيل -يتم رسمها بالأخضر الفاقع-

 gint i;
 for ( i = 1; i <= 20; i++) {
     if (i > 20 - rect) {
         cairo_set_source_rgb(cr, 0.6, 1.0, 0);
     } else {
         cairo_set_source_rgb(cr, 0.2, 0.4, 0);
     }
     cairo_rectangle(cr, 8, i*4, 30, 3);
     cairo_rectangle(cr, 42, i*4, 30, 3);
     cairo_fill(cr);
 }

بناءا على رقم ال rect نرسم 40 مستطيل باللونين الأخضر الغامق والفاقع، تذكر اننا نقوم برسم هذه المستطيلات من اعلى لأسفل.

 GtkRange *range = (GtkRange *) widget;
 GtkWidget *cpu = (GtkWidget *) data;
 GTK_CPU(cpu)->sel = gtk_range_get_value(range);

استدعاء الدالة set_value نحصل على مرجع -reference - لويدجت ال CPU ونحدد قيمة “sel” للقيمة الحالية -المختارة على ويدجت المقياس-.

 GdkRegion *region;
 ...
 region = gdk_drawable_get_clip_region(cpu->window);
 gdk_window_invalidate_region(cpu->window, region, TRUE);
 gdk_window_process_updates(cpu->window, TRUE);

هذا الكود يلغى مفعولة النافذة لويدجت ال CPU فقتوم بإعادة رسم نفسها.

cpu widget

docs/cairographics.txt · آخر تعديل: 2015/04/23 03:19 بواسطة 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki