GTK+中文社区(gtk.awaysoft.com)

 找回密码
 马上加入

QQ登录

只需一步,快速开始

查看: 4072|回复: 0

[转]Cairo 图形指南 (8) —— 裁剪与遮蔽

[复制链接]
  • TA的每日心情
    奋斗
    2021-11-19 13:15
  • 签到天数: 20 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2011-2-3 02:18:16 | 显示全部楼层 |阅读模式
       

    在这一篇中讲述裁剪(Clipping)与遮蔽(Masking)。
    裁剪
    在下面的示例中,对一幅图像进行裁剪。
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    #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;
    }



    在这一示例中,在窗口中会有一个圆形区域不断移动,并且在该区域显示位于其下的图像,仿佛是通过一个孔洞观看图像。
    ?
    1
    2
    3
    4
    5
    if (pos_x < 0 + radius) {
        delta[0] = rand() % 4 + 5;
    } else if (pos_x > width - radius) {
        delta[0] = -(rand() % 4 + 5);
    }



    当这个圆形区域碰到窗口边界,它的移动方向就会随机改变。
    ?
    1
    2
    cairo_set_source_surface(cr, image, 1, 1);
    cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);



    这里是绘制一幅图像和一个圆。注意:这时,图形尚未绘制到窗口中,它们还在内存里。
    ?
    1
    cairo_clip(cr);



    cairo_clip() 函数设定裁剪域——当前所用的路径,即 cairo_arc() 函数所创建的路径。
    ?
    1
    cairo_paint(cr);



    cairo_paint() 函数绘制当前落入裁剪域中的源。

    裁剪矩形
    下面这个示例是对一个 Java 2D 示例的模拟。
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    #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;
    }



    在这个示例中,绘制了两个矩形,一个是形状大一些的,一个是在旋转的。大点的那个矩形,持续的在进行形状的缩放,小一点的一直在旋转。在两个矩形的运动过程中进行了交集操作,它们的相交区域用黑色区域来绘制。注意:那个相交区域并非恰好是矩形,只是为了简化,将那个区域用矩形近似替代。
    ?
    1
    static gboolean xdirection = TRUE;



    这个变量决定了那个大一些的矩形的运动方向。
    ?
    1
    2
    3
    4
    5
    if (bigx > width) {
        xdirection = FALSE;
        delta = -delta;
        bigx = width;
    }



    如果那个大的矩形,其宽度增长到与窗口的宽度相等时,就开始收缩,同时矩形开始沿 y 方向收缩。
    ?
    1
    cairo_rotate(cr, rotate);



    cairo_rotate() 函数用来旋转那个小一点的矩形。
    ?
    1
    2
    3
    GdkRectangle bigrect;
    GdkRectangle rect;
    GdkRectangle intersect;



    这里定义了三个矩形区域。insersect 是那两个矩形的相交区域。
    ?
    1
    gdk_rectangle_intersect(&bigrect, &rect, &intersect);



    这个函数可完成矩形相交运算。
    ?
    1
    2
    cairo_rectangle(cr, intersect.x, intersect.y, intersect.width, intersect.height);
    cairo_fill(cr);



    绘制相交区域的矩形。


    遮蔽
    因为在源被用于外观之前,首先要被过滤。遮蔽可作为一种过滤器。遮蔽用于决定源的哪部分要被显示,哪部分不可被显示。遮蔽的不透明部分允许将源复制到外观,透明部分则不允许将源复制给外观。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    #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;

      cr = gdk_cairo_create(widget->window);

      cairo_set_source_rgb(cr, 0, 0, 0);

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



    这个小例子清楚的展示了遮蔽的基本思想。
    ?
    1
    2
    3
    surface = cairo_image_surface_create_from_png("omen.png");
    cairo_mask_surface(cr, surface, 0, 0);
    cairo_fill(cr);



    这里,是用了一幅图像作为遮蔽,然后在窗口中显示它。

    原文地址:http://liyanrui.is-programmer.com/2010/4/5/cairo-masking-and-clipping.16579.html


      
    *滑块验证:
    您需要登录后才可以回帖 登录 | 马上加入

    本版积分规则

    申请友链|Archiver|小黑屋|手机版|GTK+中文社区 ( 粤ICP备13080851号 )

    我要啦免费统计

    GMT+8, 2024-4-19 07:35 , Processed in 0.318981 second(s), 7 queries , Redis On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表