The Cairo graphics tutorial -------Clipping and masking

转载 2012年03月30日 08:21:48

In this part of the Cairo tutorial, we will talk about clipping and masking.



Clipping

Clipping is restricting of drawing to a certain area. This is done for effeciency reasons and to create interesting effects.

Clipping image

In the following example we will be clipping an image.

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

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;
}

In this example, we will clip an image. A circle is moving on the screen and showing a part of the underlying image. This is as if we looked through a hole.

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

If the circle hits the left or the right side of the window, the direction of the circle movement changes randomly.Same for the top and bottom sides.

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

Here we draw the image and a circle. Notice that we do not draw onto the window at the moment, but only in memory.

 cairo_clip(cr);

The cairo_clip() sets a clipping region. The clipping region is the current path used. The current path was created by the cairo_arc() function call.

 cairo_paint(cr);

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


Clipping image
Figure: Clipping image

Clipping a rectangle

The following example is inspired by a similar example, that I found in the Java2D demo.

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


static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
  cr = gdk_cairo_create(widget->window);

  static gboolean xdirection = TRUE;

  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;


  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;
}

In this example, we have two rectangles. A big one and a rotating one. The big rectangle continuosly shrinks and grows. The smaller one is rotating. We apply a intersect set operation on both rectangles. The area that is inside both rectangles is painted in black color. Note, that the intersection is not a perfect rectangle. To make it simpler, we approximate the area to a rectangle.

 static gboolean xdirection = TRUE;

This variable determines the direction, in which the big rectangle is moving.

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

If the big rectangle is as big as the width of the window, we change the direction. The rectangle begins shrinking in the y direction.

 cairo_rotate(cr, rotate);

The cairo_rotate() rotates the smaller rectangle.

 GdkRectangle bigrect;
 GdkRectangle rect;
 GdkRectangle intersect;

Here we define three rectangular areas. The intersect rectangle will be the intersection of our two rectangles.

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

Here we apply the intersection operation.

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

We paint the area of the intersect rectangle.


Clipping a rectangle
Figure: Clipping a rectangle

Mask

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.

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


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;
}

This small example clearly shows the basic idea behind the mask. The mask determines where to paint and where not to paint.

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

We use an image as a mask, thus displaying it on the window.


Applying a mask
Figure: Applying a mask

This chapter was about clipping and masking in Cairo.



相关文章推荐

The Cairo graphics tutorial -------Shapes and fills in Cairo

In this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fill...

html5 svg 第九章 裁剪和屏蔽 Clipping and Masking

有时候,你不希望看到整个画面,例如,你可能想画一幅画,就好像是看到通过双筒望远镜或锁孔目镜或锁孔的边界之外的一切都将是无形的。或者,您可能要设置所显示的图像,仿佛透过半透明的窗帘观看的心情。SVG实现...
  • stary1
  • stary1
  • 2013年08月02日 22:33
  • 2543

The Cairo graphics tutorial -------Transformations

In this part of the Cairo graphics programming tutorial, we will talk about transformations. ...

The Cairo graphics tutorial -------Transparency

In this part of the Cairo C API tutorial, we will talk about transparency. We will provide some basi...

The Cairo graphics tutorial -------Custom GTK widget

In this part of the Cairo graphics tutorial, we will create a custom GTK widget, where we will use t...

The Cairo graphics tutorial -------Cairo backends

The Cairo library supports various backends. In this section of the Cairo graphics tutorial, we will...

The Cairo graphics tutorial -------Basic drawing in Cairo

In this part of the Cairo graphics tutorial, we will draw some basic primitives. We will draw simple...

The Cairo graphics tutorial -------Text in Cairo

In this part of the Cairo graphics tutorial, we will work with text. Soulmate In the first...

The Cairo graphics tutorial -------Cairo definitions

Cairo definitions In this part of the Cairo graphics tutorial, we will provide some useful definiti...

Core Graphics Tutorial Part 3: Patterns and Playgrounds

原文链接:Core Graphics Tutorial Part 3: Patterns and Playgrounds 原文作者: Caroline Begbie 译文出自:开发者前线 www.d...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The Cairo graphics tutorial -------Clipping and masking
举报原因:
原因补充:

(最多只允许输入30个字)