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