Merge commit 'origin/gallium-0.1'
[mesa.git] / src / mesa / drivers / dri / r200 / r200_tex.c
1 /*
2 Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
3
4 The Weather Channel (TM) funded Tungsten Graphics to develop the
5 initial release of the Radeon 8500 driver under the XFree86 license.
6 This notice must be preserved.
7
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15
16 The above copyright notice and this permission notice (including the
17 next paragraph) shall be included in all copies or substantial
18 portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
24 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29 /*
30 * Authors:
31 * Keith Whitwell <keith@tungstengraphics.com>
32 */
33
34 #include "main/glheader.h"
35 #include "main/imports.h"
36 #include "main/colormac.h"
37 #include "main/context.h"
38 #include "main/enums.h"
39 #include "main/image.h"
40 #include "main/simple_list.h"
41 #include "main/texformat.h"
42 #include "main/texstore.h"
43 #include "main/teximage.h"
44 #include "main/texobj.h"
45
46 #include "texmem.h"
47
48 #include "r200_context.h"
49 #include "r200_state.h"
50 #include "r200_ioctl.h"
51 #include "r200_swtcl.h"
52 #include "r200_tex.h"
53
54 #include "xmlpool.h"
55
56
57
58 /**
59 * Set the texture wrap modes.
60 *
61 * \param t Texture object whose wrap modes are to be set
62 * \param swrap Wrap mode for the \a s texture coordinate
63 * \param twrap Wrap mode for the \a t texture coordinate
64 */
65
66 static void r200SetTexWrap( r200TexObjPtr t, GLenum swrap, GLenum twrap, GLenum rwrap )
67 {
68 GLboolean is_clamp = GL_FALSE;
69 GLboolean is_clamp_to_border = GL_FALSE;
70
71 t->pp_txfilter &= ~(R200_CLAMP_S_MASK | R200_CLAMP_T_MASK | R200_BORDER_MODE_D3D);
72
73 switch ( swrap ) {
74 case GL_REPEAT:
75 t->pp_txfilter |= R200_CLAMP_S_WRAP;
76 break;
77 case GL_CLAMP:
78 t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
79 is_clamp = GL_TRUE;
80 break;
81 case GL_CLAMP_TO_EDGE:
82 t->pp_txfilter |= R200_CLAMP_S_CLAMP_LAST;
83 break;
84 case GL_CLAMP_TO_BORDER:
85 t->pp_txfilter |= R200_CLAMP_S_CLAMP_GL;
86 is_clamp_to_border = GL_TRUE;
87 break;
88 case GL_MIRRORED_REPEAT:
89 t->pp_txfilter |= R200_CLAMP_S_MIRROR;
90 break;
91 case GL_MIRROR_CLAMP_EXT:
92 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
93 is_clamp = GL_TRUE;
94 break;
95 case GL_MIRROR_CLAMP_TO_EDGE_EXT:
96 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_LAST;
97 break;
98 case GL_MIRROR_CLAMP_TO_BORDER_EXT:
99 t->pp_txfilter |= R200_CLAMP_S_MIRROR_CLAMP_GL;
100 is_clamp_to_border = GL_TRUE;
101 break;
102 default:
103 _mesa_problem(NULL, "bad S wrap mode in %s", __FUNCTION__);
104 }
105
106 if (t->base.tObj->Target != GL_TEXTURE_1D) {
107 switch ( twrap ) {
108 case GL_REPEAT:
109 t->pp_txfilter |= R200_CLAMP_T_WRAP;
110 break;
111 case GL_CLAMP:
112 t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
113 is_clamp = GL_TRUE;
114 break;
115 case GL_CLAMP_TO_EDGE:
116 t->pp_txfilter |= R200_CLAMP_T_CLAMP_LAST;
117 break;
118 case GL_CLAMP_TO_BORDER:
119 t->pp_txfilter |= R200_CLAMP_T_CLAMP_GL;
120 is_clamp_to_border = GL_TRUE;
121 break;
122 case GL_MIRRORED_REPEAT:
123 t->pp_txfilter |= R200_CLAMP_T_MIRROR;
124 break;
125 case GL_MIRROR_CLAMP_EXT:
126 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
127 is_clamp = GL_TRUE;
128 break;
129 case GL_MIRROR_CLAMP_TO_EDGE_EXT:
130 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_LAST;
131 break;
132 case GL_MIRROR_CLAMP_TO_BORDER_EXT:
133 t->pp_txfilter |= R200_CLAMP_T_MIRROR_CLAMP_GL;
134 is_clamp_to_border = GL_TRUE;
135 break;
136 default:
137 _mesa_problem(NULL, "bad T wrap mode in %s", __FUNCTION__);
138 }
139 }
140
141 t->pp_txformat_x &= ~R200_CLAMP_Q_MASK;
142
143 switch ( rwrap ) {
144 case GL_REPEAT:
145 t->pp_txformat_x |= R200_CLAMP_Q_WRAP;
146 break;
147 case GL_CLAMP:
148 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
149 is_clamp = GL_TRUE;
150 break;
151 case GL_CLAMP_TO_EDGE:
152 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_LAST;
153 break;
154 case GL_CLAMP_TO_BORDER:
155 t->pp_txformat_x |= R200_CLAMP_Q_CLAMP_GL;
156 is_clamp_to_border = GL_TRUE;
157 break;
158 case GL_MIRRORED_REPEAT:
159 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR;
160 break;
161 case GL_MIRROR_CLAMP_EXT:
162 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
163 is_clamp = GL_TRUE;
164 break;
165 case GL_MIRROR_CLAMP_TO_EDGE_EXT:
166 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_LAST;
167 break;
168 case GL_MIRROR_CLAMP_TO_BORDER_EXT:
169 t->pp_txformat_x |= R200_CLAMP_Q_MIRROR_CLAMP_GL;
170 is_clamp_to_border = GL_TRUE;
171 break;
172 default:
173 _mesa_problem(NULL, "bad R wrap mode in %s", __FUNCTION__);
174 }
175
176 if ( is_clamp_to_border ) {
177 t->pp_txfilter |= R200_BORDER_MODE_D3D;
178 }
179
180 t->border_fallback = (is_clamp && is_clamp_to_border);
181 }
182
183 static void r200SetTexMaxAnisotropy( r200TexObjPtr t, GLfloat max )
184 {
185 t->pp_txfilter &= ~R200_MAX_ANISO_MASK;
186
187 if ( max <= 1.0 ) {
188 t->pp_txfilter |= R200_MAX_ANISO_1_TO_1;
189 } else if ( max <= 2.0 ) {
190 t->pp_txfilter |= R200_MAX_ANISO_2_TO_1;
191 } else if ( max <= 4.0 ) {
192 t->pp_txfilter |= R200_MAX_ANISO_4_TO_1;
193 } else if ( max <= 8.0 ) {
194 t->pp_txfilter |= R200_MAX_ANISO_8_TO_1;
195 } else {
196 t->pp_txfilter |= R200_MAX_ANISO_16_TO_1;
197 }
198 }
199
200 /**
201 * Set the texture magnification and minification modes.
202 *
203 * \param t Texture whose filter modes are to be set
204 * \param minf Texture minification mode
205 * \param magf Texture magnification mode
206 */
207
208 static void r200SetTexFilter( r200TexObjPtr t, GLenum minf, GLenum magf )
209 {
210 GLuint anisotropy = (t->pp_txfilter & R200_MAX_ANISO_MASK);
211
212 t->pp_txfilter &= ~(R200_MIN_FILTER_MASK | R200_MAG_FILTER_MASK);
213 t->pp_txformat_x &= ~R200_VOLUME_FILTER_MASK;
214
215 if ( anisotropy == R200_MAX_ANISO_1_TO_1 ) {
216 switch ( minf ) {
217 case GL_NEAREST:
218 t->pp_txfilter |= R200_MIN_FILTER_NEAREST;
219 break;
220 case GL_LINEAR:
221 t->pp_txfilter |= R200_MIN_FILTER_LINEAR;
222 break;
223 case GL_NEAREST_MIPMAP_NEAREST:
224 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_NEAREST;
225 break;
226 case GL_NEAREST_MIPMAP_LINEAR:
227 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_NEAREST;
228 break;
229 case GL_LINEAR_MIPMAP_NEAREST:
230 t->pp_txfilter |= R200_MIN_FILTER_NEAREST_MIP_LINEAR;
231 break;
232 case GL_LINEAR_MIPMAP_LINEAR:
233 t->pp_txfilter |= R200_MIN_FILTER_LINEAR_MIP_LINEAR;
234 break;
235 }
236 } else {
237 switch ( minf ) {
238 case GL_NEAREST:
239 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST;
240 break;
241 case GL_LINEAR:
242 t->pp_txfilter |= R200_MIN_FILTER_ANISO_LINEAR;
243 break;
244 case GL_NEAREST_MIPMAP_NEAREST:
245 case GL_LINEAR_MIPMAP_NEAREST:
246 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST;
247 break;
248 case GL_NEAREST_MIPMAP_LINEAR:
249 case GL_LINEAR_MIPMAP_LINEAR:
250 t->pp_txfilter |= R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR;
251 break;
252 }
253 }
254
255 /* Note we don't have 3D mipmaps so only use the mag filter setting
256 * to set the 3D texture filter mode.
257 */
258 switch ( magf ) {
259 case GL_NEAREST:
260 t->pp_txfilter |= R200_MAG_FILTER_NEAREST;
261 t->pp_txformat_x |= R200_VOLUME_FILTER_NEAREST;
262 break;
263 case GL_LINEAR:
264 t->pp_txfilter |= R200_MAG_FILTER_LINEAR;
265 t->pp_txformat_x |= R200_VOLUME_FILTER_LINEAR;
266 break;
267 }
268 }
269
270 static void r200SetTexBorderColor( r200TexObjPtr t, GLubyte c[4] )
271 {
272 t->pp_border_color = r200PackColor( 4, c[0], c[1], c[2], c[3] );
273 }
274
275
276 /**
277 * Allocate space for and load the mesa images into the texture memory block.
278 * This will happen before drawing with a new texture, or drawing with a
279 * texture after it was swapped out or teximaged again.
280 */
281
282 static r200TexObjPtr r200AllocTexObj( struct gl_texture_object *texObj )
283 {
284 r200TexObjPtr t;
285
286 t = CALLOC_STRUCT( r200_tex_obj );
287 texObj->DriverData = t;
288 if ( t != NULL ) {
289 if ( R200_DEBUG & DEBUG_TEXTURE ) {
290 fprintf( stderr, "%s( %p, %p )\n", __FUNCTION__, (void *)texObj,
291 (void *)t );
292 }
293
294 /* Initialize non-image-dependent parts of the state:
295 */
296 t->base.tObj = texObj;
297 t->border_fallback = GL_FALSE;
298
299 make_empty_list( & t->base );
300
301 r200SetTexWrap( t, texObj->WrapS, texObj->WrapT, texObj->WrapR );
302 r200SetTexMaxAnisotropy( t, texObj->MaxAnisotropy );
303 r200SetTexFilter( t, texObj->MinFilter, texObj->MagFilter );
304 r200SetTexBorderColor( t, texObj->_BorderChan );
305 }
306
307 return t;
308 }
309
310 /* try to find a format which will only need a memcopy */
311 static const struct gl_texture_format *
312 r200Choose8888TexFormat( GLenum srcFormat, GLenum srcType )
313 {
314 const GLuint ui = 1;
315 const GLubyte littleEndian = *((const GLubyte *) &ui);
316
317 if ((srcFormat == GL_RGBA && srcType == GL_UNSIGNED_INT_8_8_8_8) ||
318 (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE && !littleEndian) ||
319 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_INT_8_8_8_8_REV) ||
320 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_BYTE && littleEndian)) {
321 return &_mesa_texformat_rgba8888;
322 }
323 else if ((srcFormat == GL_RGBA && srcType == GL_UNSIGNED_INT_8_8_8_8_REV) ||
324 (srcFormat == GL_RGBA && srcType == GL_UNSIGNED_BYTE && littleEndian) ||
325 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_INT_8_8_8_8) ||
326 (srcFormat == GL_ABGR_EXT && srcType == GL_UNSIGNED_BYTE && !littleEndian)) {
327 return &_mesa_texformat_rgba8888_rev;
328 }
329 else return _dri_texformat_argb8888;
330 }
331
332 static const struct gl_texture_format *
333 r200ChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
334 GLenum format, GLenum type )
335 {
336 r200ContextPtr rmesa = R200_CONTEXT(ctx);
337 const GLboolean do32bpt =
338 ( rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_32 );
339 const GLboolean force16bpt =
340 ( rmesa->texture_depth == DRI_CONF_TEXTURE_DEPTH_FORCE_16 );
341 (void) format;
342
343 switch ( internalFormat ) {
344 case 4:
345 case GL_RGBA:
346 case GL_COMPRESSED_RGBA:
347 switch ( type ) {
348 case GL_UNSIGNED_INT_10_10_10_2:
349 case GL_UNSIGNED_INT_2_10_10_10_REV:
350 return do32bpt ? _dri_texformat_argb8888 : _dri_texformat_argb1555;
351 case GL_UNSIGNED_SHORT_4_4_4_4:
352 case GL_UNSIGNED_SHORT_4_4_4_4_REV:
353 return _dri_texformat_argb4444;
354 case GL_UNSIGNED_SHORT_5_5_5_1:
355 case GL_UNSIGNED_SHORT_1_5_5_5_REV:
356 return _dri_texformat_argb1555;
357 default:
358 return do32bpt ?
359 r200Choose8888TexFormat(format, type) : _dri_texformat_argb4444;
360 }
361
362 case 3:
363 case GL_RGB:
364 case GL_COMPRESSED_RGB:
365 switch ( type ) {
366 case GL_UNSIGNED_SHORT_4_4_4_4:
367 case GL_UNSIGNED_SHORT_4_4_4_4_REV:
368 return _dri_texformat_argb4444;
369 case GL_UNSIGNED_SHORT_5_5_5_1:
370 case GL_UNSIGNED_SHORT_1_5_5_5_REV:
371 return _dri_texformat_argb1555;
372 case GL_UNSIGNED_SHORT_5_6_5:
373 case GL_UNSIGNED_SHORT_5_6_5_REV:
374 return _dri_texformat_rgb565;
375 default:
376 return do32bpt ? _dri_texformat_argb8888 : _dri_texformat_rgb565;
377 }
378
379 case GL_RGBA8:
380 case GL_RGB10_A2:
381 case GL_RGBA12:
382 case GL_RGBA16:
383 return !force16bpt ?
384 r200Choose8888TexFormat(format, type) : _dri_texformat_argb4444;
385
386 case GL_RGBA4:
387 case GL_RGBA2:
388 return _dri_texformat_argb4444;
389
390 case GL_RGB5_A1:
391 return _dri_texformat_argb1555;
392
393 case GL_RGB8:
394 case GL_RGB10:
395 case GL_RGB12:
396 case GL_RGB16:
397 return !force16bpt ? _dri_texformat_argb8888 : _dri_texformat_rgb565;
398
399 case GL_RGB5:
400 case GL_RGB4:
401 case GL_R3_G3_B2:
402 return _dri_texformat_rgb565;
403
404 case GL_ALPHA:
405 case GL_ALPHA4:
406 case GL_ALPHA8:
407 case GL_ALPHA12:
408 case GL_ALPHA16:
409 case GL_COMPRESSED_ALPHA:
410 /* can't use a8 format since interpreting hw I8 as a8 would result
411 in wrong rgb values (same as alpha value instead of 0). */
412 return _dri_texformat_al88;
413
414 case 1:
415 case GL_LUMINANCE:
416 case GL_LUMINANCE4:
417 case GL_LUMINANCE8:
418 case GL_LUMINANCE12:
419 case GL_LUMINANCE16:
420 case GL_COMPRESSED_LUMINANCE:
421 return _dri_texformat_l8;
422
423 case 2:
424 case GL_LUMINANCE_ALPHA:
425 case GL_LUMINANCE4_ALPHA4:
426 case GL_LUMINANCE6_ALPHA2:
427 case GL_LUMINANCE8_ALPHA8:
428 case GL_LUMINANCE12_ALPHA4:
429 case GL_LUMINANCE12_ALPHA12:
430 case GL_LUMINANCE16_ALPHA16:
431 case GL_COMPRESSED_LUMINANCE_ALPHA:
432 return _dri_texformat_al88;
433
434 case GL_INTENSITY:
435 case GL_INTENSITY4:
436 case GL_INTENSITY8:
437 case GL_INTENSITY12:
438 case GL_INTENSITY16:
439 case GL_COMPRESSED_INTENSITY:
440 return _dri_texformat_i8;
441
442 case GL_YCBCR_MESA:
443 if (type == GL_UNSIGNED_SHORT_8_8_APPLE ||
444 type == GL_UNSIGNED_BYTE)
445 return &_mesa_texformat_ycbcr;
446 else
447 return &_mesa_texformat_ycbcr_rev;
448
449 case GL_RGB_S3TC:
450 case GL_RGB4_S3TC:
451 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
452 return &_mesa_texformat_rgb_dxt1;
453
454 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
455 return &_mesa_texformat_rgba_dxt1;
456
457 case GL_RGBA_S3TC:
458 case GL_RGBA4_S3TC:
459 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
460 return &_mesa_texformat_rgba_dxt3;
461
462 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
463 return &_mesa_texformat_rgba_dxt5;
464
465 default:
466 _mesa_problem(ctx,
467 "unexpected internalFormat 0x%x in r200ChooseTextureFormat",
468 (int) internalFormat);
469 return NULL;
470 }
471
472 return NULL; /* never get here */
473 }
474
475
476 static GLboolean
477 r200ValidateClientStorage( GLcontext *ctx, GLenum target,
478 GLint internalFormat,
479 GLint srcWidth, GLint srcHeight,
480 GLenum format, GLenum type, const void *pixels,
481 const struct gl_pixelstore_attrib *packing,
482 struct gl_texture_object *texObj,
483 struct gl_texture_image *texImage)
484
485 {
486 r200ContextPtr rmesa = R200_CONTEXT(ctx);
487
488 if ( R200_DEBUG & DEBUG_TEXTURE )
489 fprintf(stderr, "intformat %s format %s type %s\n",
490 _mesa_lookup_enum_by_nr( internalFormat ),
491 _mesa_lookup_enum_by_nr( format ),
492 _mesa_lookup_enum_by_nr( type ));
493
494 if (!ctx->Unpack.ClientStorage)
495 return 0;
496
497 if (ctx->_ImageTransferState ||
498 texImage->IsCompressed ||
499 texObj->GenerateMipmap)
500 return 0;
501
502
503 /* This list is incomplete, may be different on ppc???
504 */
505 switch ( internalFormat ) {
506 case GL_RGBA:
507 if ( format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8_REV ) {
508 texImage->TexFormat = _dri_texformat_argb8888;
509 }
510 else
511 return 0;
512 break;
513
514 case GL_RGB:
515 if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 ) {
516 texImage->TexFormat = _dri_texformat_rgb565;
517 }
518 else
519 return 0;
520 break;
521
522 case GL_YCBCR_MESA:
523 if ( format == GL_YCBCR_MESA &&
524 type == GL_UNSIGNED_SHORT_8_8_REV_APPLE ) {
525 texImage->TexFormat = &_mesa_texformat_ycbcr_rev;
526 }
527 else if ( format == GL_YCBCR_MESA &&
528 (type == GL_UNSIGNED_SHORT_8_8_APPLE ||
529 type == GL_UNSIGNED_BYTE)) {
530 texImage->TexFormat = &_mesa_texformat_ycbcr;
531 }
532 else
533 return 0;
534 break;
535
536 default:
537 return 0;
538 }
539
540 /* Could deal with these packing issues, but currently don't:
541 */
542 if (packing->SkipPixels ||
543 packing->SkipRows ||
544 packing->SwapBytes ||
545 packing->LsbFirst) {
546 return 0;
547 }
548
549 {
550 GLint srcRowStride = _mesa_image_row_stride(packing, srcWidth,
551 format, type);
552
553
554 if ( R200_DEBUG & DEBUG_TEXTURE )
555 fprintf(stderr, "%s: srcRowStride %d/%x\n",
556 __FUNCTION__, srcRowStride, srcRowStride);
557
558 /* Could check this later in upload, pitch restrictions could be
559 * relaxed, but would need to store the image pitch somewhere,
560 * as packing details might change before image is uploaded:
561 */
562 if (!r200IsGartMemory( rmesa, pixels, srcHeight * srcRowStride ) ||
563 (srcRowStride & 63))
564 return 0;
565
566
567 /* Have validated that _mesa_transfer_teximage would be a straight
568 * memcpy at this point. NOTE: future calls to TexSubImage will
569 * overwrite the client data. This is explicitly mentioned in the
570 * extension spec.
571 */
572 texImage->Data = (void *)pixels;
573 texImage->IsClientData = GL_TRUE;
574 texImage->RowStride = srcRowStride / texImage->TexFormat->TexelBytes;
575
576 return 1;
577 }
578 }
579
580
581 static void r200TexImage1D( GLcontext *ctx, GLenum target, GLint level,
582 GLint internalFormat,
583 GLint width, GLint border,
584 GLenum format, GLenum type, const GLvoid *pixels,
585 const struct gl_pixelstore_attrib *packing,
586 struct gl_texture_object *texObj,
587 struct gl_texture_image *texImage )
588 {
589 driTextureObject * t = (driTextureObject *) texObj->DriverData;
590
591 if ( t ) {
592 driSwapOutTextureObject( t );
593 }
594 else {
595 t = (driTextureObject *) r200AllocTexObj( texObj );
596 if (!t) {
597 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage1D");
598 return;
599 }
600 }
601
602 /* Note, this will call ChooseTextureFormat */
603 _mesa_store_teximage1d(ctx, target, level, internalFormat,
604 width, border, format, type, pixels,
605 &ctx->Unpack, texObj, texImage);
606
607 t->dirty_images[0] |= (1 << level);
608 }
609
610
611 static void r200TexSubImage1D( GLcontext *ctx, GLenum target, GLint level,
612 GLint xoffset,
613 GLsizei width,
614 GLenum format, GLenum type,
615 const GLvoid *pixels,
616 const struct gl_pixelstore_attrib *packing,
617 struct gl_texture_object *texObj,
618 struct gl_texture_image *texImage )
619 {
620 driTextureObject * t = (driTextureObject *) texObj->DriverData;
621
622 assert( t ); /* this _should_ be true */
623 if ( t ) {
624 driSwapOutTextureObject( t );
625 }
626 else {
627 t = (driTextureObject *) r200AllocTexObj( texObj );
628 if (!t) {
629 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage1D");
630 return;
631 }
632 }
633
634 _mesa_store_texsubimage1d(ctx, target, level, xoffset, width,
635 format, type, pixels, packing, texObj,
636 texImage);
637
638 t->dirty_images[0] |= (1 << level);
639 }
640
641
642 static void r200TexImage2D( GLcontext *ctx, GLenum target, GLint level,
643 GLint internalFormat,
644 GLint width, GLint height, GLint border,
645 GLenum format, GLenum type, const GLvoid *pixels,
646 const struct gl_pixelstore_attrib *packing,
647 struct gl_texture_object *texObj,
648 struct gl_texture_image *texImage )
649 {
650 driTextureObject * t = (driTextureObject *) texObj->DriverData;
651 GLuint face;
652
653 /* which cube face or ordinary 2D image */
654 switch (target) {
655 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
656 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
657 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
658 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
659 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
660 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
661 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
662 ASSERT(face < 6);
663 break;
664 default:
665 face = 0;
666 }
667
668 if ( t != NULL ) {
669 driSwapOutTextureObject( t );
670 }
671 else {
672 t = (driTextureObject *) r200AllocTexObj( texObj );
673 if (!t) {
674 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
675 return;
676 }
677 }
678
679 texImage->IsClientData = GL_FALSE;
680
681 if (r200ValidateClientStorage( ctx, target,
682 internalFormat,
683 width, height,
684 format, type, pixels,
685 packing, texObj, texImage)) {
686 if (R200_DEBUG & DEBUG_TEXTURE)
687 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__);
688 }
689 else {
690 if (R200_DEBUG & DEBUG_TEXTURE)
691 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__);
692
693 /* Normal path: copy (to cached memory) and eventually upload
694 * via another copy to GART memory and then a blit... Could
695 * eliminate one copy by going straight to (permanent) GART.
696 *
697 * Note, this will call r200ChooseTextureFormat.
698 */
699 _mesa_store_teximage2d(ctx, target, level, internalFormat,
700 width, height, border, format, type, pixels,
701 &ctx->Unpack, texObj, texImage);
702
703 t->dirty_images[face] |= (1 << level);
704 }
705 }
706
707
708 static void r200TexSubImage2D( GLcontext *ctx, GLenum target, GLint level,
709 GLint xoffset, GLint yoffset,
710 GLsizei width, GLsizei height,
711 GLenum format, GLenum type,
712 const GLvoid *pixels,
713 const struct gl_pixelstore_attrib *packing,
714 struct gl_texture_object *texObj,
715 struct gl_texture_image *texImage )
716 {
717 driTextureObject * t = (driTextureObject *) texObj->DriverData;
718 GLuint face;
719
720 /* which cube face or ordinary 2D image */
721 switch (target) {
722 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
723 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
724 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
725 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
726 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
727 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
728 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
729 ASSERT(face < 6);
730 break;
731 default:
732 face = 0;
733 }
734
735 assert( t ); /* this _should_ be true */
736 if ( t ) {
737 driSwapOutTextureObject( t );
738 }
739 else {
740 t = (driTextureObject *) r200AllocTexObj( texObj );
741 if (!t) {
742 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D");
743 return;
744 }
745 }
746
747 _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset, width,
748 height, format, type, pixels, packing, texObj,
749 texImage);
750
751 t->dirty_images[face] |= (1 << level);
752 }
753
754
755 static void r200CompressedTexImage2D( GLcontext *ctx, GLenum target, GLint level,
756 GLint internalFormat,
757 GLint width, GLint height, GLint border,
758 GLsizei imageSize, const GLvoid *data,
759 struct gl_texture_object *texObj,
760 struct gl_texture_image *texImage )
761 {
762 driTextureObject * t = (driTextureObject *) texObj->DriverData;
763 GLuint face;
764
765 /* which cube face or ordinary 2D image */
766 switch (target) {
767 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
768 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
769 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
770 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
771 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
772 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
773 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
774 ASSERT(face < 6);
775 break;
776 default:
777 face = 0;
778 }
779
780 if ( t != NULL ) {
781 driSwapOutTextureObject( t );
782 }
783 else {
784 t = (driTextureObject *) r200AllocTexObj( texObj );
785 if (!t) {
786 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexImage2D");
787 return;
788 }
789 }
790
791 texImage->IsClientData = GL_FALSE;
792 /* can't call this, different parameters. Would never evaluate to true anyway currently
793 if (r200ValidateClientStorage( ctx, target,
794 internalFormat,
795 width, height,
796 format, type, pixels,
797 packing, texObj, texImage)) {
798 if (R200_DEBUG & DEBUG_TEXTURE)
799 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__);
800 }
801 else */{
802 if (R200_DEBUG & DEBUG_TEXTURE)
803 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__);
804
805 /* Normal path: copy (to cached memory) and eventually upload
806 * via another copy to GART memory and then a blit... Could
807 * eliminate one copy by going straight to (permanent) GART.
808 *
809 * Note, this will call r200ChooseTextureFormat.
810 */
811 _mesa_store_compressed_teximage2d(ctx, target, level, internalFormat, width,
812 height, border, imageSize, data, texObj, texImage);
813
814 t->dirty_images[face] |= (1 << level);
815 }
816 }
817
818
819 static void r200CompressedTexSubImage2D( GLcontext *ctx, GLenum target, GLint level,
820 GLint xoffset, GLint yoffset,
821 GLsizei width, GLsizei height,
822 GLenum format,
823 GLsizei imageSize, const GLvoid *data,
824 struct gl_texture_object *texObj,
825 struct gl_texture_image *texImage )
826 {
827 driTextureObject * t = (driTextureObject *) texObj->DriverData;
828 GLuint face;
829
830
831 /* which cube face or ordinary 2D image */
832 switch (target) {
833 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
834 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
835 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
836 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
837 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
838 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
839 face = (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
840 ASSERT(face < 6);
841 break;
842 default:
843 face = 0;
844 }
845
846 assert( t ); /* this _should_ be true */
847 if ( t ) {
848 driSwapOutTextureObject( t );
849 }
850 else {
851 t = (driTextureObject *) r200AllocTexObj( texObj );
852 if (!t) {
853 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCompressedTexSubImage2D");
854 return;
855 }
856 }
857
858 _mesa_store_compressed_texsubimage2d(ctx, target, level, xoffset, yoffset, width,
859 height, format, imageSize, data, texObj, texImage);
860
861 t->dirty_images[face] |= (1 << level);
862 }
863
864
865 #if ENABLE_HW_3D_TEXTURE
866 static void r200TexImage3D( GLcontext *ctx, GLenum target, GLint level,
867 GLint internalFormat,
868 GLint width, GLint height, GLint depth,
869 GLint border,
870 GLenum format, GLenum type, const GLvoid *pixels,
871 const struct gl_pixelstore_attrib *packing,
872 struct gl_texture_object *texObj,
873 struct gl_texture_image *texImage )
874 {
875 driTextureObject * t = (driTextureObject *) texObj->DriverData;
876
877 if ( t ) {
878 driSwapOutTextureObject( t );
879 }
880 else {
881 t = (driTextureObject *) r200AllocTexObj( texObj );
882 if (!t) {
883 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage3D");
884 return;
885 }
886 }
887
888 texImage->IsClientData = GL_FALSE;
889
890 #if 0
891 if (r200ValidateClientStorage( ctx, target,
892 internalFormat,
893 width, height,
894 format, type, pixels,
895 packing, texObj, texImage)) {
896 if (R200_DEBUG & DEBUG_TEXTURE)
897 fprintf(stderr, "%s: Using client storage\n", __FUNCTION__);
898 }
899 else
900 #endif
901 {
902 if (R200_DEBUG & DEBUG_TEXTURE)
903 fprintf(stderr, "%s: Using normal storage\n", __FUNCTION__);
904
905 /* Normal path: copy (to cached memory) and eventually upload
906 * via another copy to GART memory and then a blit... Could
907 * eliminate one copy by going straight to (permanent) GART.
908 *
909 * Note, this will call r200ChooseTextureFormat.
910 */
911 _mesa_store_teximage3d(ctx, target, level, internalFormat,
912 width, height, depth, border,
913 format, type, pixels,
914 &ctx->Unpack, texObj, texImage);
915
916 t->dirty_images[0] |= (1 << level);
917 }
918 }
919 #endif
920
921
922 #if ENABLE_HW_3D_TEXTURE
923 static void
924 r200TexSubImage3D( GLcontext *ctx, GLenum target, GLint level,
925 GLint xoffset, GLint yoffset, GLint zoffset,
926 GLsizei width, GLsizei height, GLsizei depth,
927 GLenum format, GLenum type,
928 const GLvoid *pixels,
929 const struct gl_pixelstore_attrib *packing,
930 struct gl_texture_object *texObj,
931 struct gl_texture_image *texImage )
932 {
933 driTextureObject * t = (driTextureObject *) texObj->DriverData;
934
935 /* fprintf(stderr, "%s\n", __FUNCTION__); */
936
937 assert( t ); /* this _should_ be true */
938 if ( t ) {
939 driSwapOutTextureObject( t );
940 }
941 else {
942 t = (driTextureObject *) r200AllocTexObj( texObj );
943 if (!t) {
944 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage3D");
945 return;
946 }
947 texObj->DriverData = t;
948 }
949
950 _mesa_store_texsubimage3d(ctx, target, level, xoffset, yoffset, zoffset,
951 width, height, depth,
952 format, type, pixels, packing, texObj, texImage);
953
954 t->dirty_images[0] |= (1 << level);
955 }
956 #endif
957
958
959
960 static void r200TexEnv( GLcontext *ctx, GLenum target,
961 GLenum pname, const GLfloat *param )
962 {
963 r200ContextPtr rmesa = R200_CONTEXT(ctx);
964 GLuint unit = ctx->Texture.CurrentUnit;
965 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
966
967 if ( R200_DEBUG & DEBUG_STATE ) {
968 fprintf( stderr, "%s( %s )\n",
969 __FUNCTION__, _mesa_lookup_enum_by_nr( pname ) );
970 }
971
972 /* This is incorrect: Need to maintain this data for each of
973 * GL_TEXTURE_{123}D, GL_TEXTURE_RECTANGLE_NV, etc, and switch
974 * between them according to _ReallyEnabled.
975 */
976 switch ( pname ) {
977 case GL_TEXTURE_ENV_COLOR: {
978 GLubyte c[4];
979 GLuint envColor;
980 UNCLAMPED_FLOAT_TO_RGBA_CHAN( c, texUnit->EnvColor );
981 envColor = r200PackColor( 4, c[0], c[1], c[2], c[3] );
982 if ( rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] != envColor ) {
983 R200_STATECHANGE( rmesa, tf );
984 rmesa->hw.tf.cmd[TF_TFACTOR_0 + unit] = envColor;
985 }
986 break;
987 }
988
989 case GL_TEXTURE_LOD_BIAS_EXT: {
990 GLfloat bias, min;
991 GLuint b;
992 const int fixed_one = 0x8000000;
993
994 /* The R200's LOD bias is a signed 2's complement value with a
995 * range of -16.0 <= bias < 16.0.
996 *
997 * NOTE: Add a small bias to the bias for conform mipsel.c test.
998 */
999 bias = *param + .01;
1000 min = driQueryOptionb (&rmesa->optionCache, "no_neg_lod_bias") ?
1001 0.0 : -16.0;
1002 bias = CLAMP( bias, min, 16.0 );
1003 b = (int)(bias * fixed_one) & R200_LOD_BIAS_MASK;
1004
1005 if ( (rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] & R200_LOD_BIAS_MASK) != b ) {
1006 R200_STATECHANGE( rmesa, tex[unit] );
1007 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] &= ~R200_LOD_BIAS_MASK;
1008 rmesa->hw.tex[unit].cmd[TEX_PP_TXFORMAT_X] |= b;
1009 }
1010 break;
1011 }
1012 case GL_COORD_REPLACE_ARB:
1013 if (ctx->Point.PointSprite) {
1014 R200_STATECHANGE( rmesa, spr );
1015 if ((GLenum)param[0]) {
1016 rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] |= R200_PS_GEN_TEX_0 << unit;
1017 } else {
1018 rmesa->hw.spr.cmd[SPR_POINT_SPRITE_CNTL] &= ~(R200_PS_GEN_TEX_0 << unit);
1019 }
1020 }
1021 break;
1022 default:
1023 return;
1024 }
1025 }
1026
1027
1028 /**
1029 * Changes variables and flags for a state update, which will happen at the
1030 * next UpdateTextureState
1031 */
1032
1033 static void r200TexParameter( GLcontext *ctx, GLenum target,
1034 struct gl_texture_object *texObj,
1035 GLenum pname, const GLfloat *params )
1036 {
1037 r200TexObjPtr t = (r200TexObjPtr) texObj->DriverData;
1038
1039 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
1040 fprintf( stderr, "%s( %s )\n", __FUNCTION__,
1041 _mesa_lookup_enum_by_nr( pname ) );
1042 }
1043
1044 switch ( pname ) {
1045 case GL_TEXTURE_MIN_FILTER:
1046 case GL_TEXTURE_MAG_FILTER:
1047 case GL_TEXTURE_MAX_ANISOTROPY_EXT:
1048 r200SetTexMaxAnisotropy( t, texObj->MaxAnisotropy );
1049 r200SetTexFilter( t, texObj->MinFilter, texObj->MagFilter );
1050 break;
1051
1052 case GL_TEXTURE_WRAP_S:
1053 case GL_TEXTURE_WRAP_T:
1054 case GL_TEXTURE_WRAP_R:
1055 r200SetTexWrap( t, texObj->WrapS, texObj->WrapT, texObj->WrapR );
1056 break;
1057
1058 case GL_TEXTURE_BORDER_COLOR:
1059 r200SetTexBorderColor( t, texObj->_BorderChan );
1060 break;
1061
1062 case GL_TEXTURE_BASE_LEVEL:
1063 case GL_TEXTURE_MAX_LEVEL:
1064 case GL_TEXTURE_MIN_LOD:
1065 case GL_TEXTURE_MAX_LOD:
1066 /* This isn't the most efficient solution but there doesn't appear to
1067 * be a nice alternative. Since there's no LOD clamping,
1068 * we just have to rely on loading the right subset of mipmap levels
1069 * to simulate a clamped LOD.
1070 */
1071 driSwapOutTextureObject( (driTextureObject *) t );
1072 break;
1073
1074 default:
1075 return;
1076 }
1077
1078 /* Mark this texobj as dirty (one bit per tex unit)
1079 */
1080 t->dirty_state = TEX_ALL;
1081 }
1082
1083
1084
1085 static void r200BindTexture( GLcontext *ctx, GLenum target,
1086 struct gl_texture_object *texObj )
1087 {
1088 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
1089 fprintf( stderr, "%s( %p ) unit=%d\n", __FUNCTION__, (void *)texObj,
1090 ctx->Texture.CurrentUnit );
1091 }
1092
1093 if ( (target == GL_TEXTURE_1D)
1094 || (target == GL_TEXTURE_2D)
1095 #if ENABLE_HW_3D_TEXTURE
1096 || (target == GL_TEXTURE_3D)
1097 #endif
1098 || (target == GL_TEXTURE_CUBE_MAP)
1099 || (target == GL_TEXTURE_RECTANGLE_NV) ) {
1100 assert( texObj->DriverData != NULL );
1101 }
1102 }
1103
1104
1105 static void r200DeleteTexture( GLcontext *ctx,
1106 struct gl_texture_object *texObj )
1107 {
1108 r200ContextPtr rmesa = R200_CONTEXT(ctx);
1109 driTextureObject * t = (driTextureObject *) texObj->DriverData;
1110
1111 if ( R200_DEBUG & (DEBUG_STATE|DEBUG_TEXTURE) ) {
1112 fprintf( stderr, "%s( %p (target = %s) )\n", __FUNCTION__, (void *)texObj,
1113 _mesa_lookup_enum_by_nr( texObj->Target ) );
1114 }
1115
1116 if ( t != NULL ) {
1117 if ( rmesa ) {
1118 R200_FIREVERTICES( rmesa );
1119 }
1120
1121 driDestroyTextureObject( t );
1122 }
1123 /* Free mipmap images and the texture object itself */
1124 _mesa_delete_texture_object(ctx, texObj);
1125 }
1126
1127 /* Need:
1128 * - Same GEN_MODE for all active bits
1129 * - Same EyePlane/ObjPlane for all active bits when using Eye/Obj
1130 * - STRQ presumably all supported (matrix means incoming R values
1131 * can end up in STQ, this has implications for vertex support,
1132 * presumably ok if maos is used, though?)
1133 *
1134 * Basically impossible to do this on the fly - just collect some
1135 * basic info & do the checks from ValidateState().
1136 */
1137 static void r200TexGen( GLcontext *ctx,
1138 GLenum coord,
1139 GLenum pname,
1140 const GLfloat *params )
1141 {
1142 r200ContextPtr rmesa = R200_CONTEXT(ctx);
1143 GLuint unit = ctx->Texture.CurrentUnit;
1144 rmesa->recheck_texgen[unit] = GL_TRUE;
1145 }
1146
1147
1148 /**
1149 * Allocate a new texture object.
1150 * Called via ctx->Driver.NewTextureObject.
1151 * Note: this function will be called during context creation to
1152 * allocate the default texture objects.
1153 * Note: we could use containment here to 'derive' the driver-specific
1154 * texture object from the core mesa gl_texture_object. Not done at this time.
1155 * Fixup MaxAnisotropy according to user preference.
1156 */
1157 static struct gl_texture_object *
1158 r200NewTextureObject( GLcontext *ctx, GLuint name, GLenum target )
1159 {
1160 r200ContextPtr rmesa = R200_CONTEXT(ctx);
1161 struct gl_texture_object *obj;
1162 obj = _mesa_new_texture_object(ctx, name, target);
1163 if (!obj)
1164 return NULL;
1165 obj->MaxAnisotropy = rmesa->initialMaxAnisotropy;
1166 r200AllocTexObj( obj );
1167 return obj;
1168 }
1169
1170
1171 void r200InitTextureFuncs( struct dd_function_table *functions )
1172 {
1173 /* Note: we only plug in the functions we implement in the driver
1174 * since _mesa_init_driver_functions() was already called.
1175 */
1176 functions->ChooseTextureFormat = r200ChooseTextureFormat;
1177 functions->TexImage1D = r200TexImage1D;
1178 functions->TexImage2D = r200TexImage2D;
1179 #if ENABLE_HW_3D_TEXTURE
1180 functions->TexImage3D = r200TexImage3D;
1181 #else
1182 functions->TexImage3D = _mesa_store_teximage3d;
1183 #endif
1184 functions->TexSubImage1D = r200TexSubImage1D;
1185 functions->TexSubImage2D = r200TexSubImage2D;
1186 #if ENABLE_HW_3D_TEXTURE
1187 functions->TexSubImage3D = r200TexSubImage3D;
1188 #else
1189 functions->TexSubImage3D = _mesa_store_texsubimage3d;
1190 #endif
1191 functions->NewTextureObject = r200NewTextureObject;
1192 functions->BindTexture = r200BindTexture;
1193 functions->DeleteTexture = r200DeleteTexture;
1194 functions->IsTextureResident = driIsTextureResident;
1195
1196 functions->TexEnv = r200TexEnv;
1197 functions->TexParameter = r200TexParameter;
1198 functions->TexGen = r200TexGen;
1199
1200 functions->CompressedTexImage2D = r200CompressedTexImage2D;
1201 functions->CompressedTexSubImage2D = r200CompressedTexSubImage2D;
1202
1203 driInitTextureFormats();
1204
1205 #if 000
1206 /* moved or obsolete code */
1207 r200ContextPtr rmesa = R200_CONTEXT(ctx);
1208 driInitTextureObjects( ctx, & rmesa->swapped,
1209 DRI_TEXMGR_DO_TEXTURE_1D
1210 | DRI_TEXMGR_DO_TEXTURE_2D );
1211
1212 /* Hack: r200NewTextureObject is not yet installed when the
1213 * default textures are created. Therefore set MaxAnisotropy of the
1214 * default 2D texture now. */
1215 ctx->Shared->Default2D->MaxAnisotropy = driQueryOptionf (&rmesa->optionCache,
1216 "def_max_anisotropy");
1217 #endif
1218 }