st/vega: Create drawing surface mask as needed.
[mesa.git] / src / gallium / state_trackers / vega / mask.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "mask.h"
28
29 #include "path.h"
30 #include "image.h"
31 #include "shaders_cache.h"
32 #include "renderer.h"
33 #include "asm_util.h"
34
35 #include "pipe/p_context.h"
36 #include "pipe/p_screen.h"
37 #include "util/u_inlines.h"
38 #include "util/u_format.h"
39 #include "util/u_memory.h"
40 #include "util/u_sampler.h"
41
42 struct vg_mask_layer {
43 struct vg_object base;
44
45 VGint width;
46 VGint height;
47
48 struct pipe_sampler_view *sampler_view;
49 };
50
51 static INLINE VGboolean
52 intersect_rectangles(VGint dwidth, VGint dheight,
53 VGint swidth, VGint sheight,
54 VGint tx, VGint ty,
55 VGint twidth, VGint theight,
56 VGint *offsets,
57 VGint *location)
58 {
59 if (tx + twidth <= 0 || tx >= dwidth)
60 return VG_FALSE;
61 if (ty + theight <= 0 || ty >= dheight)
62 return VG_FALSE;
63
64 offsets[0] = 0;
65 offsets[1] = 0;
66 location[0] = tx;
67 location[1] = ty;
68
69 if (tx < 0) {
70 offsets[0] -= tx;
71 location[0] = 0;
72
73 location[2] = MIN2(tx + swidth, MIN2(dwidth, tx + twidth));
74 offsets[2] = location[2];
75 } else {
76 offsets[2] = MIN2(twidth, MIN2(dwidth - tx, swidth ));
77 location[2] = offsets[2];
78 }
79
80 if (ty < 0) {
81 offsets[1] -= ty;
82 location[1] = 0;
83
84 location[3] = MIN2(ty + sheight, MIN2(dheight, ty + theight));
85 offsets[3] = location[3];
86 } else {
87 offsets[3] = MIN2(theight, MIN2(dheight - ty, sheight));
88 location[3] = offsets[3];
89 }
90
91 return VG_TRUE;
92 }
93
94 #if DEBUG_MASKS
95 static void read_alpha_mask(void * data, VGint dataStride,
96 VGImageFormat dataFormat,
97 VGint sx, VGint sy,
98 VGint width, VGint height)
99 {
100 struct vg_context *ctx = vg_current_context();
101 struct pipe_context *pipe = ctx->pipe;
102 struct pipe_screen *screen = pipe->screen;
103
104 struct st_framebuffer *stfb = ctx->draw_buffer;
105 struct st_renderbuffer *strb = stfb->alpha_mask;
106
107 VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
108 VGfloat *df = (VGfloat*)temp;
109 VGint y = (stfb->height - sy) - 1, yStep = -1;
110 VGint i;
111 VGubyte *dst = (VGubyte *)data;
112 VGint xoffset = 0, yoffset = 0;
113
114 /* make sure rendering has completed */
115 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
116 if (sx < 0) {
117 xoffset = -sx;
118 xoffset *= _vega_size_for_format(dataFormat);
119 width += sx;
120 sx = 0;
121 }
122 if (sy < 0) {
123 yoffset = -sy;
124 height += sy;
125 sy = 0;
126 y = (stfb->height - sy) - 1;
127 yoffset *= dataStride;
128 }
129
130 {
131 struct pipe_surface *surf;
132
133 surf = screen->get_tex_surface(screen, strb->texture, 0, 0, 0,
134 PIPE_BIND_TRANSFER_READ);
135
136 /* Do a row at a time to flip image data vertically */
137 for (i = 0; i < height; i++) {
138 #if 0
139 debug_printf("%d-%d == %d\n", sy, height, y);
140 #endif
141 pipe_get_tile_rgba(surf, sx, y, width, 1, df);
142 y += yStep;
143 _vega_pack_rgba_span_float(ctx, width, temp, dataFormat,
144 dst + yoffset + xoffset);
145 dst += dataStride;
146 }
147
148 pipe_surface_reference(&surf, NULL);
149 }
150 }
151
152 void save_alpha_to_file(const char *filename)
153 {
154 struct vg_context *ctx = vg_current_context();
155 struct st_framebuffer *stfb = ctx->draw_buffer;
156 VGint *data;
157 int i, j;
158
159 data = malloc(sizeof(int) * stfb->width * stfb->height);
160 read_alpha_mask(data, stfb->width * sizeof(int),
161 VG_sRGBA_8888,
162 0, 0, stfb->width, stfb->height);
163 fprintf(stderr, "/*---------- start */\n");
164 fprintf(stderr, "const int image_width = %d;\n",
165 stfb->width);
166 fprintf(stderr, "const int image_height = %d;\n",
167 stfb->height);
168 fprintf(stderr, "const int image_data = {\n");
169 for (i = 0; i < stfb->height; ++i) {
170 for (j = 0; j < stfb->width; ++j) {
171 int rgba = data[i * stfb->height + j];
172 int argb = 0;
173 argb = (rgba >> 8);
174 argb |= ((rgba & 0xff) << 24);
175 fprintf(stderr, "0x%x, ", argb);
176 }
177 fprintf(stderr, "\n");
178 }
179 fprintf(stderr, "};\n");
180 fprintf(stderr, "/*---------- end */\n");
181 }
182 #endif
183
184 /* setup mask shader */
185 static void *setup_mask_operation(VGMaskOperation operation)
186 {
187 struct vg_context *ctx = vg_current_context();
188 void *shader = 0;
189
190 switch (operation) {
191 case VG_UNION_MASK: {
192 if (!ctx->mask.union_fs) {
193 ctx->mask.union_fs = shader_create_from_text(ctx->pipe,
194 union_mask_asm,
195 200,
196 PIPE_SHADER_FRAGMENT);
197 }
198 shader = ctx->mask.union_fs->driver;
199 }
200 break;
201 case VG_INTERSECT_MASK: {
202 if (!ctx->mask.intersect_fs) {
203 ctx->mask.intersect_fs = shader_create_from_text(ctx->pipe,
204 intersect_mask_asm,
205 200,
206 PIPE_SHADER_FRAGMENT);
207 }
208 shader = ctx->mask.intersect_fs->driver;
209 }
210 break;
211 case VG_SUBTRACT_MASK: {
212 if (!ctx->mask.subtract_fs) {
213 ctx->mask.subtract_fs = shader_create_from_text(ctx->pipe,
214 subtract_mask_asm,
215 200,
216 PIPE_SHADER_FRAGMENT);
217 }
218 shader = ctx->mask.subtract_fs->driver;
219 }
220 break;
221 case VG_SET_MASK: {
222 if (!ctx->mask.set_fs) {
223 ctx->mask.set_fs = shader_create_from_text(ctx->pipe,
224 set_mask_asm,
225 200,
226 PIPE_SHADER_FRAGMENT);
227 }
228 shader = ctx->mask.set_fs->driver;
229 }
230 break;
231 default:
232 assert(0);
233 break;
234 }
235
236 return shader;
237 }
238
239 static void mask_resource_fill(struct pipe_resource *dst,
240 int x, int y, int width, int height,
241 VGfloat coverage)
242 {
243 struct vg_context *ctx = vg_current_context();
244 VGfloat fs_consts[12] = {
245 0.0f, 0.0f, 0.0f, 0.0f, /* not used */
246 0.0f, 0.0f, 0.0f, 0.0f, /* not used */
247 0.0f, 0.0f, 0.0f, coverage /* color */
248 };
249 void *fs;
250
251 if (x < 0) {
252 width += x;
253 x = 0;
254 }
255 if (y < 0) {
256 height += y;
257 y = 0;
258 }
259
260 fs = shaders_cache_fill(ctx->sc, VEGA_SOLID_FILL_SHADER);
261
262 if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE, ~0,
263 NULL, NULL, 0, fs, (const void *) fs_consts, sizeof(fs_consts))) {
264 renderer_filter(ctx->renderer, x, y, width, height, 0, 0, 0, 0);
265 renderer_filter_end(ctx->renderer);
266 }
267
268 #if DEBUG_MASKS
269 save_alpha_to_file(0);
270 #endif
271 }
272
273
274 static void mask_using_texture(struct pipe_sampler_view *sampler_view,
275 VGboolean is_layer,
276 VGMaskOperation operation,
277 VGint x, VGint y,
278 VGint width, VGint height)
279 {
280 struct vg_context *ctx = vg_current_context();
281 struct pipe_sampler_view *dst_view = vg_get_surface_mask(ctx);
282 struct pipe_resource *dst = dst_view->texture;
283 struct pipe_resource *texture = sampler_view->texture;
284 const struct pipe_sampler_state *samplers[2];
285 struct pipe_sampler_view *views[2];
286 struct pipe_sampler_state sampler;
287 VGint offsets[4], loc[4];
288 const VGfloat ones[4] = {1.f, 1.f, 1.f, 1.f};
289 void *fs;
290
291 if (!intersect_rectangles(dst->width0, dst->height0,
292 texture->width0, texture->height0,
293 x, y, width, height,
294 offsets, loc))
295 return;
296 #if 0
297 debug_printf("Offset = [%d, %d, %d, %d]\n", offsets[0],
298 offsets[1], offsets[2], offsets[3]);
299 debug_printf("Locati = [%d, %d, %d, %d]\n", loc[0],
300 loc[1], loc[2], loc[3]);
301 #endif
302
303
304 sampler = ctx->mask.sampler;
305 sampler.normalized_coords = 1;
306 samplers[0] = &sampler;
307 views[0] = sampler_view;
308
309 /* prepare our blend surface */
310 samplers[1] = &ctx->mask.sampler;
311 views[1] = vg_prepare_blend_surface_from_mask(ctx);
312
313 fs = setup_mask_operation(operation);
314
315 if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE,
316 ~0, samplers, views, 2, fs, (const void *) ones, sizeof(ones))) {
317 /* layer should be flipped when used as a texture */
318 if (is_layer) {
319 offsets[1] += offsets[3];
320 offsets[3] = -offsets[3];
321 }
322 renderer_filter(ctx->renderer,
323 loc[0], loc[1], loc[2], loc[3],
324 offsets[0], offsets[1], offsets[2], offsets[3]);
325 renderer_filter_end(ctx->renderer);
326 }
327 }
328
329
330 #ifdef OPENVG_VERSION_1_1
331
332 struct vg_mask_layer * mask_layer_create(VGint width, VGint height)
333 {
334 struct vg_context *ctx = vg_current_context();
335 struct vg_mask_layer *mask = 0;
336
337 mask = CALLOC_STRUCT(vg_mask_layer);
338 vg_init_object(&mask->base, ctx, VG_OBJECT_MASK);
339 mask->width = width;
340 mask->height = height;
341
342 {
343 struct pipe_resource pt;
344 struct pipe_context *pipe = ctx->pipe;
345 struct pipe_screen *screen = ctx->pipe->screen;
346 struct pipe_sampler_view view_templ;
347 struct pipe_sampler_view *view = NULL;
348 struct pipe_resource *texture;
349
350 memset(&pt, 0, sizeof(pt));
351 pt.target = PIPE_TEXTURE_2D;
352 pt.format = PIPE_FORMAT_B8G8R8A8_UNORM;
353 pt.last_level = 0;
354 pt.width0 = width;
355 pt.height0 = height;
356 pt.depth0 = 1;
357 pt.bind = PIPE_BIND_SAMPLER_VIEW;
358
359 texture = screen->resource_create(screen, &pt);
360
361 if (texture) {
362 u_sampler_view_default_template(&view_templ, texture, texture->format);
363 view = pipe->create_sampler_view(pipe, texture, &view_templ);
364 }
365 pipe_resource_reference(&texture, NULL);
366 mask->sampler_view = view;
367 }
368
369 vg_context_add_object(ctx, VG_OBJECT_MASK, mask);
370
371 return mask;
372 }
373
374 void mask_layer_destroy(struct vg_mask_layer *layer)
375 {
376 struct vg_context *ctx = vg_current_context();
377
378 vg_context_remove_object(ctx, VG_OBJECT_MASK, layer);
379 pipe_sampler_view_reference(&layer->sampler_view, NULL);
380 FREE(layer);
381 }
382
383 void mask_layer_fill(struct vg_mask_layer *layer,
384 VGint x, VGint y,
385 VGint width, VGint height,
386 VGfloat value)
387 {
388 VGfloat alpha_color[4] = {0, 0, 0, 0};
389
390 alpha_color[3] = value;
391
392 mask_resource_fill(layer->sampler_view->texture,
393 x, y, width, height, value);
394 }
395
396 void mask_copy(struct vg_mask_layer *layer,
397 VGint sx, VGint sy,
398 VGint dx, VGint dy,
399 VGint width, VGint height)
400 {
401 struct vg_context *ctx = vg_current_context();
402 struct pipe_sampler_view *src = vg_get_surface_mask(ctx);
403 struct pipe_surface *surf;
404
405 /* get the destination surface */
406 surf = ctx->pipe->screen->get_tex_surface(ctx->pipe->screen,
407 layer->sampler_view->texture, 0, 0, 0, PIPE_BIND_RENDER_TARGET);
408 if (surf && renderer_copy_begin(ctx->renderer, surf, VG_FALSE, src)) {
409 /* layer should be flipped when used as a texture */
410 sy += height;
411 height = -height;
412
413 renderer_copy(ctx->renderer,
414 dx, dy, width, height,
415 sx, sy, width, height);
416 renderer_copy_end(ctx->renderer);
417 }
418
419 pipe_surface_reference(&surf, NULL);
420 }
421
422 static void mask_layer_render_to(struct vg_mask_layer *layer,
423 struct path *path,
424 VGbitfield paint_modes)
425 {
426 struct vg_context *ctx = vg_current_context();
427 struct pipe_screen *screen = ctx->pipe->screen;
428 struct pipe_sampler_view *view = vg_get_surface_mask(ctx);
429 struct matrix *mat = &ctx->state.vg.path_user_to_surface_matrix;
430 struct pipe_surface *surf;
431
432 surf = screen->get_tex_surface(screen, view->texture,
433 0, 0, 0, PIPE_BIND_RENDER_TARGET);
434
435 renderer_validate_for_mask_rendering(ctx->renderer, surf);
436
437 if (paint_modes & VG_FILL_PATH) {
438 path_fill(path, mat);
439 }
440
441 if (paint_modes & VG_STROKE_PATH){
442 path_stroke(path, mat);
443 }
444
445 pipe_surface_reference(&surf, NULL);
446 }
447
448 void mask_render_to(struct path *path,
449 VGbitfield paint_modes,
450 VGMaskOperation operation)
451 {
452 struct vg_context *ctx = vg_current_context();
453 struct st_framebuffer *stfb = ctx->draw_buffer;
454 struct vg_mask_layer *temp_layer;
455 VGint width, height;
456
457 width = stfb->width;
458 height = stfb->height;
459
460 temp_layer = mask_layer_create(width, height);
461 mask_layer_fill(temp_layer, 0, 0, width, height, 0.0f);
462
463 mask_layer_render_to(temp_layer, path, paint_modes);
464
465 mask_using_layer(temp_layer, operation, 0, 0, width, height);
466
467 mask_layer_destroy(temp_layer);
468 }
469
470 void mask_using_layer(struct vg_mask_layer *layer,
471 VGMaskOperation operation,
472 VGint x, VGint y,
473 VGint width, VGint height)
474 {
475 mask_using_texture(layer->sampler_view, VG_TRUE, operation,
476 x, y, width, height);
477 }
478
479 VGint mask_layer_width(struct vg_mask_layer *layer)
480 {
481 return layer->width;
482 }
483
484 VGint mask_layer_height(struct vg_mask_layer *layer)
485 {
486 return layer->height;
487 }
488
489
490 #endif
491
492 void mask_using_image(struct vg_image *image,
493 VGMaskOperation operation,
494 VGint x, VGint y,
495 VGint width, VGint height)
496 {
497 mask_using_texture(image->sampler_view, VG_FALSE, operation,
498 x, y, width, height);
499 }
500
501 void mask_fill(VGint x, VGint y, VGint width, VGint height,
502 VGfloat value)
503 {
504 struct vg_context *ctx = vg_current_context();
505 struct pipe_sampler_view *view = vg_get_surface_mask(ctx);
506
507 #if DEBUG_MASKS
508 debug_printf("mask_fill(%d, %d, %d, %d) with rgba(%f, %f, %f, %f)\n",
509 x, y, width, height,
510 0.0f, 0.0f, 0.0f, value);
511 #endif
512
513 mask_resource_fill(view->texture, x, y, width, height, value);
514 }
515
516 VGint mask_bind_samplers(struct pipe_sampler_state **samplers,
517 struct pipe_sampler_view **sampler_views)
518 {
519 struct vg_context *ctx = vg_current_context();
520
521 if (ctx->state.vg.masking) {
522 samplers[1] = &ctx->mask.sampler;
523 sampler_views[1] = vg_get_surface_mask(ctx);
524 return 1;
525 } else
526 return 0;
527 }