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