static cairo_bool_t _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) { return (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) || ! _cairo_matrix_is_identity (&wrapper->transform) || ! _cairo_matrix_is_identity (&wrapper->target->device_transform); }
static void _cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *m) { cairo_matrix_init_identity (m); if (! _cairo_matrix_is_identity (&wrapper->transform)) cairo_matrix_multiply (m, &wrapper->transform, m); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) cairo_matrix_multiply (m, &wrapper->target->device_transform, m); }
static void _cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *m) { cairo_matrix_init_identity (m); if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y)) cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y); if (! _cairo_matrix_is_identity (&wrapper->transform)) cairo_matrix_multiply (m, &wrapper->transform, m); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) cairo_matrix_multiply (m, &wrapper->target->device_transform, m); }
cairo_private void _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, cairo_matrix_t *ctm) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; surface->ctm = *ctm; surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); }
static void _copy_transformed_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original, const cairo_matrix_t *ctm_inverse) { _cairo_pattern_init_static_copy (pattern, original); if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); }
static void _cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *m) { cairo_matrix_init_identity (m); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse)) cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m); if (! _cairo_matrix_is_identity (&wrapper->transform)) { cairo_matrix_t inv; cairo_status_t status; inv = wrapper->transform; status = cairo_matrix_invert (&inv); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (m, &inv, m); } }
static cairo_bool_t _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *matrix) { if (_cairo_matrix_is_identity (&wrapper->target->device_transform)) return FALSE; *matrix = wrapper->target->device_transform; return TRUE; }
static cairo_int_status_t _analyze_meta_surface_pattern (cairo_analysis_surface_t *surface, const cairo_pattern_t *pattern) { cairo_surface_t *analysis = &surface->base; const cairo_surface_pattern_t *surface_pattern; cairo_status_t status; cairo_bool_t old_has_ctm; cairo_matrix_t old_ctm, p2d; cairo_rectangle_int_t old_clip; cairo_rectangle_int_t meta_extents; int old_width; int old_height; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; assert (_cairo_surface_is_meta (surface_pattern->surface)); old_width = surface->width; old_height = surface->height; old_clip = surface->current_clip; status = _cairo_surface_get_extents (surface_pattern->surface, &meta_extents); if (_cairo_status_is_error (status)) return status; surface->width = meta_extents.width; surface->height = meta_extents.height; surface->current_clip.x = 0; surface->current_clip.y = 0; surface->current_clip.width = surface->width; surface->current_clip.height = surface->height; old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; p2d = pattern->matrix; status = cairo_matrix_invert (&p2d); /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm); surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); status = _cairo_meta_surface_replay_and_create_regions (surface_pattern->surface, analysis); if (status == CAIRO_STATUS_SUCCESS) status = analysis->status; surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; surface->current_clip = old_clip; surface->width = old_width; surface->height = old_height; return status; }
static void _cairo_xml_emit_matrix (cairo_xml_t *xml, const cairo_matrix_t *matrix) { if (! _cairo_matrix_is_identity (matrix)) { _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>", matrix->xx, matrix->yx, matrix->xy, matrix->yy, matrix->x0, matrix->y0); } }
void _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, cairo_matrix_t *ctm) { cairo_analysis_surface_t *surface; if (abstract_surface->status) return; surface = (cairo_analysis_surface_t *) abstract_surface; surface->ctm = *ctm; surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); }
static cairo_int_status_t _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, const cairo_pattern_t *pattern) { const cairo_surface_pattern_t *surface_pattern; cairo_analysis_surface_t *tmp; cairo_surface_t *source, *proxy; cairo_matrix_t p2d; cairo_status_t status, analysis_status; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); source = surface_pattern->surface; proxy = _cairo_surface_has_snapshot (source, &proxy_backend); if (proxy != NULL) { /* nothing untoward found so far */ return CAIRO_STATUS_SUCCESS; } tmp = (cairo_analysis_surface_t *) _cairo_analysis_surface_create (surface->target); if (unlikely (tmp->base.status)) return tmp->base.status; proxy = attach_proxy (source, &tmp->base); p2d = pattern->matrix; status = cairo_matrix_invert (&p2d); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm); tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm); if (_cairo_surface_is_snapshot (source)) source = _cairo_surface_snapshot_get_target (source); if (_cairo_surface_is_subsurface (source)) source = _cairo_surface_subsurface_get_target (source); status = _cairo_recording_surface_replay_and_create_regions (source, &tmp->base); analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS; detach_proxy (proxy); cairo_surface_destroy (&tmp->base); if (unlikely (status)) return status; return analysis_status; }
cairo_status_t _cairo_clip_init_copy_transformed (cairo_clip_t *clip, cairo_clip_t *other, const cairo_matrix_t *matrix) { cairo_status_t status = CAIRO_STATUS_SUCCESS; int tx, ty; if (other == NULL) { _cairo_clip_init (clip); return CAIRO_STATUS_SUCCESS; } if (other->all_clipped) { _cairo_clip_init (clip); clip->all_clipped = TRUE; return CAIRO_STATUS_SUCCESS; } if (_cairo_matrix_is_identity (matrix)) { _cairo_clip_init_copy (clip, other); return CAIRO_STATUS_SUCCESS; } if (other->path != NULL) { _cairo_clip_init (clip); /* if we only need to translate, so we can reuse the caches... */ /* XXX we still loose the benefit of constructs when the copy is * deleted though. Indirect clip_paths? */ if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) { status = _cairo_clip_path_reapply_clip_path_translate (clip, other->path, tx, ty); } else { status = _cairo_clip_path_reapply_clip_path_transform (clip, other->path, matrix); if (clip->path->extents.width == 0 && clip->path->extents.height == 0) { _cairo_clip_set_all_clipped (clip); } } } return status; }
void _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, cairo_surface_t *target) { wrapper->target = cairo_surface_reference (target); cairo_matrix_init_identity (&wrapper->transform); wrapper->has_extents = FALSE; wrapper->extents.x = wrapper->extents.y = 0; wrapper->clip = NULL; wrapper->needs_transform = FALSE; if (target) { wrapper->needs_transform = ! _cairo_matrix_is_identity (&target->device_transform); } }
static cairo_clip_t * _cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper, const cairo_clip_t *clip) { cairo_clip_t *copy; copy = _cairo_clip_copy (clip); if (wrapper->has_extents) { copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents); } copy = _cairo_clip_transform (copy, &wrapper->transform); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) copy = _cairo_clip_transform (copy, &wrapper->target->device_transform); if (wrapper->clip) copy = _cairo_clip_intersect_clip (copy, wrapper->clip); return copy; }
void _cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper, const cairo_matrix_t *transform) { cairo_status_t status; if (transform == NULL || _cairo_matrix_is_identity (transform)) { cairo_matrix_init_identity (&wrapper->transform); wrapper->needs_transform = _cairo_surface_wrapper_needs_device_transform (wrapper); } else { wrapper->transform = *transform; status = cairo_matrix_invert (&wrapper->transform); /* should always be invertible unless given pathological input */ assert (status == CAIRO_STATUS_SUCCESS); wrapper->needs_transform = TRUE; } }
static void _cairo_matrix_to_render_matrix (const cairo_matrix_t *matrix, s_render_matrix_t *render_matrix) { static const s_render_matrix_t render_identity_matrix = {{ {1 << 16, 0, 0}, { 0, 1 << 16, 0}, { 0, 0, 1 << 16} }}; if (_cairo_matrix_is_identity(matrix)) { *render_matrix = render_identity_matrix; } else { render_matrix->matrix[0][0] = _cairo_fixed_from_double(matrix->xx); render_matrix->matrix[0][1] = _cairo_fixed_from_double(matrix->xy); render_matrix->matrix[0][2] = _cairo_fixed_from_double(matrix->x0); render_matrix->matrix[1][0] = _cairo_fixed_from_double(matrix->yx); render_matrix->matrix[1][1] = _cairo_fixed_from_double(matrix->yy); render_matrix->matrix[1][2] = _cairo_fixed_from_double(matrix->y0); render_matrix->matrix[2][0] = 0; render_matrix->matrix[2][1] = 0; render_matrix->matrix[2][2] = 1 << 16; } }
static inline QPainterPath path_to_qt (const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse = NULL) { qpainter_path_data data; cairo_status_t status; if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse)) ctm_inverse = NULL; data.ctm_inverse = ctm_inverse; status = _cairo_path_fixed_interpret (path, _cairo_path_to_qpainterpath_move_to, _cairo_path_to_qpainterpath_line_to, _cairo_path_to_qpainterpath_curve_to, _cairo_path_to_qpainterpath_close_path, &data); assert (status == CAIRO_STATUS_SUCCESS); return data.path; }
static inline SkPath path_to_sk (cairo_path_fixed_t *path, cairo_matrix_t *mat = NULL) { struct cpc data; cairo_status_t status; if (mat && _cairo_matrix_is_identity (mat)) mat = NULL; data.matrix = mat; status = _cairo_path_fixed_interpret (path, cpc_move_to, cpc_line_to, cpc_curve_to, cpc_close_path, &data); assert (status == CAIRO_STATUS_SUCCESS); return data.skPath; }
static void _copy_transformed_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original, const cairo_matrix_t *ctm_inverse) { _cairo_pattern_init_static_copy (pattern, original); /* apply device_transform first so that it is transformed by ctm_inverse */ if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern; cairo_surface_t *surface; surface_pattern = (cairo_surface_pattern_t *) original; surface = surface_pattern->surface; if (_cairo_surface_has_device_transform (surface)) _cairo_pattern_transform (pattern, &surface->device_transform); } if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); }
static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; double slope_dx, slope_dy; slope_dx = _cairo_fixed_to_double (dev_slope->dx); slope_dy = _cairo_fixed_to_double (dev_slope->dy); face->length = normalize_slope (&slope_dx, &slope_dy); face->dev_slope.x = slope_dx; face->dev_slope.y = slope_dy; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { /* Normalize the matrix! */ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); normalize_slope (&slope_dx, &slope_dy); if (stroker->ctm_det_positive) { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } else { face_dx = slope_dy * (stroker->style.line_width / 2.0); face_dy = - slope_dx * (stroker->style.line_width / 2.0); } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); } else { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; }
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_has_unity_scale (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); } }
void _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, pixman_transform_t *pixman_transform) { 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 = *matrix; double x = 0, y = 0; pixman_vector_t vector; 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 (0, 0) in pattern * space and adjust pixman's transform to agree with cairo's at * that point. */ /* Note: If we can't invert the transformation, skip the adjustment. */ if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) return; /* find the device space coordinate that maps to (0, 0) */ cairo_matrix_transform_point (&inv, &x, &y); /* transform the resulting device space coordinate back * to the pattern space, using pixman's transform */ vector.vector[0] = _cairo_fixed_16_16_from_double (x); vector.vector[1] = _cairo_fixed_16_16_from_double (y); vector.vector[2] = 1 << 16; if (!pixman_transform_point_3d (pixman_transform, &vector)) return; /* Ideally, the vector should now be (0, 0). We can now compensate * for the resulting error */ pixman_transform->matrix[0][2] -= vector.vector[0]; pixman_transform->matrix[1][2] -= vector.vector[1]; } }
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; }
static inline cairo_bool_t _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) { return ! _cairo_matrix_is_identity (&wrapper->target->device_transform); }
static cairo_status_t _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *pattern, cairo_gl_surface_t *dst, cairo_bool_t use_texgen) { const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern; cairo_status_t status; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (! _cairo_gl_device_has_glsl (dst->base.device)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_create_gradient_texture (dst, gradient, &operand->gradient.gradient); if (unlikely (status)) return status; if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; double x0, y0, dx, dy, sf, offset; dx = linear->pd2.x - linear->pd1.x; dy = linear->pd2.y - linear->pd1.y; sf = 1.0 / (dx * dx + dy * dy); dx *= sf; dy *= sf; x0 = linear->pd1.x; y0 = linear->pd1.y; offset = dx * x0 + dy * y0; operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT; cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0); if (! _cairo_matrix_is_identity (&pattern->matrix)) { cairo_matrix_multiply (&operand->gradient.m, &pattern->matrix, &operand->gradient.m); } } else { cairo_matrix_t m; cairo_circle_double_t circles[2]; double x0, y0, r0, dx, dy, dr; /* * Some fragment shader implementations use half-floats to * represent numbers, so the maximum number they can represent * is about 2^14. Some intermediate computations used in the * radial gradient shaders can produce results of up to 2*k^4. * Setting k=8 makes the maximum result about 8192 (assuming * that the extreme circles are not much smaller than the * destination image). */ _cairo_gradient_pattern_fit_to_range (gradient, 8., &operand->gradient.m, circles); x0 = circles[0].center.x; y0 = circles[0].center.y; r0 = circles[0].radius; dx = circles[1].center.x - x0; dy = circles[1].center.y - y0; dr = circles[1].radius - r0; operand->gradient.a = dx * dx + dy * dy - dr * dr; operand->gradient.radius_0 = r0; operand->gradient.circle_d.center.x = dx; operand->gradient.circle_d.center.y = dy; operand->gradient.circle_d.radius = dr; if (operand->gradient.a == 0) operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0; else if (pattern->extend == CAIRO_EXTEND_NONE) operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE; else operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT; cairo_matrix_init_translate (&m, -x0, -y0); cairo_matrix_multiply (&operand->gradient.m, &operand->gradient.m, &m); } operand->gradient.extend = pattern->extend; operand->gradient.texgen = use_texgen; return CAIRO_STATUS_SUCCESS; }
static SkShader* source_to_sk_shader (cairo_skia_context_t *cr, const cairo_pattern_t *pattern) { SkShader *shader = NULL; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; return new SkColorShader (color_to_sk (solid->color)); } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *surface = surface_from_pattern (pattern); cr->source = cairo_surface_reference (surface); if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; shader = SkShader::CreateBitmapShader (*esurf->bitmap, extend_to_sk (pattern->extend), extend_to_sk (pattern->extend)); } else { SkBitmap bitmap; if (! _cairo_surface_is_image (surface)) { cairo_status_t status; status = _cairo_surface_acquire_source_image (surface, &cr->source_image, &cr->source_extra); if (status) return NULL; surface = &cr->source_image->base; } if (unlikely (! surface_to_sk_bitmap (surface, bitmap))) return NULL; shader = SkShader::CreateBitmapShader (bitmap, extend_to_sk (pattern->extend), extend_to_sk (pattern->extend)); } } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */) { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; SkColor colors_stack[10]; SkScalar pos_stack[10]; SkColor *colors = colors_stack; SkScalar *pos = pos_stack; if (gradient->n_stops > 10) { colors = new SkColor[gradient->n_stops]; pos = new SkScalar[gradient->n_stops]; } for (unsigned int i = 0; i < gradient->n_stops; i++) { pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset); colors[i] = color_stop_to_sk (gradient->stops[i].color); } if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; SkPoint points[2]; points[0].set (SkFloatToScalar (linear->pd1.x), SkFloatToScalar (linear->pd1.y)); points[1].set (SkFloatToScalar (linear->pd2.x), SkFloatToScalar (linear->pd2.y)); shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops, extend_to_sk (pattern->extend)); } else { // XXX todo -- implement real radial shaders in Skia } if (gradient->n_stops > 10) { delete [] colors; delete [] pos; } } if (shader && ! _cairo_matrix_is_identity (&pattern->matrix)) shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix)); return shader; }
PatternToBrushConverter (const cairo_pattern_t *pattern) : mAcquiredImageParent(0), mAcquiredImage(0), mAcquiredImageExtra(0) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; QColor color; color.setRgbF(solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); mBrush = QBrush(color); } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; cairo_surface_t *surface = spattern->surface; if (surface->type == CAIRO_SURFACE_TYPE_QT) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; if (qs->image) { mBrush = QBrush(*qs->image); } else if (qs->pixmap) { mBrush = QBrush(*qs->pixmap); } else { // do something smart mBrush = QBrush(0xff0000ff); } } else { cairo_image_surface_t *isurf = NULL; if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { isurf = (cairo_image_surface_t*) surface; } else { void *image_extra; if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) { mAcquiredImageParent = surface; mAcquiredImage = isurf; mAcquiredImageExtra = image_extra; } else { isurf = NULL; } } if (isurf) { mBrush = QBrush (QImage ((const uchar *) isurf->data, isurf->width, isurf->height, isurf->stride, _qimage_format_from_cairo_format (isurf->format))); } else { mBrush = QBrush(0x0000ffff); } } } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { QGradient *grad; cairo_bool_t reverse_stops = FALSE; cairo_bool_t emulate_reflect = FALSE; double offset = 0.0; cairo_extend_t extend = pattern->extend; cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern; if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y, lpat->pd2.x, lpat->pd2.y); } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern; /* Based on the SVG surface code */ cairo_circle_double_t *c0, *c1; double x0, y0, r0, x1, y1, r1; if (rpat->cd1.radius < rpat->cd2.radius) { c0 = &rpat->cd1; c1 = &rpat->cd2; reverse_stops = FALSE; } else { c0 = &rpat->cd2; c1 = &rpat->cd1; reverse_stops = TRUE; } x0 = c0->center.x; y0 = c0->center.y; r0 = c0->radius; x1 = c1->center.x; y1 = c1->center.y; r1 = c1->radius; if (r0 == r1) { grad = new QRadialGradient (x1, y1, r1, x1, y1); } else { double fx = (r1 * x0 - r0 * x1) / (r1 - r0); double fy = (r1 * y0 - r0 * y1) / (r1 - r0); /* QPainter doesn't support the inner circle and use instead a gradient focal. * That means we need to emulate the cairo behaviour by processing the * cairo gradient stops. * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, * it's just a matter of stop position translation and calculation of * the corresponding SVG radial gradient focal. * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop * list that maps to the original cairo stop list. */ if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { double r_org = r1; double r, x, y; if (extend == CAIRO_EXTEND_REFLECT) { r1 = 2 * r1 - r0; emulate_reflect = TRUE; } offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; r = r1 - r0; /* New position of outer circle. */ x = r * (x1 - fx) / r_org + fx; y = r * (y1 - fy) / r_org + fy; x1 = x; y1 = y; r1 = r; r0 = 0.0; } else { offset = r0 / r1; } grad = new QRadialGradient (x1, y1, r1, fx, fy); if (extend == CAIRO_EXTEND_NONE && r0 != 0.0) grad->setColorAt (r0 / r1, Qt::transparent); } } switch (extend) { case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_PAD: grad->setSpread(QGradient::PadSpread); grad->setColorAt (0.0, Qt::transparent); grad->setColorAt (1.0, Qt::transparent); break; case CAIRO_EXTEND_REFLECT: grad->setSpread(QGradient::ReflectSpread); break; case CAIRO_EXTEND_REPEAT: grad->setSpread(QGradient::RepeatSpread); break; } for (unsigned int i = 0; i < gpat->n_stops; i++) { int index = i; if (reverse_stops) index = gpat->n_stops - i - 1; double offset = gpat->stops[i].offset; QColor color; color.setRgbF (gpat->stops[i].color.red, gpat->stops[i].color.green, gpat->stops[i].color.blue, gpat->stops[i].color.alpha); if (emulate_reflect) { offset = offset / 2.0; grad->setColorAt (1.0 - offset, color); } grad->setColorAt (offset, color); } mBrush = QBrush(*grad); delete grad; } if (mBrush.style() != Qt::NoBrush && pattern->type != CAIRO_PATTERN_TYPE_SOLID && ! _cairo_matrix_is_identity (&pattern->matrix)) { cairo_matrix_t pm = pattern->matrix; cairo_status_t status = cairo_matrix_invert (&pm); assert (status == CAIRO_STATUS_SUCCESS); mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm)); } }