/** * cairo_surface_create_for_rectangle: * @target: an existing surface for which the sub-surface will point to * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units) * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units) * @width: width of the sub-surface (in device-space units) * @height: height of the sub-surface (in device-space units) * * Create a new surface that is a rectangle within the target surface. * All operations drawn to this surface are then clipped and translated * onto the target surface. Nothing drawn via this sub-surface outside of * its bounds is drawn onto the target surface, making this a useful method * for passing constrained child surfaces to library routines that draw * directly onto the parent surface, i.e. with no further backend allocations, * double buffering or copies. * * <note><para>The semantics of subsurfaces have not been finalized yet * unless the rectangle is in full device units, is contained within * the extents of the target surface, and the target or subsurface's * device transforms are not changed.</para></note> * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * Since: 1.10 **/ cairo_surface_t * cairo_surface_create_for_rectangle (cairo_surface_t *target, double x, double y, double width, double height) { cairo_surface_subsurface_t *surface; if (unlikely (width < 0 || height < 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); surface = malloc (sizeof (cairo_surface_subsurface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); x *= target->device_transform.xx; y *= target->device_transform.yy; width *= target->device_transform.xx; height *= target->device_transform.yy; x += target->device_transform.x0; y += target->device_transform.y0; _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ target->content); /* XXX forced integer alignment */ surface->extents.x = ceil (x); surface->extents.y = ceil (y); surface->extents.width = floor (x + width) - surface->extents.x; surface->extents.height = floor (y + height) - surface->extents.y; if ((surface->extents.width | surface->extents.height) < 0) surface->extents.width = surface->extents.height = 0; if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { /* Maintain subsurfaces as 1-depth */ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target; surface->extents.x += sub->extents.x; surface->extents.y += sub->extents.y; target = sub->target; } surface->target = cairo_surface_reference (target); surface->base.type = surface->target->type; surface->snapshot = NULL; cairo_surface_set_device_scale (&surface->base, target->device_transform.xx, target->device_transform.yy); return &surface->base; }
/** * gd_embed_image_in_frame: * @source_image: * @frame_image_url: * @slice_width: * @border_width: * * Returns: (transfer full): */ GdkPixbuf * gd_embed_image_in_frame (GdkPixbuf *source_image, const gchar *frame_image_url, GtkBorder *slice_width, GtkBorder *border_width) { cairo_surface_t *surface, *embedded_surface; GdkPixbuf *retval; surface = gdk_cairo_surface_create_from_pixbuf (source_image, 0, NULL); /* Force the device scale to 1.0, since pixbufs are always in unscaled * dimensions. */ cairo_surface_set_device_scale (surface, 1.0, 1.0); embedded_surface = gd_embed_surface_in_frame (surface, frame_image_url, slice_width, border_width); retval = gdk_pixbuf_get_from_surface (embedded_surface, 0, 0, cairo_image_surface_get_width (embedded_surface), cairo_image_surface_get_height (embedded_surface)); cairo_surface_destroy (embedded_surface); cairo_surface_destroy (surface); return retval; }
void PlacesOverlayVScrollBar::UpdateConnectorTexture() { if (connector_height_ < 0) return; int width = _slider->GetWidth(); int height = connector_height_; if (connector_texture_ && connector_texture_->GetWidth() == width && connector_texture_->GetHeight() == height) return; nux::CairoGraphics cairoGraphics(CAIRO_FORMAT_ARGB32, width, height); cairo_t* cr = cairoGraphics.GetInternalContext(); cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, CONNECTOR_COLOR.red, CONNECTOR_COLOR.green, CONNECTOR_COLOR.blue, 0.8); cairo_rectangle(cr, 0, 0, static_cast<double>(width)/scale(), static_cast<double>(height)/scale()); cairo_fill(cr); connector_texture_ = texture_ptr_from_cairo_graphics(cairoGraphics); QueueDraw(); }
static void gtk_css_image_surface_draw (GtkCssImage *image, cairo_t *cr, double width, double height) { GtkCssImageSurface *surface = GTK_CSS_IMAGE_SURFACE (image); int image_width, image_height; image_width = cairo_image_surface_get_width (surface->surface); image_height = cairo_image_surface_get_height (surface->surface); if (image_width == 0 || image_height == 0 || width <= 0 || height <= 0) return; /* Update cache image if size is different */ if (surface->cache == NULL || ABS (width - surface->width) > 0.001 || ABS (height - surface->height) > 0.001) { double xscale, yscale; cairo_t *cache; /* We need the device scale (HiDPI mode) to calculate the proper size in * pixels for the image surface and set the cache device scale */ cairo_surface_get_device_scale (cairo_get_target (cr), &xscale, &yscale); /* Save original size to preserve precision */ surface->width = width; surface->height = height; /* Destroy old cache if any */ g_clear_pointer (&surface->cache, cairo_surface_destroy); /* Image big enough to contain scaled image with subpixel precision */ surface->cache = cairo_surface_create_similar_image (surface->surface, CAIRO_FORMAT_ARGB32, ceil (width*xscale), ceil (height*yscale)); cairo_surface_set_device_scale (surface->cache, xscale, yscale); cache = cairo_create (surface->cache); cairo_rectangle (cache, 0, 0, width, height); cairo_scale (cache, width / image_width, height / image_height); cairo_set_source_surface (cache, surface->surface, 0, 0); cairo_fill (cache); cairo_destroy (cache); } cairo_rectangle (cr, 0, 0, width, height); cairo_set_source_surface (cr, surface->cache ? surface->cache : surface->surface, 0, 0); cairo_fill (cr); }
cairo_t * gsk_cairo_blur_start_drawing (cairo_t *cr, float radius, GskBlurFlags blur_flags) { cairo_rectangle_int_t clip_rect; cairo_surface_t *surface; cairo_t *blur_cr; gdouble clip_radius; gdouble x_scale, y_scale; gboolean blur_x = (blur_flags & GSK_BLUR_X) != 0; gboolean blur_y = (blur_flags & GSK_BLUR_Y) != 0; if (!needs_blur (radius)) return cr; gdk_cairo_get_clip_rectangle (cr, &clip_rect); clip_radius = gsk_cairo_blur_compute_pixels (radius); x_scale = y_scale = 1; cairo_surface_get_device_scale (cairo_get_target (cr), &x_scale, &y_scale); if (blur_flags & GSK_BLUR_REPEAT) { if (!blur_x) clip_rect.width = 1; if (!blur_y) clip_rect.height = 1; } /* Create a larger surface to center the blur. */ surface = cairo_surface_create_similar_image (cairo_get_target (cr), CAIRO_FORMAT_A8, x_scale * (clip_rect.width + (blur_x ? 2 * clip_radius : 0)), y_scale * (clip_rect.height + (blur_y ? 2 * clip_radius : 0))); cairo_surface_set_device_scale (surface, x_scale, y_scale); cairo_surface_set_device_offset (surface, x_scale * ((blur_x ? clip_radius : 0) - clip_rect.x), y_scale * ((blur_y ? clip_radius : 0) - clip_rect.y)); blur_cr = cairo_create (surface); cairo_set_user_data (blur_cr, &original_cr_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy); if (cairo_has_current_point (cr)) { double x, y; cairo_get_current_point (cr, &x, &y); cairo_move_to (blur_cr, x, y); } return blur_cr; }
static void photos_image_view_draw_node (PhotosImageView *self, cairo_t *cr, GdkRectangle *rect) { const Babl *format; GeglRectangle roi; cairo_surface_t *surface = NULL; guchar *buf = NULL; gint scale_factor; gint stride; gint64 end; gint64 start; scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); roi.x = (gint) self->x_scaled + rect->x * scale_factor; roi.y = (gint) self->y_scaled + rect->y * scale_factor; roi.width = rect->width * scale_factor; roi.height = rect->height * scale_factor; format = babl_format ("cairo-ARGB32"); stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, roi.width); buf = g_malloc0 (stride * roi.height); start = g_get_monotonic_time (); gegl_node_blit (self->node, (gdouble) self->zoom_scaled, &roi, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_CACHE | GEGL_BLIT_DIRTY); end = g_get_monotonic_time (); photos_debug (PHOTOS_DEBUG_GEGL, "PhotosImageView: Node Blit: %" G_GINT64_FORMAT, end - start); surface = cairo_image_surface_create_for_data (buf, CAIRO_FORMAT_ARGB32, roi.width, roi.height, stride); cairo_surface_set_device_scale (surface, (gdouble) scale_factor, (gdouble) scale_factor); cairo_set_source_surface (cr, surface, rect->x, rect->y); cairo_paint (cr); cairo_surface_destroy (surface); g_free (buf); }
cairo_surface_t * _gtk_css_image_get_surface (GtkCssImage *image, cairo_surface_t *target, int surface_width, int surface_height) { cairo_surface_t *result; cairo_t *cr; double sx, sy; g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL); g_return_val_if_fail (surface_width > 0, NULL); g_return_val_if_fail (surface_height > 0, NULL); if (target) { #ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE cairo_surface_get_device_scale (target, &sx, &sy); #else sx = sy = 1; #endif result = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR_ALPHA, surface_width*sx, surface_height*sy); #ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE cairo_surface_set_device_scale (result, sx, sy); #endif } else result = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, surface_width, surface_height); cr = cairo_create (result); _gtk_css_image_draw (image, cr, surface_width, surface_height); cairo_destroy (cr); return result; }
cairo_surface_t * _cairo_surface_create_for_rectangle_int (cairo_surface_t *target, const cairo_rectangle_int_t *extents) { cairo_surface_subsurface_t *surface; if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE); surface = malloc (sizeof (cairo_surface_subsurface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ target->content, target->is_vector); surface->extents = *extents; surface->extents.x *= target->device_transform.xx; surface->extents.y *= target->device_transform.yy; surface->extents.width *= target->device_transform.xx; surface->extents.height *= target->device_transform.yy; surface->extents.x += target->device_transform.x0; surface->extents.y += target->device_transform.y0; surface->target = cairo_surface_reference (target); surface->base.type = surface->target->type; surface->snapshot = NULL; cairo_surface_set_device_scale (&surface->base, target->device_transform.xx, target->device_transform.yy); return &surface->base; }
void PlacesVScrollBar::UpdateTexture() { // update texture of slider int width = _slider->GetBaseWidth(); int height = _slider->GetBaseHeight(); if (slider_texture_ && slider_texture_->GetWidth() == width && slider_texture_->GetHeight() == height) return; nux::CairoGraphics cg(CAIRO_FORMAT_ARGB32, width, height); auto* cr = cg.GetInternalContext(); cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cg.DrawRoundedRectangle(cr, 1.0f, 0.0, 0.0, 1.5, 3.0, static_cast<double>(height)/scale() - 3.0); cairo_fill(cr); slider_texture_ = texture_ptr_from_cairo_graphics(cg); }
void VScrollBarOverlayWindow::UpdateTexture() { int width = THUMB_WIDTH; int height = THUMB_HEIGHT; float const aspect = 1.0f; float current_x = 0.0f; float current_y = 0.0f; auto const& bg = nux::color::WhiteSmoke; auto const& bg_selected = nux::color::White; auto const& bg_active = nux::color::Gray; auto const& arrow_color = nux::color::DarkSlateGray; auto const& bg_arrow_up = ProduceColorShade(bg, 0.86); auto const& bg_arrow_down = ProduceColorShade(bg, 1.1); auto const& bg_shadow = ProduceColorShade(bg, 0.2); auto const& bg_dark_line = ProduceColorShade(bg, 0.4); auto const& bg_bright_line = ProduceColorShade(bg, 1.2); nux::CairoGraphics cairoGraphics(CAIRO_FORMAT_ARGB32, THUMB_WIDTH.CP(scale), THUMB_HEIGHT.CP(scale)); cairo_t* cr = cairoGraphics.GetInternalContext(); cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_translate (cr, 0.5, 0.5); width--; height--; cairo_set_line_width (cr, 1.0); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // Draw backgound SetSourceRGB(cr, bg, 1.0); cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, THUMB_RADIUS, width, height); cairo_fill_preserve(cr); // Draw shaded background cairo_pattern_t* pat = cairo_pattern_create_linear(0, 0, 0, height); PatternAddRGBStop(pat, bg_arrow_up, 0.0, 0.8); PatternAddRGBStop(pat, bg_arrow_down, 1.0, 0.8); cairo_set_source(cr, pat); cairo_pattern_destroy(pat); if (current_action_ == ThumbAction::DRAGGING) { cairo_fill_preserve(cr); SetSourceRGB(cr, bg, 0.8); cairo_fill(cr); } else { cairo_fill(cr); } // Draw Page Up/Down Action if (current_action_ == ThumbAction::PAGE_UP || current_action_ == ThumbAction::PAGE_DOWN) { if (current_action_ == ThumbAction::PAGE_UP) cairo_rectangle(cr, 0, 0, width, height/2); else cairo_rectangle(cr, 0, height/2, width, height/2); SetSourceRGB(cr, bg, 0.8); cairo_fill(cr); } cairo_save(cr); // Draw Outline cairo_set_line_width (cr, 2.0); current_x += 0.5; current_y += 0.5; cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, THUMB_RADIUS - 1, width - 1, height - 1); if (HasState(ThumbState::INSIDE_SLIDER)) SetSourceRGB(cr, bg_selected, 1.0); else SetSourceRGB(cr, bg_active, 0.9); cairo_stroke(cr); cairo_restore(cr); // Draw shade outline pat = cairo_pattern_create_linear(0, 0, 0, height); PatternAddRGBStop(pat, bg_shadow, 0.5, 0.06); switch(current_action_) { case ThumbAction::NONE: PatternAddRGBStop(pat, bg_shadow, 0.0, 0.22); PatternAddRGBStop(pat, bg_shadow, 1.0, 0.22); break; case ThumbAction::DRAGGING: PatternAddRGBStop(pat, bg_shadow, 0.0, 0.2); PatternAddRGBStop(pat, bg_shadow, 1.0, 0.2); break; case ThumbAction::PAGE_UP: PatternAddRGBStop(pat, bg_shadow, 0.0, 0.1); PatternAddRGBStop(pat, bg_shadow, 1.0, 0.22); break; case ThumbAction::PAGE_DOWN: PatternAddRGBStop(pat, bg_shadow, 0.0, 0.22); PatternAddRGBStop(pat, bg_shadow, 1.0, 0.1); break; default: break; } cairo_set_source(cr, pat); cairo_pattern_destroy(pat); current_x += 0.5; current_y += 0.5; cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, THUMB_RADIUS, width- 2, height - 2); cairo_stroke(cr); current_x += 1.0; current_y += 1.0; cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, THUMB_RADIUS - 1, width - 4, height- 4); SetSourceRGB(cr, bg_bright_line, 0.6); cairo_stroke(cr); DrawBothGrips(cr, bg_dark_line, width, height); DrawLineSeperator(cr, bg_dark_line, bg_bright_line, width, height); DrawBothArrows(cr, arrow_color, width, height); thumb_texture_ = texture_ptr_from_cairo_graphics(cairoGraphics); QueueDraw(); }
static void clutter_canvas_emit_draw (ClutterCanvas *self) { ClutterCanvasPrivate *priv = self->priv; int real_width, real_height; cairo_surface_t *surface; gboolean mapped_buffer; unsigned char *data; CoglBuffer *buffer; int window_scale = 1; gboolean res; cairo_t *cr; g_assert (priv->height > 0 && priv->width > 0); priv->dirty = TRUE; if (priv->scale_factor_set) window_scale = priv->scale_factor; else g_object_get (clutter_settings_get_default (), "window-scaling-factor", &window_scale, NULL); real_width = priv->width * window_scale; real_height = priv->height * window_scale; CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d (real: %d x %d, scale: %d)", priv->width, priv->height, real_width, real_height, window_scale); if (priv->buffer == NULL) { CoglContext *ctx; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); priv->buffer = cogl_bitmap_new_with_size (ctx, real_width, real_height, CLUTTER_CAIRO_FORMAT_ARGB32); } buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer)); if (buffer == NULL) return; cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); data = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_READ_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (data != NULL) { int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, real_width, real_height, bitmap_stride); mapped_buffer = TRUE; } else { surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, real_width, real_height); mapped_buffer = FALSE; } cairo_surface_set_device_scale (surface, window_scale, window_scale); self->priv->cr = cr = cairo_create (surface); g_signal_emit (self, canvas_signals[DRAW], 0, cr, priv->width, priv->height, &res); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled () && cairo_status (cr)) { g_warning ("Drawing failed for <ClutterCanvas>[%p]: %s", self, cairo_status_to_string (cairo_status (cr))); } #endif self->priv->cr = NULL; cairo_destroy (cr); if (mapped_buffer) cogl_buffer_unmap (buffer); else { int size = cairo_image_surface_get_stride (surface) * priv->height; cogl_buffer_set_data (buffer, 0, cairo_image_surface_get_data (surface), size); } cairo_surface_destroy (surface); }
/** * gdk_cairo_draw_from_gl: * @cr: a cairo context * @window: The window we're rendering for (not necessarily into) * @source: The GL ID of the source buffer * @source_type: The type of the @source * @buffer_scale: The scale-factor that the @source buffer is allocated for * @x: The source x position in @source to start copying from in GL coordinates * @y: The source y position in @source to start copying from in GL coordinates * @width: The width of the region to draw * @height: The height of the region to draw * * This is the main way to draw GL content in GTK+. It takes a render buffer ID * (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE) * and draws it onto @cr with an OVER operation, respecting the current clip. * The top left corner of the rectangle specified by @x, @y, @width and @height * will be drawn at the current (0,0) position of the cairo_t. * * This will work for *all* cairo_t, as long as @window is realized, but the * fallback implementation that reads back the pixels from the buffer may be * used in the general case. In the case of direct drawing to a window with * no special effects applied to @cr it will however use a more efficient * approach. * * For #GL_RENDERBUFFER the code will always fall back to software for buffers * with alpha components, so make sure you use #GL_TEXTURE if using alpha. * * Calling this may change the current GL context. * * Since: 3.16 */ void gdk_cairo_draw_from_gl (cairo_t *cr, GdkWindow *window, int source, int source_type, int buffer_scale, int x, int y, int width, int height) { GdkGLContext *paint_context; cairo_surface_t *image; cairo_matrix_t matrix; int dx, dy, window_scale; gboolean trivial_transform; cairo_surface_t *group_target; GdkWindow *direct_window, *impl_window; guint framebuffer; int alpha_size = 0; cairo_region_t *clip_region; GdkGLContextPaintData *paint_data; impl_window = window->impl_window; window_scale = gdk_window_get_scale_factor (impl_window); paint_context = gdk_window_get_paint_gl_context (window, NULL); if (paint_context == NULL) { g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context"); return; } clip_region = gdk_cairo_region_from_clip (cr); gdk_gl_context_make_current (paint_context); paint_data = gdk_gl_context_get_paint_data (paint_context); if (paint_data->tmp_framebuffer == 0) glGenFramebuffersEXT (1, &paint_data->tmp_framebuffer); if (source_type == GL_RENDERBUFFER) { glBindRenderbuffer (GL_RENDERBUFFER, source); glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size); } else if (source_type == GL_TEXTURE) { glBindTexture (GL_TEXTURE_2D, source); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &alpha_size); } else { g_warning ("Unsupported gl source type %d\n", source_type); return; } group_target = cairo_get_group_target (cr); direct_window = cairo_surface_get_user_data (group_target, &direct_key); cairo_get_matrix (cr, &matrix); dx = matrix.x0; dy = matrix.y0; /* Trivial == integer-only translation */ trivial_transform = (double)dx == matrix.x0 && (double)dy == matrix.y0 && matrix.xx == 1.0 && matrix.xy == 0.0 && matrix.yx == 0.0 && matrix.yy == 1.0; /* For direct paint of non-alpha renderbuffer, we can just do a bitblit */ if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 && source_type == GL_RENDERBUFFER && alpha_size == 0 && direct_window != NULL && direct_window->current_paint.use_gl && trivial_transform && clip_region != NULL) { int unscaled_window_height; int i; /* Create a framebuffer with the source renderbuffer and make it the current target for reads */ framebuffer = paint_data->tmp_framebuffer; glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer); glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, source); glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, 0); /* Translate to impl coords */ cairo_region_translate (clip_region, dx, dy); glEnable (GL_SCISSOR_TEST); gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height); glDrawBuffer (GL_BACK); #define FLIP_Y(_y) (unscaled_window_height - (_y)) for (i = 0; i < cairo_region_num_rectangles (clip_region); i++) { cairo_rectangle_int_t clip_rect, dest; cairo_region_get_rectangle (clip_region, i, &clip_rect); clip_rect.x *= window_scale; clip_rect.y *= window_scale; clip_rect.width *= window_scale; clip_rect.height *= window_scale; glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height), clip_rect.width, clip_rect.height); dest.x = dx * window_scale; dest.y = dy * window_scale; dest.width = width * window_scale / buffer_scale; dest.height = height * window_scale / buffer_scale; if (gdk_rectangle_intersect (&clip_rect, &dest, &dest)) { int clipped_src_x = x + (dest.x - dx * window_scale); int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); glBlitFramebufferEXT(clipped_src_x, clipped_src_y, (clipped_src_x + dest.width), (clipped_src_y + dest.height), dest.x, FLIP_Y(dest.y + dest.height), dest.x + dest.width, FLIP_Y(dest.y), GL_COLOR_BUFFER_BIT, GL_NEAREST); if (impl_window->current_paint.flushed_region) { cairo_rectangle_int_t flushed_rect; flushed_rect.x = dest.x / window_scale; flushed_rect.y = dest.y / window_scale; flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; cairo_region_union_rectangle (impl_window->current_paint.flushed_region, &flushed_rect); cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, &flushed_rect); } } } glDisable (GL_SCISSOR_TEST); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); #undef FLIP_Y } /* For direct paint of alpha or non-alpha textures we can use texturing */ else if ((_gdk_gl_flags & GDK_GL_SOFTWARE_DRAW_GL) == 0 && source_type == GL_TEXTURE && direct_window != NULL && direct_window->current_paint.use_gl && trivial_transform && clip_region != NULL) { int unscaled_window_height; GLint texture_width; GLint texture_height; int i, n_rects, n_quads; GdkTexturedQuad *quads; cairo_rectangle_int_t clip_rect; /* Translate to impl coords */ cairo_region_translate (clip_region, dx, dy); if (alpha_size != 0) { cairo_region_t *opaque_region, *blend_region; opaque_region = cairo_region_copy (clip_region); cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region); cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region); if (!cairo_region_is_empty (opaque_region)) gdk_gl_texture_from_surface (impl_window->current_paint.surface, opaque_region); blend_region = cairo_region_copy (clip_region); cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region); glEnable (GL_BLEND); if (!cairo_region_is_empty (blend_region)) gdk_gl_texture_from_surface (impl_window->current_paint.surface, blend_region); cairo_region_destroy (opaque_region); cairo_region_destroy (blend_region); } glBindTexture (GL_TEXTURE_2D, source); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texture_width); glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texture_height); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glEnable (GL_SCISSOR_TEST); gdk_window_get_unscaled_size (impl_window, NULL, &unscaled_window_height); #define FLIP_Y(_y) (unscaled_window_height - (_y)) cairo_region_get_extents (clip_region, &clip_rect); glScissor (clip_rect.x * window_scale, FLIP_Y ((clip_rect.y + clip_rect.height) * window_scale), clip_rect.width * window_scale, clip_rect.height * window_scale); n_quads = 0; n_rects = cairo_region_num_rectangles (clip_region); quads = g_new (GdkTexturedQuad, n_rects); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t dest; cairo_region_get_rectangle (clip_region, i, &clip_rect); clip_rect.x *= window_scale; clip_rect.y *= window_scale; clip_rect.width *= window_scale; clip_rect.height *= window_scale; dest.x = dx * window_scale; dest.y = dy * window_scale; dest.width = width * window_scale / buffer_scale; dest.height = height * window_scale / buffer_scale; if (gdk_rectangle_intersect (&clip_rect, &dest, &dest)) { int clipped_src_x = x + (dest.x - dx * window_scale); int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale)); GdkTexturedQuad quad = { dest.x, FLIP_Y(dest.y), dest.x + dest.width, FLIP_Y(dest.y + dest.height), clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / (float)texture_height, (clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / (float)texture_height, }; quads[n_quads++] = quad; if (impl_window->current_paint.flushed_region) { cairo_rectangle_int_t flushed_rect; flushed_rect.x = dest.x / window_scale; flushed_rect.y = dest.y / window_scale; flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - flushed_rect.x; flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - flushed_rect.y; cairo_region_union_rectangle (impl_window->current_paint.flushed_region, &flushed_rect); cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region, &flushed_rect); } } } if (n_quads > 0) gdk_gl_texture_quads (paint_context, GL_TEXTURE_2D, n_quads, quads); g_free (quads); if (alpha_size != 0) glDisable (GL_BLEND); #undef FLIP_Y } else { /* Software fallback */ /* TODO: avoid reading back non-required data due to dest clip */ image = cairo_surface_create_similar_image (cairo_get_target (cr), (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale (image, buffer_scale, buffer_scale); framebuffer = paint_data->tmp_framebuffer; glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer); if (source_type == GL_RENDERBUFFER) { /* Create a framebuffer with the source renderbuffer and make it the current target for reads */ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, source); } else { glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, source, 0); } glPixelStorei (GL_PACK_ALIGNMENT, 4); glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (image) / 4); glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data (image)); glPixelStorei (GL_PACK_ROW_LENGTH, 0); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); cairo_surface_mark_dirty (image); /* Invert due to opengl having different origin */ cairo_scale (cr, 1, -1); cairo_translate (cr, 0, -height / buffer_scale); cairo_set_source_surface (cr, image, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); cairo_paint (cr); cairo_surface_destroy (image); } if (clip_region) cairo_region_destroy (clip_region); }
/** * gd_create_symbolic_icon_for_scale: * @name: * @base_size: * @scale: * * Returns: (transfer full): */ GIcon * gd_create_symbolic_icon_for_scale (const gchar *name, gint base_size, gint scale) { gchar *symbolic_name; GIcon *icon, *retval = NULL; cairo_surface_t *icon_surface; cairo_surface_t *surface; cairo_t *cr; GtkStyleContext *style; GtkWidgetPath *path; GdkPixbuf *pixbuf; GtkIconTheme *theme; GtkIconInfo *info; gint bg_size; gint emblem_size; gint total_size; gint total_size_scaled; total_size = base_size / 2; total_size_scaled = total_size * scale; bg_size = MAX (total_size / 2, _BG_MIN_SIZE); emblem_size = MAX (bg_size - 8, _EMBLEM_MIN_SIZE); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, total_size_scaled, total_size_scaled); cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale); cr = cairo_create (surface); style = gtk_style_context_new (); path = gtk_widget_path_new (); gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW); gtk_style_context_set_path (style, path); gtk_widget_path_unref (path); gtk_style_context_add_class (style, "documents-icon-bg"); gtk_render_background (style, cr, (total_size - bg_size) / 2, (total_size - bg_size) / 2, bg_size, bg_size); symbolic_name = g_strconcat (name, "-symbolic", NULL); icon = g_themed_icon_new_with_default_fallbacks (symbolic_name); g_free (symbolic_name); theme = gtk_icon_theme_get_default(); info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, emblem_size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); g_object_unref (icon); if (info == NULL) goto out; pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL); g_object_unref (info); if (pixbuf == NULL) goto out; icon_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); g_object_unref (pixbuf); gtk_render_icon_surface (style, cr, icon_surface, (total_size - emblem_size) / 2, (total_size - emblem_size) / 2); cairo_surface_destroy (icon_surface); retval = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, total_size_scaled, total_size_scaled)); out: g_object_unref (style); cairo_surface_destroy (surface); cairo_destroy (cr); return retval; }
static gboolean _eventd_nd_wl_create_buffer(EventdNdSurface *self) { struct wl_shm_pool *pool; struct wl_buffer *buffer; gint fd; gpointer data; gint width, height, stride; gsize size; width = self->width * self->context->scale; height = self->height * self->context->scale; stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); size = stride * height; gchar *filename; filename = g_build_filename(g_get_user_runtime_dir(), PACKAGE_NAME G_DIR_SEPARATOR_S "wayland-surface", NULL); fd = g_open(filename, O_CREAT | O_RDWR | O_CLOEXEC, 0); g_unlink(filename); g_free(filename); if ( fd < 0 ) { g_warning("creating a buffer file for %zu B failed: %s\n", size, g_strerror(errno)); return FALSE; } if ( ftruncate(fd, size) < 0 ) { close(fd); return FALSE; } data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ( data == MAP_FAILED ) { g_warning("mmap failed: %s\n", g_strerror(errno)); close(fd); return FALSE; } cairo_surface_t *cairo_surface; cairo_surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, 4 * width); cairo_surface_set_device_scale(cairo_surface, self->context->scale, self->context->scale); self->context->nd->notification_draw(self->notification, cairo_surface, TRUE); cairo_surface_destroy(cairo_surface); munmap(data, size); pool = wl_shm_create_pool(self->context->shm, fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); close(fd); if ( self->buffer != NULL ) _eventd_nd_wl_buffer_release(self->buffer, self->buffer->buffer); self->buffer = g_new0(EventdNdWlBuffer, 1); self->buffer->buffer = buffer; self->buffer->data = data; self->buffer->size = size; wl_buffer_add_listener(buffer, &_eventd_nd_wl_buffer_listener, self->buffer); wl_surface_damage(self->surface, 0, 0, self->width, self->height); wl_surface_attach(self->surface, self->buffer->buffer, 0, 0); if ( wl_surface_get_version(self->surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION ) wl_surface_set_buffer_scale(self->surface, self->context->scale); wl_surface_commit(self->surface); return TRUE; }
GIcon * photos_utils_create_symbolic_icon_for_scale (const gchar *name, gint base_size, gint scale) { g_autoptr (GIcon) icon = NULL; GIcon *ret_val = NULL; g_autoptr (GdkPixbuf) pixbuf = NULL; g_autoptr (GtkIconInfo) info = NULL; GtkIconTheme *theme; g_autoptr (GtkStyleContext) style = NULL; g_autoptr (GtkWidgetPath) path = NULL; cairo_surface_t *icon_surface = NULL; /* TODO: use g_autoptr */ cairo_surface_t *surface; /* TODO: use g_autoptr */ cairo_t *cr; /* TODO: use g_autoptr */ g_autofree gchar *symbolic_name = NULL; const gint bg_size = 24; const gint emblem_margin = 4; gint emblem_pos; gint emblem_size; gint total_size; gint total_size_scaled; total_size = base_size / 2; total_size_scaled = total_size * scale; emblem_size = bg_size - emblem_margin * 2; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, total_size_scaled, total_size_scaled); cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale); cr = cairo_create (surface); style = gtk_style_context_new (); path = gtk_widget_path_new (); gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW); gtk_style_context_set_path (style, path); gtk_style_context_add_class (style, "photos-icon-bg"); gtk_render_background (style, cr, total_size - bg_size, total_size - bg_size, bg_size, bg_size); symbolic_name = g_strconcat (name, "-symbolic", NULL); icon = g_themed_icon_new_with_default_fallbacks (symbolic_name); theme = gtk_icon_theme_get_default(); info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, emblem_size, scale, GTK_ICON_LOOKUP_FORCE_SIZE); if (info == NULL) goto out; pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL); if (pixbuf == NULL) goto out; icon_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); emblem_pos = total_size - emblem_size - emblem_margin; gtk_render_icon_surface (style, cr, icon_surface, emblem_pos, emblem_pos); ret_val = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, total_size_scaled, total_size_scaled)); out: cairo_surface_destroy (icon_surface); cairo_surface_destroy (surface); cairo_destroy (cr); return ret_val; }
void HudButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); dash::Style::Instance().SquareButton(cr, faked_state, "", is_rounded, 17, dash::Alignment::LEFT, true); }
static cairo_test_status_t cairo_test_for_target (cairo_test_context_t *ctx, const cairo_boilerplate_target_t *target, int dev_offset, int dev_scale, cairo_bool_t similar) { cairo_test_status_t status; cairo_surface_t *surface = NULL; cairo_t *cr; const char *empty_str = ""; char *offset_str; char *scale_str; char *base_name, *base_path; char *out_png_path; char *ref_path = NULL, *ref_png_path, *cmp_png_path = NULL; char *new_path = NULL, *new_png_path; char *xfail_path = NULL, *xfail_png_path; char *base_ref_png_path; char *base_new_png_path; char *base_xfail_png_path; char *diff_png_path; char *test_filename = NULL, *pass_filename = NULL, *fail_filename = NULL; cairo_test_status_t ret; cairo_content_t expected_content; cairo_font_options_t *font_options; const char *format; cairo_bool_t have_output = FALSE; cairo_bool_t have_result = FALSE; void *closure; double width, height; cairo_bool_t have_output_dir; #if HAVE_MEMFAULT int malloc_failure_iterations = ctx->malloc_failure; int last_fault_count = 0; #endif /* Get the strings ready that we'll need. */ format = cairo_boilerplate_content_name (target->content); if (dev_offset) xasprintf (&offset_str, ".%d", dev_offset); else offset_str = (char *) empty_str; if (dev_scale != 1) xasprintf (&scale_str, ".x%d", dev_scale); else scale_str = (char *) empty_str; xasprintf (&base_name, "%s.%s.%s%s%s%s", ctx->test_name, target->name, format, similar ? ".similar" : "", offset_str, scale_str); if (offset_str != empty_str) free (offset_str); if (scale_str != empty_str) free (scale_str); ref_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_REF_SUFFIX, CAIRO_TEST_PNG_EXTENSION); new_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_NEW_SUFFIX, CAIRO_TEST_PNG_EXTENSION); xfail_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_XFAIL_SUFFIX, CAIRO_TEST_PNG_EXTENSION); base_ref_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, NULL, NULL, format, CAIRO_TEST_REF_SUFFIX, CAIRO_TEST_PNG_EXTENSION); base_new_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, NULL, NULL, format, CAIRO_TEST_NEW_SUFFIX, CAIRO_TEST_PNG_EXTENSION); base_xfail_png_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, NULL, NULL, format, CAIRO_TEST_XFAIL_SUFFIX, CAIRO_TEST_PNG_EXTENSION); if (target->file_extension != NULL) { ref_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_REF_SUFFIX, target->file_extension); new_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_NEW_SUFFIX, target->file_extension); xfail_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, target->name, target->basename, format, CAIRO_TEST_XFAIL_SUFFIX, target->file_extension); } have_output_dir = cairo_test_mkdir (ctx->output); xasprintf (&base_path, "%s/%s", have_output_dir ? ctx->output : ".", base_name); xasprintf (&out_png_path, "%s" CAIRO_TEST_OUT_PNG, base_path); xasprintf (&diff_png_path, "%s" CAIRO_TEST_DIFF_PNG, base_path); if (ctx->test->requirements != NULL) { const char *required; required = target->is_vector ? "target=raster" : "target=vector"; if (strstr (ctx->test->requirements, required) != NULL) { cairo_test_log (ctx, "Error: Skipping for %s target %s\n", target->is_vector ? "vector" : "raster", target->name); ret = CAIRO_TEST_UNTESTED; goto UNWIND_STRINGS; } required = target->is_recording ? "target=!recording" : "target=recording"; if (strstr (ctx->test->requirements, required) != NULL) { cairo_test_log (ctx, "Error: Skipping for %s target %s\n", target->is_recording ? "recording" : "non-recording", target->name); ret = CAIRO_TEST_UNTESTED; goto UNWIND_STRINGS; } } width = ctx->test->width; height = ctx->test->height; if (width && height) { width *= dev_scale; height *= dev_scale; width += dev_offset; height += dev_offset; } #if HAVE_MEMFAULT REPEAT: MEMFAULT_CLEAR_FAULTS (); MEMFAULT_RESET_LEAKS (); ctx->last_fault_count = 0; last_fault_count = MEMFAULT_COUNT_FAULTS (); /* Pre-initialise fontconfig so that the configuration is loaded without * malloc failures (our primary goal is to test cairo fault tolerance). */ #if HAVE_FCINIT FcInit (); #endif MEMFAULT_ENABLE_FAULTS (); #endif have_output = FALSE; have_result = FALSE; /* Run the actual drawing code. */ ret = CAIRO_TEST_SUCCESS; surface = (target->create_surface) (base_path, target->content, width, height, ctx->test->width * NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS, ctx->test->height * NUM_DEVICE_SCALE + 25 * NUM_DEVICE_OFFSETS, CAIRO_BOILERPLATE_MODE_TEST, &closure); if (surface == NULL) { cairo_test_log (ctx, "Error: Failed to set %s target\n", target->name); ret = CAIRO_TEST_UNTESTED; goto UNWIND_STRINGS; } #if HAVE_MEMFAULT if (ctx->malloc_failure && MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 && cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY) { goto REPEAT; } #endif if (cairo_surface_status (surface)) { MF (MEMFAULT_PRINT_FAULTS ()); cairo_test_log (ctx, "Error: Created an error surface: %s\n", cairo_status_to_string (cairo_surface_status (surface))); ret = CAIRO_TEST_FAILURE; goto UNWIND_STRINGS; } /* Check that we created a surface of the expected type. */ if (cairo_surface_get_type (surface) != target->expected_type) { MF (MEMFAULT_PRINT_FAULTS ()); cairo_test_log (ctx, "Error: Created surface is of type %d (expected %d)\n", cairo_surface_get_type (surface), target->expected_type); ret = CAIRO_TEST_UNTESTED; goto UNWIND_SURFACE; } /* Check that we created a surface of the expected content, * (ignore the artificial CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED value). */ expected_content = cairo_boilerplate_content (target->content); if (cairo_surface_get_content (surface) != expected_content) { MF (MEMFAULT_PRINT_FAULTS ()); cairo_test_log (ctx, "Error: Created surface has content %d (expected %d)\n", cairo_surface_get_content (surface), expected_content); ret = CAIRO_TEST_FAILURE; goto UNWIND_SURFACE; } if (cairo_surface_set_user_data (surface, &cairo_boilerplate_output_basename_key, base_path, NULL)) { #if HAVE_MEMFAULT cairo_surface_destroy (surface); if (target->cleanup) target->cleanup (closure); goto REPEAT; #else ret = CAIRO_TEST_FAILURE; goto UNWIND_SURFACE; #endif } cairo_surface_set_device_offset (surface, dev_offset, dev_offset); cairo_surface_set_device_scale (surface, dev_scale, dev_scale); cr = cairo_create (surface); if (cairo_set_user_data (cr, &_cairo_test_context_key, (void*) ctx, NULL)) { #if HAVE_MEMFAULT cairo_destroy (cr); cairo_surface_destroy (surface); if (target->cleanup) target->cleanup (closure); goto REPEAT; #else ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; #endif } if (similar) cairo_push_group_with_content (cr, expected_content); /* Clear to transparent (or black) depending on whether the target * surface supports alpha. */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); /* Set all components of font_options to avoid backend differences * and reduce number of needed reference images. */ font_options = cairo_font_options_create (); cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON); cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY); cairo_set_font_options (cr, font_options); cairo_font_options_destroy (font_options); cairo_save (cr); alarm (ctx->timeout); status = (ctx->test->draw) (cr, ctx->test->width, ctx->test->height); alarm (0); cairo_restore (cr); if (similar) { cairo_pop_group_to_source (cr); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); } #if HAVE_MEMFAULT MEMFAULT_DISABLE_FAULTS (); /* repeat test after malloc failure injection */ if (ctx->malloc_failure && MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 && (status == CAIRO_TEST_NO_MEMORY || cairo_status (cr) == CAIRO_STATUS_NO_MEMORY || cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY)) { cairo_destroy (cr); cairo_surface_destroy (surface); if (target->cleanup) target->cleanup (closure); cairo_debug_reset_static_data (); #if HAVE_FCFINI FcFini (); #endif if (MEMFAULT_COUNT_LEAKS () > 0) { MEMFAULT_PRINT_FAULTS (); MEMFAULT_PRINT_LEAKS (); } goto REPEAT; } #endif /* Then, check all the different ways it could fail. */ if (status) { cairo_test_log (ctx, "Error: Function under test failed\n"); ret = status; goto UNWIND_CAIRO; } #if HAVE_MEMFAULT if (MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 && MEMFAULT_HAS_FAULTS ()) { VALGRIND_PRINTF ("Unreported memfaults..."); MEMFAULT_PRINT_FAULTS (); } #endif if (target->finish_surface != NULL) { #if HAVE_MEMFAULT /* We need to re-enable faults as most recording-surface processing * is done during cairo_surface_finish(). */ MEMFAULT_CLEAR_FAULTS (); last_fault_count = MEMFAULT_COUNT_FAULTS (); MEMFAULT_ENABLE_FAULTS (); #endif /* also check for infinite loops whilst replaying */ alarm (ctx->timeout); status = target->finish_surface (surface); alarm (0); #if HAVE_MEMFAULT MEMFAULT_DISABLE_FAULTS (); if (ctx->malloc_failure && MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 && status == CAIRO_STATUS_NO_MEMORY) { cairo_destroy (cr); cairo_surface_destroy (surface); if (target->cleanup) target->cleanup (closure); cairo_debug_reset_static_data (); #if HAVE_FCFINI FcFini (); #endif if (MEMFAULT_COUNT_LEAKS () > 0) { MEMFAULT_PRINT_FAULTS (); MEMFAULT_PRINT_LEAKS (); } goto REPEAT; } #endif if (status) { cairo_test_log (ctx, "Error: Failed to finish surface: %s\n", cairo_status_to_string (status)); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } } /* Skip image check for tests with no image (width,height == 0,0) */ if (ctx->test->width != 0 && ctx->test->height != 0) { cairo_surface_t *ref_image; cairo_surface_t *test_image; cairo_surface_t *diff_image; buffer_diff_result_t result; cairo_status_t diff_status; if (ref_png_path == NULL) { cairo_test_log (ctx, "Error: Cannot find reference image for %s\n", base_name); /* we may be running this test to generate reference images */ _xunlink (ctx, out_png_path); /* be more generous as we may need to use external renderers */ alarm (4 * ctx->timeout); test_image = target->get_image_surface (surface, 0, ctx->test->width, ctx->test->height); alarm (0); diff_status = cairo_surface_write_to_png (test_image, out_png_path); cairo_surface_destroy (test_image); if (diff_status) { if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS) ret = CAIRO_TEST_CRASHED; else ret = CAIRO_TEST_FAILURE; cairo_test_log (ctx, "Error: Failed to write output image: %s\n", cairo_status_to_string (diff_status)); } have_output = TRUE; ret = CAIRO_TEST_XFAILURE; goto UNWIND_CAIRO; } if (target->file_extension != NULL) { /* compare vector surfaces */ char *filenames[] = { ref_png_path, ref_path, new_png_path, new_path, xfail_png_path, xfail_path, base_ref_png_path, base_new_png_path, base_xfail_png_path, }; xasprintf (&test_filename, "%s.out%s", base_path, target->file_extension); xasprintf (&pass_filename, "%s.pass%s", base_path, target->file_extension); xasprintf (&fail_filename, "%s.fail%s", base_path, target->file_extension); if (cairo_test_file_is_older (pass_filename, filenames, ARRAY_LENGTH (filenames))) { _xunlink (ctx, pass_filename); } if (cairo_test_file_is_older (fail_filename, filenames, ARRAY_LENGTH (filenames))) { _xunlink (ctx, fail_filename); } if (cairo_test_files_equal (out_png_path, ref_path)) { cairo_test_log (ctx, "Vector surface matches reference.\n"); have_output = FALSE; ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, new_path)) { cairo_test_log (ctx, "Vector surface matches current failure.\n"); have_output = FALSE; ret = CAIRO_TEST_NEW; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, xfail_path)) { cairo_test_log (ctx, "Vector surface matches known failure.\n"); have_output = FALSE; ret = CAIRO_TEST_XFAILURE; goto UNWIND_CAIRO; } if (cairo_test_files_equal (test_filename, pass_filename)) { /* identical output as last known PASS */ cairo_test_log (ctx, "Vector surface matches last pass.\n"); have_output = TRUE; ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (test_filename, fail_filename)) { /* identical output as last known FAIL, fail */ cairo_test_log (ctx, "Vector surface matches last fail.\n"); have_result = TRUE; /* presume these were kept around as well */ have_output = TRUE; ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } } /* be more generous as we may need to use external renderers */ alarm (4 * ctx->timeout); test_image = target->get_image_surface (surface, 0, ctx->test->width, ctx->test->height); alarm (0); if (cairo_surface_status (test_image)) { cairo_test_log (ctx, "Error: Failed to extract image: %s\n", cairo_status_to_string (cairo_surface_status (test_image))); if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS) ret = CAIRO_TEST_CRASHED; else ret = CAIRO_TEST_FAILURE; cairo_surface_destroy (test_image); goto UNWIND_CAIRO; } _xunlink (ctx, out_png_path); diff_status = cairo_surface_write_to_png (test_image, out_png_path); if (diff_status) { cairo_test_log (ctx, "Error: Failed to write output image: %s\n", cairo_status_to_string (diff_status)); cairo_surface_destroy (test_image); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } have_output = TRUE; /* binary compare png files (no decompression) */ if (target->file_extension == NULL) { char *filenames[] = { ref_png_path, new_png_path, xfail_png_path, base_ref_png_path, base_new_png_path, base_xfail_png_path, }; xasprintf (&test_filename, "%s", out_png_path); xasprintf (&pass_filename, "%s.pass.png", base_path); xasprintf (&fail_filename, "%s.fail.png", base_path); if (cairo_test_file_is_older (pass_filename, filenames, ARRAY_LENGTH (filenames))) { _xunlink (ctx, pass_filename); } if (cairo_test_file_is_older (fail_filename, filenames, ARRAY_LENGTH (filenames))) { _xunlink (ctx, fail_filename); } if (cairo_test_files_equal (test_filename, pass_filename)) { cairo_test_log (ctx, "PNG file exactly matches last pass.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, ref_png_path)) { cairo_test_log (ctx, "PNG file exactly matches reference image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, new_png_path)) { cairo_test_log (ctx, "PNG file exactly matches current failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_NEW; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, xfail_png_path)) { cairo_test_log (ctx, "PNG file exactly matches known failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_XFAILURE; goto UNWIND_CAIRO; } if (cairo_test_files_equal (test_filename, fail_filename)) { cairo_test_log (ctx, "PNG file exactly matches last fail.\n"); have_result = TRUE; /* presume these were kept around as well */ cairo_surface_destroy (test_image); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } } else { if (cairo_test_files_equal (out_png_path, ref_png_path)) { cairo_test_log (ctx, "PNG file exactly matches reference image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, new_png_path)) { cairo_test_log (ctx, "PNG file exactly matches current failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_NEW; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, xfail_png_path)) { cairo_test_log (ctx, "PNG file exactly matches known failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_XFAILURE; goto UNWIND_CAIRO; } } if (cairo_test_files_equal (out_png_path, base_ref_png_path)) { cairo_test_log (ctx, "PNG file exactly reference image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_SUCCESS; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, base_new_png_path)) { cairo_test_log (ctx, "PNG file exactly current failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_NEW; goto UNWIND_CAIRO; } if (cairo_test_files_equal (out_png_path, base_xfail_png_path)) { cairo_test_log (ctx, "PNG file exactly known failure image.\n"); have_result = TRUE; cairo_surface_destroy (test_image); ret = CAIRO_TEST_XFAILURE; goto UNWIND_CAIRO; } /* first compare against the ideal reference */ ref_image = cairo_test_get_reference_image (ctx, base_ref_png_path, target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED); if (cairo_surface_status (ref_image)) { cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n", base_ref_png_path, cairo_status_to_string (cairo_surface_status (ref_image))); cairo_surface_destroy (test_image); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ctx->test->width, ctx->test->height); cmp_png_path = base_ref_png_path; diff_status = image_diff (ctx, test_image, ref_image, diff_image, &result); _xunlink (ctx, diff_png_path); if (diff_status || image_diff_is_failure (&result, target->error_tolerance)) { /* that failed, so check against the specific backend */ ref_image = cairo_test_get_reference_image (ctx, ref_png_path, target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED); if (cairo_surface_status (ref_image)) { cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n", ref_png_path, cairo_status_to_string (cairo_surface_status (ref_image))); cairo_surface_destroy (test_image); ret = CAIRO_TEST_FAILURE; goto UNWIND_CAIRO; } cmp_png_path = ref_png_path; diff_status = image_diff (ctx, test_image, ref_image, diff_image, &result); if (diff_status) { cairo_test_log (ctx, "Error: Failed to compare images: %s\n", cairo_status_to_string (diff_status)); ret = CAIRO_TEST_FAILURE; } else if (image_diff_is_failure (&result, target->error_tolerance)) { ret = CAIRO_TEST_FAILURE; diff_status = cairo_surface_write_to_png (diff_image, diff_png_path); if (diff_status) { cairo_test_log (ctx, "Error: Failed to write differences image: %s\n", cairo_status_to_string (diff_status)); } else { have_result = TRUE; } cairo_test_copy_file (test_filename, fail_filename); } else { /* success */ cairo_test_copy_file (test_filename, pass_filename); } } else { /* success */ cairo_test_copy_file (test_filename, pass_filename); } /* If failed, compare against the current image output, * and attempt to detect systematic failures. */ if (ret == CAIRO_TEST_FAILURE) { char *image_out_path; image_out_path = cairo_test_reference_filename (ctx, base_name, ctx->test_name, "image", "image", format, CAIRO_TEST_OUT_SUFFIX, CAIRO_TEST_PNG_EXTENSION); if (image_out_path != NULL) { if (cairo_test_files_equal (out_png_path, image_out_path)) { ret = CAIRO_TEST_XFAILURE; } else { ref_image = cairo_image_surface_create_from_png (image_out_path); if (cairo_surface_status (ref_image) == CAIRO_STATUS_SUCCESS) { diff_status = image_diff (ctx, test_image, ref_image, diff_image, &result); if (diff_status == CAIRO_STATUS_SUCCESS && !image_diff_is_failure (&result, target->error_tolerance)) { ret = CAIRO_TEST_XFAILURE; } cairo_surface_destroy (ref_image); } } free (image_out_path); } } cairo_surface_destroy (test_image); cairo_surface_destroy (diff_image); } if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) { cairo_test_log (ctx, "Error: Function under test left cairo status in an error state: %s\n", cairo_status_to_string (cairo_status (cr))); ret = CAIRO_TEST_ERROR; goto UNWIND_CAIRO; } UNWIND_CAIRO: free (test_filename); free (fail_filename); free (pass_filename); test_filename = fail_filename = pass_filename = NULL; #if HAVE_MEMFAULT if (ret == CAIRO_TEST_FAILURE) MEMFAULT_PRINT_FAULTS (); #endif cairo_destroy (cr); UNWIND_SURFACE: cairo_surface_destroy (surface); if (target->cleanup) target->cleanup (closure); #if HAVE_MEMFAULT cairo_debug_reset_static_data (); #if HAVE_FCFINI FcFini (); #endif if (MEMFAULT_COUNT_LEAKS () > 0) { if (ret != CAIRO_TEST_FAILURE) MEMFAULT_PRINT_FAULTS (); MEMFAULT_PRINT_LEAKS (); } if (ret == CAIRO_TEST_SUCCESS && --malloc_failure_iterations > 0) goto REPEAT; #endif if (have_output) cairo_test_log (ctx, "OUTPUT: %s\n", out_png_path); if (have_result) { if (cmp_png_path == NULL) { /* XXX presume we matched the normal ref last time */ cmp_png_path = ref_png_path; } cairo_test_log (ctx, "REFERENCE: %s\nDIFFERENCE: %s\n", cmp_png_path, diff_png_path); } UNWIND_STRINGS: free (out_png_path); free (ref_png_path); free (base_ref_png_path); free (ref_path); free (new_png_path); free (base_new_png_path); free (new_path); free (xfail_png_path); free (base_xfail_png_path); free (xfail_path); free (diff_png_path); free (base_path); free (base_name); return ret; }
static cairo_status_t _cairo_default_context_push_group (void *abstract_cr, cairo_content_t content) { cairo_default_context_t *cr = abstract_cr; cairo_surface_t *group_surface; cairo_clip_t *clip; cairo_status_t status; clip = _cairo_gstate_get_clip (cr->gstate); if (_cairo_clip_is_all_clipped (clip)) { group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); status = group_surface->status; if (unlikely (status)) goto bail; } else { cairo_surface_t *parent_surface; cairo_rectangle_int_t extents; cairo_bool_t bounded, is_empty; parent_surface = _cairo_gstate_get_target (cr->gstate); if (unlikely (parent_surface->status)) return parent_surface->status; if (unlikely (parent_surface->finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); /* Get the extents that we'll use in creating our new group surface */ bounded = _cairo_surface_get_extents (parent_surface, &extents); if (clip) /* XXX: This assignment just fixes a compiler warning? */ is_empty = _cairo_rectangle_intersect (&extents, _cairo_clip_get_extents (clip)); if (!bounded) { /* XXX: Generic solution? */ group_surface = cairo_recording_surface_create (content, NULL); extents.x = extents.y = 0; } else { group_surface = _cairo_surface_create_scratch (parent_surface, content, extents.width, extents.height, CAIRO_COLOR_TRANSPARENT); } status = group_surface->status; if (unlikely (status)) goto bail; /* Set device offsets on the new surface so that logically it appears at * the same location on the parent surface -- when we pop_group this, * the source pattern will get fixed up for the appropriate target surface * device offsets, so we want to set our own surface offsets from /that/, * and not from the device origin. */ cairo_surface_set_device_offset (group_surface, parent_surface->device_transform.x0 - extents.x, parent_surface->device_transform.y0 - extents.y); cairo_surface_set_device_scale (group_surface, parent_surface->device_transform.xx, parent_surface->device_transform.yy); /* If we have a current path, we need to adjust it to compensate for * the device offset just applied. */ _cairo_path_fixed_translate (cr->path, _cairo_fixed_from_int (-extents.x), _cairo_fixed_from_int (-extents.y)); } /* create a new gstate for the redirect */ status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); if (unlikely (status)) goto bail; status = _cairo_gstate_redirect_target (cr->gstate, group_surface); bail: cairo_surface_destroy (group_surface); return status; }