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_surface.h"
41#include "util/u_sampler.h"
42
43struct vg_mask_layer {
44   struct vg_object base;
45
46   VGint width;
47   VGint height;
48
49   struct pipe_sampler_view *sampler_view;
50};
51
52static INLINE VGboolean
53intersect_rectangles(VGint dwidth, VGint dheight,
54                     VGint swidth, VGint sheight,
55                     VGint tx, VGint ty,
56                     VGint twidth, VGint theight,
57                     VGint *offsets,
58                     VGint *location)
59{
60   if (tx + twidth <= 0 || tx >= dwidth)
61      return VG_FALSE;
62   if (ty + theight <= 0 || ty >= dheight)
63      return VG_FALSE;
64
65   offsets[0] = 0;
66   offsets[1] = 0;
67   location[0] = tx;
68   location[1] = ty;
69
70   if (tx < 0) {
71      offsets[0] -= tx;
72      location[0] = 0;
73
74      location[2] = MIN2(tx + swidth, MIN2(dwidth, tx + twidth));
75      offsets[2] = location[2];
76   } else {
77      offsets[2] = MIN2(twidth, MIN2(dwidth - tx, swidth ));
78      location[2] = offsets[2];
79   }
80
81   if (ty < 0) {
82      offsets[1] -= ty;
83      location[1] = 0;
84
85      location[3] = MIN2(ty + sheight, MIN2(dheight, ty + theight));
86      offsets[3] = location[3];
87   } else {
88      offsets[3] = MIN2(theight, MIN2(dheight - ty, sheight));
89      location[3] = offsets[3];
90   }
91
92   return VG_TRUE;
93}
94
95#if DEBUG_MASKS
96static void read_alpha_mask(void * data, VGint dataStride,
97                            VGImageFormat dataFormat,
98                            VGint sx, VGint sy,
99                            VGint width, VGint height)
100{
101   struct vg_context *ctx = vg_current_context();
102   struct pipe_context *pipe = ctx->pipe;
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   if (sx < 0) {
115      xoffset = -sx;
116      xoffset *= _vega_size_for_format(dataFormat);
117      width += sx;
118      sx = 0;
119   }
120   if (sy < 0) {
121      yoffset = -sy;
122      height += sy;
123      sy = 0;
124      y = (stfb->height - sy) - 1;
125      yoffset *= dataStride;
126   }
127
128   {
129      struct pipe_surface *surf;
130
131      surf = pipe->create_surface(pipe, strb->texture,  0, 0, 0,
132                                  PIPE_BIND_TRANSFER_READ);
133
134      /* Do a row at a time to flip image data vertically */
135      for (i = 0; i < height; i++) {
136#if 0
137         debug_printf("%d-%d  == %d\n", sy, height, y);
138#endif
139         pipe_get_tile_rgba(surf, sx, y, width, 1, df);
140         y += yStep;
141         _vega_pack_rgba_span_float(ctx, width, temp, dataFormat,
142                                    dst + yoffset + xoffset);
143         dst += dataStride;
144      }
145
146      pipe_surface_reference(&surf, NULL);
147   }
148}
149
150void save_alpha_to_file(const char *filename)
151{
152   struct vg_context *ctx = vg_current_context();
153   struct st_framebuffer *stfb = ctx->draw_buffer;
154   VGint *data;
155   int i, j;
156
157   data = malloc(sizeof(int) * stfb->width * stfb->height);
158   read_alpha_mask(data, stfb->width * sizeof(int),
159                   VG_sRGBA_8888,
160                   0, 0, stfb->width, stfb->height);
161   fprintf(stderr, "/*---------- start */\n");
162   fprintf(stderr, "const int image_width = %d;\n",
163           stfb->width);
164   fprintf(stderr, "const int image_height = %d;\n",
165           stfb->height);
166   fprintf(stderr, "const int image_data = {\n");
167   for (i = 0; i < stfb->height; ++i) {
168      for (j = 0; j < stfb->width; ++j) {
169         int rgba = data[i * stfb->height + j];
170         int argb = 0;
171         argb = (rgba >> 8);
172         argb |= ((rgba & 0xff) << 24);
173         fprintf(stderr, "0x%x, ", argb);
174      }
175      fprintf(stderr, "\n");
176   }
177   fprintf(stderr, "};\n");
178   fprintf(stderr, "/*---------- end */\n");
179}
180#endif
181
182/* setup mask shader */
183static void *setup_mask_operation(VGMaskOperation operation)
184{
185   struct vg_context *ctx = vg_current_context();
186   void *shader = 0;
187
188   switch (operation) {
189   case VG_UNION_MASK: {
190      if (!ctx->mask.union_fs) {
191         ctx->mask.union_fs = shader_create_from_text(ctx->pipe,
192                                                      union_mask_asm,
193                                                      200,
194                                                      PIPE_SHADER_FRAGMENT);
195      }
196      shader = ctx->mask.union_fs->driver;
197   }
198      break;
199   case VG_INTERSECT_MASK: {
200      if (!ctx->mask.intersect_fs) {
201         ctx->mask.intersect_fs = shader_create_from_text(ctx->pipe,
202                                                          intersect_mask_asm,
203                                                          200,
204                                                          PIPE_SHADER_FRAGMENT);
205      }
206      shader = ctx->mask.intersect_fs->driver;
207   }
208      break;
209   case VG_SUBTRACT_MASK: {
210      if (!ctx->mask.subtract_fs) {
211         ctx->mask.subtract_fs = shader_create_from_text(ctx->pipe,
212                                                         subtract_mask_asm,
213                                                         200,
214                                                         PIPE_SHADER_FRAGMENT);
215      }
216      shader = ctx->mask.subtract_fs->driver;
217   }
218      break;
219   case VG_SET_MASK: {
220      if (!ctx->mask.set_fs) {
221         ctx->mask.set_fs = shader_create_from_text(ctx->pipe,
222                                                    set_mask_asm,
223                                                    200,
224                                                    PIPE_SHADER_FRAGMENT);
225      }
226      shader = ctx->mask.set_fs->driver;
227   }
228      break;
229   default:
230         assert(0);
231      break;
232   }
233
234   return shader;
235}
236
237static void mask_resource_fill(struct pipe_resource *dst,
238                               int x, int y, int width, int height,
239                               VGfloat coverage)
240{
241   struct vg_context *ctx = vg_current_context();
242   VGfloat fs_consts[12] = {
243      0.0f, 0.0f, 0.0f, 0.0f, /* not used */
244      0.0f, 0.0f, 0.0f, 0.0f, /* not used */
245      0.0f, 0.0f, 0.0f, coverage /* color */
246   };
247   void *fs;
248
249   if (x < 0) {
250      width += x;
251      x = 0;
252   }
253   if (y < 0) {
254      height += y;
255      y = 0;
256   }
257
258   fs = shaders_cache_fill(ctx->sc, VEGA_SOLID_FILL_SHADER);
259
260   if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE, ~0,
261            NULL, NULL, 0, fs, (const void *) fs_consts, sizeof(fs_consts))) {
262      renderer_filter(ctx->renderer, x, y, width, height, 0, 0, 0, 0);
263      renderer_filter_end(ctx->renderer);
264   }
265
266#if DEBUG_MASKS
267   save_alpha_to_file(0);
268#endif
269}
270
271
272static void mask_using_texture(struct pipe_sampler_view *sampler_view,
273                               VGboolean is_layer,
274                               VGMaskOperation operation,
275                               VGint x, VGint y,
276                               VGint width, VGint height)
277{
278   struct vg_context *ctx = vg_current_context();
279   struct pipe_sampler_view *dst_view = vg_get_surface_mask(ctx);
280   struct pipe_resource *dst = dst_view->texture;
281   struct pipe_resource *texture = sampler_view->texture;
282   const struct pipe_sampler_state *samplers[2];
283   struct pipe_sampler_view *views[2];
284   struct pipe_sampler_state sampler;
285   VGint offsets[4], loc[4];
286   const VGfloat ones[4] = {1.f, 1.f, 1.f, 1.f};
287   void *fs;
288
289   if (!intersect_rectangles(dst->width0, dst->height0,
290                             texture->width0, texture->height0,
291                             x, y, width, height,
292                             offsets, loc))
293      return;
294#if 0
295   debug_printf("Offset = [%d, %d, %d, %d]\n", offsets[0],
296                offsets[1], offsets[2], offsets[3]);
297   debug_printf("Locati = [%d, %d, %d, %d]\n", loc[0],
298                loc[1], loc[2], loc[3]);
299#endif
300
301
302   sampler = ctx->mask.sampler;
303   sampler.normalized_coords = 1;
304   samplers[0] = &sampler;
305   views[0] = sampler_view;
306
307   /* prepare our blend surface */
308   samplers[1] = &ctx->mask.sampler;
309   views[1] = vg_prepare_blend_surface_from_mask(ctx);
310
311   fs = setup_mask_operation(operation);
312
313   if (renderer_filter_begin(ctx->renderer, dst, VG_FALSE,
314            ~0, samplers, views, 2, fs, (const void *) ones, sizeof(ones))) {
315      /* layer should be flipped when used as a texture */
316      if (is_layer) {
317         offsets[1] += offsets[3];
318         offsets[3] = -offsets[3];
319      }
320      renderer_filter(ctx->renderer,
321            loc[0], loc[1], loc[2], loc[3],
322            offsets[0], offsets[1], offsets[2], offsets[3]);
323      renderer_filter_end(ctx->renderer);
324   }
325}
326
327
328#ifdef OPENVG_VERSION_1_1
329
330struct vg_mask_layer * mask_layer_create(VGint width, VGint height)
331{
332   struct vg_context *ctx = vg_current_context();
333   struct vg_mask_layer *mask = 0;
334
335   mask = CALLOC_STRUCT(vg_mask_layer);
336   vg_init_object(&mask->base, ctx, VG_OBJECT_MASK);
337   mask->width = width;
338   mask->height = height;
339
340   {
341      struct pipe_resource pt;
342      struct pipe_context *pipe = ctx->pipe;
343      struct pipe_screen *screen = ctx->pipe->screen;
344      struct pipe_sampler_view view_templ;
345      struct pipe_sampler_view *view = NULL;
346      struct pipe_resource *texture;
347
348      memset(&pt, 0, sizeof(pt));
349      pt.target = PIPE_TEXTURE_2D;
350      pt.format = PIPE_FORMAT_B8G8R8A8_UNORM;
351      pt.last_level = 0;
352      pt.width0 = width;
353      pt.height0 = height;
354      pt.depth0 = 1;
355      pt.array_size = 1;
356      pt.bind = PIPE_BIND_SAMPLER_VIEW;
357
358      texture = screen->resource_create(screen, &pt);
359
360      if (texture) {
361         u_sampler_view_default_template(&view_templ, texture, texture->format);
362         view = pipe->create_sampler_view(pipe, texture, &view_templ);
363      }
364      pipe_resource_reference(&texture, NULL);
365      mask->sampler_view = view;
366   }
367
368   vg_context_add_object(ctx, &mask->base);
369
370   return mask;
371}
372
373void mask_layer_destroy(struct vg_mask_layer *layer)
374{
375   struct vg_context *ctx = vg_current_context();
376
377   vg_context_remove_object(ctx, &layer->base);
378   pipe_sampler_view_reference(&layer->sampler_view, NULL);
379   FREE(layer);
380}
381
382void mask_layer_fill(struct vg_mask_layer *layer,
383                     VGint x, VGint y,
384                     VGint width, VGint height,
385                     VGfloat value)
386{
387   mask_resource_fill(layer->sampler_view->texture,
388                      x, y, width, height, value);
389}
390
391void mask_copy(struct vg_mask_layer *layer,
392               VGint sx, VGint sy,
393               VGint dx, VGint dy,
394               VGint width, VGint height)
395{
396   struct vg_context *ctx = vg_current_context();
397   struct pipe_sampler_view *src = vg_get_surface_mask(ctx);
398   struct pipe_surface *surf, surf_tmpl;
399
400   /* get the destination surface */
401   u_surface_default_template(&surf_tmpl, layer->sampler_view->texture,
402                              PIPE_BIND_RENDER_TARGET);
403   surf = ctx->pipe->create_surface(ctx->pipe, layer->sampler_view->texture,
404                                    &surf_tmpl);
405   if (surf && renderer_copy_begin(ctx->renderer, surf, VG_FALSE, src)) {
406      /* layer should be flipped when used as a texture */
407      sy += height;
408      height = -height;
409
410      renderer_copy(ctx->renderer,
411            dx, dy, width, height,
412            sx, sy, width, height);
413      renderer_copy_end(ctx->renderer);
414   }
415
416   pipe_surface_reference(&surf, NULL);
417}
418
419static void mask_layer_render_to(struct vg_mask_layer *layer,
420                                 struct path *path,
421                                 VGbitfield paint_modes)
422{
423   struct vg_context *ctx = vg_current_context();
424   struct pipe_context *pipe = ctx->pipe;
425   struct pipe_sampler_view *view = vg_get_surface_mask(ctx);
426   struct matrix *mat = &ctx->state.vg.path_user_to_surface_matrix;
427   struct pipe_surface *surf, surf_tmpl;
428   u_surface_default_template(&surf_tmpl, view->texture,
429                              PIPE_BIND_RENDER_TARGET);
430   surf = pipe->create_surface(pipe, view->texture, &surf_tmpl);
431
432   renderer_validate_for_mask_rendering(ctx->renderer, surf, mat);
433
434   if (paint_modes & VG_FILL_PATH) {
435      path_fill(path);
436   }
437
438   if (paint_modes & VG_STROKE_PATH){
439      path_stroke(path);
440   }
441
442   pipe_surface_reference(&surf, NULL);
443}
444
445void mask_render_to(struct path *path,
446                    VGbitfield paint_modes,
447                    VGMaskOperation operation)
448{
449   struct vg_context *ctx = vg_current_context();
450   struct st_framebuffer *stfb = ctx->draw_buffer;
451   struct vg_mask_layer *temp_layer;
452   VGint width, height;
453
454   width = stfb->width;
455   height = stfb->height;
456
457   temp_layer = mask_layer_create(width, height);
458   mask_layer_fill(temp_layer, 0, 0, width, height, 0.0f);
459
460   mask_layer_render_to(temp_layer, path, paint_modes);
461
462   mask_using_layer(temp_layer, operation, 0, 0, width, height);
463
464   mask_layer_destroy(temp_layer);
465}
466
467void mask_using_layer(struct vg_mask_layer *layer,
468                      VGMaskOperation operation,
469                      VGint x, VGint y,
470                      VGint width, VGint height)
471{
472   mask_using_texture(layer->sampler_view, VG_TRUE, operation,
473                      x, y, width, height);
474}
475
476VGint mask_layer_width(struct vg_mask_layer *layer)
477{
478   return layer->width;
479}
480
481VGint mask_layer_height(struct vg_mask_layer *layer)
482{
483   return layer->height;
484}
485
486
487#endif
488
489void mask_using_image(struct vg_image *image,
490                      VGMaskOperation operation,
491                      VGint x, VGint y,
492                      VGint width, VGint height)
493{
494   mask_using_texture(image->sampler_view, VG_FALSE, operation,
495                      x, y, width, height);
496}
497
498void mask_fill(VGint x, VGint y, VGint width, VGint height,
499               VGfloat value)
500{
501   struct vg_context *ctx = vg_current_context();
502   struct pipe_sampler_view *view = vg_get_surface_mask(ctx);
503
504#if DEBUG_MASKS
505   debug_printf("mask_fill(%d, %d, %d, %d) with  rgba(%f, %f, %f, %f)\n",
506                x, y, width, height,
507                0.0f, 0.0f, 0.0f, value);
508#endif
509
510   mask_resource_fill(view->texture, x, y, width, height, value);
511}
512
513VGint mask_bind_samplers(struct pipe_sampler_state **samplers,
514                         struct pipe_sampler_view **sampler_views)
515{
516   struct vg_context *ctx = vg_current_context();
517
518   if (ctx->state.vg.masking) {
519      samplers[1] = &ctx->mask.sampler;
520      sampler_views[1] = vg_get_surface_mask(ctx);
521      return 1;
522   } else
523      return 0;
524}
525