diff options
-rw-r--r-- | o3d/core/cross/math_utilities.cc | 80 | ||||
-rw-r--r-- | o3d/core/cross/math_utilities.h | 2 | ||||
-rw-r--r-- | o3d/core/cross/pointer_utils.h | 2 | ||||
-rw-r--r-- | o3d/plugin/cross/texture_static_glue.cc | 235 | ||||
-rw-r--r-- | o3d/plugin/idl/texture.idl | 40 | ||||
-rw-r--r-- | o3d/tests/selenium/tests/texture-set-test.html | 26 |
6 files changed, 354 insertions, 31 deletions
diff --git a/o3d/core/cross/math_utilities.cc b/o3d/core/cross/math_utilities.cc index 0eb2d26f..95931a4 100644 --- a/o3d/core/cross/math_utilities.cc +++ b/o3d/core/cross/math_utilities.cc @@ -101,6 +101,19 @@ Matrix4 CreateOrthographicMatrix(float left, return Matrix4::identity(); } +namespace { + +// -15 stored using a single precision bias of 127 +const unsigned kHalfFloatMinBiasedExpAsSingleFpExponent = 0x38000000u; +// max exponent value in single precision that will be converted +// to Inf or Nan when stored as a half-float +const unsigned kHalfFloatMaxBiasedExpAsSingleFpExponent = 0x47800000u; +// 255 is the max exponent biased value +const unsigned kFloatMaxBiasedExponent = (0xFFu << 23); +const unsigned kHalfFloatMaxBiasedExponent = (0x1Fu << 10); + +} // anonymous namespace + uint16 FloatToHalf(float value) { union FloatAndUInt { float f; @@ -113,23 +126,13 @@ uint16 FloatToHalf(float value) { unsigned exponent; uint16 half; - // -15 stored using a single precision bias of 127 - const unsigned HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = - 0x38000000; - // max exponent value in single precision that will be converted - // to Inf or Nan when stored as a half-float - const unsigned HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000; - // 255 is the max exponent biased value - const unsigned FLOAT_MAX_BIASED_EXP = (0xFF << 23); - const unsigned HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10); - // get mantissa mantissa = v & ((1 << 23) - 1); // get exponent bits - exponent = v & FLOAT_MAX_BIASED_EXP; - if (exponent >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP) { + exponent = v & kFloatMaxBiasedExponent; + if (exponent >= kHalfFloatMaxBiasedExpAsSingleFpExponent) { // check if the original single precision float number is a NaN - if (mantissa && (exponent == FLOAT_MAX_BIASED_EXP)) { + if (mantissa && (exponent == kFloatMaxBiasedExponent)) { // we have a single precision NaN mantissa = (1 << 23) - 1; } else { @@ -137,12 +140,12 @@ uint16 FloatToHalf(float value) { mantissa = 0; } half = (static_cast<uint16>(sign) << 15) | - static_cast<uint16>(HALF_FLOAT_MAX_BIASED_EXP) | + static_cast<uint16>(kHalfFloatMaxBiasedExponent) | static_cast<uint16>(mantissa >> 13); // check if exponent is <= -15 - } else if (exponent <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) { + } else if (exponent <= kHalfFloatMinBiasedExpAsSingleFpExponent) { // store a denorm half-float value or zero - exponent = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - + exponent = (kHalfFloatMinBiasedExpAsSingleFpExponent - exponent) >> 23; mantissa >>= (14 + exponent); @@ -152,11 +155,54 @@ uint16 FloatToHalf(float value) { half = (static_cast<uint16>(sign) << 15) | static_cast<uint16>( - (exponent - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) | + (exponent - kHalfFloatMinBiasedExpAsSingleFpExponent) >> 13) | static_cast<uint16>(mantissa >> 13); } return half; } + +float HalfToFloat(uint16 half) { + unsigned int value; + unsigned int sign = static_cast<unsigned int>(half >> 15); + unsigned int mantissa = static_cast<unsigned int>(half & ((1 << 10) - 1)); + unsigned int exponent = static_cast<unsigned int>( + half & kHalfFloatMaxBiasedExponent); + + if (exponent == kHalfFloatMaxBiasedExponent) { + // we have a half-float NaN or Inf + // half-float NaNs will be converted to a single precision NaN + // half-float Infs will be converted to a single precision Inf + exponent = kFloatMaxBiasedExponent; + if (mantissa) + mantissa = (1 << 23) - 1; // set all bits to indicate a NaN + } else if (exponent == 0x0) { + // convert half-float zero/denorm to single precision value + if (mantissa) { + mantissa <<= 1; + exponent = kHalfFloatMinBiasedExpAsSingleFpExponent; + // check for leading 1 in denorm mantissa + while ((mantissa & (1 << 10)) == 0) { + // for every leading 0, decrement single precision exponent by 1 + // and shift half-float mantissa value to the left + mantissa <<= 1; + exponent -= (1 << 23); + } + // clamp the mantissa to 10-bits + mantissa &= ((1 << 10) - 1); + // shift left to generate single-precision mantissa of 23-bits + mantissa <<= 13; + } + } else { + // shift left to generate single-precision mantissa of 23-bits + mantissa <<= 13; + // generate single precision biased exponent value + exponent = (exponent << 13) + kHalfFloatMinBiasedExpAsSingleFpExponent; + } + + value = (sign << 31) | exponent | mantissa; + return *((float *)&value); +} + } // namespace Vectormath } // namespace Aos diff --git a/o3d/core/cross/math_utilities.h b/o3d/core/cross/math_utilities.h index 6870380..c4ee147 100644 --- a/o3d/core/cross/math_utilities.h +++ b/o3d/core/cross/math_utilities.h @@ -65,6 +65,8 @@ Matrix4 CreateOrthographicMatrix(float left, // Converts a 32 bit float to a 16 bit float. uint16 FloatToHalf(float value); +// Converts a 16 bit float to a 32 bit float. +float HalfToFloat(uint16 value); } // namespace Vectormath } // namespace Aos diff --git a/o3d/core/cross/pointer_utils.h b/o3d/core/cross/pointer_utils.h index 1cbd610..b16ed5c 100644 --- a/o3d/core/cross/pointer_utils.h +++ b/o3d/core/cross/pointer_utils.h @@ -48,7 +48,7 @@ T AddPointerOffset(T pointer, unsigned offset) { // Creates a typed pointer from a void pointer and an offset.
template <typename T>
-T PointerFromVoidPointer(void* pointer, unsigned offset) {
+T PointerFromVoidPointer(const void* pointer, unsigned offset) {
return reinterpret_cast<T>(
const_cast<uint8*>(reinterpret_cast<const uint8*>(pointer) + offset));
}
diff --git a/o3d/plugin/cross/texture_static_glue.cc b/o3d/plugin/cross/texture_static_glue.cc index 2e34786..94d29f9 100644 --- a/o3d/plugin/cross/texture_static_glue.cc +++ b/o3d/plugin/cross/texture_static_glue.cc @@ -34,13 +34,13 @@ #include "core/cross/error.h"
#include "core/cross/math_utilities.h"
#include "core/cross/texture.h"
+#include "core/cross/image_utils.h"
namespace {
void SetRectCheck(o3d::Texture* self,
void* data,
int pitch,
- o3d::Texture::Format format,
int destination_x,
int destination_y,
int texture_width,
@@ -50,7 +50,7 @@ void SetRectCheck(o3d::Texture* self, const std::vector<float>& values) {
unsigned num_components;
unsigned swizzle[4] = {2, 1, 0, 3};
- switch (format) {
+ switch (self->format()) {
case o3d::Texture::XRGB8:
num_components = 3;
break;
@@ -108,12 +108,12 @@ void SetRectCheck(o3d::Texture* self, &values[0] +
(source_y * source_width + source_x) * num_components;
unsigned source_stride = (source_width - copy_width) * num_components;
- switch (format) {
+ switch (self->format()) {
case o3d::Texture::ABGR16F: {
uint16* dest_line =
static_cast<uint16*>(data) +
(destination_y * texture_width + destination_x) * num_components;
- while (copy_height > 0) {
+ for (; copy_height > 0; --copy_height) {
uint16* destination = dest_line;
for (int xx = 0; xx < copy_width; ++xx) {
for (unsigned element = 0; element < num_components; ++element) {
@@ -125,7 +125,6 @@ void SetRectCheck(o3d::Texture* self, }
dest_line = o3d::AddPointerOffset<uint16*>(dest_line, pitch);
source += source_stride;
- --copy_height;
}
break;
}
@@ -134,7 +133,7 @@ void SetRectCheck(o3d::Texture* self, float* dest_line =
static_cast<float*>(data) +
(destination_y * texture_width + destination_x) * num_components;
- while (copy_height > 0) {
+ for (; copy_height > 0; --copy_height) {
float* destination = dest_line;
for (int xx = 0; xx < copy_width; ++xx) {
for (unsigned element = 0; element < num_components; ++element) {
@@ -145,7 +144,6 @@ void SetRectCheck(o3d::Texture* self, }
dest_line = o3d::AddPointerOffset<float*>(dest_line, pitch);
source += source_stride;
- --copy_height;
}
break;
}
@@ -153,7 +151,7 @@ void SetRectCheck(o3d::Texture* self, uint8* dest_line =
static_cast<uint8*>(data) +
(destination_y * texture_width + destination_x) * 4;
- while (copy_height > 0) {
+ for (; copy_height > 0; --copy_height) {
uint8* destination = dest_line;
for (int xx = 0; xx < copy_width; ++xx) {
destination[0] = static_cast<unsigned char>(
@@ -169,7 +167,6 @@ void SetRectCheck(o3d::Texture* self, }
dest_line = o3d::AddPointerOffset<uint8*>(dest_line, pitch);
source += source_stride;
- --copy_height;
}
break;
}
@@ -244,7 +241,7 @@ void SetRectCheck2D(o3d::Texture2D* self, return;
}
- SetRectCheck(self, data, helper.pitch(), self->format(),
+ SetRectCheck(self, data, helper.pitch(),
destination_x, destination_y,
texture_width, texture_height,
source_width, source_height,
@@ -320,13 +317,109 @@ void SetRectCheckCUBE(o3d::TextureCUBE* self, return;
}
- SetRectCheck(self, data, helper.pitch(), self->format(),
+ SetRectCheck(self, data, helper.pitch(),
destination_x, destination_y,
texture_width, texture_height,
source_width, source_height,
values);
}
+// Assumes dst points to width * height * num_components floats.
+void GetRect(o3d::Texture* self,
+ const void* src_data,
+ int src_pitch,
+ int x,
+ int y,
+ int width,
+ int height,
+ float* dst) {
+ unsigned num_components;
+ unsigned swizzle[4] = {2, 1, 0, 3};
+ switch (self->format()) {
+ case o3d::Texture::XRGB8:
+ num_components = 3;
+ break;
+ case o3d::Texture::R32F:
+ swizzle[0] = 0;
+ num_components = 1;
+ break;
+ case o3d::Texture::ARGB8:
+ case o3d::Texture::ABGR16F:
+ num_components = 4;
+ break;
+ case o3d::Texture::ABGR32F: {
+ num_components = 4;
+ const o3d::Texture::RGBASwizzleIndices& indices =
+ self->GetABGR32FSwizzleIndices();
+ for (int ii = 0; ii < 4; ++ii) {
+ swizzle[ii] = indices[ii];
+ }
+ break;
+ }
+ default:
+ DCHECK(false);
+ return;
+ }
+
+ switch (self->format()) {
+ case o3d::Texture::ABGR16F: {
+ uint16* src_line = o3d::PointerFromVoidPointer<uint16*>(
+ src_data,
+ (y * src_pitch)) + x * num_components;
+ for (; height > 0; --height) {
+ uint16* src = src_line;
+ for (int xx = 0; xx < width; ++xx) {
+ for (unsigned element = 0; element < num_components; ++element) {
+ dst[swizzle[element]] = Vectormath::Aos::HalfToFloat(src[element]);
+ }
+ dst += num_components;
+ src += num_components;
+ }
+ src_line = o3d::AddPointerOffset<uint16*>(src_line, src_pitch);
+ }
+ break;
+ }
+ case o3d::Texture::R32F:
+ case o3d::Texture::ABGR32F: {
+ float* src_line = o3d::PointerFromVoidPointer<float*>(
+ src_data,
+ (y * src_pitch)) + x * num_components;
+ for (; height > 0; --height) {
+ float* src = src_line;
+ for (int xx = 0; xx < width; ++xx) {
+ for (unsigned element = 0; element < num_components; ++element) {
+ dst[swizzle[element]] = src[element];
+ }
+ dst += num_components;
+ src += num_components;
+ }
+ src_line = o3d::AddPointerOffset<float*>(src_line, src_pitch);
+ }
+ break;
+ }
+ default: {
+ uint8* src_line = o3d::PointerFromVoidPointer<uint8*>(
+ src_data,
+ (y * src_pitch)) + x * num_components;
+ for (; height > 0; --height) {
+ uint8* src = src_line;
+ for (int xx = 0; xx < width; ++xx) {
+ dst[swizzle[0]] = static_cast<float>(src[0]) / 255.0f;
+ dst[swizzle[1]] = static_cast<float>(src[1]) / 255.0f;
+ dst[swizzle[2]] = static_cast<float>(src[2]) / 255.0f;
+ if (num_components == 4) {
+ dst[swizzle[3]] = static_cast<float>(src[3]) / 255.0f;
+ }
+ dst += num_components;
+ src += 4;
+ }
+ src_line = o3d::AddPointerOffset<uint8*>(src_line, src_pitch);
+ }
+ break;
+ }
+ }
+}
+
} // anonymous namespace
namespace glue {
@@ -352,6 +445,66 @@ void userglue_method_Set(o3d::Texture2D* self, const std::vector<float>& values) {
SetRectCheck2D(self, level, 0, 0, self->width(), values, true);
}
+std::vector<float> userglue_method_GetRect(o3d::Texture2D* self,
+ int level,
+ int x,
+ int y,
+ int width,
+ int height) {
+ std::vector<float> empty;
+ if (level < 0 || level >= self->levels()) {
+ O3D_ERROR(self->service_locator())
+ << "level (" << level << " out of range";
+ return empty;
+ }
+ if (width <= 0 || height <= 0) {
+ O3D_ERROR(self->service_locator())
+ << "width and height must be positive";
+ return empty;
+ }
+
+ int mip_width =
+ static_cast<int>(o3d::image::ComputeMipDimension(level, self->width()));
+ int mip_height =
+ static_cast<int>(o3d::image::ComputeMipDimension(level, self->height()));
+
+ if (x < 0 || x + width > mip_width || y < 0 || y + height > mip_height) {
+ O3D_ERROR(self->service_locator()) << "area out of range";
+ return empty;
+ }
+
+ unsigned num_components;
+ switch (self->format()) {
+ case o3d::Texture::XRGB8:
+ num_components = 3;
+ break;
+ case o3d::Texture::R32F:
+ num_components = 1;
+ break;
+ case o3d::Texture::ARGB8:
+ case o3d::Texture::ABGR16F:
+ num_components = 4;
+ break;
+ case o3d::Texture::ABGR32F: {
+ num_components = 4;
+ break;
+ }
+ default:
+ O3D_ERROR(self->service_locator())
+ << "Texture::Set not supported for this type of texture";
+ return empty;
+ }
+ o3d::Texture2D::LockHelper helper(self, level);
+ void* data = helper.GetData();
+ if (!data) {
+ O3D_ERROR(self->service_locator()) << "could not lock texture";
+ return empty;
+ }
+
+ std::vector<float> values(width * height * num_components, 0);
+ GetRect(self, data, helper.pitch(), x, y, width, height, &values[0]);
+ return values;
+}
} // namespace class_Texture2D
@@ -379,9 +532,67 @@ void userglue_method_Set(o3d::TextureCUBE* self, const std::vector<float>& values) {
SetRectCheckCUBE(self, face, level, 0, 0, self->edge_length(), values, true);
}
+std::vector<float> userglue_method_GetRect(o3d::TextureCUBE* self,
+ o3d::TextureCUBE::CubeFace face,
+ int level,
+ int x,
+ int y,
+ int width,
+ int height) {
+ std::vector<float> empty;
+ if (level < 0 || level >= self->levels()) {
+ O3D_ERROR(self->service_locator())
+ << "level (" << level << " out of range";
+ return empty;
+ }
+ if (width <= 0 || height <= 0) {
+ O3D_ERROR(self->service_locator())
+ << "width and height must be positive";
+ return empty;
+ }
+ int mip_length = static_cast<int>(o3d::image::ComputeMipDimension(
+ level, self->edge_length()));
-} // namespace class_Texture2D
+ if (x < 0 || x + width > mip_length || y < 0 || y + height > mip_length) {
+ O3D_ERROR(self->service_locator()) << "area out of range";
+ return empty;
+ }
+
+ unsigned num_components;
+ switch (self->format()) {
+ case o3d::Texture::XRGB8:
+ num_components = 3;
+ break;
+ case o3d::Texture::R32F:
+ num_components = 1;
+ break;
+ case o3d::Texture::ARGB8:
+ case o3d::Texture::ABGR16F:
+ num_components = 4;
+ break;
+ case o3d::Texture::ABGR32F: {
+ num_components = 4;
+ break;
+ }
+ default:
+ O3D_ERROR(self->service_locator())
+ << "Texture::Set not supported for this type of texture";
+ return empty;
+ }
+ o3d::TextureCUBE::LockHelper helper(self, face, level);
+ void* data = helper.GetData();
+ if (!data) {
+ O3D_ERROR(self->service_locator()) << "could not lock texture";
+ return empty;
+ }
+
+ std::vector<float> values(width * height * num_components, 0);
+ GetRect(self, data, helper.pitch(), x, y, width, height, &values[0]);
+ return values;
+}
+
+} // namespace class_TextureCUBE
} // namespace namespace_o3d
} // namespace glue
diff --git a/o3d/plugin/idl/texture.idl b/o3d/plugin/idl/texture.idl index 54b705c..062fe57 100644 --- a/o3d/plugin/idl/texture.idl +++ b/o3d/plugin/idl/texture.idl @@ -218,6 +218,25 @@ namespace o3d { float[] values); %[ + Gets a rectangular area of values from a texture. + + See o3d.Texture2D.set for details on formats. + Can not be used for compressed textures. + + \param level the mip level to get. + \param x The x coordinate of the area in the texture to retrieve. + \param y The y coordinate of the area in the texture to retrieve. + \param width The width of the area to retrieve. + \param height The height of the area to retrieve. + %] + [nocpp, userglue] + float[] GetRect(int level, + int x, + int y, + int width, + int height); + + %[ Copy pixels from source bitmap to certain mip level. Scales if the width and height of source and dest do not match. @@ -340,6 +359,27 @@ namespace o3d { float[] values); %[ + Gets a rectangular area of values from a texture. + + See o3d.Texture2D.set for details on formats. + Can not be used for compressed textures. + + \param face the face to get. + \param level the mip level to get. + \param x The x coordinate of the area in the texture to retrieve. + \param y The y coordinate of the area in the texture to retrieve. + \param width The width of the area to retrieve. + \param height The height of the area to retrieve. + %] + [nocpp, userglue] + float[] GetRect(CubeFace face, + int level, + int x, + int y, + int width, + int height); + + %[ Copy pixels from source bitmap to certain mip level. Scales if the width and height of source and dest do not match. diff --git a/o3d/tests/selenium/tests/texture-set-test.html b/o3d/tests/selenium/tests/texture-set-test.html index 891a08d..b3c37be 100644 --- a/o3d/tests/selenium/tests/texture-set-test.html +++ b/o3d/tests/selenium/tests/texture-set-test.html @@ -278,10 +278,33 @@ function initStep2(clientElements) { } texture.setRect(0, tx, ty, 32, pixels); sampler.texture = texture; + if (tx == 0 && ty == 0) { + var texturePixels = texture.getRect(0, tx, ty, 32, 32); + if (texturePixels.length != pixels.length) { + reportResult(false, 'different length'); + return; + } + for (var ndx = 0; ndx < pixels.length; ++ndx) { + // because float<->half conversion is not perfect + var difference = Math.abs(pixels[ndx] - texturePixels[ndx]); + if (difference > 0.004) { + reportResult(false, 'pixels different by:' + difference); + return; + } + } + } } } } - window.g_testResult = true; // for selenium testing. + reportResult(true, ''); +} + +function reportResult(result, msg) { + o3djs.BROWSER_ONLY; + document.getElementById('result').innerHTML = result ? + '<font color="green">success</font>' : + '<font color="red">failure: ' + msg + '</font>'; + window.g_testResult = result; // for selenium testing. } </script> @@ -293,6 +316,7 @@ function initStep2(clientElements) { <!-- Start of O3D plugin --> <div id="o3d" style="width: 400px; height: 400px;"></div> <!-- End of O3D plugin --> +<div>Result: <span id="result"></span></div> <script type="test/o3deffect" id="texture_only"> /* * Copyright 2009, Google Inc. |