api_filters.c revision e5b5d84e8a87a5603a84f8c4625592a278bcf9af
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 "VG/openvg.h"
28
29#include "vg_context.h"
30#include "image.h"
31#include "api.h"
32#include "renderer.h"
33#include "shaders_cache.h"
34#include "st_inlines.h"
35
36#include "pipe/p_context.h"
37#include "pipe/p_state.h"
38#include "util/u_inlines.h"
39#include "pipe/p_screen.h"
40#include "pipe/p_shader_tokens.h"
41
42#include "util/u_format.h"
43#include "util/u_memory.h"
44#include "util/u_sampler.h"
45#include "util/u_string.h"
46
47
48#include "asm_filters.h"
49
50
51struct filter_info {
52   struct vg_image *dst;
53   struct vg_image *src;
54   struct vg_shader * (*setup_shader)(struct vg_context *, void *);
55   void *user_data;
56   const void *const_buffer;
57   VGint const_buffer_len;
58   VGTilingMode tiling_mode;
59   struct pipe_sampler_view *extra_texture_view;
60};
61
62static INLINE struct pipe_resource *create_texture_1d(struct vg_context *ctx,
63                                                     const VGuint *color_data,
64                                                     const VGint color_data_len)
65{
66   struct pipe_context *pipe = ctx->pipe;
67   struct pipe_screen *screen = pipe->screen;
68   struct pipe_resource *tex = 0;
69   struct pipe_resource templ;
70
71   memset(&templ, 0, sizeof(templ));
72   templ.target = PIPE_TEXTURE_1D;
73   templ.format = PIPE_FORMAT_B8G8R8A8_UNORM;
74   templ.last_level = 0;
75   templ.width0 = color_data_len;
76   templ.height0 = 1;
77   templ.depth0 = 1;
78   templ.bind = PIPE_BIND_SAMPLER_VIEW;
79
80   tex = screen->resource_create(screen, &templ);
81
82   { /* upload color_data */
83      struct pipe_transfer *transfer =
84         pipe_get_transfer(pipe, tex,
85				0, 0, 0,
86				PIPE_TRANSFER_READ_WRITE ,
87				0, 0, tex->width0, tex->height0);
88      void *map = pipe->transfer_map(pipe, transfer);
89      memcpy(map, color_data, sizeof(VGint)*color_data_len);
90      pipe->transfer_unmap(pipe, transfer);
91      pipe->transfer_destroy(pipe, transfer);
92   }
93
94   return tex;
95}
96
97static INLINE struct pipe_sampler_view *create_texture_1d_view(struct vg_context *ctx,
98                                                               const VGuint *color_data,
99                                                               const VGint color_data_len)
100{
101   struct pipe_context *pipe = ctx->pipe;
102   struct pipe_resource *texture;
103   struct pipe_sampler_view view_templ;
104   struct pipe_sampler_view *view;
105
106   texture = create_texture_1d(ctx, color_data, color_data_len);
107
108   if (!texture)
109      return NULL;
110
111   u_sampler_view_default_template(&view_templ, texture, texture->format);
112   view = pipe->create_sampler_view(pipe, texture, &view_templ);
113   /* want the texture to go away if the view is freed */
114   pipe_resource_reference(&texture, NULL);
115
116   return view;
117}
118
119static INLINE struct pipe_surface * setup_framebuffer(struct vg_image *dst)
120{
121   struct vg_context *ctx = vg_current_context();
122   struct pipe_context *pipe = ctx->pipe;
123   struct pipe_framebuffer_state fb;
124   struct pipe_surface *dst_surf = pipe->screen->get_tex_surface(
125      pipe->screen, dst->sampler_view->texture, 0, 0, 0,
126      PIPE_BIND_RENDER_TARGET);
127
128   /* drawing dest */
129   memset(&fb, 0, sizeof(fb));
130   fb.width  = dst->x + dst_surf->width;
131   fb.height = dst->y + dst_surf->height;
132   fb.nr_cbufs = 1;
133   fb.cbufs[0] = dst_surf;
134   {
135      VGint i;
136      for (i = 1; i < PIPE_MAX_COLOR_BUFS; ++i)
137         fb.cbufs[i] = 0;
138   }
139   cso_set_framebuffer(ctx->cso_context, &fb);
140
141   return dst_surf;
142}
143
144static void setup_viewport(struct vg_image *dst)
145{
146   struct vg_context *ctx = vg_current_context();
147   vg_set_viewport(ctx, VEGA_Y0_TOP);
148}
149
150static void setup_blend()
151{
152   struct vg_context *ctx = vg_current_context();
153   struct pipe_blend_state blend;
154   memset(&blend, 0, sizeof(blend));
155   blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
156   blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
157   blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO;
158   blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
159   if (ctx->state.vg.filter_channel_mask & VG_RED)
160      blend.rt[0].colormask |= PIPE_MASK_R;
161   if (ctx->state.vg.filter_channel_mask & VG_GREEN)
162      blend.rt[0].colormask |= PIPE_MASK_G;
163   if (ctx->state.vg.filter_channel_mask & VG_BLUE)
164      blend.rt[0].colormask |= PIPE_MASK_B;
165   if (ctx->state.vg.filter_channel_mask & VG_ALPHA)
166      blend.rt[0].colormask |= PIPE_MASK_A;
167   blend.rt[0].blend_enable = 0;
168   cso_set_blend(ctx->cso_context, &blend);
169}
170
171static void setup_constant_buffer(struct vg_context *ctx, const void *buffer,
172                                  VGint param_bytes)
173{
174   struct pipe_context *pipe = ctx->pipe;
175   struct pipe_resource **cbuf = &ctx->filter.buffer;
176
177   /* We always need to get a new buffer, to keep the drivers simple and
178    * avoid gratuitous rendering synchronization. */
179   pipe_resource_reference(cbuf, NULL);
180
181   *cbuf = pipe_buffer_create(pipe->screen,
182                              PIPE_BIND_CONSTANT_BUFFER,
183                              param_bytes);
184
185   if (*cbuf) {
186      st_no_flush_pipe_buffer_write(ctx, *cbuf,
187                                    0, param_bytes, buffer);
188   }
189
190   ctx->pipe->set_constant_buffer(ctx->pipe, PIPE_SHADER_FRAGMENT, 0, *cbuf);
191}
192
193static void setup_samplers(struct vg_context *ctx, struct filter_info *info)
194{
195   struct pipe_sampler_state *samplers[PIPE_MAX_SAMPLERS];
196   struct pipe_sampler_view *sampler_views[PIPE_MAX_SAMPLERS];
197   struct pipe_sampler_state sampler[3];
198   int num_samplers = 0;
199   int num_textures = 0;
200
201   samplers[0] = NULL;
202   samplers[1] = NULL;
203   samplers[2] = NULL;
204   samplers[3] = NULL;
205   sampler_views[0] = NULL;
206   sampler_views[1] = NULL;
207   sampler_views[2] = NULL;
208   sampler_views[3] = NULL;
209
210   memset(&sampler[0], 0, sizeof(struct pipe_sampler_state));
211   sampler[0].wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
212   sampler[0].wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
213   sampler[0].wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
214   sampler[0].min_img_filter = PIPE_TEX_MIPFILTER_LINEAR;
215   sampler[0].mag_img_filter = PIPE_TEX_MIPFILTER_LINEAR;
216   sampler[0].normalized_coords = 1;
217
218   switch(info->tiling_mode) {
219   case VG_TILE_FILL:
220      sampler[0].wrap_s = PIPE_TEX_WRAP_CLAMP_TO_BORDER;
221      sampler[0].wrap_t = PIPE_TEX_WRAP_CLAMP_TO_BORDER;
222      memcpy(sampler[0].border_color,
223             ctx->state.vg.tile_fill_color,
224             sizeof(VGfloat) * 4);
225      break;
226   case VG_TILE_PAD:
227      sampler[0].wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
228      sampler[0].wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
229      break;
230   case VG_TILE_REPEAT:
231      sampler[0].wrap_s = PIPE_TEX_WRAP_REPEAT;
232      sampler[0].wrap_t = PIPE_TEX_WRAP_REPEAT;
233      break;
234   case VG_TILE_REFLECT:
235      sampler[0].wrap_s = PIPE_TEX_WRAP_MIRROR_REPEAT;
236      sampler[0].wrap_t = PIPE_TEX_WRAP_MIRROR_REPEAT;
237      break;
238   default:
239      debug_assert(!"Unknown tiling mode");
240   }
241
242   samplers[0] = &sampler[0];
243   sampler_views[0] = info->src->sampler_view;
244   ++num_samplers;
245   ++num_textures;
246
247   if (info->extra_texture_view) {
248      memcpy(&sampler[1], &sampler[0], sizeof(struct pipe_sampler_state));
249      samplers[1] = &sampler[1];
250      sampler_views[1] = info->extra_texture_view;
251      ++num_samplers;
252      ++num_textures;
253   }
254
255
256   cso_set_samplers(ctx->cso_context, num_samplers, (const struct pipe_sampler_state **)samplers);
257   cso_set_fragment_sampler_views(ctx->cso_context, num_textures, sampler_views);
258}
259
260static struct vg_shader * setup_color_matrix(struct vg_context *ctx, void *user_data)
261{
262   struct vg_shader *shader =
263      shader_create_from_text(ctx->pipe, color_matrix_asm, 200,
264         PIPE_SHADER_FRAGMENT);
265   cso_set_fragment_shader_handle(ctx->cso_context, shader->driver);
266   return shader;
267}
268
269static struct vg_shader * setup_convolution(struct vg_context *ctx, void *user_data)
270{
271   char buffer[1024];
272   VGint num_consts = (VGint)(long)(user_data);
273   struct vg_shader *shader;
274
275   util_snprintf(buffer, 1023, convolution_asm, num_consts, num_consts / 2 + 1);
276
277   shader = shader_create_from_text(ctx->pipe, buffer, 200,
278                                    PIPE_SHADER_FRAGMENT);
279
280   cso_set_fragment_shader_handle(ctx->cso_context, shader->driver);
281   return shader;
282}
283
284static struct vg_shader * setup_lookup(struct vg_context *ctx, void *user_data)
285{
286   struct vg_shader *shader =
287      shader_create_from_text(ctx->pipe, lookup_asm,
288                              200, PIPE_SHADER_FRAGMENT);
289
290   cso_set_fragment_shader_handle(ctx->cso_context, shader->driver);
291   return shader;
292}
293
294
295static struct vg_shader * setup_lookup_single(struct vg_context *ctx, void *user_data)
296{
297   char buffer[1024];
298   VGImageChannel channel = (VGImageChannel)(user_data);
299   struct vg_shader *shader;
300
301   switch(channel) {
302   case VG_RED:
303      util_snprintf(buffer, 1023, lookup_single_asm, "xxxx");
304      break;
305   case VG_GREEN:
306      util_snprintf(buffer, 1023, lookup_single_asm, "yyyy");
307      break;
308   case VG_BLUE:
309      util_snprintf(buffer, 1023, lookup_single_asm, "zzzz");
310      break;
311   case VG_ALPHA:
312      util_snprintf(buffer, 1023, lookup_single_asm, "wwww");
313      break;
314   default:
315      debug_assert(!"Unknown color channel");
316   }
317
318   shader = shader_create_from_text(ctx->pipe, buffer, 200,
319                                    PIPE_SHADER_FRAGMENT);
320
321   cso_set_fragment_shader_handle(ctx->cso_context, shader->driver);
322   return shader;
323}
324
325static void execute_filter(struct vg_context *ctx,
326                           struct filter_info *info)
327{
328   struct pipe_surface *dst_surf;
329   struct vg_shader *shader;
330
331   cso_save_framebuffer(ctx->cso_context);
332   cso_save_fragment_shader(ctx->cso_context);
333   cso_save_viewport(ctx->cso_context);
334   cso_save_blend(ctx->cso_context);
335   cso_save_samplers(ctx->cso_context);
336   cso_save_fragment_sampler_views(ctx->cso_context);
337
338   dst_surf = setup_framebuffer(info->dst);
339   setup_viewport(info->dst);
340   setup_blend();
341   setup_constant_buffer(ctx, info->const_buffer, info->const_buffer_len);
342   shader = info->setup_shader(ctx, info->user_data);
343   setup_samplers(ctx, info);
344
345   renderer_draw_texture(ctx->renderer,
346                         info->src->sampler_view->texture,
347                         info->dst->x, info->dst->y,
348                         info->dst->x + info->dst->width,
349                         info->dst->y + info->dst->height,
350                         info->dst->x, info->dst->y,
351                         info->dst->x + info->dst->width,
352                         info->dst->y + info->dst->height);
353
354   cso_restore_framebuffer(ctx->cso_context);
355   cso_restore_fragment_shader(ctx->cso_context);
356   cso_restore_viewport(ctx->cso_context);
357   cso_restore_blend(ctx->cso_context);
358   cso_restore_samplers(ctx->cso_context);
359   cso_restore_fragment_sampler_views(ctx->cso_context);
360
361   vg_shader_destroy(ctx, shader);
362
363   pipe_surface_reference(&dst_surf, NULL);
364}
365
366void vegaColorMatrix(VGImage dst, VGImage src,
367                     const VGfloat * matrix)
368{
369   struct vg_context *ctx = vg_current_context();
370   struct vg_image *d, *s;
371   struct filter_info info;
372
373   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
374      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
375      return;
376   }
377   if (!matrix || !is_aligned(matrix)) {
378      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
379      return;
380   }
381
382   d = (struct vg_image*)dst;
383   s = (struct vg_image*)src;
384
385   if (vg_image_overlaps(d, s)) {
386      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
387      return;
388   }
389
390   info.dst = d;
391   info.src = s;
392   info.setup_shader = &setup_color_matrix;
393   info.user_data = NULL;
394   info.const_buffer = matrix;
395   info.const_buffer_len = 20 * sizeof(VGfloat);
396   info.tiling_mode = VG_TILE_PAD;
397   info.extra_texture_view = NULL;
398   execute_filter(ctx, &info);
399}
400
401static VGfloat texture_offset(VGfloat width, VGint kernelSize, VGint current, VGint shift)
402{
403   VGfloat diff = current - shift;
404
405   return diff / width;
406}
407
408void vegaConvolve(VGImage dst, VGImage src,
409                  VGint kernelWidth, VGint kernelHeight,
410                  VGint shiftX, VGint shiftY,
411                  const VGshort * kernel,
412                  VGfloat scale,
413                  VGfloat bias,
414                  VGTilingMode tilingMode)
415{
416   struct vg_context *ctx = vg_current_context();
417   VGfloat *buffer;
418   VGint buffer_len;
419   VGint i, j;
420   VGint idx = 0;
421   struct vg_image *d, *s;
422   VGint kernel_size = kernelWidth * kernelHeight;
423   struct filter_info info;
424   const VGint max_kernel_size = vgGeti(VG_MAX_KERNEL_SIZE);
425
426   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
427      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
428      return;
429   }
430
431   if (kernelWidth <= 0 || kernelHeight <= 0 ||
432      kernelWidth > max_kernel_size || kernelHeight > max_kernel_size) {
433      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
434      return;
435   }
436
437   if (!kernel || !is_aligned_to(kernel, 2)) {
438      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
439      return;
440   }
441
442   if (tilingMode < VG_TILE_FILL ||
443       tilingMode > VG_TILE_REFLECT) {
444      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
445      return;
446   }
447
448   d = (struct vg_image*)dst;
449   s = (struct vg_image*)src;
450
451   if (vg_image_overlaps(d, s)) {
452      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
453      return;
454   }
455
456   vg_validate_state(ctx);
457
458   buffer_len = 8 + 2 * 4 * kernel_size;
459   buffer = (VGfloat*)malloc(buffer_len * sizeof(VGfloat));
460
461   buffer[0] = 0.f;
462   buffer[1] = 1.f;
463   buffer[2] = 2.f; /*unused*/
464   buffer[3] = 4.f; /*unused*/
465
466   buffer[4] = kernelWidth * kernelHeight;
467   buffer[5] = scale;
468   buffer[6] = bias;
469   buffer[7] = 0.f;
470
471   idx = 8;
472   for (j = 0; j < kernelHeight; ++j) {
473      for (i = 0; i < kernelWidth; ++i) {
474         VGint index = j * kernelWidth + i;
475         VGfloat x, y;
476
477         x = texture_offset(s->width, kernelWidth, i, shiftX);
478         y = texture_offset(s->height, kernelHeight, j, shiftY);
479
480         buffer[idx + index*4 + 0] = x;
481         buffer[idx + index*4 + 1] = y;
482         buffer[idx + index*4 + 2] = 0.f;
483         buffer[idx + index*4 + 3] = 0.f;
484      }
485   }
486   idx += kernel_size * 4;
487
488   for (j = 0; j < kernelHeight; ++j) {
489      for (i = 0; i < kernelWidth; ++i) {
490         /* transpose the kernel */
491         VGint index = j * kernelWidth + i;
492         VGint kindex = (kernelWidth - i - 1) * kernelHeight + (kernelHeight - j - 1);
493         buffer[idx + index*4 + 0] = kernel[kindex];
494         buffer[idx + index*4 + 1] = kernel[kindex];
495         buffer[idx + index*4 + 2] = kernel[kindex];
496         buffer[idx + index*4 + 3] = kernel[kindex];
497      }
498   }
499
500   info.dst = d;
501   info.src = s;
502   info.setup_shader = &setup_convolution;
503   info.user_data = (void*)(long)(buffer_len/4);
504   info.const_buffer = buffer;
505   info.const_buffer_len = buffer_len * sizeof(VGfloat);
506   info.tiling_mode = tilingMode;
507   info.extra_texture_view = NULL;
508   execute_filter(ctx, &info);
509
510   free(buffer);
511}
512
513void vegaSeparableConvolve(VGImage dst, VGImage src,
514                           VGint kernelWidth,
515                           VGint kernelHeight,
516                           VGint shiftX, VGint shiftY,
517                           const VGshort * kernelX,
518                           const VGshort * kernelY,
519                           VGfloat scale,
520                           VGfloat bias,
521                           VGTilingMode tilingMode)
522{
523   struct vg_context *ctx = vg_current_context();
524   VGshort *kernel;
525   VGint i, j, idx = 0;
526   const VGint max_kernel_size = vgGeti(VG_MAX_SEPARABLE_KERNEL_SIZE);
527
528   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
529      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
530      return;
531   }
532
533   if (kernelWidth <= 0 || kernelHeight <= 0 ||
534       kernelWidth > max_kernel_size || kernelHeight > max_kernel_size) {
535      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
536      return;
537   }
538
539   if (!kernelX || !kernelY ||
540       !is_aligned_to(kernelX, 2) || !is_aligned_to(kernelY, 2)) {
541      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
542      return;
543   }
544   if (tilingMode < VG_TILE_FILL ||
545       tilingMode > VG_TILE_REFLECT) {
546      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
547      return;
548   }
549   kernel = malloc(sizeof(VGshort)*kernelWidth*kernelHeight);
550   for (i = 0; i < kernelWidth; ++i) {
551      for (j = 0; j < kernelHeight; ++j) {
552         kernel[idx] = kernelX[i] * kernelY[j];
553         ++idx;
554      }
555   }
556   vgConvolve(dst, src, kernelWidth, kernelHeight, shiftX, shiftY,
557              kernel, scale, bias, tilingMode);
558   free(kernel);
559}
560
561static INLINE VGfloat compute_gaussian_componenet(VGfloat x, VGfloat y,
562                                                  VGfloat stdDeviationX,
563                                                  VGfloat stdDeviationY)
564{
565   VGfloat mult = 1 / ( 2 * M_PI * stdDeviationX * stdDeviationY);
566   VGfloat e = exp( - ( pow(x, 2)/(2*pow(stdDeviationX, 2)) +
567                        pow(y, 2)/(2*pow(stdDeviationY, 2)) ) );
568   return mult * e;
569}
570
571static INLINE VGint compute_kernel_size(VGfloat deviation)
572{
573   VGint size = ceil(2.146 * deviation);
574   if (size > 11)
575      return 11;
576   return size;
577}
578
579static void compute_gaussian_kernel(VGfloat *kernel,
580                                    VGint width, VGint height,
581                                    VGfloat stdDeviationX,
582                                    VGfloat stdDeviationY)
583{
584   VGint i, j;
585   VGfloat scale = 0.0f;
586
587   for (j = 0; j < height; ++j) {
588      for (i = 0; i < width; ++i) {
589         VGint idx =  (height - j -1) * width + (width - i -1);
590         kernel[idx] = compute_gaussian_componenet(i-(ceil(width/2))-1,
591                                                   j-ceil(height/2)-1,
592                                                   stdDeviationX, stdDeviationY);
593         scale += kernel[idx];
594      }
595   }
596
597   for (j = 0; j < height; ++j) {
598      for (i = 0; i < width; ++i) {
599         VGint idx = j * width + i;
600         kernel[idx] /= scale;
601      }
602   }
603}
604
605void vegaGaussianBlur(VGImage dst, VGImage src,
606                      VGfloat stdDeviationX,
607                      VGfloat stdDeviationY,
608                      VGTilingMode tilingMode)
609{
610   struct vg_context *ctx = vg_current_context();
611   struct vg_image *d, *s;
612   VGfloat *buffer, *kernel;
613   VGint kernel_width, kernel_height, kernel_size;
614   VGint buffer_len;
615   VGint idx, i, j;
616   struct filter_info info;
617
618   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
619      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
620      return;
621   }
622   if (stdDeviationX <= 0 || stdDeviationY <= 0) {
623      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
624      return;
625   }
626
627   if (tilingMode < VG_TILE_FILL ||
628       tilingMode > VG_TILE_REFLECT) {
629      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
630      return;
631   }
632
633   d = (struct vg_image*)dst;
634   s = (struct vg_image*)src;
635
636   if (vg_image_overlaps(d, s)) {
637      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
638      return;
639   }
640
641   kernel_width = compute_kernel_size(stdDeviationX);
642   kernel_height = compute_kernel_size(stdDeviationY);
643   kernel_size = kernel_width * kernel_height;
644   kernel = malloc(sizeof(VGfloat)*kernel_size);
645   compute_gaussian_kernel(kernel, kernel_width, kernel_height,
646                           stdDeviationX, stdDeviationY);
647
648   buffer_len = 8 + 2 * 4 * kernel_size;
649   buffer = (VGfloat*)malloc(buffer_len * sizeof(VGfloat));
650
651   buffer[0] = 0.f;
652   buffer[1] = 1.f;
653   buffer[2] = 2.f; /*unused*/
654   buffer[3] = 4.f; /*unused*/
655
656   buffer[4] = kernel_width * kernel_height;
657   buffer[5] = 1.f;/*scale*/
658   buffer[6] = 0.f;/*bias*/
659   buffer[7] = 0.f;
660
661   idx = 8;
662   for (j = 0; j < kernel_height; ++j) {
663      for (i = 0; i < kernel_width; ++i) {
664         VGint index = j * kernel_width + i;
665         VGfloat x, y;
666
667         x = texture_offset(s->width, kernel_width, i, kernel_width/2);
668         y = texture_offset(s->height, kernel_height, j, kernel_height/2);
669
670         buffer[idx + index*4 + 0] = x;
671         buffer[idx + index*4 + 1] = y;
672         buffer[idx + index*4 + 2] = 0.f;
673         buffer[idx + index*4 + 3] = 0.f;
674      }
675   }
676   idx += kernel_size * 4;
677
678   for (j = 0; j < kernel_height; ++j) {
679      for (i = 0; i < kernel_width; ++i) {
680         /* transpose the kernel */
681         VGint index = j * kernel_width + i;
682         VGint kindex = (kernel_width - i - 1) * kernel_height + (kernel_height - j - 1);
683         buffer[idx + index*4 + 0] = kernel[kindex];
684         buffer[idx + index*4 + 1] = kernel[kindex];
685         buffer[idx + index*4 + 2] = kernel[kindex];
686         buffer[idx + index*4 + 3] = kernel[kindex];
687      }
688   }
689
690   info.dst = d;
691   info.src = s;
692   info.setup_shader = &setup_convolution;
693   info.user_data = (void*)(long)(buffer_len/4);
694   info.const_buffer = buffer;
695   info.const_buffer_len = buffer_len * sizeof(VGfloat);
696   info.tiling_mode = tilingMode;
697   info.extra_texture_view = NULL;
698   execute_filter(ctx, &info);
699
700   free(buffer);
701   free(kernel);
702}
703
704void vegaLookup(VGImage dst, VGImage src,
705                const VGubyte * redLUT,
706                const VGubyte * greenLUT,
707                const VGubyte * blueLUT,
708                const VGubyte * alphaLUT,
709                VGboolean outputLinear,
710                VGboolean outputPremultiplied)
711{
712   struct vg_context *ctx = vg_current_context();
713   struct vg_image *d, *s;
714   VGuint color_data[256];
715   VGint i;
716   struct pipe_sampler_view *lut_texture_view;
717   VGfloat buffer[4];
718   struct filter_info info;
719
720   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
721      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
722      return;
723   }
724
725   if (!redLUT || !greenLUT || !blueLUT || !alphaLUT) {
726      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
727      return;
728   }
729
730   d = (struct vg_image*)dst;
731   s = (struct vg_image*)src;
732
733   if (vg_image_overlaps(d, s)) {
734      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
735      return;
736   }
737
738   for (i = 0; i < 256; ++i) {
739      color_data[i] = blueLUT[i] << 24 | greenLUT[i] << 16 |
740                      redLUT[i]  <<  8 | alphaLUT[i];
741   }
742   lut_texture_view = create_texture_1d_view(ctx, color_data, 255);
743
744   buffer[0] = 0.f;
745   buffer[1] = 0.f;
746   buffer[2] = 1.f;
747   buffer[3] = 1.f;
748
749   info.dst = d;
750   info.src = s;
751   info.setup_shader = &setup_lookup;
752   info.user_data = NULL;
753   info.const_buffer = buffer;
754   info.const_buffer_len = 4 * sizeof(VGfloat);
755   info.tiling_mode = VG_TILE_PAD;
756   info.extra_texture_view = lut_texture_view;
757
758   execute_filter(ctx, &info);
759
760   pipe_sampler_view_reference(&lut_texture_view, NULL);
761}
762
763void vegaLookupSingle(VGImage dst, VGImage src,
764                      const VGuint * lookupTable,
765                      VGImageChannel sourceChannel,
766                      VGboolean outputLinear,
767                      VGboolean outputPremultiplied)
768{
769   struct vg_context *ctx = vg_current_context();
770   struct vg_image *d, *s;
771   struct pipe_sampler_view *lut_texture_view;
772   VGfloat buffer[4];
773   struct filter_info info;
774   VGuint color_data[256];
775   VGint i;
776
777   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
778      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
779      return;
780   }
781
782   if (!lookupTable || !is_aligned(lookupTable)) {
783      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
784      return;
785   }
786
787   if (sourceChannel != VG_RED && sourceChannel != VG_GREEN &&
788       sourceChannel != VG_BLUE && sourceChannel != VG_ALPHA) {
789      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
790      return;
791   }
792
793   d = (struct vg_image*)dst;
794   s = (struct vg_image*)src;
795
796   if (vg_image_overlaps(d, s)) {
797      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
798      return;
799   }
800
801   for (i = 0; i < 256; ++i) {
802      VGuint rgba = lookupTable[i];
803      VGubyte blue, green, red, alpha;
804      red   = (rgba & 0xff000000)>>24;
805      green = (rgba & 0x00ff0000)>>16;
806      blue  = (rgba & 0x0000ff00)>> 8;
807      alpha = (rgba & 0x000000ff)>> 0;
808      color_data[i] = blue << 24 | green << 16 |
809                      red  <<  8 | alpha;
810   }
811   lut_texture_view = create_texture_1d_view(ctx, color_data, 256);
812
813   buffer[0] = 0.f;
814   buffer[1] = 0.f;
815   buffer[2] = 1.f;
816   buffer[3] = 1.f;
817
818   info.dst = d;
819   info.src = s;
820   info.setup_shader = &setup_lookup_single;
821   info.user_data = (void*)sourceChannel;
822   info.const_buffer = buffer;
823   info.const_buffer_len = 4 * sizeof(VGfloat);
824   info.tiling_mode = VG_TILE_PAD;
825   info.extra_texture_view = lut_texture_view;
826
827   execute_filter(ctx, &info);
828
829   pipe_sampler_view_reference(&lut_texture_view, NULL);
830}
831