api_filters.c revision 9f61e43b4903c6cf0ac03a479ec9ed7b15fd6ccf
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 "handle.h"
33#include "renderer.h"
34#include "shaders_cache.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
41#include "util/u_format.h"
42#include "util/u_sampler.h"
43#include "util/u_string.h"
44
45#include "asm_filters.h"
46
47
48struct filter_info {
49   struct vg_image *dst;
50   struct vg_image *src;
51   struct vg_shader * (*setup_shader)(struct vg_context *, void *);
52   void *user_data;
53   const void *const_buffer;
54   VGint const_buffer_len;
55   VGTilingMode tiling_mode;
56   struct pipe_sampler_view *extra_texture_view;
57};
58
59static INLINE struct pipe_resource *create_texture_1d(struct vg_context *ctx,
60                                                     const VGuint *color_data,
61                                                     const VGint color_data_len)
62{
63   struct pipe_context *pipe = ctx->pipe;
64   struct pipe_screen *screen = pipe->screen;
65   struct pipe_resource *tex = 0;
66   struct pipe_resource templ;
67
68   memset(&templ, 0, sizeof(templ));
69   templ.target = PIPE_TEXTURE_1D;
70   templ.format = PIPE_FORMAT_B8G8R8A8_UNORM;
71   templ.last_level = 0;
72   templ.width0 = color_data_len;
73   templ.height0 = 1;
74   templ.depth0 = 1;
75   templ.array_size = 1;
76   templ.bind = PIPE_BIND_SAMPLER_VIEW;
77
78   tex = screen->resource_create(screen, &templ);
79
80   { /* upload color_data */
81      struct pipe_transfer *transfer =
82         pipe_get_transfer(pipe, tex,
83                           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->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_resource *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_resource_reference(&texture, NULL);
113
114   return view;
115}
116
117static struct vg_shader * setup_color_matrix(struct vg_context *ctx, void *user_data)
118{
119   struct vg_shader *shader =
120      shader_create_from_text(ctx->pipe, color_matrix_asm, 200,
121         PIPE_SHADER_FRAGMENT);
122   return shader;
123}
124
125static struct vg_shader * setup_convolution(struct vg_context *ctx, void *user_data)
126{
127   char buffer[1024];
128   VGint num_consts = (VGint)(long)(user_data);
129   struct vg_shader *shader;
130
131   util_snprintf(buffer, 1023, convolution_asm, num_consts, num_consts / 2 + 1);
132
133   shader = shader_create_from_text(ctx->pipe, buffer, 200,
134                                    PIPE_SHADER_FRAGMENT);
135
136   return shader;
137}
138
139static struct vg_shader * setup_lookup(struct vg_context *ctx, void *user_data)
140{
141   struct vg_shader *shader =
142      shader_create_from_text(ctx->pipe, lookup_asm,
143                              200, PIPE_SHADER_FRAGMENT);
144
145   return shader;
146}
147
148
149static struct vg_shader * setup_lookup_single(struct vg_context *ctx, void *user_data)
150{
151   char buffer[1024];
152   VGImageChannel channel = (VGImageChannel)(user_data);
153   struct vg_shader *shader;
154
155   switch(channel) {
156   case VG_RED:
157      util_snprintf(buffer, 1023, lookup_single_asm, "xxxx");
158      break;
159   case VG_GREEN:
160      util_snprintf(buffer, 1023, lookup_single_asm, "yyyy");
161      break;
162   case VG_BLUE:
163      util_snprintf(buffer, 1023, lookup_single_asm, "zzzz");
164      break;
165   case VG_ALPHA:
166      util_snprintf(buffer, 1023, lookup_single_asm, "wwww");
167      break;
168   default:
169      debug_assert(!"Unknown color channel");
170   }
171
172   shader = shader_create_from_text(ctx->pipe, buffer, 200,
173                                    PIPE_SHADER_FRAGMENT);
174
175   return shader;
176}
177
178static void execute_filter(struct vg_context *ctx,
179                           struct filter_info *info)
180{
181   struct vg_shader *shader;
182   const struct pipe_sampler_state *samplers[2];
183   struct pipe_sampler_view *views[2];
184   struct pipe_sampler_state sampler;
185   uint tex_wrap;
186
187   memset(&sampler, 0, sizeof(sampler));
188   sampler.min_img_filter = PIPE_TEX_FILTER_LINEAR;
189   sampler.mag_img_filter = PIPE_TEX_FILTER_LINEAR;
190   sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
191   sampler.normalized_coords = 1;
192
193   switch (info->tiling_mode) {
194   case VG_TILE_FILL:
195      tex_wrap = PIPE_TEX_WRAP_CLAMP_TO_BORDER;
196      /* copy border color */
197      memcpy(sampler.border_color.f, ctx->state.vg.tile_fill_color,
198            sizeof(sampler.border_color));
199      break;
200   case VG_TILE_PAD:
201      tex_wrap = PIPE_TEX_WRAP_CLAMP_TO_EDGE;;
202      break;
203   case VG_TILE_REPEAT:
204      tex_wrap = PIPE_TEX_WRAP_REPEAT;;
205      break;
206   case VG_TILE_REFLECT:
207      tex_wrap = PIPE_TEX_WRAP_MIRROR_REPEAT;
208      break;
209   default:
210      debug_assert(!"Unknown tiling mode");
211      tex_wrap = 0;
212      break;
213   }
214
215   sampler.wrap_s = tex_wrap;
216   sampler.wrap_t = tex_wrap;
217   sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
218
219   samplers[0] = samplers[1] = &sampler;
220   views[0] = info->src->sampler_view;
221   views[1] = info->extra_texture_view;
222
223   shader = info->setup_shader(ctx, info->user_data);
224
225   if (renderer_filter_begin(ctx->renderer,
226            info->dst->sampler_view->texture, VG_TRUE,
227            ctx->state.vg.filter_channel_mask,
228            samplers, views, (info->extra_texture_view) ? 2 : 1,
229            shader->driver, info->const_buffer, info->const_buffer_len)) {
230      renderer_filter(ctx->renderer,
231            info->dst->x, info->dst->y, info->dst->width, info->dst->height,
232            info->src->x, info->src->y, info->src->width, info->src->height);
233      renderer_filter_end(ctx->renderer);
234   }
235
236   vg_shader_destroy(ctx, shader);
237}
238
239void vegaColorMatrix(VGImage dst, VGImage src,
240                     const VGfloat * matrix)
241{
242   struct vg_context *ctx = vg_current_context();
243   struct vg_image *d, *s;
244   struct filter_info info;
245
246   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
247      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
248      return;
249   }
250   if (!matrix || !is_aligned(matrix)) {
251      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
252      return;
253   }
254
255   d = handle_to_image(dst);
256   s = handle_to_image(src);
257
258   if (vg_image_overlaps(d, s)) {
259      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
260      return;
261   }
262
263   info.dst = d;
264   info.src = s;
265   info.setup_shader = &setup_color_matrix;
266   info.user_data = NULL;
267   info.const_buffer = matrix;
268   info.const_buffer_len = 20 * sizeof(VGfloat);
269   info.tiling_mode = VG_TILE_PAD;
270   info.extra_texture_view = NULL;
271   execute_filter(ctx, &info);
272}
273
274static VGfloat texture_offset(VGfloat width, VGint kernelSize, VGint current, VGint shift)
275{
276   VGfloat diff = current - shift;
277
278   return diff / width;
279}
280
281void vegaConvolve(VGImage dst, VGImage src,
282                  VGint kernelWidth, VGint kernelHeight,
283                  VGint shiftX, VGint shiftY,
284                  const VGshort * kernel,
285                  VGfloat scale,
286                  VGfloat bias,
287                  VGTilingMode tilingMode)
288{
289   struct vg_context *ctx = vg_current_context();
290   VGfloat *buffer;
291   VGint buffer_len;
292   VGint i, j;
293   VGint idx = 0;
294   struct vg_image *d, *s;
295   VGint kernel_size = kernelWidth * kernelHeight;
296   struct filter_info info;
297   const VGint max_kernel_size = vegaGeti(VG_MAX_KERNEL_SIZE);
298
299   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
300      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
301      return;
302   }
303
304   if (kernelWidth <= 0 || kernelHeight <= 0 ||
305      kernelWidth > max_kernel_size || kernelHeight > max_kernel_size) {
306      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
307      return;
308   }
309
310   if (!kernel || !is_aligned_to(kernel, 2)) {
311      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
312      return;
313   }
314
315   if (tilingMode < VG_TILE_FILL ||
316       tilingMode > VG_TILE_REFLECT) {
317      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
318      return;
319   }
320
321   d = handle_to_image(dst);
322   s = handle_to_image(src);
323
324   if (vg_image_overlaps(d, s)) {
325      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
326      return;
327   }
328
329   vg_validate_state(ctx);
330
331   buffer_len = 8 + 2 * 4 * kernel_size;
332   buffer = (VGfloat*)malloc(buffer_len * sizeof(VGfloat));
333
334   buffer[0] = 0.f;
335   buffer[1] = 1.f;
336   buffer[2] = 2.f; /*unused*/
337   buffer[3] = 4.f; /*unused*/
338
339   buffer[4] = kernelWidth * kernelHeight;
340   buffer[5] = scale;
341   buffer[6] = bias;
342   buffer[7] = 0.f;
343
344   idx = 8;
345   for (j = 0; j < kernelHeight; ++j) {
346      for (i = 0; i < kernelWidth; ++i) {
347         VGint index = j * kernelWidth + i;
348         VGfloat x, y;
349
350         x = texture_offset(s->width, kernelWidth, i, shiftX);
351         y = texture_offset(s->height, kernelHeight, j, shiftY);
352
353         buffer[idx + index*4 + 0] = x;
354         buffer[idx + index*4 + 1] = y;
355         buffer[idx + index*4 + 2] = 0.f;
356         buffer[idx + index*4 + 3] = 0.f;
357      }
358   }
359   idx += kernel_size * 4;
360
361   for (j = 0; j < kernelHeight; ++j) {
362      for (i = 0; i < kernelWidth; ++i) {
363         /* transpose the kernel */
364         VGint index = j * kernelWidth + i;
365         VGint kindex = (kernelWidth - i - 1) * kernelHeight + (kernelHeight - j - 1);
366         buffer[idx + index*4 + 0] = kernel[kindex];
367         buffer[idx + index*4 + 1] = kernel[kindex];
368         buffer[idx + index*4 + 2] = kernel[kindex];
369         buffer[idx + index*4 + 3] = kernel[kindex];
370      }
371   }
372
373   info.dst = d;
374   info.src = s;
375   info.setup_shader = &setup_convolution;
376   info.user_data = (void*)(long)(buffer_len/4);
377   info.const_buffer = buffer;
378   info.const_buffer_len = buffer_len * sizeof(VGfloat);
379   info.tiling_mode = tilingMode;
380   info.extra_texture_view = NULL;
381   execute_filter(ctx, &info);
382
383   free(buffer);
384}
385
386void vegaSeparableConvolve(VGImage dst, VGImage src,
387                           VGint kernelWidth,
388                           VGint kernelHeight,
389                           VGint shiftX, VGint shiftY,
390                           const VGshort * kernelX,
391                           const VGshort * kernelY,
392                           VGfloat scale,
393                           VGfloat bias,
394                           VGTilingMode tilingMode)
395{
396   struct vg_context *ctx = vg_current_context();
397   VGshort *kernel;
398   VGint i, j, idx = 0;
399   const VGint max_kernel_size = vegaGeti(VG_MAX_SEPARABLE_KERNEL_SIZE);
400
401   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
402      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
403      return;
404   }
405
406   if (kernelWidth <= 0 || kernelHeight <= 0 ||
407       kernelWidth > max_kernel_size || kernelHeight > max_kernel_size) {
408      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
409      return;
410   }
411
412   if (!kernelX || !kernelY ||
413       !is_aligned_to(kernelX, 2) || !is_aligned_to(kernelY, 2)) {
414      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
415      return;
416   }
417   if (tilingMode < VG_TILE_FILL ||
418       tilingMode > VG_TILE_REFLECT) {
419      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
420      return;
421   }
422   kernel = malloc(sizeof(VGshort)*kernelWidth*kernelHeight);
423   for (i = 0; i < kernelWidth; ++i) {
424      for (j = 0; j < kernelHeight; ++j) {
425         kernel[idx] = kernelX[i] * kernelY[j];
426         ++idx;
427      }
428   }
429   vegaConvolve(dst, src, kernelWidth, kernelHeight, shiftX, shiftY,
430                kernel, scale, bias, tilingMode);
431   free(kernel);
432}
433
434static INLINE VGfloat compute_gaussian_componenet(VGfloat x, VGfloat y,
435                                                  VGfloat stdDeviationX,
436                                                  VGfloat stdDeviationY)
437{
438   VGfloat mult = 1 / ( 2 * M_PI * stdDeviationX * stdDeviationY);
439   VGfloat e = exp( - ( pow(x, 2)/(2*pow(stdDeviationX, 2)) +
440                        pow(y, 2)/(2*pow(stdDeviationY, 2)) ) );
441   return mult * e;
442}
443
444static INLINE VGint compute_kernel_size(VGfloat deviation)
445{
446   VGint size = ceil(2.146 * deviation);
447   if (size > 11)
448      return 11;
449   return size;
450}
451
452static void compute_gaussian_kernel(VGfloat *kernel,
453                                    VGint width, VGint height,
454                                    VGfloat stdDeviationX,
455                                    VGfloat stdDeviationY)
456{
457   VGint i, j;
458   VGfloat scale = 0.0f;
459
460   for (j = 0; j < height; ++j) {
461      for (i = 0; i < width; ++i) {
462         VGint idx =  (height - j -1) * width + (width - i -1);
463         kernel[idx] = compute_gaussian_componenet(i-(ceil(width/2))-1,
464                                                   j-ceil(height/2)-1,
465                                                   stdDeviationX, stdDeviationY);
466         scale += kernel[idx];
467      }
468   }
469
470   for (j = 0; j < height; ++j) {
471      for (i = 0; i < width; ++i) {
472         VGint idx = j * width + i;
473         kernel[idx] /= scale;
474      }
475   }
476}
477
478void vegaGaussianBlur(VGImage dst, VGImage src,
479                      VGfloat stdDeviationX,
480                      VGfloat stdDeviationY,
481                      VGTilingMode tilingMode)
482{
483   struct vg_context *ctx = vg_current_context();
484   struct vg_image *d, *s;
485   VGfloat *buffer, *kernel;
486   VGint kernel_width, kernel_height, kernel_size;
487   VGint buffer_len;
488   VGint idx, i, j;
489   struct filter_info info;
490
491   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
492      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
493      return;
494   }
495   if (stdDeviationX <= 0 || stdDeviationY <= 0) {
496      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
497      return;
498   }
499
500   if (tilingMode < VG_TILE_FILL ||
501       tilingMode > VG_TILE_REFLECT) {
502      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
503      return;
504   }
505
506   d = handle_to_image(dst);
507   s = handle_to_image(src);
508
509   if (vg_image_overlaps(d, s)) {
510      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
511      return;
512   }
513
514   kernel_width = compute_kernel_size(stdDeviationX);
515   kernel_height = compute_kernel_size(stdDeviationY);
516   kernel_size = kernel_width * kernel_height;
517   kernel = malloc(sizeof(VGfloat)*kernel_size);
518   compute_gaussian_kernel(kernel, kernel_width, kernel_height,
519                           stdDeviationX, stdDeviationY);
520
521   buffer_len = 8 + 2 * 4 * kernel_size;
522   buffer = (VGfloat*)malloc(buffer_len * sizeof(VGfloat));
523
524   buffer[0] = 0.f;
525   buffer[1] = 1.f;
526   buffer[2] = 2.f; /*unused*/
527   buffer[3] = 4.f; /*unused*/
528
529   buffer[4] = kernel_width * kernel_height;
530   buffer[5] = 1.f;/*scale*/
531   buffer[6] = 0.f;/*bias*/
532   buffer[7] = 0.f;
533
534   idx = 8;
535   for (j = 0; j < kernel_height; ++j) {
536      for (i = 0; i < kernel_width; ++i) {
537         VGint index = j * kernel_width + i;
538         VGfloat x, y;
539
540         x = texture_offset(s->width, kernel_width, i, kernel_width/2);
541         y = texture_offset(s->height, kernel_height, j, kernel_height/2);
542
543         buffer[idx + index*4 + 0] = x;
544         buffer[idx + index*4 + 1] = y;
545         buffer[idx + index*4 + 2] = 0.f;
546         buffer[idx + index*4 + 3] = 0.f;
547      }
548   }
549   idx += kernel_size * 4;
550
551   for (j = 0; j < kernel_height; ++j) {
552      for (i = 0; i < kernel_width; ++i) {
553         /* transpose the kernel */
554         VGint index = j * kernel_width + i;
555         VGint kindex = (kernel_width - i - 1) * kernel_height + (kernel_height - j - 1);
556         buffer[idx + index*4 + 0] = kernel[kindex];
557         buffer[idx + index*4 + 1] = kernel[kindex];
558         buffer[idx + index*4 + 2] = kernel[kindex];
559         buffer[idx + index*4 + 3] = kernel[kindex];
560      }
561   }
562
563   info.dst = d;
564   info.src = s;
565   info.setup_shader = &setup_convolution;
566   info.user_data = (void*)(long)(buffer_len/4);
567   info.const_buffer = buffer;
568   info.const_buffer_len = buffer_len * sizeof(VGfloat);
569   info.tiling_mode = tilingMode;
570   info.extra_texture_view = NULL;
571   execute_filter(ctx, &info);
572
573   free(buffer);
574   free(kernel);
575}
576
577void vegaLookup(VGImage dst, VGImage src,
578                const VGubyte * redLUT,
579                const VGubyte * greenLUT,
580                const VGubyte * blueLUT,
581                const VGubyte * alphaLUT,
582                VGboolean outputLinear,
583                VGboolean outputPremultiplied)
584{
585   struct vg_context *ctx = vg_current_context();
586   struct vg_image *d, *s;
587   VGuint color_data[256];
588   VGint i;
589   struct pipe_sampler_view *lut_texture_view;
590   VGfloat buffer[4];
591   struct filter_info info;
592
593   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
594      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
595      return;
596   }
597
598   if (!redLUT || !greenLUT || !blueLUT || !alphaLUT) {
599      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
600      return;
601   }
602
603   d = handle_to_image(dst);
604   s = handle_to_image(src);
605
606   if (vg_image_overlaps(d, s)) {
607      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
608      return;
609   }
610
611   for (i = 0; i < 256; ++i) {
612      color_data[i] = blueLUT[i] << 24 | greenLUT[i] << 16 |
613                      redLUT[i]  <<  8 | alphaLUT[i];
614   }
615   lut_texture_view = create_texture_1d_view(ctx, color_data, 255);
616
617   buffer[0] = 0.f;
618   buffer[1] = 0.f;
619   buffer[2] = 1.f;
620   buffer[3] = 1.f;
621
622   info.dst = d;
623   info.src = s;
624   info.setup_shader = &setup_lookup;
625   info.user_data = NULL;
626   info.const_buffer = buffer;
627   info.const_buffer_len = 4 * sizeof(VGfloat);
628   info.tiling_mode = VG_TILE_PAD;
629   info.extra_texture_view = lut_texture_view;
630
631   execute_filter(ctx, &info);
632
633   pipe_sampler_view_reference(&lut_texture_view, NULL);
634}
635
636void vegaLookupSingle(VGImage dst, VGImage src,
637                      const VGuint * lookupTable,
638                      VGImageChannel sourceChannel,
639                      VGboolean outputLinear,
640                      VGboolean outputPremultiplied)
641{
642   struct vg_context *ctx = vg_current_context();
643   struct vg_image *d, *s;
644   struct pipe_sampler_view *lut_texture_view;
645   VGfloat buffer[4];
646   struct filter_info info;
647   VGuint color_data[256];
648   VGint i;
649
650   if (dst == VG_INVALID_HANDLE || src == VG_INVALID_HANDLE) {
651      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
652      return;
653   }
654
655   if (!lookupTable || !is_aligned(lookupTable)) {
656      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
657      return;
658   }
659
660   if (sourceChannel != VG_RED && sourceChannel != VG_GREEN &&
661       sourceChannel != VG_BLUE && sourceChannel != VG_ALPHA) {
662      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
663      return;
664   }
665
666   d = handle_to_image(dst);
667   s = handle_to_image(src);
668
669   if (vg_image_overlaps(d, s)) {
670      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
671      return;
672   }
673
674   vg_validate_state(ctx);
675
676   for (i = 0; i < 256; ++i) {
677      VGuint rgba = lookupTable[i];
678      VGubyte blue, green, red, alpha;
679      red   = (rgba & 0xff000000)>>24;
680      green = (rgba & 0x00ff0000)>>16;
681      blue  = (rgba & 0x0000ff00)>> 8;
682      alpha = (rgba & 0x000000ff)>> 0;
683      color_data[i] = blue << 24 | green << 16 |
684                      red  <<  8 | alpha;
685   }
686   lut_texture_view = create_texture_1d_view(ctx, color_data, 256);
687
688   buffer[0] = 0.f;
689   buffer[1] = 0.f;
690   buffer[2] = 1.f;
691   buffer[3] = 1.f;
692
693   info.dst = d;
694   info.src = s;
695   info.setup_shader = &setup_lookup_single;
696   info.user_data = (void*)sourceChannel;
697   info.const_buffer = buffer;
698   info.const_buffer_len = 4 * sizeof(VGfloat);
699   info.tiling_mode = VG_TILE_PAD;
700   info.extra_texture_view = lut_texture_view;
701
702   execute_filter(ctx, &info);
703
704   pipe_sampler_view_reference(&lut_texture_view, NULL);
705}
706