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