1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Intel Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  * The Original Code is the cairo graphics library.
29  *
30  * The Initial Developer of the Original Code is Red Hat, Inc.
31  *
32  * Contributor(s):
33  *	Chris Wilson <chris@chris-wilson.co.uk>
34  */
35 
36 #include "cairoint.h"
37 
38 #include "cairo-composite-rectangles-private.h"
39 #include "cairo-drm-i915-private.h"
40 #include "cairo-error-private.h"
41 #include "cairo-rtree-private.h"
42 #include "cairo-clip-inline.h"
43 
44 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)45 i915_emit_glyph_rectangle_zero (i915_device_t *device,
46 				i915_shader_t *shader,
47 				int x1, int y1,
48 				int x2, int y2,
49 				intel_glyph_t *glyph)
50 {
51     float *v;
52 
53     /* Each vertex is:
54      *   2 vertex coordinates
55      */
56 
57     v = i915_add_rectangle (device);
58     *v++ = x2; *v++ = y2;
59     *v++ = x1; *v++ = y2;
60     *v++ = x1; *v++ = y1;
61 }
62 
63 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)64 i915_emit_glyph_rectangle_constant (i915_device_t *device,
65 				    i915_shader_t *shader,
66 				    int x1, int y1,
67 				    int x2, int y2,
68 				    intel_glyph_t *glyph)
69 {
70     float *v;
71 
72     /* Each vertex is:
73      *   2 vertex coordinates
74      *   2 glyph texture coordinates
75      */
76 
77     v = i915_add_rectangle (device);
78 
79     /* bottom right */
80     *v++ = x2; *v++ = y2;
81     *v++ = glyph->texcoord[0];
82 
83     /* bottom left */
84     *v++ = x1; *v++ = y2;
85     *v++ = glyph->texcoord[1];
86 
87     /* top left */
88     *v++ = x1; *v++ = y1;
89     *v++ = glyph->texcoord[2];
90 }
91 
92 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)93 i915_emit_glyph_rectangle_general (i915_device_t *device,
94 				   i915_shader_t *shader,
95 				   int x1, int y1,
96 				   int x2, int y2,
97 				   intel_glyph_t *glyph)
98 {
99     double s, t;
100     float *v;
101 
102     /* Each vertex is:
103      *   2 vertex coordinates
104      *   [0-2] source texture coordinates
105      *   2 glyph texture coordinates
106      */
107 
108     v = i915_add_rectangle (device);
109 
110     /* bottom right */
111     *v++ = x2; *v++ = y2;
112     s = x2, t = y2;
113     switch (shader->source.type.vertex) {
114     case VS_ZERO:
115     case VS_CONSTANT:
116 	break;
117     case VS_LINEAR:
118 	*v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
119 	break;
120     case VS_TEXTURE:
121 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
122 	*v++ = s; *v++ = t;
123 	break;
124     case VS_TEXTURE_16:
125 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
126 	*v++ = texcoord_2d_16 (s, t);
127 	break;
128     }
129     *v++ = glyph->texcoord[0];
130 
131     /* bottom left */
132     *v++ = x1; *v++ = y2;
133     s = x1, t = y2;
134     switch (shader->source.type.vertex) {
135     case VS_ZERO:
136     case VS_CONSTANT:
137 	break;
138     case VS_LINEAR:
139 	*v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
140 	break;
141     case VS_TEXTURE:
142 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
143 	*v++ = s; *v++ = t;
144 	break;
145     case VS_TEXTURE_16:
146 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
147 	*v++ = texcoord_2d_16 (s, t);
148 	break;
149     }
150     *v++ = glyph->texcoord[1];
151 
152     /* top left */
153     *v++ = x1; *v++ = y1;
154     s = x1, t = y2;
155     switch (shader->source.type.vertex) {
156     case VS_ZERO:
157     case VS_CONSTANT:
158 	break;
159     case VS_LINEAR:
160 	*v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
161 	break;
162     case VS_TEXTURE:
163 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
164 	*v++ = s; *v++ = t;
165 	break;
166     case VS_TEXTURE_16:
167 	cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
168 	*v++ = texcoord_2d_16 (s, t);
169 	break;
170     }
171     *v++ = glyph->texcoord[2];
172 }
173 
174 typedef void
175 (*i915_emit_glyph_rectangle_func_t) (i915_device_t *device,
176 				     i915_shader_t *shader,
177 				     int x1, int y1,
178 				     int x2, int y2,
179 				     intel_glyph_t *glyph);
180 
181 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)182 i915_surface_mask_internal (i915_surface_t *dst,
183 			    cairo_operator_t		 op,
184 			    const cairo_pattern_t	*source,
185 			    i915_surface_t *mask,
186 			    cairo_clip_t		*clip,
187 			    const cairo_composite_rectangles_t *extents)
188 {
189     i915_device_t *device;
190     i915_shader_t shader;
191     cairo_region_t *clip_region = NULL;
192     cairo_status_t status;
193 
194     i915_shader_init (&shader, dst, op, 1.);
195 
196     status = i915_shader_acquire_pattern (&shader, &shader.source,
197 					  source, &extents->bounded);
198     if (unlikely (status))
199 	return status;
200 
201     shader.mask.type.vertex = VS_TEXTURE_16;
202     shader.mask.type.pattern = PATTERN_TEXTURE;
203     shader.mask.type.fragment = FS_TEXTURE;
204     shader.mask.base.content = mask->intel.drm.base.content;
205     shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
206     shader.mask.base.n_samplers = 1;
207     shader.mask.base.sampler[0] =
208 	(MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
209 	i915_texture_filter (CAIRO_FILTER_NEAREST);
210     shader.mask.base.sampler[1] =
211 	SS3_NORMALIZED_COORDS |
212 	i915_texture_extend (CAIRO_EXTEND_NONE);
213 
214     cairo_matrix_init_translate (&shader.mask.base.matrix,
215 				 -extents->bounded.x,
216 				 -extents->bounded.y);
217     cairo_matrix_scale (&shader.mask.base.matrix,
218 			1. / mask->intel.drm.width,
219 			1. / mask->intel.drm.height);
220 
221     shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo));
222     shader.mask.base.offset[0] = 0;
223     shader.mask.base.map[0] = mask->map0;
224     shader.mask.base.map[1] = mask->map1;
225 
226     if (clip != NULL) {
227 	status = _cairo_clip_get_region (clip, &clip_region);
228 
229 	if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
230 	    clip_region = NULL;
231 
232 	if (status == CAIRO_INT_STATUS_UNSUPPORTED)
233 	    i915_shader_set_clip (&shader, clip);
234     }
235 
236     status = cairo_device_acquire (dst->intel.drm.base.device);
237     if (unlikely (status))
238 	goto CLEANUP_SHADER;
239 
240     device = i915_device (dst);
241 
242     status = i915_shader_commit (&shader, device);
243     if (unlikely (status))
244 	goto CLEANUP_DEVICE;
245 
246     if (clip_region != NULL) {
247 	unsigned int n, num_rectangles;
248 
249 	num_rectangles = cairo_region_num_rectangles (clip_region);
250 	for (n = 0; n < num_rectangles; n++) {
251 	    cairo_rectangle_int_t rect;
252 
253 	    cairo_region_get_rectangle (clip_region, n, &rect);
254 
255 	    shader.add_rectangle (&shader,
256 				  rect.x, rect.y,
257 				  rect.x + rect.width, rect.y + rect.height);
258 	}
259     } else {
260 	shader.add_rectangle (&shader,
261 			      extents->bounded.x, extents->bounded.y,
262 			      extents->bounded.x + extents->bounded.width,
263 			      extents->bounded.y + extents->bounded.height);
264     }
265 
266     if (! extents->is_bounded)
267 	status = i915_fixup_unbounded (dst, extents, clip);
268 
269 CLEANUP_DEVICE:
270     cairo_device_release (&device->intel.base.base);
271 CLEANUP_SHADER:
272     i915_shader_fini (&shader);
273     return status;
274 }
275 
276 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)277 i915_surface_glyphs (void			*abstract_surface,
278 		     cairo_operator_t		 op,
279 		     const cairo_pattern_t	*source,
280 		     cairo_glyph_t		*glyphs,
281 		     int			 num_glyphs,
282 		     cairo_scaled_font_t	*scaled_font,
283 		     cairo_clip_t		*clip,
284 		     int *num_remaining)
285 {
286     i915_surface_t *surface = abstract_surface;
287     i915_surface_t *mask = NULL;
288     i915_device_t *device;
289     i915_shader_t shader;
290     cairo_composite_rectangles_t extents;
291     cairo_clip_t local_clip;
292     cairo_bool_t have_clip = FALSE;
293     cairo_bool_t overlap;
294     cairo_region_t *clip_region = NULL;
295     intel_bo_t *last_bo = NULL;
296     i915_emit_glyph_rectangle_func_t emit_func;
297     cairo_scaled_glyph_t *glyph_cache[64];
298     cairo_status_t status;
299     int mask_x = 0, mask_y = 0;
300     int i = 0;
301 
302     *num_remaining = 0;
303     status = _cairo_composite_rectangles_init_for_glyphs (&extents,
304 							  surface->intel.drm.width,
305 							  surface->intel.drm.height,
306 							  op, source,
307 							  scaled_font,
308 							  glyphs, num_glyphs,
309 							  clip,
310 							  &overlap);
311     if (unlikely (status))
312 	return status;
313 
314     if (_cairo_clip_contains_rectangle (clip, &extents.mask))
315 	clip = NULL;
316 
317     if (clip != NULL && extents.is_bounded) {
318 	clip = _cairo_clip_init_copy (&local_clip, clip);
319 	status = _cairo_clip_rectangle (clip, &extents.bounded);
320 	if (unlikely (status))
321 	    return status;
322 
323 	have_clip = TRUE;
324     }
325 
326     if (clip != NULL) {
327 	status = _cairo_clip_get_region (clip, &clip_region);
328 	if (unlikely (_cairo_status_is_error (status) ||
329 		      status == CAIRO_INT_STATUS_NOTHING_TO_DO))
330 	{
331 	    if (have_clip)
332 		_cairo_clip_fini (&local_clip);
333 	    return status;
334 	}
335     }
336 
337     if (i915_surface_needs_tiling (surface)) {
338 	ASSERT_NOT_REACHED;
339 	return CAIRO_INT_STATUS_UNSUPPORTED;
340     }
341 
342     if (overlap || ! extents.is_bounded) {
343 	cairo_format_t format;
344 
345 	format = CAIRO_FORMAT_A8;
346 	if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
347 	    format = CAIRO_FORMAT_ARGB32;
348 
349 	mask = (i915_surface_t *)
350 	    i915_surface_create_internal (&i915_device (surface)->intel.base,
351 					  format,
352 					  extents.bounded.width,
353 					  extents.bounded.height,
354 					  I915_TILING_DEFAULT,
355 					  TRUE);
356 	if (unlikely (mask->intel.drm.base.status))
357 	    return mask->intel.drm.base.status;
358 
359 	status = i915_surface_clear (mask);
360 	if (unlikely (status)) {
361 	    cairo_surface_destroy (&mask->intel.drm.base);
362 	    return status;
363 	}
364 
365 	i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.);
366 
367 	status = i915_shader_acquire_pattern (&shader, &shader.source,
368 					      &_cairo_pattern_white.base,
369 					      &extents.bounded);
370 	if (unlikely (status)) {
371 	    cairo_surface_destroy (&mask->intel.drm.base);
372 	    return status;
373 	}
374 
375 	mask_x = -extents.bounded.x;
376 	mask_y = -extents.bounded.y;
377     } else {
378 	i915_shader_init (&shader, surface, op, 1.);
379 
380 	status = i915_shader_acquire_pattern (&shader, &shader.source,
381 					      source, &extents.bounded);
382 	if (unlikely (status))
383 	    return status;
384 
385 	if (clip != NULL) {
386 	    status = _cairo_clip_get_region (clip, &clip_region);
387 
388 	    if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
389 		clip_region = NULL;
390 
391 	    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
392 		i915_shader_set_clip (&shader, clip);
393 	}
394     }
395 
396     shader.mask.type.fragment = FS_TEXTURE;
397     shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
398     shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
399     shader.mask.base.n_samplers = 1;
400     shader.mask.base.sampler[0] =
401 	(MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
402 	i915_texture_filter (CAIRO_FILTER_NEAREST);
403     shader.mask.base.sampler[1] =
404 	SS3_NORMALIZED_COORDS |
405 	i915_texture_extend (CAIRO_EXTEND_NONE);
406 
407     switch (shader.source.type.vertex) {
408     case VS_ZERO:
409 	emit_func = i915_emit_glyph_rectangle_zero;
410 	break;
411     case VS_CONSTANT:
412 	emit_func = i915_emit_glyph_rectangle_constant;
413 	break;
414     default:
415     case VS_LINEAR:
416     case VS_TEXTURE:
417     case VS_TEXTURE_16:
418 	emit_func = i915_emit_glyph_rectangle_general;
419 	break;
420     }
421 
422     status = cairo_device_acquire (surface->intel.drm.base.device);
423     if (unlikely (status))
424 	goto CLEANUP_SHADER;
425 
426     device = i915_device (surface);
427 
428     _cairo_scaled_font_freeze_cache (scaled_font);
429     if (scaled_font->surface_private == NULL) {
430 	scaled_font->surface_private = device;
431 	scaled_font->surface_backend = surface->intel.drm.base.backend;
432 	cairo_list_add (&scaled_font->link, &device->intel.fonts);
433     }
434 
435     memset (glyph_cache, 0, sizeof (glyph_cache));
436 
437     for (i = 0; i < num_glyphs; i++) {
438 	cairo_scaled_glyph_t *scaled_glyph;
439 	int x, y, x1, x2, y1, y2;
440 	int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
441 	intel_glyph_t *glyph;
442 
443 	scaled_glyph = glyph_cache[cache_index];
444 	if (scaled_glyph == NULL ||
445 	    _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
446 	{
447 	    status = _cairo_scaled_glyph_lookup (scaled_font,
448 						 glyphs[i].index,
449 						 CAIRO_SCALED_GLYPH_INFO_METRICS,
450 						 &scaled_glyph);
451 	    if (unlikely (status))
452 		goto FINISH;
453 
454 	    glyph_cache[cache_index] = scaled_glyph;
455 	}
456 
457 	if (unlikely (scaled_glyph->metrics.width  == 0 ||
458 		      scaled_glyph->metrics.height == 0))
459 	{
460 	    continue;
461 	}
462 
463 	/* XXX glyph images are snapped to pixel locations */
464 	x = _cairo_lround (glyphs[i].x);
465 	y = _cairo_lround (glyphs[i].y);
466 
467 	x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
468 	y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
469 	x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
470 	y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
471 
472 	if (x2 < extents.bounded.x ||
473 	    y2 < extents.bounded.y ||
474 	    x1 > extents.bounded.x + extents.bounded.width ||
475 	    y1 > extents.bounded.y + extents.bounded.height)
476 	{
477 	    continue;
478 	}
479 
480 	if (scaled_glyph->surface_private == NULL) {
481 	    status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph);
482 	    if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) {
483 		status = CAIRO_STATUS_SUCCESS;
484 		continue;
485 	    }
486 	    if (unlikely (status))
487 		goto FINISH;
488 	}
489 
490 	glyph = intel_glyph_pin (scaled_glyph->surface_private);
491 	if (glyph->cache->buffer.bo != last_bo) {
492 	    intel_buffer_cache_t *cache = glyph->cache;
493 
494 	    shader.mask.base.bo = cache->buffer.bo;
495 	    shader.mask.base.offset[0] = cache->buffer.offset;
496 	    shader.mask.base.map[0] = cache->buffer.map0;
497 	    shader.mask.base.map[1] = cache->buffer.map1;
498 	    shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
499 
500 	    status = i915_shader_commit (&shader, device);
501 	    if (unlikely (status))
502 		goto FINISH;
503 
504 	    last_bo = cache->buffer.bo;
505 	}
506 
507 	x2 = x1 + glyph->width;
508 	y2 = y1 + glyph->height;
509 
510 	if (mask_x)
511 	    x1 += mask_x, x2 += mask_x;
512 	if (mask_y)
513 	    y1 += mask_y, y2 += mask_y;
514 
515 	/* XXX clip glyph */
516 	emit_func (device, &shader, x1, y1, x2, y2, glyph);
517     }
518 
519     status = CAIRO_STATUS_SUCCESS;
520   FINISH:
521     _cairo_scaled_font_thaw_cache (scaled_font);
522     cairo_device_release (surface->intel.drm.base.device);
523   CLEANUP_SHADER:
524     i915_shader_fini (&shader);
525 
526     if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
527 	cairo_path_fixed_t path;
528 
529 	_cairo_path_fixed_init (&path);
530 	status = _cairo_scaled_font_glyph_path (scaled_font,
531 						glyphs + i, num_glyphs - i,
532 						&path);
533 	if (mask_x | mask_y) {
534 	    _cairo_path_fixed_translate (&path,
535 					 _cairo_fixed_from_int (mask_x),
536 					 _cairo_fixed_from_int (mask_y));
537 	}
538 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
539 	    status = surface->intel.drm.base.backend->fill (shader.target,
540 							    shader.op,
541 							    mask != NULL ? &_cairo_pattern_white.base : source,
542 							    &path,
543 							    CAIRO_FILL_RULE_WINDING,
544 							    0,
545 							    scaled_font->options.antialias,
546 							    clip);
547 	}
548 	_cairo_path_fixed_fini (&path);
549     }
550 
551     if (mask != NULL) {
552 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
553 	    status = i915_surface_mask_internal (surface, op, source, mask,
554 					         clip, &extents);
555 	}
556 	cairo_surface_finish (&mask->intel.drm.base);
557 	cairo_surface_destroy (&mask->intel.drm.base);
558     }
559 
560     if (have_clip)
561 	_cairo_clip_fini (&local_clip);
562 
563     return status;
564 }
565