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