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