在這一篇中講述裁剪(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 #include #include 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 <> delta[0] = rand () % 4 + 5; } else if (pos_x > width - radius) { delta[0] = -( rand () % 4 + 5); } if (pos_y <> 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 <> 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 #include #include 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 <> bigx = 1; delta = -delta; } if (bigy > height) { xdirection = TRUE; delta = -delta; bigy = height; } if (bigy <> 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 #include 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); |
這裡,是用了一幅圖像作為遮蔽,然後在窗口中顯示它。
沒有留言:
張貼留言