| | 23 | /***** Copy functions ****************************************/ |
|---|
| | 24 | /*************************************************************/ |
|---|
| | 25 | /** |
|---|
| | 26 | * gdk_pixbuf_copy_to_surface: |
|---|
| | 27 | * |
|---|
| | 28 | * Copies a rectanguar area in a pixbuf to a cairo surface. This |
|---|
| | 29 | * function should be as fast as possible which I don't know if it is. |
|---|
| | 30 | **/ |
|---|
| | 31 | static void |
|---|
| | 32 | gdk_pixbuf_copy_to_surface (GdkPixbuf *pixbuf, |
|---|
| | 33 | GdkRectangle *rect, |
|---|
| | 34 | cairo_surface_t *surface) |
|---|
| | 35 | { |
|---|
| | 36 | cairo_t *cr = cairo_create (surface); |
|---|
| | 37 | gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); |
|---|
| | 38 | cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); |
|---|
| | 39 | cairo_clip (cr); |
|---|
| | 40 | cairo_paint (cr); |
|---|
| | 41 | cairo_destroy (cr); |
|---|
| | 42 | } |
|---|
| | 43 | |
|---|
| | 44 | /** |
|---|
| | 45 | * cairo_image_surface_copy: |
|---|
| | 46 | * |
|---|
| | 47 | * Copies a rectangular area in a cairo surface on another surface. |
|---|
| | 48 | **/ |
|---|
| | 49 | static void |
|---|
| | 50 | cairo_image_surface_copy (cairo_surface_t *src, |
|---|
| | 51 | GdkRectangle *rect, |
|---|
| | 52 | cairo_surface_t *dst) |
|---|
| | 53 | { |
|---|
| | 54 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface (src); |
|---|
| | 55 | cairo_t *cr = cairo_create (dst); |
|---|
| | 56 | cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); |
|---|
| | 57 | cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); |
|---|
| | 58 | cairo_clip (cr); |
|---|
| | 59 | cairo_set_source (cr, pattern); |
|---|
| | 60 | cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); |
|---|
| | 61 | cairo_paint (cr); |
|---|
| | 62 | cairo_destroy (cr); |
|---|
| | 63 | cairo_pattern_destroy (pattern); |
|---|
| | 64 | } |
|---|
| | 65 | |
|---|
| | 66 | |
|---|
| | 67 | /*************************************************************/ |
|---|
| | 70 | /** |
|---|
| | 71 | * gtk_image_tool_cairo_set_redraw_area: |
|---|
| | 72 | * |
|---|
| | 73 | * Clips the input rectangle against the pixbuf size and sets it as |
|---|
| | 74 | * the new redraw area. |
|---|
| | 75 | **/ |
|---|
| | 76 | static void |
|---|
| | 77 | gtk_image_tool_cairo_set_redraw_area (GtkImageToolCairo *cairo, |
|---|
| | 78 | GdkRectangle *rect) |
|---|
| | 79 | { |
|---|
| | 80 | GdkPixbuf *pixbuf = cairo->pixbuf; |
|---|
| | 81 | GdkRectangle pb_rect = {0, 0, |
|---|
| | 82 | gdk_pixbuf_get_width (pixbuf), |
|---|
| | 83 | gdk_pixbuf_get_height (pixbuf)}; |
|---|
| | 84 | gdk_rectangle_intersect (&pb_rect, rect, rect); |
|---|
| | 85 | cairo->redraw_area = *rect; |
|---|
| | 86 | } |
|---|
| | 87 | |
|---|
| | 88 | /** |
|---|
| | 89 | * gtk_image_tool_cairo_surface_changed: |
|---|
| | 90 | * |
|---|
| | 91 | * Invalidate the cache and force a redraw of the damaged area. I'm |
|---|
| | 92 | * not sure whether the queued redraw area should be padded or not. |
|---|
| | 93 | **/ |
|---|
| | 94 | static void |
|---|
| | 95 | gtk_image_tool_cairo_surface_changed (GtkImageToolCairo *cairo, |
|---|
| | 96 | GdkRectangle *rect) |
|---|
| | 97 | { |
|---|
| | 98 | gdk_pixbuf_draw_cache_invalidate (cairo->cache); |
|---|
| | 99 | GdkRectangle wid_rect; |
|---|
| | 100 | gtk_image_view_image_to_widget_rect (cairo->view, rect, &wid_rect); |
|---|
| | 101 | gtk_widget_queue_draw_area (GTK_WIDGET (cairo->view), |
|---|
| | 102 | wid_rect.x, wid_rect.y, |
|---|
| | 103 | wid_rect.width, wid_rect.height); |
|---|
| | 104 | } |
|---|
| | 105 | |
|---|
| | 106 | /** |
|---|
| | 107 | * gtk_image_tool_cairo_null_surfaces: |
|---|
| | 108 | * |
|---|
| | 109 | * Ensure that the cairo surfaces are destroyed and set to %NULL. |
|---|
| | 110 | **/ |
|---|
| | 111 | static void |
|---|
| | 112 | gtk_image_tool_cairo_null_surfaces (GtkImageToolCairo *cairo) |
|---|
| | 113 | { |
|---|
| | 114 | if (!cairo->image_surface) |
|---|
| | 115 | return; |
|---|
| | 116 | cairo_surface_destroy (cairo->image_surface); |
|---|
| | 117 | cairo_surface_destroy (cairo->draw_surface); |
|---|
| | 118 | cairo->image_surface = NULL; |
|---|
| | 119 | cairo->draw_surface = NULL; |
|---|
| | 120 | } |
|---|
| | 149 | |
|---|
| | 150 | GdkRectangle old_damage_area, new_damage_area; |
|---|
| | 151 | gtk_iimage_cairo_shaper_get_damage_area (cairo->shaper, &old_damage_area); |
|---|
| | 152 | gtk_iimage_cairo_shaper_motion (cairo->shaper, image_rect.x, image_rect.y); |
|---|
| | 153 | gtk_iimage_cairo_shaper_get_damage_area (cairo->shaper, &new_damage_area); |
|---|
| | 154 | |
|---|
| | 155 | // Union the areas to get the complete redraw area. |
|---|
| | 156 | if (cairo->redraw_area.x == -1) |
|---|
| | 157 | cairo->redraw_area = old_damage_area; |
|---|
| | 158 | |
|---|
| | 159 | GdkRectangle total_damage_area; |
|---|
| | 160 | gdk_rectangle_union (&cairo->redraw_area, |
|---|
| | 161 | &new_damage_area, |
|---|
| | 162 | &total_damage_area); |
|---|
| | 163 | gtk_image_tool_cairo_set_redraw_area (cairo, &total_damage_area); |
|---|
| | 164 | gtk_image_tool_cairo_surface_changed (cairo, &cairo->redraw_area); |
|---|
| | 165 | return TRUE; |
|---|
| | 166 | } |
|---|
| | 167 | |
|---|
| | 168 | static gboolean |
|---|
| | 169 | button_press (GtkIImageTool *tool, |
|---|
| | 170 | GdkEventButton *ev) |
|---|
| | 171 | { |
|---|
| | 172 | GtkImageToolCairo *cairo = GTK_IMAGE_TOOL_CAIRO (tool); |
|---|
| | 173 | GdkRectangle widget_rect = {ev->x, ev->y, 0, 0}; |
|---|
| | 174 | GdkRectangle image_rect; |
|---|
| | 175 | if (!gtk_image_view_widget_to_image_rect (cairo->view, |
|---|
| | 176 | &widget_rect, |
|---|
| | 177 | &image_rect)) |
|---|
| | 178 | return FALSE; |
|---|
| | 192 | |
|---|
| | 193 | GdkRectangle damage_area; |
|---|
| | 194 | gtk_iimage_cairo_shaper_get_damage_area (cairo->shaper, &damage_area); |
|---|
| | 195 | gtk_image_tool_cairo_set_redraw_area (cairo, &damage_area); |
|---|
| | 196 | |
|---|
| | 197 | // Let the shaper draw on the image surface. |
|---|
| | 198 | cairo_t *cr = cairo_create (cairo->image_surface); |
|---|
| | 199 | cairo_rectangle (cr, |
|---|
| | 200 | cairo->redraw_area.x, cairo->redraw_area.y, |
|---|
| | 201 | cairo->redraw_area.width, cairo->redraw_area.height); |
|---|
| | 202 | cairo_clip (cr); |
|---|
| | 203 | gtk_iimage_cairo_shaper_draw (cairo->shaper, cr); |
|---|
| | 204 | cairo_destroy (cr); |
|---|
| | 205 | |
|---|
| 89 | | gdk_pixbuf_draw_cache_invalidate (cairo->cache); |
|---|
| | 229 | GdkPixbuf *pixbuf = gtk_image_view_get_pixbuf (cairo->view); |
|---|
| | 230 | if (!pixbuf) |
|---|
| | 231 | { |
|---|
| | 232 | gtk_image_tool_cairo_null_surfaces (cairo); |
|---|
| | 233 | return; |
|---|
| | 234 | } |
|---|
| | 235 | |
|---|
| | 236 | // The pixbuf instance is the same but data in it has changed, we |
|---|
| | 237 | // can ignore that. |
|---|
| | 238 | if (pixbuf == cairo->pixbuf && cairo->overwritable_pixbuf) |
|---|
| | 239 | return; |
|---|
| | 240 | |
|---|
| | 241 | cairo->pixbuf = pixbuf; |
|---|
| | 242 | |
|---|
| | 243 | int width = gdk_pixbuf_get_width (pixbuf); |
|---|
| | 244 | int height = gdk_pixbuf_get_height (pixbuf); |
|---|
| | 245 | cairo_format_t format = CAIRO_FORMAT_RGB24; |
|---|
| | 246 | if (gdk_pixbuf_get_has_alpha (pixbuf)) |
|---|
| | 247 | format = CAIRO_FORMAT_ARGB32; |
|---|
| | 248 | |
|---|
| | 249 | if (!cairo->image_surface |
|---|
| | 250 | || cairo_image_surface_get_width (cairo->image_surface) != width |
|---|
| | 251 | || cairo_image_surface_get_height (cairo->image_surface) != height |
|---|
| | 252 | || cairo_image_surface_get_format (cairo->image_surface) != format) |
|---|
| | 253 | { |
|---|
| | 254 | gtk_image_tool_cairo_null_surfaces (cairo); |
|---|
| | 255 | cairo->image_surface = cairo_image_surface_create (format, |
|---|
| | 256 | width, height); |
|---|
| | 257 | cairo->draw_surface = cairo_image_surface_create (format, |
|---|
| | 258 | width, height); |
|---|
| | 259 | } |
|---|
| | 260 | |
|---|
| | 261 | // The damaged area. |
|---|
| | 262 | GdkRectangle damage = {0, 0, width, height}; |
|---|
| | 263 | if (rect) |
|---|
| | 264 | damage = *rect; |
|---|
| | 265 | |
|---|
| | 266 | // Copy contents to cairo surfaces and schedule a redraw. |
|---|
| | 267 | gdk_pixbuf_copy_to_surface (pixbuf, &damage, cairo->image_surface); |
|---|
| | 268 | cairo_image_surface_copy (cairo->image_surface, |
|---|
| | 269 | &damage, |
|---|
| | 270 | cairo->draw_surface); |
|---|
| | 271 | gtk_image_tool_cairo_surface_changed (cairo, &damage); |
|---|