/** * _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; }
/* Emit the string of glyphs using the 'TJ' operator. * * The TJ operator takes an array of strings of glyphs. Each string of * glyphs is displayed using the glyph advances of each glyph to * position the glyphs. A relative adjustment to the glyph advance may * be specified by including the adjustment between two strings. The * adjustment is in units of text space * -1000. */ static cairo_status_t _cairo_pdf_operators_emit_glyph_string_with_positioning ( cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream) { int i; _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) { double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; int rounded_delta; delta = -1000.0*delta; /* As the delta is in 1/1000 of a unit of text space, * rounding to an integer should still provide sufficient * precision. We round the delta before adding to Tm_x so * that we keep track of the accumulated rounding error in * the PDF interpreter and compensate for it when * calculating subsequent deltas. */ rounded_delta = _cairo_lround (delta); if (abs(rounded_delta) < 3) rounded_delta = 0; if (rounded_delta != 0) { if (pdf_operators->is_latin) { _cairo_output_stream_printf (stream, ")%d(", rounded_delta); } else { _cairo_output_stream_printf (stream, ">%d<", rounded_delta); } } /* Convert the rounded delta back to text * space before adding to the current text * position. */ delta = rounded_delta/-1000.0; pdf_operators->cur_x += delta; } _cairo_pdf_operators_emit_glyph_index (pdf_operators, stream, pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); }
static cairo_status_t render_glyphs (cairo_gl_surface_t *dst, int dst_x, int dst_y, cairo_operator_t op, cairo_surface_t *source, cairo_composite_glyphs_info_t *info, cairo_bool_t *has_component_alpha, cairo_clip_t *clip, cairo_bool_t via_msaa_compositor) { cairo_format_t last_format = CAIRO_FORMAT_INVALID; cairo_gl_glyph_cache_t *cache = NULL; cairo_gl_context_t *ctx; cairo_gl_composite_t setup; cairo_int_status_t status; int i = 0; cairo_bool_t is_argb32; TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, info->extents.x, info->extents.y, info->extents.width, info->extents.height)); *has_component_alpha = FALSE; status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; /* Traps compositor never has CLEAR operator. */ is_argb32 = info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || info->font->options.antialias == CAIRO_ANTIALIAS_BEST; /* If we are invoked by traps compositor, we keep what is in code otherwise, we handle non-subpixel/best directly in msaa compositor. */ if (!via_msaa_compositor) status = _cairo_gl_composite_init (&setup, op, dst, TRUE); else if (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || info->font->options.antialias == CAIRO_ANTIALIAS_BEST) status = _cairo_gl_composite_init (&setup, op, dst, TRUE); else status = _cairo_gl_composite_init (&setup, op, dst, FALSE); if (unlikely (status)) goto FINISH; if (source == NULL) { _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); } else { _cairo_gl_composite_set_source_operand (&setup, source_to_operand (source)); } if (setup.src.type == CAIRO_GL_OPERAND_CONSTANT) setup.src.use_color_attribute = TRUE; _cairo_gl_composite_set_clip (&setup, clip); for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; cairo_gl_glyph_t *glyph; double x_offset, y_offset; double x1, x2, y1, y2; status = _cairo_scaled_glyph_lookup (info->font, info->glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto FINISH; if (scaled_glyph->surface->width == 0 || scaled_glyph->surface->height == 0) { continue; } if (scaled_glyph->surface->format != last_format) { status = cairo_gl_context_get_glyph_cache (ctx, scaled_glyph->surface->format, &cache); if (unlikely (status)) goto FINISH; last_format = scaled_glyph->surface->format; /* In msaa compositor, clear operator needs component alpha, we need to reset to FALSE if previously clear operator has set it to TRUE. */ if (via_msaa_compositor) { if (op == CAIRO_OPERATOR_CLEAR || is_argb32) cache->surface->operand.texture.attributes.has_component_alpha = TRUE; else cache->surface->operand.texture.attributes.has_component_alpha = FALSE; } _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; /* XXX Shoot me. */ status = _cairo_gl_composite_begin (&setup, &ctx); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) goto FINISH; } if (scaled_glyph->dev_private_key != cache) { cairo_scaled_glyph_private_t *priv; priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); if (priv) { scaled_glyph->dev_private_key = cache; scaled_glyph->dev_private = cairo_container_of (priv, cairo_gl_glyph_t, base); } else { status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* Cache is full, so flush existing prims and try again. */ _cairo_gl_composite_flush (ctx); _cairo_gl_glyph_cache_unlock (cache); status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); } if (unlikely (_cairo_int_status_is_error (status))) goto FINISH; } } x_offset = scaled_glyph->surface->base.device_transform.x0; y_offset = scaled_glyph->surface->base.device_transform.y0; x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); x2 = x1 + scaled_glyph->surface->width; y2 = y1 + scaled_glyph->surface->height; glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); _cairo_gl_composite_emit_glyph (ctx, x1, y1, x2, y2, glyph->p1.x, glyph->p1.y, glyph->p2.x, glyph->p2.y); } status = CAIRO_STATUS_SUCCESS; FINISH: status = _cairo_gl_context_release (ctx, status); _cairo_gl_composite_fini (&setup); return status; }
static cairo_int_status_t _cairo_user_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_user_scaled_font_t *scaled_font = abstract_font; cairo_surface_t *meta_surface = scaled_glyph->meta_surface; if (!scaled_glyph->meta_surface) { cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; cairo_text_extents_t extents = scaled_font->default_glyph_extents; cairo_t *cr; cr = _cairo_user_scaled_font_create_meta_context (scaled_font); if (face->scaled_font_methods.render_glyph) status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); else status = CAIRO_STATUS_USER_FONT_ERROR; if (status == CAIRO_STATUS_SUCCESS) status = cairo_status (cr); meta_surface = cairo_surface_reference (cairo_get_target (cr)); cairo_destroy (cr); if (status) { cairo_surface_destroy (meta_surface); return status; } _cairo_scaled_glyph_set_meta_surface (scaled_glyph, &scaled_font->base, meta_surface); /* set metrics */ if (extents.width == 0.) { /* Compute extents.x/y/width/height from meta_surface, in font space */ cairo_box_t bbox; double x1, y1, x2, y2; double x_scale, y_scale; cairo_surface_t *null_surface; cairo_surface_t *analysis_surface; null_surface = _cairo_null_surface_create (cairo_surface_get_content (meta_surface)); analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); cairo_surface_destroy (null_surface); _cairo_analysis_surface_set_ctm (analysis_surface, &scaled_font->extent_scale); status = _cairo_meta_surface_replay (meta_surface, analysis_surface); _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); cairo_surface_destroy (analysis_surface); if (status) return status; _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); x_scale = scaled_font->extent_x_scale; y_scale = scaled_font->extent_y_scale; extents.x_bearing = x1 * x_scale; extents.y_bearing = y1 * y_scale; extents.width = (x2 - x1) * x_scale; extents.height = (y2 - y1) * y_scale; } if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale; extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale; } _cairo_scaled_glyph_set_metrics (scaled_glyph, &scaled_font->base, &extents); } if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { cairo_surface_t *surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_format_t format; int width, height; /* TODO * extend the glyph cache to support argb glyphs. * need to figure out the semantics and interaction with subpixel * rendering first. */ width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); switch (scaled_font->base.options.antialias) { default: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; } surface = cairo_image_surface_create (format, width, height); cairo_surface_set_device_offset (surface, - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); status = _cairo_meta_surface_replay (meta_surface, surface); if (status) { cairo_surface_destroy(surface); return status; } _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *) surface); } if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { cairo_path_fixed_t *path = _cairo_path_fixed_create (); if (!path) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_meta_surface_get_path (meta_surface, path); if (status) { _cairo_path_fixed_destroy (path); return status; } _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); } return status; }
static cairo_int_status_t composite_glyphs (cairo_image_surface_t *dst, void *closure, cairo_operator_t op, const cairo_pattern_t *pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents) { cairo_composite_glyphs_info_t *info = closure; pixman_image_t *mask; cairo_status_t status; int i; mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height, NULL, 0); if (unlikely (mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = CAIRO_STATUS_SUCCESS; _cairo_scaled_font_freeze_cache (info->font); for (i = 0; i < info->num_glyphs; i++) { cairo_image_surface_t *glyph_surface; cairo_scaled_glyph_t *scaled_glyph; unsigned long glyph_index = info->glyphs[i].index; int x, y; status = _cairo_scaled_glyph_lookup (info->font, glyph_index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) break; glyph_surface = scaled_glyph->surface; if (glyph_surface->width && glyph_surface->height) { /* round glyph locations to the nearest pixel */ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ x = _cairo_lround (info->glyphs[i].x - glyph_surface->base.device_transform.x0); y = _cairo_lround (info->glyphs[i].y - glyph_surface->base.device_transform.y0); pixman_image_composite32 (PIXMAN_OP_ADD, glyph_surface->pixman_image, NULL, mask, 0, 0, 0, 0, x - extents->x, y - extents->y, glyph_surface->width, glyph_surface->height); } } _cairo_scaled_font_thaw_cache (info->font); if (status == CAIRO_STATUS_SUCCESS) { cairo_rectangle_int_t sample; pixman_image_t *src; int src_x, src_y; _cairo_pattern_sampled_area (pattern, extents, &sample); src = _pixman_image_for_pattern (dst, pattern, FALSE, extents, &sample, &src_x, &src_y); if (src != NULL) { dst_x = extents->x - dst_x; dst_y = extents->y - dst_y; pixman_image_composite32 (_pixman_operator (op), src, mask, dst->pixman_image, src_x + dst_x, src_y + dst_y, 0, 0, dst_x, dst_y, extents->width, extents->height); pixman_image_unref (src); } else status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } pixman_image_unref (mask); return status; }
cairo_int_status_t _cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_bool_t glyph_indexing) { #if CAIRO_HAS_WIN32_FONT WORD glyph_buf_stack[STACK_GLYPH_SIZE]; WORD *glyph_buf = glyph_buf_stack; int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; int *dxy_buf = dxy_buf_stack; BOOL win_result = 0; int i, j; cairo_solid_pattern_t *solid_pattern; COLORREF color; cairo_matrix_t device_to_logical; int start_x, start_y; double user_x, user_y; int logical_x, logical_y; unsigned int glyph_index_option; /* We can only handle win32 fonts */ assert (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32); /* We can only handle opaque solid color sources and destinations */ assert (_cairo_pattern_is_opaque_solid(source)); assert (dst->format == CAIRO_FORMAT_RGB24); solid_pattern = (cairo_solid_pattern_t *)source; color = RGB(((int)solid_pattern->color.red_short) >> 8, ((int)solid_pattern->color.green_short) >> 8, ((int)solid_pattern->color.blue_short) >> 8); cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); SaveDC(dst->dc); cairo_win32_scaled_font_select_font(scaled_font, dst->dc); SetTextColor(dst->dc, color); SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); SetBkMode(dst->dc, TRANSPARENT); if (num_glyphs > STACK_GLYPH_SIZE) { glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); } /* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all * previous dx values may start to diverge from the current glyph's x * coordinate due to accumulated rounding error. As a result strings could * be painted shorter or longer than expected. */ user_x = glyphs[0].x; user_y = glyphs[0].y; cairo_matrix_transform_point(&device_to_logical, &user_x, &user_y); logical_x = _cairo_lround (user_x); logical_y = _cairo_lround (user_y); start_x = logical_x; start_y = logical_y; for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { glyph_buf[i] = (WORD) glyphs[i].index; if (i == num_glyphs - 1) { dxy_buf[j] = 0; dxy_buf[j+1] = 0; } else { double next_user_x = glyphs[i+1].x; double next_user_y = glyphs[i+1].y; int next_logical_x, next_logical_y; cairo_matrix_transform_point(&device_to_logical, &next_user_x, &next_user_y); next_logical_x = _cairo_lround (next_user_x); next_logical_y = _cairo_lround (next_user_y); dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y); logical_x = next_logical_x; logical_y = next_logical_y; } } if (glyph_indexing) glyph_index_option = ETO_GLYPH_INDEX; else glyph_index_option = 0; win_result = ExtTextOutW(dst->dc, start_x, start_y, glyph_index_option | ETO_PDY, NULL, glyph_buf, num_glyphs, dxy_buf); if (!win_result) { _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); } RestoreDC(dst->dc, -1); if (glyph_buf != glyph_buf_stack) { free(glyph_buf); free(dxy_buf); } return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif }
static cairo_status_t render_glyphs (cairo_gl_surface_t *dst, int dst_x, int dst_y, cairo_operator_t op, cairo_surface_t *source, cairo_composite_glyphs_info_t *info, cairo_bool_t *has_component_alpha, cairo_clip_t *clip) { cairo_format_t last_format = CAIRO_FORMAT_INVALID; cairo_gl_glyph_cache_t *cache = NULL; cairo_gl_context_t *ctx; cairo_gl_emit_glyph_t emit = NULL; cairo_gl_composite_t setup; cairo_int_status_t status; int i = 0; TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, info->extents.x, info->extents.y, info->extents.width, info->extents.height)); *has_component_alpha = FALSE; status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; status = _cairo_gl_composite_init (&setup, op, dst, TRUE); if (unlikely (status)) goto FINISH; if (source == NULL) { _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); } else { _cairo_gl_composite_set_source_operand (&setup, source_to_operand (source)); } _cairo_gl_composite_set_clip (&setup, clip); for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; cairo_gl_glyph_t *glyph; double x_offset, y_offset; double x1, x2, y1, y2; status = _cairo_scaled_glyph_lookup (info->font, info->glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto FINISH; if (scaled_glyph->surface->width == 0 || scaled_glyph->surface->height == 0) { continue; } if (scaled_glyph->surface->format != last_format) { status = cairo_gl_context_get_glyph_cache (ctx, scaled_glyph->surface->format, &cache); if (unlikely (status)) goto FINISH; last_format = scaled_glyph->surface->format; _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; /* XXX Shoot me. */ status = _cairo_gl_composite_begin (&setup, &ctx); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) goto FINISH; emit = _cairo_gl_context_choose_emit_glyph (ctx); } if (scaled_glyph->dev_private_key != cache) { cairo_scaled_glyph_private_t *priv; priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); if (priv) { scaled_glyph->dev_private_key = cache; scaled_glyph->dev_private = cairo_container_of (priv, cairo_gl_glyph_t, base); } else { status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* Cache is full, so flush existing prims and try again. */ _cairo_gl_composite_flush (ctx); _cairo_gl_glyph_cache_unlock (cache); status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); } if (unlikely (_cairo_int_status_is_error (status))) goto FINISH; } } x_offset = scaled_glyph->surface->base.device_transform.x0; y_offset = scaled_glyph->surface->base.device_transform.y0; x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); x2 = x1 + scaled_glyph->surface->width; y2 = y1 + scaled_glyph->surface->height; glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); assert (emit); emit (ctx, x1, y1, x2, y2, glyph->p1.x, glyph->p1.y, glyph->p2.x, glyph->p2.y); } status = CAIRO_STATUS_SUCCESS; FINISH: status = _cairo_gl_context_release (ctx, status); _cairo_gl_composite_fini (&setup); return status; }
static cairo_status_t _render_glyphs (cairo_gl_surface_t *dst, int dst_x, int dst_y, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, const cairo_rectangle_int_t *glyph_extents, cairo_scaled_font_t *scaled_font, cairo_region_t *clip_region, int *remaining_glyphs) { cairo_format_t last_format = (cairo_format_t) -1; cairo_gl_glyph_cache_t *cache = NULL; cairo_gl_context_t *ctx; cairo_gl_glyphs_setup_t setup; cairo_gl_composite_setup_t composite_setup; cairo_status_t status; int i = 0; GLuint vbo = 0; status = _cairo_gl_operand_init (&composite_setup.src, source, dst, glyph_extents->x, glyph_extents->y, dst_x, dst_y, glyph_extents->width, glyph_extents->height); if (unlikely (status)) return status; ctx = _cairo_gl_context_acquire (dst->ctx); /* Set up the mask to source from the incoming vertex color. */ glActiveTexture (GL_TEXTURE1); glEnable (GL_TEXTURE_2D); /* IN: dst.argb = src.argb * mask.aaaa */ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE1); glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE1); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); _cairo_gl_set_destination (dst); _cairo_gl_set_operator (dst, op); _cairo_gl_set_src_operand (ctx, &composite_setup); _cairo_scaled_font_freeze_cache (scaled_font); if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto CLEANUP_FONT; } if (scaled_font->surface_private == NULL) { /* XXX couple into list to remove on context destruction */ scaled_font->surface_private = ctx; scaled_font->surface_backend = &_cairo_gl_surface_backend; } /* Create our VBO so that we can accumulate a bunch of glyph primitives * into one giant DrawArrays. */ memset(&setup, 0, sizeof(setup)); setup.composite = &composite_setup; setup.clip = clip_region; setup.dst = dst; setup.vertex_size = 4; if (composite_setup.src.type == OPERAND_TEXTURE) setup.vertex_size += 2; setup.vbo_size = num_glyphs * 4 * setup.vertex_size; if (setup.vbo_size > 4096) setup.vbo_size = 4096; glGenBuffersARB (1, &vbo); glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo); glVertexPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), (void *)(uintptr_t)(0)); glEnableClientState (GL_VERTEX_ARRAY); if (composite_setup.src.type == OPERAND_TEXTURE) { /* Note that we're packing texcoord 0 after texcoord 1, for * convenience. */ glClientActiveTexture (GL_TEXTURE0); glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), (void *)(uintptr_t)(4 * sizeof (GLfloat))); glEnableClientState (GL_TEXTURE_COORD_ARRAY); } glClientActiveTexture (GL_TEXTURE1); glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat), (void *)(uintptr_t)(2 * sizeof (GLfloat))); glEnableClientState (GL_TEXTURE_COORD_ARRAY); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; double x_offset, y_offset; double x1, x2, y1, y2; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto FINISH; if (scaled_glyph->surface->width == 0 || scaled_glyph->surface->height == 0) { continue; } if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FINISH; } if (scaled_glyph->surface->format != last_format) { /* Switching textures, so flush any queued prims. */ _cairo_gl_flush_glyphs (ctx, &setup); glActiveTexture (GL_TEXTURE1); cache = cairo_gl_context_get_glyph_cache (ctx, scaled_glyph->surface->format); glBindTexture (GL_TEXTURE_2D, cache->tex); last_format = scaled_glyph->surface->format; } if (scaled_glyph->surface_private == NULL) { status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph); if (unlikely (_cairo_status_is_error (status))) goto FINISH; if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* Cache is full, so flush existing prims and try again. */ _cairo_gl_flush_glyphs (ctx, &setup); _cairo_gl_glyph_cache_unlock (cache); } status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph); if (unlikely (status)) goto FINISH; } x_offset = scaled_glyph->surface->base.device_transform.x0; y_offset = scaled_glyph->surface->base.device_transform.y0; x1 = _cairo_lround (glyphs[i].x - x_offset); y1 = _cairo_lround (glyphs[i].y - y_offset); x2 = x1 + scaled_glyph->surface->width; y2 = y1 + scaled_glyph->surface->height; _cairo_gl_emit_glyph_rectangle (ctx, &setup, x1, y1, x2, y2, _cairo_gl_glyph_cache_lock (cache, scaled_glyph)); } status = CAIRO_STATUS_SUCCESS; FINISH: _cairo_gl_flush_glyphs (ctx, &setup); CLEANUP_FONT: _cairo_scaled_font_thaw_cache (scaled_font); glDisable (GL_BLEND); glDisable (GL_SCISSOR_TEST); glDisableClientState (GL_VERTEX_ARRAY); glClientActiveTexture (GL_TEXTURE0); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glActiveTexture (GL_TEXTURE0); glDisable (GL_TEXTURE_2D); glClientActiveTexture (GL_TEXTURE1); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glActiveTexture (GL_TEXTURE1); glDisable (GL_TEXTURE_2D); if (vbo != 0) { glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0); glDeleteBuffersARB (1, &vbo); } _cairo_gl_context_release (ctx); _cairo_gl_operand_destroy (&composite_setup.src); *remaining_glyphs = num_glyphs - i; return status; }
cairo_int_status_t i965_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *g, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { i965_surface_t *surface = abstract_surface; i965_surface_t *mask = NULL; i965_device_t *device; i965_glyphs_t glyphs; cairo_composite_rectangles_t extents; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_bool_t overlap; cairo_region_t *clip_region = NULL; intel_bo_t *last_bo = NULL; cairo_scaled_glyph_t *glyph_cache[64]; cairo_status_t status; int mask_x = 0, mask_y = 0; int i = 0; *num_remaining = 0; status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface->intel.drm.width, surface->intel.drm.height, op, source, scaled_font, g, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_rectangle (clip, &extents.mask)) clip = NULL; if (clip != NULL && extents.is_bounded) { clip = _cairo_clip_init_copy (&local_clip, clip); status = _cairo_clip_rectangle (clip, &extents.bounded); if (unlikely (status)) return status; have_clip = TRUE; } if (overlap || ! extents.is_bounded) { cairo_format_t format; format = CAIRO_FORMAT_A8; if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) format = CAIRO_FORMAT_ARGB32; mask = (i965_surface_t *) i965_surface_create_internal (&i965_device (surface)->intel.base, format, extents.bounded.width, extents.bounded.height, I965_TILING_DEFAULT, TRUE); if (unlikely (mask->intel.drm.base.status)) return mask->intel.drm.base.status; status = _cairo_surface_paint (&mask->intel.drm.base, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, NULL); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } i965_shader_init (&glyphs.shader, mask, CAIRO_OPERATOR_ADD); status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, &_cairo_pattern_white.base, &extents.bounded); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } mask_x = -extents.bounded.x; mask_y = -extents.bounded.y; } else { i965_shader_init (&glyphs.shader, surface, op); status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, source, &extents.bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&glyphs.shader, clip); } } glyphs.head.next = NULL; glyphs.head.bo = NULL; glyphs.head.count = 0; glyphs.tail = &glyphs.head; device = i965_device (surface); if (mask != NULL || clip_region == NULL) { glyphs.get_rectangle = i965_glyphs_emit_rectangle; } else { glyphs.get_rectangle = i965_glyphs_accumulate_rectangle; glyphs.head.bo = intel_bo_create (&device->intel, I965_VERTEX_SIZE, I965_VERTEX_SIZE, FALSE, I915_TILING_NONE, 0); if (unlikely (glyphs.head.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glyphs.vbo_base = intel_bo_map (&device->intel, glyphs.head.bo); } glyphs.vbo_offset = 0; status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto CLEANUP_GLYPHS; _cairo_scaled_font_freeze_cache (scaled_font); //private = _cairo_scaled_font_get_device (scaled_font, device); if (scaled_font->surface_private == NULL) { scaled_font->surface_private = device; scaled_font->surface_backend = surface->intel.drm.base.backend; cairo_list_add (&scaled_font->link, &device->intel.fonts); } memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; int x, y, x1, x2, y1, y2; int cache_index = g[i].index % ARRAY_LENGTH (glyph_cache); intel_glyph_t *glyph; scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != g[i].index) { status = _cairo_scaled_glyph_lookup (scaled_font, g[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) goto FINISH; glyph_cache[cache_index] = scaled_glyph; } if (unlikely (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)) { continue; } /* XXX glyph images are snapped to pixel locations */ x = _cairo_lround (g[i].x); y = _cairo_lround (g[i].y); x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); if (x2 < extents.bounded.x || y2 < extents.bounded.y || x1 > extents.bounded.x + extents.bounded.width || y1 > extents.bounded.y + extents.bounded.height) { continue; } if (scaled_glyph->surface_private == NULL) { status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { status = CAIRO_STATUS_SUCCESS; continue; } if (unlikely (status)) goto FINISH; } glyph = intel_glyph_pin (scaled_glyph->surface_private); if (glyph->cache->buffer.bo != last_bo) { intel_buffer_cache_t *cache = glyph->cache; glyphs.shader.mask.type.vertex = VS_GLYPHS; glyphs.shader.mask.type.fragment = FS_GLYPHS; glyphs.shader.mask.type.pattern = PATTERN_BASE; glyphs.shader.mask.base.bo = cache->buffer.bo; glyphs.shader.mask.base.format = cache->buffer.format; glyphs.shader.mask.base.width = cache->buffer.width; glyphs.shader.mask.base.height = cache->buffer.height; glyphs.shader.mask.base.stride = cache->buffer.stride; glyphs.shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST); glyphs.shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE); glyphs.shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ glyphs.shader.committed = FALSE; status = i965_shader_commit (&glyphs.shader, device); if (unlikely (status)) goto FINISH; last_bo = cache->buffer.bo; } x2 = x1 + glyph->width; y2 = y1 + glyph->height; if (mask_x) x1 += mask_x, x2 += mask_x; if (mask_y) y1 += mask_y, y2 += mask_y; i965_add_glyph_rectangle (&glyphs, x1, y1, x2, y2, glyph); } if (mask != NULL && clip_region != NULL) i965_clipped_vertices (device, &glyphs.head, clip_region); status = CAIRO_STATUS_SUCCESS; FINISH: _cairo_scaled_font_thaw_cache (scaled_font); cairo_device_release (surface->intel.drm.base.device); CLEANUP_GLYPHS: i965_shader_fini (&glyphs.shader); if (glyphs.head.bo != NULL) { struct i965_vbo *vbo, *next; intel_bo_destroy (&device->intel, glyphs.head.bo); for (vbo = glyphs.head.next; vbo != NULL; vbo = next) { next = vbo->next; intel_bo_destroy (&device->intel, vbo->bo); free (vbo); } } if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { cairo_path_fixed_t path; _cairo_path_fixed_init (&path); status = _cairo_scaled_font_glyph_path (scaled_font, g + i, num_glyphs - i, &path); if (mask_x | mask_y) { _cairo_path_fixed_translate (&path, _cairo_fixed_from_int (mask_x), _cairo_fixed_from_int (mask_y)); } if (likely (status == CAIRO_STATUS_SUCCESS)) { status = surface->intel.drm.base.backend->fill (glyphs.shader.target, glyphs.shader.op, mask != NULL ? &_cairo_pattern_white.base : source, &path, CAIRO_FILL_RULE_WINDING, 0, scaled_font->options.antialias, clip); } _cairo_path_fixed_fini (&path); } if (mask != NULL) { if (likely (status == CAIRO_STATUS_SUCCESS)) { status = i965_surface_mask_internal (surface, op, source, mask, clip, &extents); } cairo_surface_finish (&mask->intel.drm.base); cairo_surface_destroy (&mask->intel.drm.base); } if (have_clip) _cairo_clip_fini (&local_clip); return status; }