/** * _cairo_matrix_is_pixman_translation: * @matrix: a matrix * @filter: the filter to be used on the pattern transformed by @matrix * @x_offset: the translation in the X direction * @y_offset: the translation in the Y direction * * Checks if @matrix translated by (x_offset, y_offset) can be * represented using just an offset (within the range pixman can * accept) and an identity matrix. * * Passing a non-zero value in x_offset/y_offset has the same effect * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and * setting x_offset and y_offset to 0. * * Upon return x_offset and y_offset contain the translation vector if * the return value is %TRUE. If the return value is %FALSE, they will * not be modified. * * Return value: %TRUE if @matrix can be represented as a pixman * translation, %FALSE otherwise. **/ cairo_bool_t _cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, cairo_filter_t filter, int *x_offset, int *y_offset) { double tx, ty; if (!_cairo_matrix_is_translation (matrix)) return FALSE; if (matrix->x0 == 0. && matrix->y0 == 0.) return TRUE; tx = matrix->x0 + *x_offset; ty = matrix->y0 + *y_offset; if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { tx = _pixman_nearest_sample (tx); ty = _pixman_nearest_sample (ty); } else if (tx != floor (tx) || ty != floor (ty)) { return FALSE; } if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT) return FALSE; *x_offset = _cairo_lround (tx); *y_offset = _cairo_lround (ty); return TRUE; }
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)); assert (_cairo_matrix_is_translation (&target->device_transform)); _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ target->content); surface->extents = *extents; 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; return &surface->base; }
/** * 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)); assert (_cairo_matrix_is_translation (&target->device_transform)); 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; return &surface->base; }
cairo_bool_t _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, int *itx, int *ity) { if (_cairo_matrix_is_translation (matrix)) { cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0); cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0); if (_cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed)) { if (itx) *itx = _cairo_fixed_integer_part (x0_fixed); if (ity) *ity = _cairo_fixed_integer_part (y0_fixed); return TRUE; } } return FALSE; }
cairo_status_t _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)]; cairo_glyph_t *dev_glyphs = stack_glyphs; cairo_scaled_font_t *dev_scaled_font = scaled_font; cairo_pattern_union_t source_copy; cairo_font_options_t options; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; cairo_surface_get_font_options (wrapper->target, &options); cairo_font_options_merge (&options, &scaled_font->options); if (wrapper->needs_transform) { cairo_matrix_t m; int i; _cairo_surface_wrapper_get_transform (wrapper, &m); if (! _cairo_matrix_is_translation (&wrapper->transform)) { cairo_matrix_t ctm; /* XXX No device-transform? A bug in the tangle of layers? */ _cairo_matrix_multiply (&ctm, &wrapper->transform, &scaled_font->ctm); dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &ctm, &options); } if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } for (i = 0; i < num_glyphs; i++) { dev_glyphs[i] = glyphs[i]; cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); } status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } else { if (! cairo_font_options_equal (&options, &scaled_font->options)) { dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &scaled_font->ctm, &options); } /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed * to modify the glyph array that's passed in. We must always * copy the array before handing it to the backend. */ if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, utf8, utf8_len, dev_glyphs, num_glyphs, clusters, num_clusters, cluster_flags, dev_scaled_font, dev_clip); FINISH: _cairo_clip_destroy (dev_clip); if (dev_glyphs != stack_glyphs) free (dev_glyphs); if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); return status; }
static cairo_int_status_t _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_traps_t *traps) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; /* This special-case rectilinear stroker only supports * miter-joined lines (not curves) and a translation-only matrix * (though it could probably be extended to support a matrix with * uniform, integer scaling). * * It also only supports horizontal and vertical line_to * elements. But we don't catch that here, but instead return * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any * non-rectilinear line_to is encountered. */ if (path->has_curve_to) return CAIRO_INT_STATUS_UNSUPPORTED; if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) return CAIRO_INT_STATUS_UNSUPPORTED; /* If the miter limit turns right angles into bevels, then we * can't use this optimization. Remember, the ratio is * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, * which we round for safety. */ if (stroke_style->miter_limit < M_SQRT2) return CAIRO_INT_STATUS_UNSUPPORTED; if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! (_cairo_matrix_is_identity (ctm) || _cairo_matrix_is_translation (ctm))) { return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, traps); if (traps->has_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, &traps->limits); } status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); if (unlikely (status)) _cairo_traps_clear (traps); return status; }
void _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, pixman_transform_t *pixman_transform, double xc, double yc) { static const pixman_transform_t pixman_identity_transform = {{ {1 << 16, 0, 0}, { 0, 1 << 16, 0}, { 0, 0, 1 << 16} }}; if (_cairo_matrix_is_identity (matrix)) { *pixman_transform = pixman_identity_transform; } else { cairo_matrix_t inv; unsigned max_iterations; pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); pixman_transform->matrix[2][0] = 0; pixman_transform->matrix[2][1] = 0; pixman_transform->matrix[2][2] = 1 << 16; /* The conversion above breaks cairo's translation invariance: * a translation of (a, b) in device space translates to * a translation of (xx * a + xy * b, yx * a + yy * b) * for cairo, while pixman uses rounded versions of xx ... yy. * This error increases as a and b get larger. * * To compensate for this, we fix the point (xc, yc) in pattern * space and adjust pixman's transform to agree with cairo's at * that point. */ if (_cairo_matrix_is_translation (matrix)) return; /* Note: If we can't invert the transformation, skip the adjustment. */ inv = *matrix; if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) return; /* find the pattern space coordinate that maps to (xc, yc) */ xc += .5; yc += .5; /* offset for the pixel centre */ max_iterations = 5; do { double x,y; pixman_vector_t vector; cairo_fixed_16_16_t dx, dy; vector.vector[0] = _cairo_fixed_16_16_from_double (xc); vector.vector[1] = _cairo_fixed_16_16_from_double (yc); vector.vector[2] = 1 << 16; if (! pixman_transform_point_3d (pixman_transform, &vector)) return; x = pixman_fixed_to_double (vector.vector[0]); y = pixman_fixed_to_double (vector.vector[1]); cairo_matrix_transform_point (&inv, &x, &y); /* Ideally, the vector should now be (xc, yc). * We can now compensate for the resulting error. */ x -= xc; y -= yc; cairo_matrix_transform_distance (matrix, &x, &y); dx = _cairo_fixed_16_16_from_double (x); dy = _cairo_fixed_16_16_from_double (y); pixman_transform->matrix[0][2] -= dx; pixman_transform->matrix[1][2] -= dy; if (dx == 0 && dy == 0) break; } while (--max_iterations); } }