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