Cairo FAQ Clipping and Masking

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
(Новая: 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 an...)
(Удалено по требованию автора...)
 
Строка 1: Строка 1:
-
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.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
</source>
 
-
 
-
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.
 
-
 
-
<source lang="cpp">
 
-
if (pos_x < 0 + radius) {
 
-
    delta[0] = rand() % 4 + 5;
 
-
} else if (pos_x > width - radius) {
 
-
    delta[0] = -(rand() % 4 + 5);
 
-
}
 
-
</source>
 
-
 
-
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.
 
-
 
-
<source lang="cpp">
 
-
cairo_set_source_surface(cr, image, 1, 1);
 
-
cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
 
-
</source>
 
-
 
-
Here we draw the image and a circle. Notice that we do not draw onto the window at the moment, but only in memory.
 
-
 
-
<source lang="cpp">
 
-
cairo_clip(cr);
 
-
</source>
 
-
 
-
The <b>cairo_clip()</b> sets a clipping region. The clipping region is the current path used.
 
-
The current path was created by the <b>cairo_arc()</b> function call.
 
-
 
-
<source lang="cpp">
 
-
cairo_paint(cr);
 
-
</source>
 
-
 
-
The <b>cairo_paint()</b> paints the current source everywhere within the current clip region.
 
-
 
-
[[image: cairo_faq_clipimage.png | center]]
 
-
 
-
=== Clipping a rectangle ===
 
-
 
-
The following example is inspired by a similar example, that I found in the Java2D demo.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
  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;
 
-
}
 
-
</source>
 
-
 
-
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.
 
-
 
-
<source lang="cpp">
 
-
static gboolean xdirection = TRUE;
 
-
</source>
 
-
 
-
This variable determines the direction, in which the big rectangle is moving.
 
-
 
-
<source lang="cpp">
 
-
if (bigx > width) {
 
-
    xdirection = FALSE;
 
-
    delta = -delta;
 
-
    bigx = width;
 
-
}
 
-
</source>
 
-
 
-
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.
 
-
 
-
<source lang="cpp">
 
-
cairo_rotate(cr, rotate);
 
-
</source>
 
-
 
-
The <b>cairo_rotate()</b> rotates the smaller rectangle.
 
-
 
-
<source lang="cpp">
 
-
GdkRectangle bigrect;
 
-
GdkRectangle rect;
 
-
GdkRectangle intersect;
 
-
 
-
</source>
 
-
 
-
Here we define three rectangular areas. The <b>intersect</b> rectangle will be the intersection of our two rectangles.
 
-
 
-
<source lang="cpp">
 
-
gdk_rectangle_intersect(&bigrect, &rect, &intersect);
 
-
</source>
 
-
 
-
Here we apply the intersection operation.
 
-
 
-
<source lang="cpp">
 
-
cairo_rectangle(cr, intersect.x, intersect.y, intersect.width, intersect.height);
 
-
cairo_fill(cr);
 
-
</source>
 
-
 
-
We paint the area of the intersect rectangle.
 
-
 
-
[[image: cairo_faq_cliprectangle.png | center]]
 
-
 
-
== 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.
 
-
 
-
<source lang="cpp">
 
-
#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;
 
-
}
 
-
</source>
 
-
 
-
This small example clearly shows the basic idea behind the mask. The mask determines where to paint and where not to paint.
 
-
 
-
<source lang="cpp">
 
-
surface = cairo_image_surface_create_from_png("omen.png");
 
-
cairo_mask_surface(cr, surface, 0, 0);
 
-
cairo_fill(cr);
 
-
</source>
 
-
 
-
We use an image as a mask, thus displaying it on the window.
 
-
 
-
[[image: cairo_faq_mask.png | center]]
 
-
 
-
 
-
[[Категория:GTK+]]
 
-
[[Категория:Cairo]]
 

Текущая версия на 11:30, 7 апреля 2009