image.c revision f914cd1796845164109c837a111c39ba64852ad4
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 "image.h"
28
29#include "vg_translate.h"
30#include "vg_context.h"
31#include "matrix.h"
32#include "renderer.h"
33#include "util_array.h"
34#include "api_consts.h"
35#include "shaders_cache.h"
36#include "shader.h"
37
38#include "pipe/p_context.h"
39#include "pipe/p_screen.h"
40#include "util/u_inlines.h"
41#include "util/u_blit.h"
42#include "util/u_format.h"
43#include "util/u_tile.h"
44#include "util/u_memory.h"
45#include "util/u_math.h"
46#include "util/u_sampler.h"
47
48static enum pipe_format vg_format_to_pipe(VGImageFormat format)
49{
50   switch(format) {
51   case VG_sRGB_565:
52      return PIPE_FORMAT_B5G6R5_UNORM;
53   case VG_sRGBA_5551:
54      return PIPE_FORMAT_B5G5R5A1_UNORM;
55   case VG_sRGBA_4444:
56      return PIPE_FORMAT_B4G4R4A4_UNORM;
57   case VG_sL_8:
58   case VG_lL_8:
59      return PIPE_FORMAT_L8_UNORM;
60   case VG_BW_1:
61      return PIPE_FORMAT_B8G8R8A8_UNORM;
62   case VG_A_8:
63      return PIPE_FORMAT_A8_UNORM;
64#ifdef OPENVG_VERSION_1_1
65   case VG_A_1:
66   case VG_A_4:
67      return PIPE_FORMAT_A8_UNORM;
68#endif
69   default:
70      return PIPE_FORMAT_B8G8R8A8_UNORM;
71   }
72}
73
74static INLINE void vg_sync_size(VGfloat *src_loc, VGfloat *dst_loc)
75{
76   src_loc[2] = MIN2(src_loc[2], dst_loc[2]);
77   src_loc[3] = MIN2(src_loc[3], dst_loc[3]);
78   dst_loc[2] = src_loc[2];
79   dst_loc[3] = src_loc[3];
80}
81
82
83static void vg_copy_texture(struct vg_context *ctx,
84                            struct pipe_resource *dst, VGint dx, VGint dy,
85                            struct pipe_sampler_view *src, VGint sx, VGint sy,
86                            VGint width, VGint height)
87{
88   VGfloat dst_loc[4], src_loc[4];
89   VGfloat dst_bounds[4], src_bounds[4];
90   VGfloat src_shift[4], dst_shift[4], shift[4];
91
92   dst_loc[0] = dx;
93   dst_loc[1] = dy;
94   dst_loc[2] = width;
95   dst_loc[3] = height;
96   dst_bounds[0] = 0.f;
97   dst_bounds[1] = 0.f;
98   dst_bounds[2] = dst->width0;
99   dst_bounds[3] = dst->height0;
100
101   src_loc[0] = sx;
102   src_loc[1] = sy;
103   src_loc[2] = width;
104   src_loc[3] = height;
105   src_bounds[0] = 0.f;
106   src_bounds[1] = 0.f;
107   src_bounds[2] = src->texture->width0;
108   src_bounds[3] = src->texture->height0;
109
110   vg_bound_rect(src_loc, src_bounds, src_shift);
111   vg_bound_rect(dst_loc, dst_bounds, dst_shift);
112   shift[0] = src_shift[0] - dst_shift[0];
113   shift[1] = src_shift[1] - dst_shift[1];
114
115   if (shift[0] < 0)
116      vg_shift_rectx(src_loc, src_bounds, -shift[0]);
117   else
118      vg_shift_rectx(dst_loc, dst_bounds, shift[0]);
119
120   if (shift[1] < 0)
121      vg_shift_recty(src_loc, src_bounds, -shift[1]);
122   else
123      vg_shift_recty(dst_loc, dst_bounds, shift[1]);
124
125   vg_sync_size(src_loc, dst_loc);
126
127   if (src_loc[2] >= 0 && src_loc[3] >= 0 &&
128       dst_loc[2] >= 0 && dst_loc[3] >= 0) {
129      renderer_copy_texture(ctx->renderer,
130                            src,
131                            src_loc[0],
132                            src_loc[1] + src_loc[3],
133                            src_loc[0] + src_loc[2],
134                            src_loc[1],
135                            dst,
136                            dst_loc[0],
137                            dst_loc[1] + dst_loc[3],
138                            dst_loc[0] + dst_loc[2],
139                            dst_loc[1]);
140   }
141
142}
143
144void vg_copy_surface(struct vg_context *ctx,
145                     struct pipe_surface *dst, VGint dx, VGint dy,
146                     struct pipe_surface *src, VGint sx, VGint sy,
147                     VGint width, VGint height)
148{
149   VGfloat dst_loc[4], src_loc[4];
150   VGfloat dst_bounds[4], src_bounds[4];
151   VGfloat src_shift[4], dst_shift[4], shift[4];
152
153   dst_loc[0] = dx;
154   dst_loc[1] = dy;
155   dst_loc[2] = width;
156   dst_loc[3] = height;
157   dst_bounds[0] = 0.f;
158   dst_bounds[1] = 0.f;
159   dst_bounds[2] = dst->width;
160   dst_bounds[3] = dst->height;
161
162   src_loc[0] = sx;
163   src_loc[1] = sy;
164   src_loc[2] = width;
165   src_loc[3] = height;
166   src_bounds[0] = 0.f;
167   src_bounds[1] = 0.f;
168   src_bounds[2] = src->width;
169   src_bounds[3] = src->height;
170
171   vg_bound_rect(src_loc, src_bounds, src_shift);
172   vg_bound_rect(dst_loc, dst_bounds, dst_shift);
173   shift[0] = src_shift[0] - dst_shift[0];
174   shift[1] = src_shift[1] - dst_shift[1];
175
176   if (shift[0] < 0)
177      vg_shift_rectx(src_loc, src_bounds, -shift[0]);
178   else
179      vg_shift_rectx(dst_loc, dst_bounds, shift[0]);
180
181   if (shift[1] < 0)
182      vg_shift_recty(src_loc, src_bounds, -shift[1]);
183   else
184      vg_shift_recty(dst_loc, dst_bounds, shift[1]);
185
186   vg_sync_size(src_loc, dst_loc);
187
188   if (src_loc[2] > 0 && src_loc[3] > 0 &&
189       dst_loc[2] > 0 && dst_loc[3] > 0) {
190      if (src == dst)
191         renderer_copy_surface(ctx->renderer,
192                               src,
193                               src_loc[0],
194                               src->height - (src_loc[1] + src_loc[3]),
195                               src_loc[0] + src_loc[2],
196                               src->height - src_loc[1],
197                               dst,
198                               dst_loc[0],
199                               dst->height - (dst_loc[1] + dst_loc[3]),
200                               dst_loc[0] + dst_loc[2],
201                               dst->height - dst_loc[1],
202                               0, 0);
203      else
204         renderer_copy_surface(ctx->renderer,
205                               src,
206                               src_loc[0],
207                               src->height - src_loc[1],
208                               src_loc[0] + src_loc[2],
209                               src->height - (src_loc[1] + src_loc[3]),
210                               dst,
211                               dst_loc[0],
212                               dst->height - (dst_loc[1] + dst_loc[3]),
213                               dst_loc[0] + dst_loc[2],
214                               dst->height - dst_loc[1],
215                               0, 0);
216   }
217
218}
219
220static struct pipe_resource *image_texture(struct vg_image *img)
221{
222   struct pipe_resource *tex = img->sampler_view->texture;
223   return tex;
224}
225
226
227static void image_cleari(struct vg_image *img, VGint clear_colori,
228                         VGint x, VGint y, VGint width, VGint height)
229{
230   VGint *clearbuf;
231   VGint i;
232   VGfloat dwidth, dheight;
233
234   clearbuf = malloc(sizeof(VGint)*width*height);
235   for (i = 0; i < width*height; ++i)
236      clearbuf[i] = clear_colori;
237
238   dwidth = MIN2(width, img->width);
239   dheight = MIN2(height, img->height);
240
241   image_sub_data(img, clearbuf, width * sizeof(VGint),
242                  VG_sRGBA_8888,
243                  x, y, dwidth, dheight);
244   free(clearbuf);
245}
246
247struct vg_image * image_create(VGImageFormat format,
248                               VGint width, VGint height)
249{
250   struct vg_context *ctx = vg_current_context();
251   struct pipe_context *pipe = ctx->pipe;
252   struct vg_image *image = CALLOC_STRUCT(vg_image);
253   enum pipe_format pformat = vg_format_to_pipe(format);
254   struct pipe_resource pt, *newtex;
255   struct pipe_sampler_view view_templ;
256   struct pipe_sampler_view *view;
257   struct pipe_screen *screen = ctx->pipe->screen;
258
259   vg_init_object(&image->base, ctx, VG_OBJECT_IMAGE);
260
261   image->format = format;
262   image->width = width;
263   image->height = height;
264
265   image->sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
266   image->sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
267   image->sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
268   image->sampler.min_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
269   image->sampler.mag_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
270   image->sampler.normalized_coords = 1;
271
272   assert(screen->is_format_supported(screen, pformat, PIPE_TEXTURE_2D,
273                                      0, PIPE_BIND_SAMPLER_VIEW, 0));
274
275   memset(&pt, 0, sizeof(pt));
276   pt.target = PIPE_TEXTURE_2D;
277   pt.format = pformat;
278   pt.last_level = 0;
279   pt.width0 = width;
280   pt.height0 = height;
281   pt.depth0 = 1;
282   pt.bind = PIPE_BIND_SAMPLER_VIEW;
283
284   newtex = screen->resource_create(screen, &pt);
285
286   debug_assert(newtex);
287
288   u_sampler_view_default_template(&view_templ, newtex, newtex->format);
289   view = pipe->create_sampler_view(pipe, newtex, &view_templ);
290   /* want the texture to go away if the view is freed */
291   pipe_resource_reference(&newtex, NULL);
292
293   image->sampler_view = view;
294
295   vg_context_add_object(ctx, VG_OBJECT_IMAGE, image);
296
297   image_cleari(image, 0, 0, 0, image->width, image->height);
298   return image;
299}
300
301void image_destroy(struct vg_image *img)
302{
303   struct vg_context *ctx = vg_current_context();
304   vg_context_remove_object(ctx, VG_OBJECT_IMAGE, img);
305
306
307   if (img->parent) {
308      /* remove img from the parent child array */
309      int idx;
310      struct vg_image **array =
311         (struct vg_image **)img->parent->children_array->data;
312
313      for (idx = 0; idx < img->parent->children_array->num_elements; ++idx) {
314         struct vg_image *child = array[idx];
315         if (child == img) {
316            break;
317         }
318      }
319      debug_assert(idx < img->parent->children_array->num_elements);
320      array_remove_element(img->parent->children_array, idx);
321   }
322
323   if (img->children_array && img->children_array->num_elements) {
324      /* reparent the children */
325      VGint i;
326      struct vg_image *parent = img->parent;
327      struct vg_image **children =
328         (struct vg_image **)img->children_array->data;
329      if (!parent) {
330         VGint min_x = children[0]->x;
331         parent = children[0];
332
333         for (i = 1; i < img->children_array->num_elements; ++i) {
334            struct vg_image *child = children[i];
335            if (child->x < min_x) {
336               parent = child;
337            }
338         }
339      }
340
341      for (i = 0; i < img->children_array->num_elements; ++i) {
342         struct vg_image *child = children[i];
343         if (child != parent) {
344            child->parent = parent;
345            if (!parent->children_array) {
346               parent->children_array = array_create(
347                  sizeof(struct vg_image*));
348            }
349            array_append_data(parent->children_array,
350                              &child, 1);
351         } else
352            child->parent = NULL;
353      }
354      array_destroy(img->children_array);
355   }
356
357   pipe_sampler_view_reference(&img->sampler_view, NULL);
358   FREE(img);
359}
360
361void image_clear(struct vg_image *img,
362                 VGint x, VGint y, VGint width, VGint height)
363{
364   struct vg_context *ctx = vg_current_context();
365   VGfloat *clear_colorf = ctx->state.vg.clear_color;
366   VGubyte r, g, b ,a;
367   VGint clear_colori;
368   /* FIXME: this is very nasty */
369   r = float_to_ubyte(clear_colorf[0]);
370   g = float_to_ubyte(clear_colorf[1]);
371   b = float_to_ubyte(clear_colorf[2]);
372   a = float_to_ubyte(clear_colorf[3]);
373   clear_colori = r << 24 | g << 16 | b << 8 | a;
374   image_cleari(img, clear_colori, x, y, width, height);
375}
376
377void image_sub_data(struct vg_image *image,
378                    const void * data,
379                    VGint dataStride,
380                    VGImageFormat dataFormat,
381                    VGint x, VGint y,
382                    VGint width, VGint height)
383{
384   const VGint yStep = 1;
385   VGubyte *src = (VGubyte *)data;
386   VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
387   VGfloat *df = (VGfloat*)temp;
388   VGint i;
389   struct vg_context *ctx = vg_current_context();
390   struct pipe_context *pipe = ctx->pipe;
391   struct pipe_resource *texture = image_texture(image);
392   VGint xoffset = 0, yoffset = 0;
393
394   if (x < 0) {
395      xoffset -= x;
396      width += x;
397      x = 0;
398   }
399   if (y < 0) {
400      yoffset -= y;
401      height += y;
402      y = 0;
403   }
404
405   if (width <= 0 || height <= 0) {
406      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
407      return;
408   }
409
410   if (x > image->width || y > image->width) {
411      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
412      return;
413   }
414
415   if (x + width > image->width) {
416      width = image->width - x;
417   }
418
419   if (y + height > image->height) {
420      height = image->height - y;
421   }
422
423   { /* upload color_data */
424      struct pipe_transfer *transfer = pipe_get_transfer(
425         pipe, texture, 0, 0, 0,
426         PIPE_TRANSFER_WRITE, 0, 0, texture->width0, texture->height0);
427      src += (dataStride * yoffset);
428      for (i = 0; i < height; i++) {
429         _vega_unpack_float_span_rgba(ctx, width, xoffset, src, dataFormat, temp);
430         pipe_put_tile_rgba(pipe, transfer, x+image->x, y+image->y, width, 1, df);
431         y += yStep;
432         src += dataStride;
433      }
434      pipe->transfer_destroy(pipe, transfer);
435   }
436}
437
438void image_get_sub_data(struct vg_image * image,
439                        void * data,
440                        VGint dataStride,
441                        VGImageFormat dataFormat,
442                        VGint sx, VGint sy,
443                        VGint width, VGint height)
444{
445   struct vg_context *ctx = vg_current_context();
446   struct pipe_context *pipe = ctx->pipe;
447   VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
448   VGfloat *df = (VGfloat*)temp;
449   VGint y = 0, yStep = 1;
450   VGint i;
451   VGubyte *dst = (VGubyte *)data;
452
453   {
454      struct pipe_transfer *transfer =
455         pipe_get_transfer(pipe,
456                                  image->sampler_view->texture,  0, 0, 0,
457                                  PIPE_TRANSFER_READ,
458                                  0, 0,
459                                  image->x + image->width,
460                                  image->y + image->height);
461      /* Do a row at a time to flip image data vertically */
462      for (i = 0; i < height; i++) {
463#if 0
464         debug_printf("%d-%d  == %d\n", sy, height, y);
465#endif
466         pipe_get_tile_rgba(pipe, transfer, sx+image->x, y, width, 1, df);
467         y += yStep;
468         _vega_pack_rgba_span_float(ctx, width, temp, dataFormat, dst);
469         dst += dataStride;
470      }
471
472      pipe->transfer_destroy(pipe, transfer);
473   }
474}
475
476struct vg_image * image_child_image(struct vg_image *parent,
477                                    VGint x, VGint y,
478                                    VGint width, VGint height)
479{
480   struct vg_context *ctx = vg_current_context();
481   struct vg_image *image = CALLOC_STRUCT(vg_image);
482
483   vg_init_object(&image->base, ctx, VG_OBJECT_IMAGE);
484
485   image->x = parent->x + x;
486   image->y = parent->y + y;
487   image->width = width;
488   image->height = height;
489   image->parent = parent;
490   image->sampler_view = NULL;
491   pipe_sampler_view_reference(&image->sampler_view,
492                               parent->sampler_view);
493
494   image->sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
495   image->sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
496   image->sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
497   image->sampler.min_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
498   image->sampler.mag_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
499   image->sampler.normalized_coords = 1;
500
501   if (!parent->children_array)
502      parent->children_array = array_create(
503         sizeof(struct vg_image*));
504
505   array_append_data(parent->children_array,
506                     &image, 1);
507
508   vg_context_add_object(ctx, VG_OBJECT_IMAGE, image);
509
510   return image;
511}
512
513void image_copy(struct vg_image *dst, VGint dx, VGint dy,
514                struct vg_image *src, VGint sx, VGint sy,
515                VGint width, VGint height,
516                VGboolean dither)
517{
518   struct vg_context *ctx = vg_current_context();
519
520   if (width <= 0 || height <= 0) {
521      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
522      return;
523   }
524   /* make sure rendering has completed */
525   ctx->pipe->flush(ctx->pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
526   vg_copy_texture(ctx, dst->sampler_view->texture, dst->x + dx, dst->y + dy,
527                   src->sampler_view, src->x + sx, src->y + sy, width, height);
528}
529
530void image_draw(struct vg_image *img)
531{
532   struct vg_context *ctx = vg_current_context();
533   VGfloat x1, y1;
534   VGfloat x2, y2;
535   VGfloat x3, y3;
536   VGfloat x4, y4;
537   struct matrix *matrix;
538
539   x1 = 0;
540   y1 = 0;
541   x2 = img->width;
542   y2 = 0;
543   x3 = img->width;
544   y3 = img->height;
545   x4 = 0;
546   y4 = img->height;
547
548   matrix = &ctx->state.vg.image_user_to_surface_matrix;
549
550   matrix_map_point(matrix, x1, y1, &x1, &y1);
551   matrix_map_point(matrix, x2, y2, &x2, &y2);
552   matrix_map_point(matrix, x3, y3, &x3, &y3);
553   matrix_map_point(matrix, x4, y4, &x4, &y4);
554
555   shader_set_drawing_image(ctx->shader, VG_TRUE);
556   shader_set_paint(ctx->shader, ctx->state.vg.fill_paint);
557   shader_set_image(ctx->shader, img);
558   shader_bind(ctx->shader);
559
560   renderer_texture_quad(ctx->renderer, image_texture(img),
561                         img->x, img->y, img->x + img->width, img->y + img->height,
562                         x1, y1, x2, y2, x3, y3, x4, y4);
563}
564
565void image_set_pixels(VGint dx, VGint dy,
566                      struct vg_image *src, VGint sx, VGint sy,
567                      VGint width, VGint height)
568{
569   struct vg_context *ctx = vg_current_context();
570   struct pipe_context *pipe = ctx->pipe;
571   struct pipe_screen *screen = pipe->screen;
572   struct pipe_surface *surf;
573   struct st_renderbuffer *strb = ctx->draw_buffer->strb;
574
575   /* make sure rendering has completed */
576   pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
577
578   surf = screen->get_tex_surface(screen, image_texture(src),  0, 0, 0,
579                                  0 /* no bind flags as surf isn't actually used??? */);
580
581   vg_copy_surface(ctx, strb->surface, dx, dy,
582                   surf, sx+src->x, sy+src->y, width, height);
583
584   screen->tex_surface_destroy(surf);
585}
586
587void image_get_pixels(struct vg_image *dst, VGint dx, VGint dy,
588                      VGint sx, VGint sy,
589                      VGint width, VGint height)
590{
591   struct vg_context *ctx = vg_current_context();
592   struct pipe_context *pipe = ctx->pipe;
593   struct pipe_screen *screen = pipe->screen;
594   struct pipe_surface *surf;
595   struct st_renderbuffer *strb = ctx->draw_buffer->strb;
596
597   /* flip the y coordinates */
598   /*dy = dst->height - dy - height;*/
599
600   /* make sure rendering has completed */
601   pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
602
603   surf = screen->get_tex_surface(screen, image_texture(dst),  0, 0, 0,
604                                  0 /* no bind flags as surf isn't actually used??? */);
605
606   vg_copy_surface(ctx, surf, dst->x + dx, dst->y + dy,
607                   strb->surface, sx, sy, width, height);
608
609   pipe_surface_reference(&surf, NULL);
610}
611
612
613VGboolean vg_image_overlaps(struct vg_image *dst,
614                            struct vg_image *src)
615{
616   if (dst == src || dst->parent == src ||
617       dst == src->parent)
618      return VG_TRUE;
619   if (dst->parent && dst->parent == src->parent) {
620      VGfloat left1 = dst->x;
621      VGfloat left2 = src->x;
622      VGfloat right1 = dst->x + dst->width;
623      VGfloat right2 = src->x + src->width;
624      VGfloat bottom1 = dst->y;
625      VGfloat bottom2 = src->y;
626      VGfloat top1 = dst->y + dst->height;
627      VGfloat top2 = src->y + src->height;
628
629      return !(left2 > right1 || right2 < left1 ||
630               top2 > bottom1 || bottom2 < top1);
631   }
632   return VG_FALSE;
633}
634
635VGint image_bind_samplers(struct vg_image *img, struct pipe_sampler_state **samplers,
636                          struct pipe_sampler_view **sampler_views)
637{
638   img->sampler.min_img_filter = image_sampler_filter(img->base.ctx);
639   img->sampler.mag_img_filter = image_sampler_filter(img->base.ctx);
640   samplers[3] = &img->sampler;
641   sampler_views[3] = img->sampler_view;
642   return 1;
643}
644
645VGint image_sampler_filter(struct vg_context *ctx)
646{
647    switch(ctx->state.vg.image_quality) {
648    case VG_IMAGE_QUALITY_NONANTIALIASED:
649       return PIPE_TEX_FILTER_NEAREST;
650       break;
651    case VG_IMAGE_QUALITY_FASTER:
652       return PIPE_TEX_FILTER_NEAREST;
653       break;
654    case VG_IMAGE_QUALITY_BETTER:
655       /* possibly use anisotropic filtering */
656       return PIPE_TEX_FILTER_LINEAR;
657       break;
658    default:
659       debug_printf("Unknown image quality");
660    }
661    return PIPE_TEX_FILTER_NEAREST;
662}
663