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