istereo2
diff libs/libpng/pngwtran.c @ 2:81d35769f546
added the tunnel effect source
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 19 Sep 2015 05:51:51 +0300 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/libpng/pngwtran.c Sat Sep 19 05:51:51 2015 +0300 1.3 @@ -0,0 +1,572 @@ 1.4 + 1.5 +/* pngwtran.c - transforms the data in a row for PNG writers 1.6 + * 1.7 + * Last changed in libpng 1.2.9 April 14, 2006 1.8 + * For conditions of distribution and use, see copyright notice in png.h 1.9 + * Copyright (c) 1998-2006 Glenn Randers-Pehrson 1.10 + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) 1.11 + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) 1.12 + */ 1.13 + 1.14 +#define PNG_INTERNAL 1.15 +#include "png.h" 1.16 +#ifdef PNG_WRITE_SUPPORTED 1.17 + 1.18 +/* Transform the data according to the user's wishes. The order of 1.19 + * transformations is significant. 1.20 + */ 1.21 +void /* PRIVATE */ 1.22 +png_do_write_transformations(png_structp png_ptr) 1.23 +{ 1.24 + png_debug(1, "in png_do_write_transformations\n"); 1.25 + 1.26 + if (png_ptr == NULL) 1.27 + return; 1.28 + 1.29 +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) 1.30 + if (png_ptr->transformations & PNG_USER_TRANSFORM) 1.31 + if (png_ptr->write_user_transform_fn != NULL) 1.32 + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ 1.33 + (png_ptr, /* png_ptr */ 1.34 + &(png_ptr->row_info), /* row_info: */ 1.35 + /* png_uint_32 width; width of row */ 1.36 + /* png_uint_32 rowbytes; number of bytes in row */ 1.37 + /* png_byte color_type; color type of pixels */ 1.38 + /* png_byte bit_depth; bit depth of samples */ 1.39 + /* png_byte channels; number of channels (1-4) */ 1.40 + /* png_byte pixel_depth; bits per pixel (depth*channels) */ 1.41 + png_ptr->row_buf + 1); /* start of pixel data for row */ 1.42 +#endif 1.43 +#if defined(PNG_WRITE_FILLER_SUPPORTED) 1.44 + if (png_ptr->transformations & PNG_FILLER) 1.45 + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, 1.46 + png_ptr->flags); 1.47 +#endif 1.48 +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) 1.49 + if (png_ptr->transformations & PNG_PACKSWAP) 1.50 + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.51 +#endif 1.52 +#if defined(PNG_WRITE_PACK_SUPPORTED) 1.53 + if (png_ptr->transformations & PNG_PACK) 1.54 + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, 1.55 + (png_uint_32)png_ptr->bit_depth); 1.56 +#endif 1.57 +#if defined(PNG_WRITE_SWAP_SUPPORTED) 1.58 + if (png_ptr->transformations & PNG_SWAP_BYTES) 1.59 + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.60 +#endif 1.61 +#if defined(PNG_WRITE_SHIFT_SUPPORTED) 1.62 + if (png_ptr->transformations & PNG_SHIFT) 1.63 + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, 1.64 + &(png_ptr->shift)); 1.65 +#endif 1.66 +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) 1.67 + if (png_ptr->transformations & PNG_SWAP_ALPHA) 1.68 + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.69 +#endif 1.70 +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) 1.71 + if (png_ptr->transformations & PNG_INVERT_ALPHA) 1.72 + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.73 +#endif 1.74 +#if defined(PNG_WRITE_BGR_SUPPORTED) 1.75 + if (png_ptr->transformations & PNG_BGR) 1.76 + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.77 +#endif 1.78 +#if defined(PNG_WRITE_INVERT_SUPPORTED) 1.79 + if (png_ptr->transformations & PNG_INVERT_MONO) 1.80 + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); 1.81 +#endif 1.82 +} 1.83 + 1.84 +#if defined(PNG_WRITE_PACK_SUPPORTED) 1.85 +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The 1.86 + * row_info bit depth should be 8 (one pixel per byte). The channels 1.87 + * should be 1 (this only happens on grayscale and paletted images). 1.88 + */ 1.89 +void /* PRIVATE */ 1.90 +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) 1.91 +{ 1.92 + png_debug(1, "in png_do_pack\n"); 1.93 + if (row_info->bit_depth == 8 && 1.94 +#if defined(PNG_USELESS_TESTS_SUPPORTED) 1.95 + row != NULL && row_info != NULL && 1.96 +#endif 1.97 + row_info->channels == 1) 1.98 + { 1.99 + switch ((int)bit_depth) 1.100 + { 1.101 + case 1: 1.102 + { 1.103 + png_bytep sp, dp; 1.104 + int mask, v; 1.105 + png_uint_32 i; 1.106 + png_uint_32 row_width = row_info->width; 1.107 + 1.108 + sp = row; 1.109 + dp = row; 1.110 + mask = 0x80; 1.111 + v = 0; 1.112 + 1.113 + for (i = 0; i < row_width; i++) 1.114 + { 1.115 + if (*sp != 0) 1.116 + v |= mask; 1.117 + sp++; 1.118 + if (mask > 1) 1.119 + mask >>= 1; 1.120 + else 1.121 + { 1.122 + mask = 0x80; 1.123 + *dp = (png_byte)v; 1.124 + dp++; 1.125 + v = 0; 1.126 + } 1.127 + } 1.128 + if (mask != 0x80) 1.129 + *dp = (png_byte)v; 1.130 + break; 1.131 + } 1.132 + case 2: 1.133 + { 1.134 + png_bytep sp, dp; 1.135 + int shift, v; 1.136 + png_uint_32 i; 1.137 + png_uint_32 row_width = row_info->width; 1.138 + 1.139 + sp = row; 1.140 + dp = row; 1.141 + shift = 6; 1.142 + v = 0; 1.143 + for (i = 0; i < row_width; i++) 1.144 + { 1.145 + png_byte value; 1.146 + 1.147 + value = (png_byte)(*sp & 0x03); 1.148 + v |= (value << shift); 1.149 + if (shift == 0) 1.150 + { 1.151 + shift = 6; 1.152 + *dp = (png_byte)v; 1.153 + dp++; 1.154 + v = 0; 1.155 + } 1.156 + else 1.157 + shift -= 2; 1.158 + sp++; 1.159 + } 1.160 + if (shift != 6) 1.161 + *dp = (png_byte)v; 1.162 + break; 1.163 + } 1.164 + case 4: 1.165 + { 1.166 + png_bytep sp, dp; 1.167 + int shift, v; 1.168 + png_uint_32 i; 1.169 + png_uint_32 row_width = row_info->width; 1.170 + 1.171 + sp = row; 1.172 + dp = row; 1.173 + shift = 4; 1.174 + v = 0; 1.175 + for (i = 0; i < row_width; i++) 1.176 + { 1.177 + png_byte value; 1.178 + 1.179 + value = (png_byte)(*sp & 0x0f); 1.180 + v |= (value << shift); 1.181 + 1.182 + if (shift == 0) 1.183 + { 1.184 + shift = 4; 1.185 + *dp = (png_byte)v; 1.186 + dp++; 1.187 + v = 0; 1.188 + } 1.189 + else 1.190 + shift -= 4; 1.191 + 1.192 + sp++; 1.193 + } 1.194 + if (shift != 4) 1.195 + *dp = (png_byte)v; 1.196 + break; 1.197 + } 1.198 + } 1.199 + row_info->bit_depth = (png_byte)bit_depth; 1.200 + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); 1.201 + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, 1.202 + row_info->width); 1.203 + } 1.204 +} 1.205 +#endif 1.206 + 1.207 +#if defined(PNG_WRITE_SHIFT_SUPPORTED) 1.208 +/* Shift pixel values to take advantage of whole range. Pass the 1.209 + * true number of bits in bit_depth. The row should be packed 1.210 + * according to row_info->bit_depth. Thus, if you had a row of 1.211 + * bit depth 4, but the pixels only had values from 0 to 7, you 1.212 + * would pass 3 as bit_depth, and this routine would translate the 1.213 + * data to 0 to 15. 1.214 + */ 1.215 +void /* PRIVATE */ 1.216 +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) 1.217 +{ 1.218 + png_debug(1, "in png_do_shift\n"); 1.219 +#if defined(PNG_USELESS_TESTS_SUPPORTED) 1.220 + if (row != NULL && row_info != NULL && 1.221 +#else 1.222 + if ( 1.223 +#endif 1.224 + row_info->color_type != PNG_COLOR_TYPE_PALETTE) 1.225 + { 1.226 + int shift_start[4], shift_dec[4]; 1.227 + int channels = 0; 1.228 + 1.229 + if (row_info->color_type & PNG_COLOR_MASK_COLOR) 1.230 + { 1.231 + shift_start[channels] = row_info->bit_depth - bit_depth->red; 1.232 + shift_dec[channels] = bit_depth->red; 1.233 + channels++; 1.234 + shift_start[channels] = row_info->bit_depth - bit_depth->green; 1.235 + shift_dec[channels] = bit_depth->green; 1.236 + channels++; 1.237 + shift_start[channels] = row_info->bit_depth - bit_depth->blue; 1.238 + shift_dec[channels] = bit_depth->blue; 1.239 + channels++; 1.240 + } 1.241 + else 1.242 + { 1.243 + shift_start[channels] = row_info->bit_depth - bit_depth->gray; 1.244 + shift_dec[channels] = bit_depth->gray; 1.245 + channels++; 1.246 + } 1.247 + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) 1.248 + { 1.249 + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; 1.250 + shift_dec[channels] = bit_depth->alpha; 1.251 + channels++; 1.252 + } 1.253 + 1.254 + /* with low row depths, could only be grayscale, so one channel */ 1.255 + if (row_info->bit_depth < 8) 1.256 + { 1.257 + png_bytep bp = row; 1.258 + png_uint_32 i; 1.259 + png_byte mask; 1.260 + png_uint_32 row_bytes = row_info->rowbytes; 1.261 + 1.262 + if (bit_depth->gray == 1 && row_info->bit_depth == 2) 1.263 + mask = 0x55; 1.264 + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) 1.265 + mask = 0x11; 1.266 + else 1.267 + mask = 0xff; 1.268 + 1.269 + for (i = 0; i < row_bytes; i++, bp++) 1.270 + { 1.271 + png_uint_16 v; 1.272 + int j; 1.273 + 1.274 + v = *bp; 1.275 + *bp = 0; 1.276 + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) 1.277 + { 1.278 + if (j > 0) 1.279 + *bp |= (png_byte)((v << j) & 0xff); 1.280 + else 1.281 + *bp |= (png_byte)((v >> (-j)) & mask); 1.282 + } 1.283 + } 1.284 + } 1.285 + else if (row_info->bit_depth == 8) 1.286 + { 1.287 + png_bytep bp = row; 1.288 + png_uint_32 i; 1.289 + png_uint_32 istop = channels * row_info->width; 1.290 + 1.291 + for (i = 0; i < istop; i++, bp++) 1.292 + { 1.293 + 1.294 + png_uint_16 v; 1.295 + int j; 1.296 + int c = (int)(i%channels); 1.297 + 1.298 + v = *bp; 1.299 + *bp = 0; 1.300 + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) 1.301 + { 1.302 + if (j > 0) 1.303 + *bp |= (png_byte)((v << j) & 0xff); 1.304 + else 1.305 + *bp |= (png_byte)((v >> (-j)) & 0xff); 1.306 + } 1.307 + } 1.308 + } 1.309 + else 1.310 + { 1.311 + png_bytep bp; 1.312 + png_uint_32 i; 1.313 + png_uint_32 istop = channels * row_info->width; 1.314 + 1.315 + for (bp = row, i = 0; i < istop; i++) 1.316 + { 1.317 + int c = (int)(i%channels); 1.318 + png_uint_16 value, v; 1.319 + int j; 1.320 + 1.321 + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); 1.322 + value = 0; 1.323 + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) 1.324 + { 1.325 + if (j > 0) 1.326 + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); 1.327 + else 1.328 + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); 1.329 + } 1.330 + *bp++ = (png_byte)(value >> 8); 1.331 + *bp++ = (png_byte)(value & 0xff); 1.332 + } 1.333 + } 1.334 + } 1.335 +} 1.336 +#endif 1.337 + 1.338 +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) 1.339 +void /* PRIVATE */ 1.340 +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) 1.341 +{ 1.342 + png_debug(1, "in png_do_write_swap_alpha\n"); 1.343 +#if defined(PNG_USELESS_TESTS_SUPPORTED) 1.344 + if (row != NULL && row_info != NULL) 1.345 +#endif 1.346 + { 1.347 + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) 1.348 + { 1.349 + /* This converts from ARGB to RGBA */ 1.350 + if (row_info->bit_depth == 8) 1.351 + { 1.352 + png_bytep sp, dp; 1.353 + png_uint_32 i; 1.354 + png_uint_32 row_width = row_info->width; 1.355 + for (i = 0, sp = dp = row; i < row_width; i++) 1.356 + { 1.357 + png_byte save = *(sp++); 1.358 + *(dp++) = *(sp++); 1.359 + *(dp++) = *(sp++); 1.360 + *(dp++) = *(sp++); 1.361 + *(dp++) = save; 1.362 + } 1.363 + } 1.364 + /* This converts from AARRGGBB to RRGGBBAA */ 1.365 + else 1.366 + { 1.367 + png_bytep sp, dp; 1.368 + png_uint_32 i; 1.369 + png_uint_32 row_width = row_info->width; 1.370 + 1.371 + for (i = 0, sp = dp = row; i < row_width; i++) 1.372 + { 1.373 + png_byte save[2]; 1.374 + save[0] = *(sp++); 1.375 + save[1] = *(sp++); 1.376 + *(dp++) = *(sp++); 1.377 + *(dp++) = *(sp++); 1.378 + *(dp++) = *(sp++); 1.379 + *(dp++) = *(sp++); 1.380 + *(dp++) = *(sp++); 1.381 + *(dp++) = *(sp++); 1.382 + *(dp++) = save[0]; 1.383 + *(dp++) = save[1]; 1.384 + } 1.385 + } 1.386 + } 1.387 + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 1.388 + { 1.389 + /* This converts from AG to GA */ 1.390 + if (row_info->bit_depth == 8) 1.391 + { 1.392 + png_bytep sp, dp; 1.393 + png_uint_32 i; 1.394 + png_uint_32 row_width = row_info->width; 1.395 + 1.396 + for (i = 0, sp = dp = row; i < row_width; i++) 1.397 + { 1.398 + png_byte save = *(sp++); 1.399 + *(dp++) = *(sp++); 1.400 + *(dp++) = save; 1.401 + } 1.402 + } 1.403 + /* This converts from AAGG to GGAA */ 1.404 + else 1.405 + { 1.406 + png_bytep sp, dp; 1.407 + png_uint_32 i; 1.408 + png_uint_32 row_width = row_info->width; 1.409 + 1.410 + for (i = 0, sp = dp = row; i < row_width; i++) 1.411 + { 1.412 + png_byte save[2]; 1.413 + save[0] = *(sp++); 1.414 + save[1] = *(sp++); 1.415 + *(dp++) = *(sp++); 1.416 + *(dp++) = *(sp++); 1.417 + *(dp++) = save[0]; 1.418 + *(dp++) = save[1]; 1.419 + } 1.420 + } 1.421 + } 1.422 + } 1.423 +} 1.424 +#endif 1.425 + 1.426 +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) 1.427 +void /* PRIVATE */ 1.428 +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) 1.429 +{ 1.430 + png_debug(1, "in png_do_write_invert_alpha\n"); 1.431 +#if defined(PNG_USELESS_TESTS_SUPPORTED) 1.432 + if (row != NULL && row_info != NULL) 1.433 +#endif 1.434 + { 1.435 + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) 1.436 + { 1.437 + /* This inverts the alpha channel in RGBA */ 1.438 + if (row_info->bit_depth == 8) 1.439 + { 1.440 + png_bytep sp, dp; 1.441 + png_uint_32 i; 1.442 + png_uint_32 row_width = row_info->width; 1.443 + for (i = 0, sp = dp = row; i < row_width; i++) 1.444 + { 1.445 + /* does nothing 1.446 + *(dp++) = *(sp++); 1.447 + *(dp++) = *(sp++); 1.448 + *(dp++) = *(sp++); 1.449 + */ 1.450 + sp+=3; dp = sp; 1.451 + *(dp++) = (png_byte)(255 - *(sp++)); 1.452 + } 1.453 + } 1.454 + /* This inverts the alpha channel in RRGGBBAA */ 1.455 + else 1.456 + { 1.457 + png_bytep sp, dp; 1.458 + png_uint_32 i; 1.459 + png_uint_32 row_width = row_info->width; 1.460 + 1.461 + for (i = 0, sp = dp = row; i < row_width; i++) 1.462 + { 1.463 + /* does nothing 1.464 + *(dp++) = *(sp++); 1.465 + *(dp++) = *(sp++); 1.466 + *(dp++) = *(sp++); 1.467 + *(dp++) = *(sp++); 1.468 + *(dp++) = *(sp++); 1.469 + *(dp++) = *(sp++); 1.470 + */ 1.471 + sp+=6; dp = sp; 1.472 + *(dp++) = (png_byte)(255 - *(sp++)); 1.473 + *(dp++) = (png_byte)(255 - *(sp++)); 1.474 + } 1.475 + } 1.476 + } 1.477 + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 1.478 + { 1.479 + /* This inverts the alpha channel in GA */ 1.480 + if (row_info->bit_depth == 8) 1.481 + { 1.482 + png_bytep sp, dp; 1.483 + png_uint_32 i; 1.484 + png_uint_32 row_width = row_info->width; 1.485 + 1.486 + for (i = 0, sp = dp = row; i < row_width; i++) 1.487 + { 1.488 + *(dp++) = *(sp++); 1.489 + *(dp++) = (png_byte)(255 - *(sp++)); 1.490 + } 1.491 + } 1.492 + /* This inverts the alpha channel in GGAA */ 1.493 + else 1.494 + { 1.495 + png_bytep sp, dp; 1.496 + png_uint_32 i; 1.497 + png_uint_32 row_width = row_info->width; 1.498 + 1.499 + for (i = 0, sp = dp = row; i < row_width; i++) 1.500 + { 1.501 + /* does nothing 1.502 + *(dp++) = *(sp++); 1.503 + *(dp++) = *(sp++); 1.504 + */ 1.505 + sp+=2; dp = sp; 1.506 + *(dp++) = (png_byte)(255 - *(sp++)); 1.507 + *(dp++) = (png_byte)(255 - *(sp++)); 1.508 + } 1.509 + } 1.510 + } 1.511 + } 1.512 +} 1.513 +#endif 1.514 + 1.515 +#if defined(PNG_MNG_FEATURES_SUPPORTED) 1.516 +/* undoes intrapixel differencing */ 1.517 +void /* PRIVATE */ 1.518 +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) 1.519 +{ 1.520 + png_debug(1, "in png_do_write_intrapixel\n"); 1.521 + if ( 1.522 +#if defined(PNG_USELESS_TESTS_SUPPORTED) 1.523 + row != NULL && row_info != NULL && 1.524 +#endif 1.525 + (row_info->color_type & PNG_COLOR_MASK_COLOR)) 1.526 + { 1.527 + int bytes_per_pixel; 1.528 + png_uint_32 row_width = row_info->width; 1.529 + if (row_info->bit_depth == 8) 1.530 + { 1.531 + png_bytep rp; 1.532 + png_uint_32 i; 1.533 + 1.534 + if (row_info->color_type == PNG_COLOR_TYPE_RGB) 1.535 + bytes_per_pixel = 3; 1.536 + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) 1.537 + bytes_per_pixel = 4; 1.538 + else 1.539 + return; 1.540 + 1.541 + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) 1.542 + { 1.543 + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); 1.544 + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); 1.545 + } 1.546 + } 1.547 + else if (row_info->bit_depth == 16) 1.548 + { 1.549 + png_bytep rp; 1.550 + png_uint_32 i; 1.551 + 1.552 + if (row_info->color_type == PNG_COLOR_TYPE_RGB) 1.553 + bytes_per_pixel = 6; 1.554 + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) 1.555 + bytes_per_pixel = 8; 1.556 + else 1.557 + return; 1.558 + 1.559 + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) 1.560 + { 1.561 + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); 1.562 + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); 1.563 + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); 1.564 + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); 1.565 + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); 1.566 + *(rp ) = (png_byte)((red >> 8) & 0xff); 1.567 + *(rp+1) = (png_byte)(red & 0xff); 1.568 + *(rp+4) = (png_byte)((blue >> 8) & 0xff); 1.569 + *(rp+5) = (png_byte)(blue & 0xff); 1.570 + } 1.571 + } 1.572 + } 1.573 +} 1.574 +#endif /* PNG_MNG_FEATURES_SUPPORTED */ 1.575 +#endif /* PNG_WRITE_SUPPORTED */