/* Now that the gstate is fully initialized and ready for the eventual * _cairo_gstate_fini(), we can check for errors (and not worry about
* the resource deallocation). */ return target->status;
}
/** * _cairo_gstate_init_copy: * * Initialize @gstate by performing a deep copy of state fields from * @other. Note that gstate->next is not copied but is set to %NULL by * this function.
**/ static cairo_status_t
_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
{
cairo_status_t status;
gstate->target = cairo_surface_reference (other->target); /* parent_target is always set to NULL; it's only ever set by redirect_target */
gstate->parent_target = NULL;
gstate->original_target = cairo_surface_reference (other->original_target);
/** * _cairo_gstate_save: * @gstate: input/output gstate pointer * * Makes a copy of the current state of @gstate and saves it * to @gstate->next, then put the address of the newly allcated * copy into @gstate. _cairo_gstate_restore() reverses this.
**/
cairo_status_t
_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
{
cairo_gstate_t *top;
cairo_status_t status;
if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
top = *freelist; if (top == NULL) {
top = _cairo_malloc (sizeof (cairo_gstate_t)); if (unlikely (top == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
*freelist = top->next;
status = _cairo_gstate_init_copy (top, *gstate); if (unlikely (status)) {
top->next = *freelist;
*freelist = top; return status;
}
top->next = *gstate;
*gstate = top;
return CAIRO_STATUS_SUCCESS;
}
/** * _cairo_gstate_restore: * @gstate: input/output gstate pointer * * Reverses the effects of one _cairo_gstate_save() call.
**/
cairo_status_t
_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
{
cairo_gstate_t *top;
top = *gstate; if (top->next == NULL) return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
/** * _cairo_gstate_redirect_target: * @gstate: a #cairo_gstate_t * @child: the new child target * * Redirect @gstate rendering to a "child" target. The original * "parent" target with which the gstate was created will not be * affected. See _cairo_gstate_get_target().
**/
cairo_status_t
_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
{ /* If this gstate is already redirected, this is an error; we need a
* new gstate to be able to redirect */
assert (gstate->parent_target == NULL);
/* Set up our new parent_target based on our current target; * gstate->parent_target will take the ref that is held by gstate->target
*/
gstate->parent_target = gstate->target;
/* Now set up our new target; we overwrite gstate->target directly,
* since its ref is now owned by gstate->parent_target */
gstate->target = cairo_surface_reference (child);
gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform);
cairo_list_move (&gstate->device_transform_observer.link,
&gstate->target->device_transform_observers);
/* The clip is in surface backend coordinates for the previous target;
* translate it into the child's backend coordinates. */
_cairo_clip_destroy (gstate->clip);
gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip,
child->device_transform.x0 - gstate->parent_target->device_transform.x0,
child->device_transform.y0 - gstate->parent_target->device_transform.y0);
return CAIRO_STATUS_SUCCESS;
}
/** * _cairo_gstate_is_group: * @gstate: a #cairo_gstate_t * * Check if _cairo_gstate_redirect_target has been called on the head * of the stack. * * Return value: %TRUE if @gstate is redirected to a target different * than the previous state in the stack, %FALSE otherwise.
**/
cairo_bool_t
_cairo_gstate_is_group (cairo_gstate_t *gstate)
{ return gstate->parent_target != NULL;
}
/** * _cairo_gstate_get_target: * @gstate: a #cairo_gstate_t * * Return the current drawing target; if drawing is not redirected, * this will be the same as _cairo_gstate_get_original_target(). * * Return value: the current target surface
**/
cairo_surface_t *
_cairo_gstate_get_target (cairo_gstate_t *gstate)
{ return gstate->target;
}
/** * _cairo_gstate_get_original_target: * @gstate: a #cairo_gstate_t * * Return the original target with which @gstate was created. This * function always returns the original target independent of any * child target that may have been set with * _cairo_gstate_redirect_target. * * Return value: the original target surface
**/
cairo_surface_t *
_cairo_gstate_get_original_target (cairo_gstate_t *gstate)
{ return gstate->original_target;
}
/** * _cairo_gstate_get_clip: * @gstate: a #cairo_gstate_t * * This space left intentionally blank. * * Return value: a pointer to the gstate's #cairo_clip_t structure.
**/
cairo_clip_t *
_cairo_gstate_get_clip (cairo_gstate_t *gstate)
{ return gstate->clip;
}
cairo_pattern_t *
_cairo_gstate_get_source (cairo_gstate_t *gstate)
{ if (gstate->source == &_cairo_pattern_black.base) { /* do not expose the static object to the user */
gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
}
if (dash_total == 0.0) return _cairo_error (CAIRO_STATUS_INVALID_DASH);
/* An odd dash value indicate symmetric repeating, so the total
* is twice as long. */ if (gstate->stroke_style.num_dashes & 1) {
dash_total *= 2;
on_total += off_total;
}
/* The dashing code doesn't like a negative offset or a big positive * offset, so we compute an equivalent offset which is guaranteed to be
* positive and less than twice the pattern length. */
offset = fmod (offset, dash_total); if (offset < 0.0)
offset += dash_total; if (offset <= 0.0) /* Take care of -0 */
offset = 0.0;
gstate->stroke_style.dash_offset = offset;
if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (! ISFINITE (sx) || ! ISFINITE (sy)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
if (x1)
*x1 = px1; if (y1)
*y1 = py1; if (x2)
*x2 = px2; if (y2)
*y2 = py2;
}
staticvoid
_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original)
{ /* First check if the we can replace the original with a much simpler * pattern. For example, gradients that are uniform or just have a single * stop can sometimes be replaced with a solid.
*/
op = gstate->op; if (op != CAIRO_OPERATOR_SOURCE) return op;
pattern = gstate->source; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; if (solid->color.alpha_short <= 0x00ff) {
op = CAIRO_OPERATOR_CLEAR;
} elseif ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) { if ((solid->color.red_short |
solid->color.green_short |
solid->color.blue_short) <= 0x00ff)
{
op = CAIRO_OPERATOR_CLEAR;
}
}
} elseif (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; if (surface->surface->is_clear &&
surface->surface->content & CAIRO_CONTENT_ALPHA)
{
op = CAIRO_OPERATOR_CLEAR;
}
} else { const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; if (gradient->n_stops == 0)
op = CAIRO_OPERATOR_CLEAR;
}
return op;
}
static cairo_status_t
_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
{ if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
((const cairo_mesh_pattern_t *) pattern)->current_patch))
{ /* If current patch != NULL, the pattern is under construction
* and cannot be used as a source */ return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
}
/* Before we perform the expensive stroke analysis, * check whether the point is within the extents of the path.
*/
_cairo_path_fixed_approximate_stroke_extents (path,
&gstate->stroke_style,
&gstate->ctm,
gstate->target->is_vector,
&extents); if (x < extents.x || x > extents.x + extents.width ||
y < extents.y || y > extents.y + extents.height)
{
*inside_ret = FALSE; return CAIRO_STATUS_SUCCESS;
}
status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status;
*scaled_font = gstate->scaled_font;
return CAIRO_STATUS_SUCCESS;
}
/* * Like everything else in this file, fonts involve Too Many Coordinate Spaces; * it is easy to get confused about what's going on. * * The user's view * --------------- * * Users ask for things in user space. When cairo starts, a user space unit * is about 1/96 inch, which is similar to (but importantly different from) * the normal "point" units most users think in terms of. When a user * selects a font, its scale is set to "one user unit". The user can then * independently scale the user coordinate system *or* the font matrix, in * order to adjust the rendered size of the font. * * Metrics are returned in user space, whether they are obtained from * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t * which is a font specialized to a particular scale matrix, CTM, and target * surface. * * The font's view * --------------- * * Fonts are designed and stored (in say .ttf files) in "font space", which * describes an "EM Square" (a design tile) and has some abstract number * such as 1000, 1024, or 2048 units per "EM". This is basically an * uninteresting space for us, but we need to remember that it exists. * * Font resources (from libraries or operating systems) render themselves * to a particular device. Since they do not want to make most programmers * worry about the font design space, the scaling API is simplified to * involve just telling the font the required pixel size of the EM square * (that is, in device space). * * * Cairo's gstate view * ------------------- * * In addition to the CTM and CTM inverse, we keep a matrix in the gstate * called the "font matrix" which describes the user's most recent * font-scaling or font-transforming request. This is kept in terms of an * abstract scale factor, composed with the CTM and used to set the font's * pixel size. So if the user asks to "scale the font by 12", the matrix * is: * * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] * * It is an affine matrix, like all cairo matrices, where its tx and ty * components are used to "nudging" fonts around and are handled in gstate * and then ignored by the "scaled-font" layer. * * In order to perform any action on a font, we must build an object * called a #cairo_font_scale_t; this contains the central 2x2 matrix * resulting from "font matrix * CTM" (sans the font matrix translation * components as stated in the previous paragraph). * * We pass this to the font when making requests of it, which causes it to * reply for a particular [user request, device] combination, under the CTM * (to accommodate the "zoom in" == "bigger fonts" issue above). * * The other terms in our communication with the font are therefore in * device space. When we ask it to perform text->glyph conversion, it will * produce a glyph string in device space. Glyph vectors we pass to it for * measuring or rendering should be in device space. The metrics which we * get back from the font will be in device space. The contents of the * global glyph image cache will be in device space. * * * Cairo's public view * ------------------- * * Since the values entering and leaving via public API calls are in user * space, the gstate functions typically need to multiply arguments by the * CTM (for user-input glyph vectors), and return values by the CTM inverse * (for font responses such as metrics or glyph vectors). *
*/
/* For really huge font sizes, we can just do path;fill instead of * show_glyphs, as show_glyphs would put excess pressure on the cache, * and moreover, not all components below us correctly handle huge font * sizes. I wanted to set the limit at 256. But alas, seems like cairo's * rasterizer is something like ten times slower than freetype's for huge * sizes. So, no win just yet. For now, do it for insanely-huge sizes, * just to make sure we don't make anyone unhappy. When we get a really * fast rasterizer in cairo, we may want to readjust this. *
* Needless to say, do this only if show_text_glyphs is not available. */ if (cairo_surface_has_show_text_glyphs (gstate->target) ||
_cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240)
{
/** * _cairo_gstate_transform_glyphs_to_backend: * @gstate: a #cairo_gstate_t * @glyphs: the array of #cairo_glyph_t objects to be transformed * @num_glyphs: the number of elements in @glyphs * @transformed_glyphs: a pre-allocated array of at least @num_glyphs * #cairo_glyph_t objects * @num_transformed_glyphs: the number of elements in @transformed_glyphs * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be * dropped * * Transform an array of glyphs to backend space by first adding the offset * of the font matrix, then transforming from user space to backend space. * The result of the transformation is placed in @transformed_glyphs. * * This also uses information from the scaled font and the surface to * cull/drop glyphs that will not be visible.
**/ staticvoid
_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, 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_glyph_t *transformed_glyphs, int *num_transformed_glyphs,
cairo_text_cluster_t *transformed_clusters)
{
cairo_rectangle_int_t surface_extents;
cairo_matrix_t *ctm = &gstate->ctm;
cairo_matrix_t *font_matrix = &gstate->font_matrix;
cairo_matrix_t *device_transform = &gstate->target->device_transform;
cairo_bool_t drop = FALSE; double x1 = 0, x2 = 0, y1 = 0, y2 = 0; int i, j, k;
drop = TRUE; if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
drop = FALSE; /* unbounded surface */
} else { double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); if (surface_extents.width == 0 || surface_extents.height == 0) { /* No visible area. Don't draw anything */
*num_transformed_glyphs = 0; return;
} /* XXX We currently drop any glyphs that have their position outside * of the surface boundaries by a safety margin depending on the * font scale. This however can fail in extreme cases where the * font has really long swashes for example... We can correctly * handle that by looking the glyph up and using its device bbox * to device if it's going to be visible, but I'm not inclined to * do that now.
*/
x1 = surface_extents.x - scale10;
y1 = surface_extents.y - scale10;
x2 = surface_extents.x + (int) surface_extents.width + scale10;
y2 = surface_extents.y + (int) surface_extents.height + scale10;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.