summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--o3d/core/cross/math_utilities.cc80
-rw-r--r--o3d/core/cross/math_utilities.h2
-rw-r--r--o3d/core/cross/pointer_utils.h2
-rw-r--r--o3d/plugin/cross/texture_static_glue.cc235
-rw-r--r--o3d/plugin/idl/texture.idl40
-rw-r--r--o3d/tests/selenium/tests/texture-set-test.html26
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.