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