cairo-drm-i915-glyphs.c [plain text]
#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"
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;
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;
v = i915_add_rectangle (device);
*v++ = x2; *v++ = y2;
*v++ = glyph->texcoord[0];
*v++ = x1; *v++ = y2;
*v++ = glyph->texcoord[1];
*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;
v = i915_add_rectangle (device);
*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];
*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];
*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;
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;
}
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;
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;
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;
}