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