/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-drm-i915-private.h" #include "cairo-error-private.h" #include "cairo-rtree-private.h" #include "cairo-clip-inline.h" static void i915_emit_glyph_rectangle_zero (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { float *v; /* Each vertex is: * 2 vertex coordinates */ v = i915_add_rectangle (device); *v++ = x2; *v++ = y2; *v++ = x1; *v++ = y2; *v++ = x1; *v++ = y1; } static void i915_emit_glyph_rectangle_constant (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { float *v; /* Each vertex is: * 2 vertex coordinates * 2 glyph texture coordinates */ v = i915_add_rectangle (device); /* bottom right */ *v++ = x2; *v++ = y2; *v++ = glyph->texcoord[0]; /* bottom left */ *v++ = x1; *v++ = y2; *v++ = glyph->texcoord[1]; /* top left */ *v++ = x1; *v++ = y1; *v++ = glyph->texcoord[2]; } static void i915_emit_glyph_rectangle_general (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { double s, t; float *v; /* Each vertex is: * 2 vertex coordinates * [0-2] source texture coordinates * 2 glyph texture coordinates */ v = i915_add_rectangle (device); /* bottom right */ *v++ = x2; *v++ = y2; s = x2, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[0]; /* bottom left */ *v++ = x1; *v++ = y2; s = x1, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[1]; /* top left */ *v++ = x1; *v++ = y1; s = x1, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[2]; } typedef void (*i915_emit_glyph_rectangle_func_t) (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph); static cairo_status_t i915_surface_mask_internal (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, i915_surface_t *mask, cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { i915_device_t *device; i915_shader_t shader; cairo_region_t *clip_region = NULL; cairo_status_t status; i915_shader_init (&shader, dst, op, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, source, &extents->bounded); if (unlikely (status)) return status; shader.mask.type.vertex = VS_TEXTURE_16; shader.mask.type.pattern = PATTERN_TEXTURE; shader.mask.type.fragment = FS_TEXTURE; shader.mask.base.content = mask->intel.drm.base.content; shader.mask.base.texfmt = TEXCOORDFMT_2D_16; shader.mask.base.n_samplers = 1; shader.mask.base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); shader.mask.base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_translate (&shader.mask.base.matrix, -extents->bounded.x, -extents->bounded.y); cairo_matrix_scale (&shader.mask.base.matrix, 1. / mask->intel.drm.width, 1. / mask->intel.drm.height); shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo)); shader.mask.base.offset[0] = 0; shader.mask.base.map[0] = mask->map0; shader.mask.base.map[1] = mask->map1; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i915_shader_set_clip (&shader, clip); } status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SHADER; device = i915_device (dst); status = i915_shader_commit (&shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; if (clip_region != NULL) { unsigned int n, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (n = 0; n < num_rectangles; n++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, n, &rect); shader.add_rectangle (&shader, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); } } else { shader.add_rectangle (&shader, extents->bounded.x, extents->bounded.y, extents->bounded.x + extents->bounded.width, extents->bounded.y + extents->bounded.height); } if (! extents->is_bounded) status = i915_fixup_unbounded (dst, extents, clip); CLEANUP_DEVICE: cairo_device_release (&device->intel.base.base); CLEANUP_SHADER: i915_shader_fini (&shader); return status; } cairo_int_status_t i915_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { i915_surface_t *surface = abstract_surface; i915_surface_t *mask = NULL; i915_device_t *device; i915_shader_t shader; 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; i915_emit_glyph_rectangle_func_t emit_func; 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, glyphs, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (_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 (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (unlikely (_cairo_status_is_error (status) || status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } } if (i915_surface_needs_tiling (surface)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } 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 = (i915_surface_t *) i915_surface_create_internal (&i915_device (surface)->intel.base, format, extents.bounded.width, extents.bounded.height, I915_TILING_DEFAULT, TRUE); if (unlikely (mask->intel.drm.base.status)) return mask->intel.drm.base.status; status = i915_surface_clear (mask); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.); status = i915_shader_acquire_pattern (&shader, &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 { i915_shader_init (&shader, surface, op, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, source, &extents.bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i915_shader_set_clip (&shader, clip); } } shader.mask.type.fragment = FS_TEXTURE; shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ shader.mask.base.texfmt = TEXCOORDFMT_2D_16; shader.mask.base.n_samplers = 1; shader.mask.base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); shader.mask.base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); switch (shader.source.type.vertex) { case VS_ZERO: emit_func = i915_emit_glyph_rectangle_zero; break; case VS_CONSTANT: emit_func = i915_emit_glyph_rectangle_constant; break; default: case VS_LINEAR: case VS_TEXTURE: case VS_TEXTURE_16: emit_func = i915_emit_glyph_rectangle_general; break; } status = cairo_device_acquire (surface->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SHADER; device = i915_device (surface); _cairo_scaled_font_freeze_cache (scaled_font); 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 = glyphs[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) != glyphs[i].index) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[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 (glyphs[i].x); y = _cairo_lround (glyphs[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; shader.mask.base.bo = cache->buffer.bo; shader.mask.base.offset[0] = cache->buffer.offset; shader.mask.base.map[0] = cache->buffer.map0; shader.mask.base.map[1] = cache->buffer.map1; shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ status = i915_shader_commit (&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; /* XXX clip glyph */ emit_func (device, &shader, x1, y1, x2, y2, glyph); } status = CAIRO_STATUS_SUCCESS; FINISH: _cairo_scaled_font_thaw_cache (scaled_font); cairo_device_release (surface->intel.drm.base.device); CLEANUP_SHADER: i915_shader_fini (&shader); 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, glyphs + 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 (shader.target, 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 = i915_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; }