diff options
Diffstat (limited to 'o3d')
33 files changed, 1976 insertions, 587 deletions
diff --git a/o3d/converter/cross/texture_stub.h b/o3d/converter/cross/texture_stub.h index 78e971c..43888d4 100644 --- a/o3d/converter/cross/texture_stub.h +++ b/o3d/converter/cross/texture_stub.h @@ -62,12 +62,15 @@ class Texture2DStub : public Texture2D { enable_render_surfaces) {} virtual ~Texture2DStub() {} - // Locks the image buffer of a given mipmap level for writing from main - // memory. - virtual bool Lock(int level, void** texture_data) { return true; } - - // Unlocks this texture and returns it to Stub control. - virtual bool Unlock(int level) { return true; } + // Overridden from Texture2D + virtual void SetRect(int level, + unsigned left, + unsigned top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch) { + } // Returns a RenderSurface object associated with a mip_level of a texture. // Parameters: @@ -88,6 +91,16 @@ class Texture2DStub : public Texture2D { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Locks the image buffer of a given mipmap level for writing from main + // memory. + virtual bool Lock(int level, void** texture_data, int* pitch) { + return false; + } + + // Unlocks this texture and returns it to Stub control. + virtual bool Unlock(int level) { return true; } + private: DISALLOW_COPY_AND_ASSIGN(Texture2DStub); }; @@ -111,14 +124,16 @@ class TextureCUBEStub : public TextureCUBE { enable_render_surfaces) {} virtual ~TextureCUBEStub() {} - // Locks the image buffer of a given face and mipmap level for loading - // from main memory. - virtual bool Lock(CubeFace face, int level, void** texture_data) { - return true; - } - - // Unlocks the image buffer of a given face and mipmap level. - virtual bool Unlock(CubeFace face, int level) { return true; } + // Overridden from TextureCUBE + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch) { + }; // Returns a RenderSurface object associated with a given cube face and // mip_level of a texture. @@ -143,6 +158,17 @@ class TextureCUBEStub : public TextureCUBE { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Locks the image buffer of a given face and mipmap level for loading + // from main memory. + virtual bool Lock( + CubeFace face, int level, void** texture_data, int* pitch) { + return false; + } + + // Unlocks the image buffer of a given face and mipmap level. + virtual bool Unlock(CubeFace face, int level) { return true; } + private: DISALLOW_COPY_AND_ASSIGN(TextureCUBEStub); }; diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc index ecf00d6..4dbf330 100644 --- a/o3d/core/cross/bitmap.cc +++ b/o3d/core/cross/bitmap.cc @@ -44,6 +44,7 @@ #include "utils/cross/file_path_utils.h" #include "base/file_path.h" #include "base/file_util.h" +#include "core/cross/texture.h" #include "import/cross/raw_data.h" #include "import/cross/memory_buffer.h" #include "import/cross/memory_stream.h" @@ -92,9 +93,9 @@ unsigned int Bitmap::GetBufferSize(unsigned int width, switch (format) { case Texture::XRGB8: case Texture::ARGB8: - return 4 * sizeof(unsigned char) * pixels; // NOLINT + return 4 * sizeof(uint8) * pixels; // NOLINT case Texture::ABGR16F: - return 4 * sizeof(unsigned short) * pixels; // NOLINT + return 4 * sizeof(uint16) * pixels; // NOLINT case Texture::R32F: return sizeof(float) * pixels; // NOLINT case Texture::ABGR32F: @@ -164,18 +165,109 @@ void Bitmap::Allocate(Texture::Format format, AllocateData(); } -unsigned char *Bitmap::GetMipData(unsigned int level, - TextureCUBE::CubeFace face) const { +uint8 *Bitmap::GetMipData(unsigned int level) const { DCHECK(level < num_mipmaps_); + DCHECK(!is_cubemap_); if (!image_data_.get()) return NULL; - unsigned char *data = image_data_.get(); - if (is_cubemap_) { + uint8 *data = image_data_.get(); + return data + GetMipChainSize(width_, height_, format_, level); +} + +uint8 *Bitmap::GetFaceMipData(TextureCUBE::CubeFace face, + unsigned int level) const { + DCHECK(level < num_mipmaps_); + if (!image_data_.get()) return NULL; + uint8 *data = image_data_.get(); + if (is_cubemap()) { data += (face - TextureCUBE::FACE_POSITIVE_X) * GetMipChainSize(width_, height_, format_, num_mipmaps_); } return data + GetMipChainSize(width_, height_, format_, level); } +void Bitmap::SetRect( + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + DCHECK(src_data); + DCHECK(level < num_mipmaps() && level >= 0); + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + DCHECK(dst_left + src_width <= mip_width && + dst_top + src_height <= mip_height); + bool compressed = Texture::IsCompressedFormat(format()); + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + DCHECK(!compressed || entire_rect); + + uint8* dst = + GetMipData(level) + + Bitmap::GetMipChainSize(mip_width, dst_top, format(), 1) + + Bitmap::GetMipChainSize(dst_left, 1, format(), 1); + + const uint8* src = static_cast<const uint8*>(src_data); + if (!compressed) { + unsigned bytes_per_line = GetMipChainSize(src_width, 1, format(), 1); + int dst_pitch = Bitmap::GetMipChainSize(mip_width, 1, format(), 1); + for (unsigned yy = 0; yy < src_height; ++yy) { + memcpy(dst, src, bytes_per_line); + src += src_pitch; + dst += dst_pitch; + } + } else { + memcpy(dst, + src, + Bitmap::GetMipChainSize(mip_width, mip_height, format(), 1)); + } +} + +void Bitmap::SetFaceRect( + TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + DCHECK(src_data); + DCHECK(level < num_mipmaps() && level >= 0); + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + DCHECK(dst_left + src_width <= mip_width && + dst_top + src_height <= mip_height); + bool compressed = Texture::IsCompressedFormat(format()); + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + DCHECK(!compressed || entire_rect); + + uint8* dst = + GetFaceMipData(face, level) + + Bitmap::GetMipChainSize(mip_width, dst_top, format(), 1) + + Bitmap::GetMipChainSize(dst_left, 1, format(), 1); + + const uint8* src = static_cast<const uint8*>(src_data); + if (!compressed) { + unsigned bytes_per_line = GetMipChainSize(src_width, 1, format(), 1); + int dst_pitch = Bitmap::GetMipChainSize(mip_width, 1, format(), 1); + for (unsigned yy = 0; yy < src_height; ++yy) { + memcpy(dst, src, bytes_per_line); + src += src_pitch; + dst += dst_pitch; + } + } else { + memcpy(dst, + src, + Bitmap::GetMipChainSize(mip_width, mip_height, format(), 1)); + } +} + bool Bitmap::LoadFromStream(MemoryReadStream *stream, const String &filename, @@ -340,8 +432,8 @@ void Bitmap::DrawImage(Bitmap* src_img, return; } - unsigned char* src_img_data = src_img->image_data(); - unsigned char* dst_img_data = image_data(); + uint8* src_img_data = src_img->image_data(); + uint8* dst_img_data = image_data(); // crop part of image from src img, scale it in // bilinear interpolation fashion, and paste it @@ -349,7 +441,8 @@ void Bitmap::DrawImage(Bitmap* src_img, LanczosScale(src_img_data, src_x, src_y, src_width, src_height, src_img->width_, src_img->height_, - dst_img_data, dst_x, dst_y, + dst_img_data, width_, + dst_x, dst_y, dst_width, dst_height, width_, height_, components); } @@ -358,7 +451,7 @@ void Bitmap::LanczosScale(const uint8* src, int src_x, int src_y, int src_width, int src_height, int src_img_width, int src_img_height, - uint8* dest, + uint8* dest, int dest_pitch, int dest_x, int dest_y, int dest_width, int dest_height, int dest_img_width, int dest_img_height, @@ -379,23 +472,27 @@ void Bitmap::LanczosScale(const uint8* src, LanczosResize1D(src, src_x, src_y, src_width, src_height, src_img_width, src_img_height, - temp.get(), temp_x, temp_y, temp_width, + temp.get(), temp_img_width * components, + temp_x, temp_y, temp_width, temp_img_width, temp_img_height, true, components); // Scale the temp buffer vertically to get the final result. LanczosResize1D(temp.get(), temp_x, temp_y, temp_height, temp_width, temp_img_width, temp_img_height, - dest, dest_x, dest_y, dest_height, + dest, dest_pitch, + dest_x, dest_y, dest_height, dest_img_width, dest_img_height, false, components); } void Bitmap::LanczosResize1D(const uint8* src, int src_x, int src_y, int width, int height, int src_bmp_width, int src_bmp_height, - uint8* out, int dest_x, int dest_y, + uint8* out, int dest_pitch, + int dest_x, int dest_y, int nwidth, int dest_bmp_width, int dest_bmp_height, bool isWidth, int components) { + int pitch = dest_pitch / components; // calculate scale factor and init the weight array for lanczos filter. float scale = fabs(static_cast<float>(width) / nwidth); float support = kFilterSize * scale; @@ -459,7 +556,7 @@ void Bitmap::LanczosResize1D(const uint8* src, int src_x, int src_y, const uint8* inrow = src + ((src_bmp_height - (src_y + base_y) - 1) * src_bmp_width + src_x + xmin) * components; uint8* outpix = out + ((dest_bmp_height - (dest_y + base_y) - 1) * - dest_bmp_width + dest_x + x) * components; + pitch + dest_x + x) * components; int step = components; if (width < 0) step = -1 * step; @@ -475,7 +572,7 @@ void Bitmap::LanczosResize1D(const uint8* src, int src_x, int src_y, (src_y + xmin) - 1) * src_bmp_width) * components; uint8* outpix = out + (dest_x + base_y + (dest_bmp_height - - (dest_y + x) - 1) * dest_bmp_width) * components; + (dest_y + x) - 1) * pitch) * components; int step = src_bmp_width * components; if (width < 0) @@ -545,7 +642,7 @@ Bitmap::ImageFileType Bitmap::GetFileTypeFromMimeType(const char *mime_type) { return Bitmap::UNKNOWN; } -void Bitmap::XYZToXYZA(unsigned char *image_data, int pixel_count) { +void Bitmap::XYZToXYZA(uint8 *image_data, int pixel_count) { // We do this pixel by pixel, starting from the end to avoid overlapping // problems. for (int i = pixel_count - 1; i >= 0; --i) { @@ -556,9 +653,9 @@ void Bitmap::XYZToXYZA(unsigned char *image_data, int pixel_count) { } } -void Bitmap::RGBAToBGRA(unsigned char *image_data, int pixel_count) { +void Bitmap::RGBAToBGRA(uint8 *image_data, int pixel_count) { for (int i = 0; i < pixel_count; ++i) { - unsigned char c = image_data[i*4+0]; + uint8 c = image_data[i*4+0]; image_data[i*4+0] = image_data[i*4+2]; image_data[i*4+2] = c; } @@ -580,10 +677,10 @@ static void FilterTexel(unsigned int x, unsigned int y, unsigned int dst_width, unsigned int dst_height, - unsigned char *dst_data, + uint8 *dst_data, unsigned int src_width, unsigned int src_height, - const unsigned char *src_data, + const uint8 *src_data, unsigned int components) { DCHECK(Bitmap::CheckImageDimensions(src_width, src_height)); DCHECK(Bitmap::CheckImageDimensions(dst_width, dst_height)); @@ -660,7 +757,7 @@ static void FilterTexel(unsigned int x, uint64 value = accum[c] / (src_height * src_width); DCHECK_LE(value, 255u); dst_data[(y * dst_width + x) * components + c] = - static_cast<unsigned char>(value); + static_cast<uint8>(value); } } @@ -668,7 +765,7 @@ bool Bitmap::GenerateMipmaps(unsigned int base_width, unsigned int base_height, Texture::Format format, unsigned int num_mipmaps, - unsigned char *data) { + uint8 *data) { DCHECK(CheckImageDimensions(base_width, base_height)); unsigned int components = 0; switch (format) { @@ -687,13 +784,13 @@ bool Bitmap::GenerateMipmaps(unsigned int base_width, return false; } DCHECK_GE(std::max(base_width, base_height) >> (num_mipmaps-1), 1u); - unsigned char *mip_data = data; + uint8 *mip_data = data; unsigned int mip_width = base_width; unsigned int mip_height = base_height; for (unsigned int level = 1; level < num_mipmaps; ++level) { unsigned int prev_width = mip_width; unsigned int prev_height = mip_height; - unsigned char *prev_data = mip_data; + uint8 *prev_data = mip_data; mip_data += components * mip_width * mip_height; DCHECK_EQ(mip_data, data + GetMipChainSize(base_width, base_height, format, level)); @@ -733,74 +830,105 @@ bool Bitmap::GenerateMipmaps(unsigned int base_width, bool Bitmap::ScaleUpToPOT(unsigned int width, unsigned int height, Texture::Format format, - const unsigned char *src, - unsigned char *dst) { + const uint8 *src, + uint8 *dst, + int dst_pitch) { DCHECK(CheckImageDimensions(width, height)); unsigned int components = 0; switch (format) { case Texture::XRGB8: case Texture::ARGB8: - components = 4; - break; case Texture::ABGR16F: case Texture::R32F: case Texture::ABGR32F: + break; case Texture::DXT1: case Texture::DXT3: case Texture::DXT5: case Texture::UNKNOWN_FORMAT: - DLOG(ERROR) << "Up-scaling is not supported for format: " << format; + DCHECK(false); return false; } unsigned int pot_width = GetPOTSize(width); unsigned int pot_height = GetPOTSize(height); if (pot_width == width && pot_height == height && src == dst) return true; - return Scale(width, height, format, src, pot_width, pot_height, dst); + return Scale( + width, height, format, src, pot_width, pot_height, dst, dst_pitch); +} + +namespace { + +template <typename T> +void PointScale( + unsigned components, + const uint8* src, + unsigned src_width, + unsigned src_height, + uint8* dst, + int dst_pitch, + unsigned dst_width, + unsigned dst_height) { + const T* use_src = reinterpret_cast<const T*>(src); + T* use_dst = reinterpret_cast<T*>(dst); + int pitch = dst_pitch / sizeof(*use_src) / components; + // Start from the end to be able to do it in place. + for (unsigned int y = dst_height - 1; y < dst_height; --y) { + // max value for y is dst_height - 1, which makes : + // base_y = (2*dst_height - 1) * src_height / (2 * dst_height) + // which is < src_height. + unsigned int base_y = ((y * 2 + 1) * src_height) / (dst_height * 2); + DCHECK_LT(base_y, src_height); + for (unsigned int x = dst_width - 1; x < dst_width; --x) { + unsigned int base_x = ((x * 2 + 1) * src_width) / (dst_width * 2); + DCHECK_LT(base_x, src_width); + for (unsigned int c = 0; c < components; ++c) { + use_dst[(y * pitch + x) * components + c] = + use_src[(base_y * src_width + base_x) * components + c]; + } + } + } } +} // anonymous namespace + // Scales the image using basic point filtering. bool Bitmap::Scale(unsigned int src_width, unsigned int src_height, Texture::Format format, - const unsigned char *src, + const uint8 *src, unsigned int dst_width, unsigned int dst_height, - unsigned char *dst) { + uint8 *dst, + int dst_pitch) { DCHECK(CheckImageDimensions(src_width, src_height)); DCHECK(CheckImageDimensions(dst_width, dst_height)); - unsigned int components = 0; switch (format) { case Texture::XRGB8: - case Texture::ARGB8: - components = 4; + case Texture::ARGB8: { + PointScale<uint8>(4, src, src_width, src_height, + dst, dst_pitch, dst_width, dst_height); break; - case Texture::ABGR16F: + } + case Texture::ABGR16F: { + PointScale<uint16>(4, src, src_width, src_height, + dst, dst_pitch, dst_width, dst_height); + break; + } case Texture::R32F: - case Texture::ABGR32F: + case Texture::ABGR32F: { + PointScale<float>(format == Texture::R32F ? 1 : 4, + src, src_width, src_height, + dst, dst_pitch, dst_width, dst_height); + break; + } case Texture::DXT1: case Texture::DXT3: case Texture::DXT5: case Texture::UNKNOWN_FORMAT: - DLOG(ERROR) << "Up-scaling is not supported for format: " << format; + DCHECK(false); return false; } - // Start from the end to be able to do it in place. - for (unsigned int y = dst_height - 1; y < dst_height; --y) { - // max value for y is dst_height - 1, which makes : - // base_y = (2*dst_height - 1) * src_height / (2 * dst_height) - // which is < src_height. - unsigned int base_y = ((y * 2 + 1) * src_height) / (dst_height * 2); - DCHECK_LT(base_y, src_height); - for (unsigned int x = dst_width - 1; x < dst_width; --x) { - unsigned int base_x = ((x * 2 + 1) * src_width) / (dst_width * 2); - DCHECK_LT(base_x, src_width); - for (unsigned int c = 0; c < components; ++c) { - dst[(y * dst_width + x) * components + c] = - src[(base_y * src_width + base_x) * components + c]; - } - } - } return true; } @@ -915,10 +1043,10 @@ bool Bitmap::CheckAlphaIsOne() const { int faces = is_cubemap() ? 6 : 1; for (int face = 0; face < faces; ++face) { for (unsigned int level = 0; level < num_mipmaps(); ++level) { - const unsigned char *data = GetMipData( - level, - static_cast<TextureCUBE::CubeFace>(face)) + 3; - const unsigned char* end = data + Bitmap::GetBufferSize( + const uint8 *data = GetFaceMipData( + static_cast<TextureCUBE::CubeFace>(face), + level) + 3; + const uint8* end = data + Bitmap::GetBufferSize( std::max(1U, width() >> level), std::max(1U, height() >> level), format()); @@ -936,10 +1064,10 @@ bool Bitmap::CheckAlphaIsOne() const { int faces = is_cubemap() ? 6 : 1; for (int face = 0; face < faces; ++face) { for (unsigned int level = 0; level < num_mipmaps(); ++level) { - const unsigned char *data = GetMipData( - level, - static_cast<TextureCUBE::CubeFace>(face)); - const unsigned char* end = data + Bitmap::GetBufferSize( + const uint8 *data = GetFaceMipData( + static_cast<TextureCUBE::CubeFace>(face), + level); + const uint8* end = data + Bitmap::GetBufferSize( std::max(1U, width() >> level), std::max(1U, height() >> level), format()); @@ -965,10 +1093,10 @@ bool Bitmap::CheckAlphaIsOne() const { int faces = is_cubemap() ? 6 : 1; for (int face = 0; face < faces; ++face) { for (unsigned int level = 0; level < num_mipmaps(); ++level) { - const unsigned char *data = GetMipData( - level, - static_cast<TextureCUBE::CubeFace>(face)) + 6; - const unsigned char* end = data + Bitmap::GetBufferSize( + const uint8 *data = GetFaceMipData( + static_cast<TextureCUBE::CubeFace>(face), + level) + 6; + const uint8* end = data + Bitmap::GetBufferSize( std::max(1U, width() >> level), std::max(1U, height() >> level), format()); @@ -988,10 +1116,10 @@ bool Bitmap::CheckAlphaIsOne() const { int faces = is_cubemap() ? 6 : 1; for (int face = 0; face < faces; ++face) { for (unsigned int level = 0; level < num_mipmaps(); ++level) { - const unsigned char* data = GetMipData( - level, - static_cast<TextureCUBE::CubeFace>(face)) + 12; - const unsigned char* end = data + Bitmap::GetBufferSize( + const uint8* data = GetFaceMipData( + static_cast<TextureCUBE::CubeFace>(face), + level) + 12; + const uint8* end = data + Bitmap::GetBufferSize( std::max(1U, width() >> level), std::max(1U, height() >> level), format()); diff --git a/o3d/core/cross/bitmap.h b/o3d/core/cross/bitmap.h index bebe6c6..87fab79 100644 --- a/o3d/core/cross/bitmap.h +++ b/o3d/core/cross/bitmap.h @@ -86,6 +86,18 @@ class Bitmap : public ParamObject { width <= kMaxImageDimension && height < kMaxImageDimension;
}
+ // Computes the width and height of a mip.
+ static void GetMipSize(int level,
+ unsigned width,
+ unsigned height,
+ unsigned* mip_width,
+ unsigned* mip_height) {
+ unsigned w = width >> level;
+ unsigned h = height >> level;
+ *mip_width = w > 0 ? w : 1u;
+ *mip_height = h > 0 ? h : 1u;
+ }
+
// Creates a copy of a bitmap, copying the pixels as well.
// Parameters:
// source: the source bitmap.
@@ -125,7 +137,7 @@ class Bitmap : public ParamObject { // Allocates a bitmap with initialized parameters.
// data is zero-initialized
void AllocateData() {
- image_data_.reset(new unsigned char[GetTotalSize()]);
+ image_data_.reset(new uint8[GetTotalSize()]);
memset(image_data_.get(), 0, GetTotalSize());
}
@@ -134,6 +146,49 @@ class Bitmap : public ParamObject { image_data_.reset(NULL);
}
+ // Sets a rectangular region of this bitmap.
+ // If the bitmap is a DXT format, the only acceptable values
+ // for left, top, width and height are 0, 0, bitmap->width, bitmap->height
+ //
+ // Parameters:
+ // level: The mipmap level to modify
+ // dst_left: The left edge of the rectangular area to modify.
+ // dst_top: The top edge of the rectangular area to modify.
+ // width: The width of the rectangular area to modify.
+ // height: The of the rectangular area to modify.
+ // src_data: The source pixels.
+ // src_pitch: If the format is uncompressed this is the number of bytes
+ // per row of pixels. If compressed this value is unused.
+ void SetRect(int level,
+ unsigned dst_left,
+ unsigned dst_top,
+ unsigned width,
+ unsigned height,
+ const void* src_data,
+ int src_pitch);
+
+ // Sets a rectangular region of this bitmap.
+ // If the bitmap is a DXT format, the only acceptable values
+ // for left, top, width and height are 0, 0, bitmap->width, bitmap->height
+ //
+ // Parameters:
+ // level: The mipmap level to modify
+ // dst_left: The left edge of the rectangular area to modify.
+ // dst_top: The top edge of the rectangular area to modify.
+ // width: The width of the rectangular area to modify.
+ // height: The of the rectangular area to modify.
+ // src_data: The source pixels.
+ // src_pitch: If the format is uncompressed this is the number of bytes
+ // per row of pixels. If compressed this value is unused.
+ void SetFaceRect(TextureCUBE::CubeFace face,
+ int level,
+ unsigned dst_left,
+ unsigned dst_top,
+ unsigned width,
+ unsigned height,
+ const void* src_data,
+ int src_pitch);
+
// Gets the total size of the bitmap data, counting all faces and mip levels.
unsigned int GetTotalSize() {
return (is_cubemap_ ? 6 : 1) *
@@ -145,15 +200,19 @@ class Bitmap : public ParamObject { unsigned int height,
Texture::Format format);
+ // Gets the image data for a given mip-map level.
+ // Parameters:
+ // level: mip level to get.
+ uint8 *GetMipData(unsigned int level) const;
+
// Gets the image data for a given mip-map level and cube map face.
// Parameters:
+ // face: face of cube to get.
// level: mip level to get.
- // face: face of cube to get. This parameter is ignored if
- // this bitmap is not a cube map.
- unsigned char *GetMipData(unsigned int level,
- TextureCUBE::CubeFace face) const;
+ uint8 *GetFaceMipData(TextureCUBE::CubeFace face,
+ unsigned int level) const;
- unsigned char *image_data() const { return image_data_.get(); }
+ uint8 *image_data() const { return image_data_.get(); }
Texture::Format format() const { return format_; }
unsigned int width() const { return width_; }
unsigned int height() const { return height_; }
@@ -281,7 +340,7 @@ class Bitmap : public ParamObject { int src_x, int src_y,
int src_width, int src_height,
int src_img_width, int src_img_height,
- uint8* dest,
+ uint8* dest, int dest_pitch,
int dest_x, int dest_y,
int dest_width, int dest_height,
int dest_img_width, int dest_img_height,
@@ -295,10 +354,10 @@ class Bitmap : public ParamObject { // Adds filler alpha byte (0xff) after every pixel. Assumes buffer was
// allocated with enough storage)
// can convert RGB -> RGBA, BGR -> BGRA, etc.
- static void XYZToXYZA(unsigned char *image_data, int pixel_count);
+ static void XYZToXYZA(uint8 *image_data, int pixel_count);
// Swaps Red and Blue components in the image.
- static void RGBAToBGRA(unsigned char *image_data, int pixel_count);
+ static void RGBAToBGRA(uint8 *image_data, int pixel_count);
// Gets the number of mip-maps required for a full chain starting at
// width x height.
@@ -334,7 +393,7 @@ class Bitmap : public ParamObject { unsigned int base_height,
Texture::Format format,
unsigned int num_mipmaps,
- unsigned char *data);
+ uint8 *data);
// Scales an image up to power-of-two textures, using point filtering.
// NOTE: this doesn't work for DXTC, or floating-point images.
@@ -345,13 +404,15 @@ class Bitmap : public ParamObject { // format: the format of the data.
// src: the data containing the source data of the original image.
// dst: a buffer with enough space for the power-of-two version. Pixels are
- // written from the end to the beginning so dst can be the same buffer as
- // src.
+ // written from the end to the beginning so dst can be the same buffer
+ // as src.
+ // dst_pitch: Number of bytes across 1 row of pixels.
static bool ScaleUpToPOT(unsigned int width,
unsigned int height,
Texture::Format format,
- const unsigned char *src,
- unsigned char *dst);
+ const uint8 *src,
+ uint8 *dst,
+ int dst_pitch);
// Scales an image to an arbitrary size, using point filtering.
// NOTE: this doesn't work for DXTC, or floating-point images.
@@ -364,15 +425,17 @@ class Bitmap : public ParamObject { // dst_width: the width of the target image.
// dst_height: the height of the target image.
// dst: a buffer with enough space for the target version. Pixels are
- // written from the end to the beginning so dst can be the same buffer as
- // src if the transformation is an upscaling.
+ // written from the end to the beginning so dst can be the same buffer
+ // as src if the transformation is an upscaling.
+ // dst_pitch: Number of bytes across 1 row of pixels.
static bool Scale(unsigned int src_width,
unsigned int src_height,
Texture::Format format,
- const unsigned char *src,
+ const uint8 *src,
unsigned int dst_width,
unsigned int dst_height,
- unsigned char *dst);
+ uint8 *dst,
+ int dst_pitch);
// adjust start points and boundaries when using DrawImage data
// in bitmap and textures.
@@ -455,7 +518,8 @@ class Bitmap : public ParamObject { static void LanczosResize1D(const uint8* src, int src_x, int src_y,
int width, int height,
int src_bmp_width, int src_bmp_height,
- uint8* dest, int dest_x, int dest_y,
+ uint8* dest, int dest_pitch,
+ int dest_x, int dest_y,
int nwidth,
int dest_bmp_width, int dest_bmp_height,
bool isWidth, int components);
diff --git a/o3d/core/cross/bitmap_test.cc b/o3d/core/cross/bitmap_test.cc index 230a589..3fe8be0 100644 --- a/o3d/core/cross/bitmap_test.cc +++ b/o3d/core/cross/bitmap_test.cc @@ -570,7 +570,7 @@ TEST_F(BitmapTest, LoadDDSFileDXT1Mipmap) { EXPECT_EQ(256, bitmap->height()); EXPECT_EQ(9, bitmap->num_mipmaps()); for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + EXPECT_TRUE(bitmap->GetMipData(i) != NULL); } EXPECT_TRUE(TestBitmapData(*bitmap, kdxt1_256x256_mipmap)); } @@ -605,7 +605,7 @@ TEST_F(BitmapTest, LoadDDSFileDXT3Mipmap) { EXPECT_EQ(256, bitmap->height()); EXPECT_EQ(9, bitmap->num_mipmaps()); for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + EXPECT_TRUE(bitmap->GetMipData(i) != NULL); } EXPECT_TRUE(TestBitmapData(*bitmap, kdxt3_256x256_mipmap)); } @@ -640,7 +640,7 @@ TEST_F(BitmapTest, LoadDDSFileDXT5Mipmap) { EXPECT_EQ(256, bitmap->height()); EXPECT_EQ(9, bitmap->num_mipmaps()); for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + EXPECT_TRUE(bitmap->GetMipData(i) != NULL); } EXPECT_TRUE(TestBitmapData(*bitmap, kdxt5_256x256_mipmap)); } @@ -844,13 +844,14 @@ TEST_F(BitmapTest, ScaleUpToPOT) { scoped_array<unsigned char> data(new unsigned char[dst_size]); ASSERT_TRUE(data.get() != NULL); // Check that scaling works when source and destination don't alias - Bitmap::ScaleUpToPOT(kWidth, kHeight, format, kScaleUPDataNPOT, data.get()); + Bitmap::ScaleUpToPOT(kWidth, kHeight, format, kScaleUPDataNPOT, data.get(), + 4 * 4); EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size)); // Check that scaling works when source and destination do alias memset(data.get(), 0, dst_size); memcpy(data.get(), kScaleUPDataNPOT, src_size); - Bitmap::ScaleUpToPOT(kWidth, kHeight, format, data.get(), data.get()); + Bitmap::ScaleUpToPOT(kWidth, kHeight, format, data.get(), data.get(), 4 * 4); EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size)); } diff --git a/o3d/core/cross/buffer.cc b/o3d/core/cross/buffer.cc index ba2eb0a..3cc5afb 100644 --- a/o3d/core/cross/buffer.cc +++ b/o3d/core/cross/buffer.cc @@ -35,6 +35,7 @@ #include "core/cross/precompile.h" #include "core/cross/buffer.h" #include "core/cross/client_info.h" +#include "core/cross/pointer_utils.h" #include "core/cross/renderer.h" #include "core/cross/features.h" #include "core/cross/error.h" diff --git a/o3d/core/cross/canvas.cc b/o3d/core/cross/canvas.cc index 26fabbd..7dccb2d 100644 --- a/o3d/core/cross/canvas.cc +++ b/o3d/core/cross/canvas.cc @@ -154,9 +154,8 @@ void Canvas::DrawBitmap(Texture2D* texture2d, return; } - void* texture_data; - Texture2DLockHelper lock_helper(texture2d, 0); - texture_data = lock_helper.GetData(); + Texture2D::LockHelper lock_helper(texture2d, 0); + uint8* texture_data = lock_helper.GetDataAs<uint8>(); if (!texture_data) { return; } @@ -177,7 +176,11 @@ void Canvas::DrawBitmap(Texture2D* texture2d, unsigned char* bitmap_data = static_cast<unsigned char*>( bitmap.getPixels()); - memcpy(bitmap_data, texture_data, width * height * 4); + for (int yy = 0; yy < height; ++yy) { + memcpy(bitmap_data + yy * width, + texture_data + yy * lock_helper.pitch(), + width * 4); + } if (texture2d->format() == Texture2D::XRGB8) { // Set the alpha to 1 @@ -242,13 +245,8 @@ bool Canvas::CopyToTexture(Texture2D* texture_2d) { int width = sk_bitmap_.width(); int height = sk_bitmap_.height(); - Texture2DLockHelper lock_helper_0(texture_2d, 0); - void* texture_data = lock_helper_0.GetData(); - if (!texture_data) { - return false; - } - - memcpy(texture_data, sk_bitmap_.getPixels(), width * height * 4); + texture_2d->SetRect(0, 0, 0, width, height, + sk_bitmap_.getPixels(), width * 4); // Fill in all the mipmap levels of the texture by drawing scaled down // versions of the canvas bitmap contents. @@ -256,21 +254,17 @@ bool Canvas::CopyToTexture(Texture2D* texture_2d) { int levels = texture_2d->levels(); for (int i = 1; (!levels && width > 1 && height > 1) || i < levels; i++) { - width = width >> 1; - height = height >> 1; + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - Texture2DLockHelper lock_helper_n(texture_2d, i); - texture_data = lock_helper_n.GetData(); - if (!texture_data) { - return false; - } - - bitmap.setPixels(texture_data); + scoped_array<uint8> buffer(new uint8[width * height * 4]); + bitmap.setPixels(buffer.get()); SkCanvas canvas(bitmap); SkScalar scaleFactor = SkScalarDiv(SK_Scalar1, SkIntToScalar(1 << i)); canvas.scale(scaleFactor, scaleFactor); canvas.drawBitmap(sk_bitmap_, 0, 0); + texture_2d->SetRect(i, 0, 0, width, height, bitmap.getPixels(), width * 4); } return true; diff --git a/o3d/core/cross/command_buffer/texture_cb.cc b/o3d/core/cross/command_buffer/texture_cb.cc index 3d530a7..ff03c6a 100644 --- a/o3d/core/cross/command_buffer/texture_cb.cc +++ b/o3d/core/cross/command_buffer/texture_cb.cc @@ -110,7 +110,7 @@ void UpdateResourceFromBitmap(RendererCB *renderer, CommandBufferHelper *helper = renderer->helper(); unsigned int mip_width = std::max(1U, bitmap.width() >> level); unsigned int mip_height = std::max(1U, bitmap.height() >> level); - unsigned char *mip_data = bitmap.GetMipData(level, face); + unsigned char *mip_data = bitmap.GetFaceMipData(face, level); unsigned int mip_size = Bitmap::GetBufferSize(mip_width, mip_height, bitmap.format()); if (resize_to_pot) { @@ -130,7 +130,8 @@ void UpdateResourceFromBitmap(RendererCB *renderer, // for the NPOT->POT case. DCHECK(buffer); Bitmap::Scale(mip_width, mip_height, bitmap.format(), mip_data, - pot_width, pot_height, buffer); + pot_width, pot_height, buffer, + Bitmap::GetMipChainSize(pot_width, 1, bitmap.format(), 1)); mip_width = pot_width; mip_height = pot_height; mip_size = pot_size; @@ -206,7 +207,7 @@ void CopyBackResourceToBitmap(RendererCB *renderer, args[9].value_uint32 = allocator->GetOffset(buffer); helper->AddCommand(command_buffer::GET_TEXTURE_DATA, 10, args); helper->Finish(); - memcpy(bitmap.GetMipData(level, face), buffer, mip_size); + memcpy(bitmap.GetFaceMipData(face, level), buffer, mip_size); allocator->Free(buffer); } @@ -313,9 +314,20 @@ Texture2DCB* Texture2DCB::Create(ServiceLocator* service_locator, return texture; } +void Texture2DCB::SetRect(int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + // TODO(gman): Someone needs to implement this. + DCHECK(false); +} + // Locks the given mipmap level of this texture for loading from main memory, // and returns a pointer to the buffer. -bool Texture2DCB::Lock(int level, void** data) { +bool Texture2DCB::Lock(int level, void** data, int* pitch) { if (level >= levels() || level < 0) { O3D_ERROR(service_locator()) << "Trying to lock inexistent level " << level @@ -332,7 +344,18 @@ bool Texture2DCB::Lock(int level, void** data) { DCHECK_EQ(has_levels_, 0); backing_bitmap_->Allocate(format(), width(), height(), levels(), false); } - *data = backing_bitmap_->GetMipData(level, TextureCUBE::FACE_POSITIVE_X); + *data = backing_bitmap_->GetMipData(level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + if (IsCompressed()) { + *pitch = Bitmap::GetMipChainSize(mip_width, 1,format(), 1); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } if (!HasLevel(level)) { DCHECK(!resize_to_pot_); DCHECK_EQ(backing_bitmap_->width(), width()); @@ -489,9 +512,21 @@ TextureCUBECB* TextureCUBECB::Create(ServiceLocator* service_locator, return texture; } +void TextureCUBECB::SetRect(TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + // TODO(gman): Someone needs to implement this. + DCHECK(false); +} + // Locks the given face and mipmap level of this texture for loading from // main memory, and returns a pointer to the buffer. -bool TextureCUBECB::Lock(CubeFace face, int level, void** data) { +bool TextureCUBECB::Lock(CubeFace face, int level, void** data, int* pitch) { if (level >= levels() || level < 0) { O3D_ERROR(service_locator()) << "Trying to lock inexistent level " << level @@ -512,7 +547,19 @@ bool TextureCUBECB::Lock(CubeFace face, int level, void** data) { backing_bitmap_->Allocate(format(), edge_length(), edge_length(), levels(), true); } - *data = backing_bitmap_->GetMipData(level, face); + *data = backing_bitmap_->GetFaceMipData(face, level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, edge_length(), edge_length(), + &mip_width, &mip_height); + if (IsCompressed()) { + *pitch = Bitmap::GetMipChainSize(mip_width, 1,format(), 1); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } if (!HasLevel(level, face)) { // TODO: add some API so we don't have to copy back the data if we // will rewrite it all. diff --git a/o3d/core/cross/command_buffer/texture_cb.h b/o3d/core/cross/command_buffer/texture_cb.h index ee525ac..e000a8e 100644 --- a/o3d/core/cross/command_buffer/texture_cb.h +++ b/o3d/core/cross/command_buffer/texture_cb.h @@ -64,12 +64,14 @@ class Texture2DCB : public Texture2D { Bitmap *bitmap, bool enable_render_surfaces); - // Locks the image buffer of a given mipmap level for writing from main - // memory. - virtual bool Lock(int level, void** texture_data); - - // Unlocks this texture and returns it to OpenCB control. - virtual bool Unlock(int level); + // Overridden from Texture2D + virtual void SetRect(int level, + unsigned left, + unsigned top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); // Returns a RenderSurface object associated with a mip_level of a texture. // Parameters: @@ -91,6 +93,12 @@ class Texture2DCB : public Texture2D { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from Texture2D + virtual bool Lock(int level, void** texture_data, int* pitch); + + // Overridden from Texture2D + virtual bool Unlock(int level); private: // Initializes the Texture2DCB from a preexisting OpenCB texture handle @@ -133,12 +141,15 @@ class TextureCUBECB : public TextureCUBE { Bitmap *bitmap, bool enable_render_surfaces); - // Locks the image buffer of a given face and mipmap level for loading - // from main memory. - virtual bool Lock(CubeFace face, int level, void** texture_data); - - // Unlocks the image buffer of a given face and mipmap level. - virtual bool Unlock(CubeFace face, int level); + // Overridden from TextureCUBE + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); // Returns a RenderSurface object associated with a given cube face and // mip_level of a texture. @@ -164,6 +175,14 @@ class TextureCUBECB : public TextureCUBE { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from TextureCUBE + virtual bool Lock( + CubeFace face, int level, void** texture_data, int* pitch); + + // Overridden from TextureCUBE + virtual bool Unlock(CubeFace face, int level); + private: // Creates a texture from a pre-existing texture resource. TextureCUBECB(ServiceLocator* service_locator, diff --git a/o3d/core/cross/fake_vertex_source.cc b/o3d/core/cross/fake_vertex_source.cc index c8e0b6f..c2bec2c 100644 --- a/o3d/core/cross/fake_vertex_source.cc +++ b/o3d/core/cross/fake_vertex_source.cc @@ -35,6 +35,7 @@ #include "core/cross/precompile.h" #include "core/cross/fake_vertex_source.h" +#include "core/cross/pointer_utils.h" #include "core/cross/buffer.h" namespace o3d { diff --git a/o3d/core/cross/field.cc b/o3d/core/cross/field.cc index 1ccbfde..79115d6 100644 --- a/o3d/core/cross/field.cc +++ b/o3d/core/cross/field.cc @@ -38,6 +38,7 @@ #include "core/cross/field.h" #include "core/cross/error.h" #include "core/cross/buffer.h" +#include "core/cross/pointer_utils.h" #include "core/cross/types.h" #include "core/cross/renderer.h" #include "import/cross/memory_stream.h" diff --git a/o3d/core/cross/field.h b/o3d/core/cross/field.h index 212b003..2a64098 100644 --- a/o3d/core/cross/field.h +++ b/o3d/core/cross/field.h @@ -42,20 +42,6 @@ namespace o3d { -// Adds an arbitrary byte offset to a typed pointer. -template <typename T> -T AddPointerOffset(T pointer, unsigned offset) { - return reinterpret_cast<T>( - const_cast<uint8*>(reinterpret_cast<const uint8*>(pointer) + offset)); -} - -// Creates a typed pointer from a void pointer and an offset. -template <typename T> -T PointerFromVoidPointer(void* pointer, unsigned offset) { - return reinterpret_cast<T>( - const_cast<uint8*>(reinterpret_cast<const uint8*>(pointer) + offset)); -} - class Buffer; class MemoryReadStream; diff --git a/o3d/core/cross/gl/texture_gl.cc b/o3d/core/cross/gl/texture_gl.cc index f904d3d..ff9588f 100644 --- a/o3d/core/cross/gl/texture_gl.cc +++ b/o3d/core/cross/gl/texture_gl.cc @@ -36,6 +36,7 @@ #include "core/cross/precompile.h" #include "core/cross/error.h" #include "core/cross/types.h" +#include "core/cross/pointer_utils.h" #include "core/cross/gl/renderer_gl.h" #include "core/cross/gl/render_surface_gl.h" #include "core/cross/gl/texture_gl.h" @@ -154,11 +155,12 @@ static bool UpdateGLImageFromBitmap(GLenum target, DCHECK(bitmap.image_data()); unsigned int mip_width = std::max(1U, bitmap.width() >> level); unsigned int mip_height = std::max(1U, bitmap.height() >> level); - const unsigned char *mip_data = bitmap.GetMipData(level, face); + const unsigned char *mip_data = bitmap.GetFaceMipData(face, level); unsigned int mip_size = Bitmap::GetBufferSize(mip_width, mip_height, bitmap.format()); scoped_array<unsigned char> temp_data; if (resize_to_pot) { + DCHECK(!Texture::IsCompressedFormat(bitmap.format())); unsigned int pot_width = std::max(1U, Bitmap::GetPOTSize(bitmap.width()) >> level); unsigned int pot_height = @@ -167,7 +169,8 @@ static bool UpdateGLImageFromBitmap(GLenum target, bitmap.format()); temp_data.reset(new unsigned char[pot_size]); Bitmap::Scale(mip_width, mip_height, bitmap.format(), mip_data, - pot_width, pot_height, temp_data.get()); + pot_width, pot_height, temp_data.get(), + Bitmap::GetMipChainSize(pot_width, 1, bitmap.format(), 1)); mip_width = pot_width; mip_height = pot_height; mip_size = pot_size; @@ -218,7 +221,7 @@ static bool CreateGLImagesAndUpload(GLenum target, for (unsigned int i = 0; i < bitmap.num_mipmaps(); ++i) { // Upload pixels directly if we can, otherwise it will be done with // UpdateGLImageFromBitmap afterwards. - unsigned char *data = resize_to_pot ? NULL : bitmap.GetMipData(i, face); + unsigned char *data = resize_to_pot ? NULL : bitmap.GetFaceMipData(face, i); if (format) { glTexImage2D(target, i, internal_format, mip_width, mip_height, @@ -376,9 +379,96 @@ Texture2DGL::~Texture2DGL() { CHECK_GL_ERROR(); } +void Texture2DGL::SetRect(int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect on non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + DCHECK(backing_bitmap_->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap_->SetRect( + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(level); + } else { + glBindTexture(GL_TEXTURE_2D, gl_texture_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), &gl_internal_format, + &gl_data_type); + if (gl_format) { + if (src_pitch == Bitmap::GetMipChainSize(src_width, 1, format(), 1)) { + glTexSubImage2D(GL_TEXTURE_2D, level, + dst_left, dst_top, + src_width, src_height, + gl_format, + gl_data_type, + src_data); + } else { + for (int yy = 0; yy < src_height; ++yy) { + glTexSubImage2D(GL_TEXTURE_2D, level, + dst_left, dst_top + yy, + src_width, 1, + gl_format, + gl_data_type, + src_data); + src_data = AddPointerOffset<const void*>(src_data, src_pitch); + } + } + } else { + glCompressedTexSubImage2D( + GL_TEXTURE_2D, level, 0, 0, src_width, src_height, + gl_internal_format, + Bitmap::GetMipChainSize(src_width, src_height, format(), 1), + src_data); + } + } +} + // Locks the given mipmap level of this texture for loading from main memory, // and returns a pointer to the buffer. -bool Texture2DGL::Lock(int level, void** data) { +bool Texture2DGL::Lock(int level, void** data, int* pitch) { + DCHECK(data); + DCHECK(pitch); DLOG(INFO) << "Texture2DGL Lock"; renderer_->MakeCurrentLazy(); if (level >= levels() || level < 0) { @@ -397,7 +487,18 @@ bool Texture2DGL::Lock(int level, void** data) { DCHECK_EQ(has_levels_, 0u); backing_bitmap_->Allocate(format(), width(), height(), levels(), false); } - *data = backing_bitmap_->GetMipData(level, TextureCUBE::FACE_POSITIVE_X); + *data = backing_bitmap_->GetMipData(level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + if (IsCompressed()) { + *pitch = Bitmap::GetMipChainSize(mip_width, 1,format(), 1); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } if (!HasLevel(level)) { // TODO: add some API so we don't have to copy back the data if we // will rewrite it all. @@ -651,9 +752,104 @@ RenderSurface::Ref TextureCUBEGL::GetRenderSurface(TextureCUBE::CubeFace face, return render_surface; } +void TextureCUBEGL::SetRect(TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (static_cast<int>(face) < 0 || static_cast<int>(face) >= NUMBER_OF_FACES) { + O3D_ERROR(service_locator()) + << "Trying to SetRect invalid face " << face << " on Texture \"" + << name() << "\""; + return; + } + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize( + level, edge_length(), edge_length(), &mip_width, &mip_height); + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + DCHECK(backing_bitmap_->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap_->SetFaceRect(face, + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(level, face); + } else { + // TODO(gman): Should this bind be using a FACE id? + glBindTexture(GL_TEXTURE_2D, gl_texture_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), &gl_internal_format, + &gl_data_type); + int gl_face = kCubemapFaceList[face]; + if (gl_format) { + if (src_pitch == Bitmap::GetMipChainSize(src_width, 1, format(), 1)) { + glTexSubImage2D(gl_face, level, + dst_left, dst_top, + src_width, src_height, + gl_format, + gl_data_type, + src_data); + } else { + for (int yy = 0; yy < src_height; ++yy) { + glTexSubImage2D(gl_face, level, + dst_left, dst_top + yy, + src_width, 1, + gl_format, + gl_data_type, + src_data); + src_data = AddPointerOffset<const void*>(src_data, src_pitch); + } + } + } else { + glCompressedTexSubImage2D( + GL_TEXTURE_2D, level, 0, 0, src_width, src_height, + gl_internal_format, + Bitmap::GetMipChainSize(src_width, src_height, format(), 1), + src_data); + } + } +} + // Locks the given face and mipmap level of this texture for loading from // main memory, and returns a pointer to the buffer. -bool TextureCUBEGL::Lock(CubeFace face, int level, void** data) { +bool TextureCUBEGL::Lock(CubeFace face, int level, void** data, int* pitch) { DLOG(INFO) << "TextureCUBEGL Lock"; renderer_->MakeCurrentLazy(); if (level >= levels() || level < 0) { @@ -676,7 +872,19 @@ bool TextureCUBEGL::Lock(CubeFace face, int level, void** data) { backing_bitmap_->Allocate(format(), edge_length(), edge_length(), levels(), true); } - *data = backing_bitmap_->GetMipData(level, face); + *data = backing_bitmap_->GetFaceMipData(face, level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, edge_length(), edge_length(), + &mip_width, &mip_height); + if (IsCompressed()) { + *pitch = Bitmap::GetMipChainSize(mip_width, 1,format(), 1); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } GLenum gl_target = kCubemapFaceList[face]; if (!HasLevel(level, face)) { // TODO: add some API so we don't have to copy back the data if we diff --git a/o3d/core/cross/gl/texture_gl.h b/o3d/core/cross/gl/texture_gl.h index a647d47..14d0be7 100644 --- a/o3d/core/cross/gl/texture_gl.h +++ b/o3d/core/cross/gl/texture_gl.h @@ -68,6 +68,15 @@ class Texture2DGL : public Texture2D { virtual ~Texture2DGL(); + // Overridden from Texture2D + virtual void SetRect(int level, + unsigned left, + unsigned top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); + // Creates a new Texture2DGL with the given specs. If the GL texture // creation fails then it returns NULL otherwise it returns a pointer to the // newly created Texture object. @@ -76,13 +85,6 @@ class Texture2DGL : public Texture2D { Bitmap *bitmap, bool enable_render_surfaces); - // Locks the image buffer of a given mipmap level for writing from main - // memory. - virtual bool Lock(int level, void** texture_data); - - // Unlocks this texture and returns it to OpenGL control. - virtual bool Unlock(int level); - // Returns the implementation-specific texture handle for this texture. void* GetTextureHandle() const { return reinterpret_cast<void*>(gl_texture_); @@ -103,6 +105,13 @@ class Texture2DGL : public Texture2D { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from Texture2D + virtual bool Lock(int level, void** texture_data, int* pitch); + + // Overridden from Texture2D + virtual bool Unlock(int level); + private: // Initializes the Texture2DGL from a preexisting OpenGL texture handle // and raw Bitmap data. @@ -151,12 +160,15 @@ class TextureCUBEGL : public TextureCUBE { Bitmap *bitmap, bool enable_render_surfaces); - // Locks the image buffer of a given face and mipmap level for loading - // from main memory. - virtual bool Lock(CubeFace face, int level, void** texture_data); - - // Unlocks the image buffer of a given face and mipmap level. - virtual bool Unlock(CubeFace face, int level); + // Overridden from TextureCUBE + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); // Returns the implementation-specific texture handle for this texture. virtual void* GetTextureHandle() const { @@ -182,6 +194,13 @@ class TextureCUBEGL : public TextureCUBE { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from TextureCUBE + virtual bool Lock(CubeFace face, int level, void** texture_data, int* pitch); + + // Overridden from TextureCUBE + virtual bool Unlock(CubeFace face, int level); + private: // Creates a texture from a pre-existing GL texture object. TextureCUBEGL(ServiceLocator* service_locator, diff --git a/o3d/core/cross/message_queue.cc b/o3d/core/cross/message_queue.cc index bb5f8da..9e0f579 100644 --- a/o3d/core/cross/message_queue.cc +++ b/o3d/core/cross/message_queue.cc @@ -34,14 +34,14 @@ // the communication of external code (clients) with O3D (server) via the // NativeClient IMC library. +#include "core/cross/precompile.h" #if defined(OS_MACOSX) | defined(OS_LINUX) #include <sys/types.h> #include <unistd.h> #endif - -#include "core/cross/precompile.h" #include "core/cross/message_queue.h" #include "core/cross/object_manager.h" +#include "core/cross/bitmap.h" #include "core/cross/texture.h" #include "core/cross/error.h" @@ -146,10 +146,10 @@ MessageQueue::MessageQueue(ServiceLocator* service_locator) // browsers running o3d at the same time as well as a count to // distinguish between multiple instances of o3d running in the same // browser. - base::snprintf(server_socket_address_.path, - sizeof(server_socket_address_.path), - "%s%d%d", kServerSocketAddressPrefix, (proc_id & 0xFFFF), - next_message_queue_id_); + ::base::snprintf(server_socket_address_.path, + sizeof(server_socket_address_.path), + "%s%d%d", kServerSocketAddressPrefix, (proc_id & 0xFFFF), + next_message_queue_id_); next_message_queue_id_++; } @@ -521,9 +521,8 @@ bool MessageQueue::ProcessAllocateSharedMemory(ConnectedClient* client, // bitmap using data stored in a shared memory region. The client sends the // id of the shared memory region, an offset in that region, the id of the // Texture object, the level to be modified and the number of bytes to copy. -// TODO: Check that the number of bytes copied are equal to the size -// occupied by that level in the texture. This is essentially asynchronous as -// the client will not receive a response back from the server +// This is essentially asynchronous as the client will not receive a response +// back from the server bool MessageQueue::ProcessUpdateTexture2D(ConnectedClient* client, int message_length, MessageId message_id, @@ -591,20 +590,25 @@ bool MessageQueue::ProcessUpdateTexture2D(ConnectedClient* client, return false; } - void* texture_data; - bool locked = texture_object->Lock(level, &texture_data); - if (!locked) { - O3D_ERROR(service_locator_) << "Failed to lock texture"; + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, texture_object->width(), texture_object->height(), + &mip_width, &mip_height); + + + if (number_of_bytes != Bitmap::GetMipChainSize(mip_width, mip_height, + texture_object->format(), 1)) { + O3D_ERROR(service_locator_) + << "texture_size does not match size of texture level (" + << offset << " + " << number_of_bytes << " > " << info->size_; SendBooleanResponse(client->client_handle(), false); return false; } - // TODO: verify that we don't end up writing past the end of the - // memory allocated for that texture level. void *target_address = static_cast<char*>(info->mapped_address_) + offset; - memcpy(texture_data, target_address, number_of_bytes); - - texture_object->Unlock(level); + texture_object->SetRect( + level, 0, 0, mip_width, mip_height, target_address, + Bitmap::GetMipChainSize(mip_width, 1, texture_object->format(), 1)); SendBooleanResponse(client->client_handle(), true); return true; diff --git a/o3d/core/cross/pointer_utils.h b/o3d/core/cross/pointer_utils.h new file mode 100644 index 0000000..1cbd610 --- /dev/null +++ b/o3d/core/cross/pointer_utils.h @@ -0,0 +1,58 @@ +/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file contains the declaration for a few templated function to help with
+// pointers.
+
+#ifndef O3D_CORE_CROSS_POINTER_UTILS_H_
+#define O3D_CORE_CROSS_POINTER_UTILS_H_
+
+#include "core/cross/types.h"
+
+namespace o3d {
+
+// Adds an arbitrary byte offset to a typed pointer.
+template <typename T>
+T AddPointerOffset(T pointer, unsigned offset) {
+ return reinterpret_cast<T>(
+ const_cast<uint8*>(reinterpret_cast<const uint8*>(pointer) + offset));
+}
+
+// Creates a typed pointer from a void pointer and an offset.
+template <typename T>
+T PointerFromVoidPointer(void* pointer, unsigned offset) {
+ return reinterpret_cast<T>(
+ const_cast<uint8*>(reinterpret_cast<const uint8*>(pointer) + offset));
+}
+
+} // namespace o3d
+
+#endif // O3D_CORE_CROSS_POINTER_UTILS_H_
diff --git a/o3d/core/cross/renderer.cc b/o3d/core/cross/renderer.cc index fe80a1a..8d292d9 100644 --- a/o3d/core/cross/renderer.cc +++ b/o3d/core/cross/renderer.cc @@ -147,14 +147,9 @@ void Renderer::InitCommon() { DCHECK(!error_sampler_.IsNull()); error_sampler_->set_name(O3D_STRING_CONSTANT("errorSampler")); - // TODO: remove ifdef when textures are implemented on CB -#ifndef RENDERER_CB DCHECK(!texture.IsNull()); texture->set_name(O3D_STRING_CONSTANT("errorTexture")); texture->set_alpha_is_one(true); - void* texture_data; - bool locked = texture->Lock(0, &texture_data); - DCHECK(locked); static unsigned char error_texture_data[] = { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, @@ -189,14 +184,7 @@ void Renderer::InitCommon() { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, }; - DCHECK(sizeof error_texture_data == - Bitmap::GetBufferSize(texture->width(), - texture->height(), - texture->format())); - memcpy(texture_data, error_texture_data, sizeof error_texture_data); - bool unlocked = texture->Unlock(0); - DCHECK(unlocked); -#endif + texture->SetRect(0, 0, 0, 8, 8, error_texture_data, 8 * 4); error_sampler_->set_mag_filter(Sampler::POINT); error_sampler_->set_min_filter(Sampler::POINT); diff --git a/o3d/core/cross/skin.cc b/o3d/core/cross/skin.cc index 507ae09..4349abe 100644 --- a/o3d/core/cross/skin.cc +++ b/o3d/core/cross/skin.cc @@ -35,6 +35,7 @@ #include "core/cross/precompile.h" #include "core/cross/skin.h" #include "core/cross/error.h" +#include "core/cross/pointer_utils.h" #include "import/cross/memory_stream.h" #include "import/cross/raw_data.h" diff --git a/o3d/core/cross/skin_test.cc b/o3d/core/cross/skin_test.cc index 46a4358..5b1de83 100644 --- a/o3d/core/cross/skin_test.cc +++ b/o3d/core/cross/skin_test.cc @@ -36,6 +36,7 @@ #include "tests/common/win/testing_common.h" #include "core/cross/error.h" #include "core/cross/skin.h" +#include "core/cross/pointer_utils.h" #include "core/cross/primitive.h" #include "import/cross/memory_buffer.h" #include "import/cross/memory_stream.h" diff --git a/o3d/core/cross/stream_bank_test.cc b/o3d/core/cross/stream_bank_test.cc index b2d49c2..a154df3 100644 --- a/o3d/core/cross/stream_bank_test.cc +++ b/o3d/core/cross/stream_bank_test.cc @@ -35,6 +35,7 @@ #include "core/cross/client.h" #include "core/cross/skin.h" #include "core/cross/stream_bank.h" +#include "core/cross/pointer_utils.h" #include "core/cross/fake_vertex_source.h" #include "tests/common/win/testing_common.h" diff --git a/o3d/core/cross/texture.cc b/o3d/core/cross/texture.cc index 008e2ef..8f71fd8 100644 --- a/o3d/core/cross/texture.cc +++ b/o3d/core/cross/texture.cc @@ -127,16 +127,9 @@ void Texture2D::DrawImage(Bitmap* src_img, static_cast<unsigned int>(src_height) == src_img->height() && static_cast<unsigned int>(dst_width) == mip_width && static_cast<unsigned int>(dst_height) == mip_height) { - void* data = NULL; - if (!Lock(dest_mip, &data)) - return; - - uint8* mip_data = static_cast<uint8*>(data); - unsigned int size = Bitmap::GetMipChainSize(mip_width, mip_height, - format(), 1); - memcpy(mip_data, src_img->image_data(), size); - this->Unlock(dest_mip); - + SetRect(dest_mip, 0, 0, mip_width, mip_height, + src_img->image_data(), + Bitmap::GetMipChainSize(src_img->width(), 1, format(), 1)); return; } if (src_img->format() == Texture::XRGB8 || @@ -150,43 +143,43 @@ void Texture2D::DrawImage(Bitmap* src_img, return; } - void* data = NULL; - if (!Lock(dest_mip, &data)) + LockHelper helper(this, dest_mip); + uint8* mip_data = helper.GetDataAs<uint8>(); + if (!mip_data) { return; + } uint8* src_img_data = src_img->image_data(); - uint8* mip_data = static_cast<uint8*>(data); Bitmap::LanczosScale(src_img_data, src_x, src_y, src_width, src_height, src_img->width(), src_img->height(), - mip_data, dst_x, dst_y, + mip_data, helper.pitch(), + dst_x, dst_y, dst_width, dst_height, mip_width, mip_height, components); - - this->Unlock(dest_mip); } ObjectBase::Ref Texture2D::Create(ServiceLocator* service_locator) { return ObjectBase::Ref(); } -Texture2DLockHelper::Texture2DLockHelper(Texture2D* texture, int level) +Texture2D::LockHelper::LockHelper(Texture2D* texture, int level) : texture_(texture), level_(level), data_(NULL), locked_(false) { } -Texture2DLockHelper::~Texture2DLockHelper() { +Texture2D::LockHelper::~LockHelper() { if (locked_) { texture_->Unlock(level_); } } -void* Texture2DLockHelper::GetData() { +void* Texture2D::LockHelper::GetData() { if (!locked_) { - locked_ = texture_->Lock(level_, &data_); + locked_ = texture_->Lock(level_, &data_, &pitch_); if (!locked_) { O3D_ERROR(texture_->service_locator()) << "Unable to lock buffer '" << texture_->name() << "'"; @@ -280,17 +273,9 @@ void TextureCUBE::DrawImage(Bitmap* src_img, static_cast<unsigned int>(src_height) == src_img->height() && static_cast<unsigned int>(dst_width) == mip_length && static_cast<unsigned int>(dst_height) == mip_length) { - // get mip data by lock method. - void* data = NULL; - if (!Lock(dest_face, dest_mip, &data)) - return; - - uint8* mip_data = static_cast<uint8*>(data); - unsigned int size = Bitmap::GetMipChainSize(mip_length, mip_length, - format(), 1); - memcpy(mip_data, src_img->image_data(), size); - this->Unlock(dest_face, dest_mip); - + SetRect(dest_face, dest_mip, 0, 0, mip_length, mip_length, + src_img->image_data(), + Bitmap::GetMipChainSize(src_img->width(), 1, format(), 1)); return; } if (src_img->format() == Texture::XRGB8 || @@ -304,22 +289,49 @@ void TextureCUBE::DrawImage(Bitmap* src_img, return; } - void* data = NULL; - if (!Lock(dest_face, dest_mip, &data)) { + LockHelper helper(this, dest_face, dest_mip); + uint8* mip_data = helper.GetDataAs<uint8>(); + if (!mip_data) { return; } uint8* src_img_data = src_img->image_data(); - uint8* mip_data = static_cast<uint8*>(data); Bitmap::LanczosScale(src_img_data, src_x, src_y, src_width, src_height, src_img->width(), src_img->height(), - mip_data, dst_x, dst_y, + mip_data, helper.pitch(), + dst_x, dst_y, dst_width, dst_height, mip_length, mip_length, components); +} + +TextureCUBE::LockHelper::LockHelper( + TextureCUBE* texture, + CubeFace face, + int level) + : texture_(texture), + face_(face), + level_(level), + data_(NULL), + locked_(false) { +} + +TextureCUBE::LockHelper::~LockHelper() { + if (locked_) { + texture_->Unlock(face_, level_); + } +} - this->Unlock(dest_face, dest_mip); +void* TextureCUBE::LockHelper::GetData() { + if (!locked_) { + locked_ = texture_->Lock(face_, level_, &data_, &pitch_); + if (!locked_) { + O3D_ERROR(texture_->service_locator()) + << "Unable to lock buffer '" << texture_->name() << "'"; + } + } + return data_; } } // namespace o3d diff --git a/o3d/core/cross/texture.h b/o3d/core/cross/texture.h index 49ff0d2..721a7ec 100644 --- a/o3d/core/cross/texture.h +++ b/o3d/core/cross/texture.h @@ -49,6 +49,40 @@ class Bitmap; class Texture2D : public Texture { public: typedef SmartPointer<Texture2D> Ref; + + // Class to help lock Texture2D. Automatically unlocks texture in destructor. + class LockHelper { + public: + explicit LockHelper(Texture2D* texture, int level); + ~LockHelper(); + + int pitch() const { + return pitch_; + } + + // Gets a pointer to the data of the buffer, locking the buffer if + // necessary. + // Returns: + // Pointer to data in buffer or NULL if there was an error. + void* GetData(); + + // Typed version of GetData + template <typename T> + T* GetDataAs() { + return reinterpret_cast<T*>(GetData()); + } + + private: + Texture2D* texture_; + int level_; + int pitch_; + void* data_; + bool locked_; + + DISALLOW_COPY_AND_ASSIGN(LockHelper); + }; + + Texture2D(ServiceLocator* service_locator, int width, int height, @@ -70,24 +104,26 @@ class Texture2D : public Texture { return height_param_->value(); } - // Returns a pointer to the internal texture data for the given mipmap level. - // Lock must be called before the texture data can be modified. + // Sets a rectangular region of this texture. + // If the texture is a DXT format, the only acceptable values + // for left, top, width and height are 0, 0, texture->width, texture->height + // // Parameters: - // level: [in] the mipmap level to be modified - // texture_data: [out] a pointer to the current texture data - // Returns: - // true if the operation succeeds - virtual bool Lock(int level, void** texture_data) = 0; - - // Notifies the texture object that the internal texture data has been - // been modified. Unlock must be called in conjunction with Lock. Modifying - // the contents of the texture after Unlock has been called could lead to - // unpredictable behavior. - // Parameters: - // level: [in] the mipmap level that was modified - // Returns: - // true if the operation succeeds - virtual bool Unlock(int level) = 0; + // level: The mipmap level to modify + // dst_left: The left edge of the rectangular area to modify. + // dst_top: The top edge of the rectangular area to modify. + // width: The width of the rectangular area to modify. + // height: The of the rectangular area to modify. + // src_data: The source pixels. + // src_pitch: If the format is uncompressed this is the number of bytes + // per row of pixels. If compressed this value is unused. + virtual void SetRect(int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch) = 0; // Returns a RenderSurface object associated with a mip_level of a texture. // Parameters: @@ -116,6 +152,27 @@ class Texture2D : public Texture { int dest_width, int dest_height, int dest_mip); protected: + // Returns a pointer to the internal texture data for the given mipmap level. + // Lock must be called before the texture data can be modified. + // Parameters: + // level: [in] the mipmap level to be modified + // texture_data: [out] a pointer to the current texture data + // pitch: bytes across 1 row of pixels if uncompressed format. bytes across 1 + // row of blocks if compressed format. + // Returns: + // true if the operation succeeds + virtual bool Lock(int level, void** texture_data, int* pitch) = 0; + + // Notifies the texture object that the internal texture data has been + // been modified. Unlock must be called in conjunction with Lock. Modifying + // the contents of the texture after Unlock has been called could lead to + // unpredictable behavior. + // Parameters: + // level: [in] the mipmap level that was modified + // Returns: + // true if the operation succeeds + virtual bool Unlock(int level) = 0; + // Returns true if the mip-map level has been locked. bool IsLocked(unsigned int level) { DCHECK_LT(static_cast<int>(level), levels()); @@ -138,32 +195,6 @@ class Texture2D : public Texture { DISALLOW_COPY_AND_ASSIGN(Texture2D); }; -// Class to help lock Texture2D. Automatically unlocks texture in destructor. -class Texture2DLockHelper { - public: - explicit Texture2DLockHelper(Texture2D* texture, int level); - ~Texture2DLockHelper(); - - // Gets a pointer to the data of the buffer, locking the buffer if necessary. - // Returns: - // Pointer to data in buffer or NULL if there was an error. - void* GetData(); - - // Typed version of GetData - template <typename T> - T* GetDataAs() { - return reinterpret_cast<T*>(GetData()); - } - - private: - Texture2D* texture_; - int level_; - void* data_; - bool locked_; - - DISALLOW_COPY_AND_ASSIGN(Texture2DLockHelper); -}; - class TextureCUBE : public Texture { public: typedef SmartPointer<TextureCUBE> Ref; @@ -178,6 +209,40 @@ class TextureCUBE : public Texture { NUMBER_OF_FACES, }; + // Class to help lock TextureCUBE. + // Automatically unlocks texture in destructor. + class LockHelper { + public: + explicit LockHelper(TextureCUBE* texture, CubeFace face, int level); + ~LockHelper(); + + int pitch() const { + return pitch_; + } + + // Gets a pointer to the data of the buffer, locking the buffer if + // necessary. + // Returns: + // Pointer to data in buffer or NULL if there was an error. + void* GetData(); + + // Typed version of GetData + template <typename T> + T* GetDataAs() { + return reinterpret_cast<T*>(GetData()); + } + + private: + TextureCUBE* texture_; + CubeFace face_; + int level_; + int pitch_; + void* data_; + bool locked_; + + DISALLOW_COPY_AND_ASSIGN(LockHelper); + }; + static const char* kEdgeLengthParamName; TextureCUBE(ServiceLocator* service_locator, @@ -193,27 +258,28 @@ class TextureCUBE : public Texture { return edge_length_param_->value(); } - // Returns a pointer to the internal texture data for the given face and - // mipmap level. - // Lock must be called before the texture data can be modified. + // Sets a rectangular region of this texture. + // If the texture is a DXT format, the only acceptable values + // for left, top, width and height are 0, 0, texture->width, texture->height + // // Parameters: - // face: [in] the index of the cube face to be modified - // level: [in] the mipmap level to be modified - // texture_data: [out] a pointer to the current texture data - // Returns: - // true if the operation succeeds - virtual bool Lock(CubeFace face, int level, void** texture_data) = 0; - - // Notifies the texture object that the internal texture data has been - // been modified. Unlock must be called in conjunction with Lock. - // Modifying the contents of the texture after Unlock has been called could - // lead to unpredictable behavior. - // Parameters: - // face: [in] the index of the cube face that was modified - // level: [in] the mipmap level that was modified - // Returns: - // true if the operation succeeds - virtual bool Unlock(CubeFace face, int level) = 0; + // face: The face of the cube to modify. + // level: The mipmap level to modify + // dst_left: The left edge of the rectangular area to modify. + // dst_top: The top edge of the rectangular area to modify. + // width: The width of the rectangular area to modify. + // height: The of the rectangular area to modify. + // src_data: buffer to get pixels from. + // src_pitch: If the format is uncompressed this is the number of bytes + // per row of pixels. If compressed this value is unused. + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch) = 0; // Returns a RenderSurface object associated with a given cube face and // mip_level of a texture. @@ -247,6 +313,31 @@ class TextureCUBE : public Texture { int dest_height, CubeFace face, int dest_mip); protected: + // Returns a pointer to the internal texture data for the given face and + // mipmap level. + // Lock must be called before the texture data can be modified. + // Parameters: + // face: [in] the index of the cube face to be modified + // level: [in] the mipmap level to be modified + // texture_data: [out] a pointer to the current texture data + // pitch: bytes across 1 row of pixels if uncompressed format. bytes across 1 + // row of blocks if compressed format. + // Returns: + // true if the operation succeeds + virtual bool Lock( + CubeFace face, int level, void** texture_data, int* pitch) = 0; + + // Notifies the texture object that the internal texture data has been + // been modified. Unlock must be called in conjunction with Lock. + // Modifying the contents of the texture after Unlock has been called could + // lead to unpredictable behavior. + // Parameters: + // face: [in] the index of the cube face that was modified + // level: [in] the mipmap level that was modified + // Returns: + // true if the operation succeeds + virtual bool Unlock(CubeFace face, int level) = 0; + // Returns true if the mip-map level has been locked. bool IsLocked(unsigned int level, CubeFace face) { DCHECK_LT(static_cast<int>(level), levels()); diff --git a/o3d/core/cross/texture_base.cc b/o3d/core/cross/texture_base.cc index d4b2d19..b416ae5 100644 --- a/o3d/core/cross/texture_base.cc +++ b/o3d/core/cross/texture_base.cc @@ -35,6 +35,7 @@ #include "core/cross/precompile.h" #include "core/cross/texture_base.h" #include "core/cross/pack.h" +#include "core/cross/bitmap.h" namespace o3d { diff --git a/o3d/core/cross/texture_base.h b/o3d/core/cross/texture_base.h index c6a144d..395480c 100644 --- a/o3d/core/cross/texture_base.h +++ b/o3d/core/cross/texture_base.h @@ -90,6 +90,14 @@ class Texture : public ParamObject { // Returns the implementation-specific texture handle. virtual void* GetTextureHandle() const = 0; + static bool IsCompressedFormat(Format format) { + return format == DXT1 || format == DXT3 || format == DXT5; + } + + bool IsCompressed() const { + return IsCompressedFormat(format_); + } + bool alpha_is_one() const { return alpha_is_one_; } void set_alpha_is_one(bool value) { alpha_is_one_ = value; } @@ -126,7 +134,8 @@ class Texture : public ParamObject { } // Whether or not to resize NPOT textures to POT when passing to the - // underlying graphics API. + // underlying graphics API. This should only be true in the if the texture + // is not POT and if it is not compressed. bool resize_to_pot_; static void RegisterSurface(RenderSurface* surface, Pack* pack); @@ -161,7 +170,7 @@ class ParamTexture : public TypedRefParam<Texture> { friend class IClassManager; static ObjectBase::Ref Create(ServiceLocator* service_locator); - O3D_DECL_CLASS(ParamTexture, RefParamBase) + O3D_DECL_CLASS(ParamTexture, RefParamBase); DISALLOW_COPY_AND_ASSIGN(ParamTexture); }; diff --git a/o3d/core/cross/texture_base_test.cc b/o3d/core/cross/texture_base_test.cc new file mode 100644 index 0000000..8b13973 --- /dev/null +++ b/o3d/core/cross/texture_base_test.cc @@ -0,0 +1,100 @@ +/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file implements unit tests for class Texture.
+
+#include "tests/common/win/testing_common.h"
+#include "core/cross/texture_base.h"
+#include "core/cross/object_manager.h"
+
+namespace o3d {
+
+namespace {
+
+Texture::RGBASwizzleIndices swizzle;
+
+class MockTexture : public Texture {
+ public:
+ typedef SmartPointer<MockTexture> Ref;
+
+ MockTexture(ServiceLocator* service_locator,
+ Texture::Format format,
+ int levels,
+ bool alpha_is_one,
+ bool resize_to_pot,
+ bool enable_render_surfaces)
+ : Texture(service_locator, format, levels, alpha_is_one, resize_to_pot,
+ enable_render_surfaces) {
+ }
+
+ virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices() {
+ return swizzle;
+ };
+
+ virtual void* GetTextureHandle() const {
+ return NULL;
+ };
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockTexture);
+};
+
+} // anonymous namespace
+
+class TextureTest : public testing::Test {
+ protected:
+
+ TextureTest()
+ : object_manager_(g_service_locator) {
+ }
+
+ private:
+ ServiceDependency<ObjectManager> object_manager_;
+};
+
+TEST_F(TextureTest, Basic) {
+ MockTexture::Ref texture(new MockTexture(
+ g_service_locator,
+ Texture::XRGB8,
+ 1,
+ false,
+ false,
+ false));
+ ASSERT_TRUE(texture != NULL);
+ EXPECT_EQ(texture->format(), Texture::XRGB8);
+ EXPECT_EQ(texture->levels(), 1);
+ EXPECT_FALSE(texture->alpha_is_one());
+ EXPECT_FALSE(texture->render_surfaces_enabled());
+}
+
+} // namespace o3d
+
diff --git a/o3d/core/cross/texture_test.cc b/o3d/core/cross/texture_test.cc new file mode 100644 index 0000000..f14c87f --- /dev/null +++ b/o3d/core/cross/texture_test.cc @@ -0,0 +1,108 @@ +/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This file implements unit tests for class Texture.
+
+#include "tests/common/win/testing_common.h"
+#include "core/cross/texture_base.h"
+#include "core/cross/object_manager.h"
+#include "core/cross/pack.h"
+
+namespace o3d {
+
+class Texture2DTest : public testing::Test {
+ protected:
+ Texture2DTest()
+ : object_manager_(g_service_locator) {
+ }
+
+ virtual void SetUp() {
+ pack_ = object_manager_->CreatePack();
+ }
+
+ virtual void TearDown() {
+ pack_->Destroy();
+ }
+
+ Pack* pack() { return pack_; }
+
+ private:
+ ServiceDependency<ObjectManager> object_manager_;
+ Pack* pack_;
+};
+
+TEST_F(Texture2DTest, Basic) {
+ Texture2D* texture = pack()->CreateTexture2D(8, 8, Texture::ARGB8, 1, false);
+ ASSERT_TRUE(texture != NULL);
+ EXPECT_TRUE(texture->IsA(Texture2D::GetApparentClass()));
+ EXPECT_TRUE(texture->IsA(Texture::GetApparentClass()));
+ EXPECT_TRUE(texture->IsA(ParamObject::GetApparentClass()));
+ EXPECT_EQ(texture->format(), Texture::ARGB8);
+ EXPECT_EQ(texture->levels(), 1);
+ EXPECT_FALSE(texture->render_surfaces_enabled());
+}
+
+class TextureCUBETest : public testing::Test {
+ protected:
+ TextureCUBETest()
+ : object_manager_(g_service_locator) {
+ }
+
+ virtual void SetUp() {
+ pack_ = object_manager_->CreatePack();
+ }
+
+ virtual void TearDown() {
+ pack_->Destroy();
+ }
+
+ Pack* pack() { return pack_; }
+
+ private:
+ ServiceDependency<ObjectManager> object_manager_;
+ Pack* pack_;
+};
+
+TEST_F(TextureCUBETest, Basic) {
+ TextureCUBE* texture =
+ pack()->CreateTextureCUBE(8, Texture::ARGB8, 1, false);
+ ASSERT_TRUE(texture != NULL);
+ EXPECT_TRUE(texture->IsA(TextureCUBE::GetApparentClass()));
+ EXPECT_TRUE(texture->IsA(Texture::GetApparentClass()));
+ EXPECT_TRUE(texture->IsA(ParamObject::GetApparentClass()));
+ EXPECT_EQ(texture->format(), Texture::ARGB8);
+ EXPECT_EQ(texture->levels(), 1);
+ EXPECT_FALSE(texture->render_surfaces_enabled());
+}
+
+} // namespace o3d
+
diff --git a/o3d/core/cross/vertex_source_test.cc b/o3d/core/cross/vertex_source_test.cc index e64d3a3..cea3e2d 100644 --- a/o3d/core/cross/vertex_source_test.cc +++ b/o3d/core/cross/vertex_source_test.cc @@ -36,6 +36,7 @@ #include "tests/common/win/testing_common.h" #include "core/cross/vertex_source.h" #include "core/cross/fake_vertex_source.h" +#include "core/cross/pointer_utils.h" #include "core/cross/evaluation_counter.h" namespace o3d { diff --git a/o3d/core/win/d3d9/texture_d3d9.cc b/o3d/core/win/d3d9/texture_d3d9.cc index 54ed901..489900f 100644 --- a/o3d/core/win/d3d9/texture_d3d9.cc +++ b/o3d/core/win/d3d9/texture_d3d9.cc @@ -213,6 +213,117 @@ class TextureSurfaceConstructor : public SurfaceConstructor { DISALLOW_COPY_AND_ASSIGN(TextureSurfaceConstructor); }; +void SetTextureRectUncompressed(Texture::Format format, + const uint8* src, + int src_pitch, + unsigned src_width, + unsigned src_height, + uint8* dst, + int dst_pitch) { + size_t bytes_per_line = Bitmap::GetMipChainSize(src_width, 1, format, 1); + for (unsigned yy = 0; yy < src_height; ++yy) { + memcpy(dst, src, bytes_per_line); + src += src_pitch; + dst += dst_pitch; + } +} + +void SetTextureRectCompressed(Texture::Format format, + const uint8* src, + unsigned src_width, + unsigned src_height, + uint8* dst, + int dst_pitch) { + unsigned blocks_across = (src_width + 3) / 4; + unsigned blocks_down = (src_height + 3) / 4; + unsigned bytes_per_block = format == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + for (unsigned yy = 0; yy < blocks_down; ++yy) { + memcpy(dst, src, bytes_per_row); + src += bytes_per_row; + dst += dst_pitch; + } +} + +void SetTextureRect( + ServiceLocator* service_locator, + IDirect3DTexture9* d3d_texture, + Texture::Format format, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + DCHECK(src_data); + bool compressed = Texture::IsCompressedFormat(format); + + RECT rect = {dst_left, dst_top, src_width, src_height}; + D3DLOCKED_RECT out_rect = {0}; + + if (!HR(d3d_texture->LockRect( + level, &out_rect, compressed ? NULL : &rect, 0))) { + O3D_ERROR(service_locator) << "Failed to Lock Texture2D (D3D9)"; + return; + } + + const uint8* src = static_cast<const uint8*>(src_data); + uint8* dst = static_cast<uint8*>(out_rect.pBits); + if (!compressed) { + SetTextureRectUncompressed(format, src, src_pitch, src_width, src_height, + dst, out_rect.Pitch); + } else { + SetTextureRectCompressed( + format, src, src_width, src_height, dst, out_rect.Pitch); + } + if (!HR(d3d_texture->UnlockRect(level))) { + O3D_ERROR(service_locator) << "Failed to Unlock Texture2D (D3D9)"; + return; + } +} + +void SetTextureFaceRect( + ServiceLocator* service_locator, + IDirect3DCubeTexture9* d3d_texture, + Texture::Format format, + TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + DCHECK(src_data); + bool compressed = Texture::IsCompressedFormat(format); + + RECT rect = {dst_left, dst_top, src_width, src_height}; + D3DLOCKED_RECT out_rect = {0}; + + D3DCUBEMAP_FACES d3d_face = DX9CubeFace(face); + + if (!HR(d3d_texture->LockRect( + d3d_face, level, &out_rect, compressed ? NULL : &rect, 0))) { + O3D_ERROR(service_locator) << "Failed to Lock TextureCUBE (D3D9)"; + return; + } + + const uint8* src = static_cast<const uint8*>(src_data); + uint8* dst = static_cast<uint8*>(out_rect.pBits); + if (!compressed) { + SetTextureRectUncompressed(format, src, src_pitch, src_width, src_height, + dst, out_rect.Pitch); + } else { + SetTextureRectCompressed( + format, src, src_width, src_height, dst, out_rect.Pitch); + } + if (!HR(d3d_texture->UnlockRect(d3d_face, level))) { + O3D_ERROR(service_locator) << "Failed to Unlock TextureCUBE (D3D9)"; + return; + } +} + } // unnamed namespace // Constructs a 2D texture object from the given (existing) D3D 2D texture. @@ -267,11 +378,7 @@ Texture2DD3D9* Texture2DD3D9::Create(ServiceLocator* service_locator, texture->backing_bitmap_->SetFrom(bitmap); if (texture->backing_bitmap_->image_data()) { for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { - if (!texture->UpdateBackedMipLevel(i)) { - DLOG(ERROR) << "Failed to upload bitmap to texture."; - delete texture; - return NULL; - } + texture->UpdateBackedMipLevel(i); mip_width = std::max(1U, mip_width >> 1); mip_height = std::max(1U, mip_height >> 1); } @@ -291,7 +398,7 @@ Texture2DD3D9::~Texture2DD3D9() { d3d_texture_ = NULL; } -bool Texture2DD3D9::UpdateBackedMipLevel(unsigned int level) { +void Texture2DD3D9::UpdateBackedMipLevel(unsigned int level) { DCHECK_LT(level, static_cast<unsigned int>(levels())); DCHECK(backing_bitmap_->image_data()); DCHECK_EQ(backing_bitmap_->width(), width()); @@ -299,45 +406,49 @@ bool Texture2DD3D9::UpdateBackedMipLevel(unsigned int level) { DCHECK_EQ(backing_bitmap_->format(), format()); DCHECK_EQ(backing_bitmap_->num_mipmaps(), levels()); - unsigned int mip_width = std::max(1, width() >> level); - unsigned int mip_height = std::max(1, height() >> level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); unsigned int rect_width = mip_width; unsigned int rect_height = mip_height; - if (resize_to_pot_) { - rect_width = std::max(1U, Bitmap::GetPOTSize(width()) >> level); - rect_height = std::max(1U, Bitmap::GetPOTSize(height()) >> level); - } + rect_width = std::max(1U, Bitmap::GetPOTSize(width()) >> level); + rect_height = std::max(1U, Bitmap::GetPOTSize(height()) >> level); RECT rect = {0, 0, rect_width, rect_height}; - D3DLOCKED_RECT out_rect; - out_rect.pBits = 0; + D3DLOCKED_RECT out_rect = {0}; if (!HR(d3d_texture_->LockRect(level, &out_rect, &rect, 0))) { - DLOG(ERROR) << "Failed to lock texture level " << level << "."; - return false; + O3D_ERROR(service_locator()) + << "Failed to lock texture level " << level << "."; + return; } DCHECK(out_rect.pBits); - // TODO: check that the returned pitch is what we expect. + uint8* dst = static_cast<uint8*>(out_rect.pBits); - const unsigned char *mip_data = - backing_bitmap_->GetMipData(level, TextureCUBE::FACE_POSITIVE_X); + const uint8 *mip_data = backing_bitmap_->GetMipData(level); if (resize_to_pot_) { Bitmap::Scale(mip_width, mip_height, format(), mip_data, rect_width, rect_height, - static_cast<unsigned char *>(out_rect.pBits)); + static_cast<uint8 *>(out_rect.pBits), + out_rect.Pitch); } else { - unsigned int mip_size = - Bitmap::GetBufferSize(mip_width, mip_height, format()); - memcpy(out_rect.pBits, mip_data, mip_size); + if (!IsCompressed()) { + SetTextureRectUncompressed( + format(), mip_data, + Bitmap::GetMipChainSize(mip_width, 1, format(), 1), + mip_width, mip_height, + dst, out_rect.Pitch); + } else { + SetTextureRectCompressed( + format(), mip_data, mip_width, mip_height, dst, out_rect.Pitch); + } } if (!HR(d3d_texture_->UnlockRect(level))) { O3D_ERROR(service_locator()) << "Failed to unlock texture level " << level << "."; - return false; } - return true; } RenderSurface::Ref Texture2DD3D9::GetRenderSurface(int mip_level, Pack* pack) { @@ -371,9 +482,75 @@ RenderSurface::Ref Texture2DD3D9::GetRenderSurface(int mip_level, Pack* pack) { return render_surface; } +void Texture2DD3D9::SetRect(int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect on non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + DCHECK(backing_bitmap_->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap_->SetRect( + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(level); + } else { + SetTextureRect(service_locator(), + d3d_texture_, + format(), + level, + dst_left, + dst_top, + src_width, + src_height, + src_data, + src_pitch); + } +} + // Locks the given mipmap level of this texture for loading from main memory, // and returns a pointer to the buffer. -bool Texture2DD3D9::Lock(int level, void** texture_data) { +bool Texture2DD3D9::Lock(int level, void** texture_data, int* pitch) { + DCHECK(texture_data); + DCHECK(pitch); if (level >= levels() || level < 0) { O3D_ERROR(service_locator()) << "Trying to lock inexistent level " << level << " on Texture \"" @@ -393,8 +570,11 @@ bool Texture2DD3D9::Lock(int level, void** texture_data) { } if (resize_to_pot_) { DCHECK(backing_bitmap_->image_data()); - *texture_data = backing_bitmap_->GetMipData(level, - TextureCUBE::FACE_POSITIVE_X); + *texture_data = backing_bitmap_->GetMipData(level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize(level, width(), height(), &mip_width, &mip_height); + *pitch = Bitmap::GetMipChainSize(mip_width, 1, format(), 1); locked_levels_ |= 1 << level; return true; } else { @@ -403,6 +583,7 @@ bool Texture2DD3D9::Lock(int level, void** texture_data) { if (HR(d3d_texture_->LockRect(level, &out_rect, &rect, 0))) { *texture_data = out_rect.pBits; + *pitch = out_rect.Pitch; locked_levels_ |= 1 << level; return true; } else { @@ -429,7 +610,8 @@ bool Texture2DD3D9::Unlock(int level) { } bool result = false; if (resize_to_pot_) { - result = UpdateBackedMipLevel(level); + UpdateBackedMipLevel(level); + result = true; } else { result = HR(d3d_texture_->UnlockRect(level)); } @@ -524,11 +706,7 @@ TextureCUBED3D9* TextureCUBED3D9::Create(ServiceLocator* service_locator, for (int face = 0; face < 6; ++face) { unsigned int mip_edge = edge; for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { - if (!texture->UpdateBackedMipLevel(i, static_cast<CubeFace>(face))) { - DLOG(ERROR) << "Failed to upload bitmap to texture."; - delete texture; - return NULL; - } + texture->UpdateBackedMipLevel(static_cast<CubeFace>(face), i); mip_edge = std::max(1U, mip_edge >> 1); } } @@ -558,8 +736,8 @@ TextureCUBED3D9::~TextureCUBED3D9() { d3d_cube_texture_ = NULL; } -bool TextureCUBED3D9::UpdateBackedMipLevel(unsigned int level, - TextureCUBE::CubeFace face) { +void TextureCUBED3D9::UpdateBackedMipLevel(TextureCUBE::CubeFace face, + unsigned int level) { DCHECK_LT(level, static_cast<unsigned int>(levels())); DCHECK(backing_bitmap_->image_data()); DCHECK(backing_bitmap_->is_cubemap()); @@ -575,40 +753,45 @@ bool TextureCUBED3D9::UpdateBackedMipLevel(unsigned int level, } RECT rect = {0, 0, rect_edge, rect_edge}; - D3DLOCKED_RECT out_rect; - out_rect.pBits = 0; + D3DLOCKED_RECT out_rect = {0}; + D3DCUBEMAP_FACES d3d_face = DX9CubeFace(face); - if (!HR(d3d_cube_texture_->LockRect(DX9CubeFace(face), level, &out_rect, - &rect, 0))) { + if (!HR(d3d_cube_texture_->LockRect(d3d_face, level, &out_rect, &rect, 0))) { O3D_ERROR(service_locator()) << "Failed to lock texture level " << level << " face " << face << "."; - return false; + return; } DCHECK(out_rect.pBits); - // TODO: check that the returned pitch is what we expect. + uint8* dst = static_cast<uint8*>(out_rect.pBits); - const unsigned char *mip_data = backing_bitmap_->GetMipData(level, face); + const uint8 *mip_data = backing_bitmap_->GetFaceMipData(face, level); if (resize_to_pot_) { Bitmap::Scale(mip_edge, mip_edge, format(), mip_data, rect_edge, rect_edge, - static_cast<unsigned char *>(out_rect.pBits)); + static_cast<uint8 *>(out_rect.pBits), + out_rect.Pitch); } else { - unsigned int mip_size = - Bitmap::GetBufferSize(mip_edge, mip_edge, format()); - memcpy(out_rect.pBits, mip_data, mip_size); + if (!IsCompressed()) { + SetTextureRectUncompressed( + format(), mip_data, + Bitmap::GetMipChainSize(mip_edge, 1, format(), 1), + mip_edge, mip_edge, + dst, out_rect.Pitch); + } else { + SetTextureRectCompressed( + format(), mip_data, mip_edge, mip_edge, dst, out_rect.Pitch); + } } - if (!HR(d3d_cube_texture_->UnlockRect(DX9CubeFace(face), level))) { + if (!HR(d3d_cube_texture_->UnlockRect(d3d_face, level))) { O3D_ERROR(service_locator()) << "Failed to unlock texture level " << level << " face " << face << "."; - return false; } - return true; } -RenderSurface::Ref TextureCUBED3D9::GetRenderSurface(CubeFace face, +RenderSurface::Ref TextureCUBED3D9::GetRenderSurface(TextureCUBE::CubeFace face, int mip_level, Pack* pack) { if (!render_surfaces_enabled()) { @@ -641,9 +824,85 @@ RenderSurface::Ref TextureCUBED3D9::GetRenderSurface(CubeFace face, return render_surface; } +void TextureCUBED3D9::SetRect(TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (static_cast<int>(face) < 0 || static_cast<int>(face) >= NUMBER_OF_FACES) { + O3D_ERROR(service_locator()) + << "Trying to SetRect invalid face " << face << " on Texture \"" + << name() << "\""; + return; + } + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width; + unsigned mip_height; + Bitmap::GetMipSize( + level, edge_length(), edge_length(), &mip_width, &mip_height); + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + DCHECK(backing_bitmap_->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap_->SetFaceRect(face, + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(face, level); + } else { + SetTextureFaceRect(service_locator(), + d3d_cube_texture_, + format(), + face, + level, + dst_left, + dst_top, + src_width, + src_height, + src_data, + src_pitch); + } +} + // Locks the given face and mipmap level of this texture for loading from // main memory, and returns a pointer to the buffer. -bool TextureCUBED3D9::Lock(CubeFace face, int level, void** texture_data) { +bool TextureCUBED3D9::Lock( + CubeFace face, int level, void** texture_data, int* pitch) { + DCHECK(texture_data); + DCHECK(pitch); if (level >= levels() || level < 0) { O3D_ERROR(service_locator()) << "Trying to lock inexistent level " << level << " on Texture \"" @@ -663,7 +922,12 @@ bool TextureCUBED3D9::Lock(CubeFace face, int level, void** texture_data) { } if (resize_to_pot_) { DCHECK(backing_bitmap_->image_data()); - *texture_data = backing_bitmap_->GetMipData(level, face); + *texture_data = backing_bitmap_->GetFaceMipData(face, level); + unsigned int mip_width; + unsigned int mip_height; + Bitmap::GetMipSize( + level, edge_length(), edge_length(), &mip_width, &mip_height); + *pitch = Bitmap::GetMipChainSize(mip_width, 1, format(), 1); locked_levels_[face] |= 1 << level; return true; } else { @@ -673,6 +937,7 @@ bool TextureCUBED3D9::Lock(CubeFace face, int level, void** texture_data) { if (HR(d3d_cube_texture_->LockRect(DX9CubeFace(face), level, &out_rect, &rect, 0))) { *texture_data = out_rect.pBits; + *pitch = out_rect.Pitch; locked_levels_[face] |= 1 << level; return true; } else { @@ -699,7 +964,8 @@ bool TextureCUBED3D9::Unlock(CubeFace face, int level) { } bool result = false; if (resize_to_pot_) { - result = UpdateBackedMipLevel(level, face); + UpdateBackedMipLevel(face, level); + result = true; } else { result = HR(d3d_cube_texture_->UnlockRect(DX9CubeFace(face), level)); diff --git a/o3d/core/win/d3d9/texture_d3d9.h b/o3d/core/win/d3d9/texture_d3d9.h index 8a19913..5f10951 100644 --- a/o3d/core/win/d3d9/texture_d3d9.h +++ b/o3d/core/win/d3d9/texture_d3d9.h @@ -32,8 +32,8 @@ // This file contains the declarations for Texture2DD3D9 and TextureCUBED3D9. -#ifndef O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H__ -#define O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H__ +#ifndef O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H_ +#define O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H_ #include <atlbase.h> #include <vector> @@ -65,13 +65,14 @@ class Texture2DD3D9 : public Texture2D { virtual ~Texture2DD3D9(); - // Locks the image buffer of a given mipmap level for loading from - // main memory. A pointer to the current contents of the texture is returned - // in texture_data. - virtual bool Lock(int level, void** texture_data); - - // Notifies DX9 that the texture data has been updated. - virtual bool Unlock(int level); + // Overridden from Texture2D + virtual void SetRect(int level, + unsigned left, + unsigned top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); // Returns the implementation-specific texture handle for this texture. virtual void* GetTextureHandle() const { return d3d_texture_; } @@ -94,6 +95,13 @@ class Texture2DD3D9 : public Texture2D { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from Texture2D + virtual bool Lock(int level, void** texture_data, int* pitch); + + // Overridden from Texture2D + virtual bool Unlock(int level); + private: // Initializes the Texture2DD3D9 from a DX9 texture. Texture2DD3D9(ServiceLocator* service_locator, @@ -104,7 +112,7 @@ class Texture2DD3D9 : public Texture2D { // Updates a mip level, sending it from the backing bitmap to Direct3D, // rescaling it if resize_to_pot_ is set. - bool UpdateBackedMipLevel(unsigned int level); + void UpdateBackedMipLevel(unsigned int level); // A pointer to the Direct3D 2D texture object containing this texture. CComPtr<IDirect3DTexture9> d3d_texture_; @@ -130,13 +138,15 @@ class TextureCUBED3D9 : public TextureCUBE { virtual ~TextureCUBED3D9(); - // Locks the image buffer of a given face and mipmap level for loading from - // main memory. - bool Lock(CubeFace face, int level, void** texture_data); - - // Notifies DX9 that the image buffer of a given face and mipmap level has - // been updated. - bool Unlock(CubeFace face, int level); + // Overridden from TextureCUBE + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); // Returns the implementation-specific texture handle for this texture. virtual void* GetTextureHandle() const { return d3d_cube_texture_; } @@ -163,6 +173,13 @@ class TextureCUBED3D9 : public TextureCUBE { // RGBA to the internal format used by the rendering API. virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + protected: + // Overridden from TextureCUBE + bool Lock(CubeFace face, int level, void** texture_data, int* pitch); + + // Overridden from TextureCUBE + bool Unlock(CubeFace face, int level); + private: TextureCUBED3D9(ServiceLocator* service_locator, IDirect3DCubeTexture9* tex, @@ -172,7 +189,7 @@ class TextureCUBED3D9 : public TextureCUBE { // Updates a mip level, sending it from the backing bitmap to Direct3D, // rescaling it if resize_to_pot_ is set. - bool UpdateBackedMipLevel(unsigned int level, CubeFace face); + void UpdateBackedMipLevel(CubeFace face, unsigned int level); // A pointer to the Direct3D cube texture object containing this texture. CComPtr<IDirect3DCubeTexture9> d3d_cube_texture_; @@ -185,4 +202,4 @@ class TextureCUBED3D9 : public TextureCUBE { } // namespace o3d -#endif // O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H__ +#endif // O3D_CORE_WIN_D3D9_TEXTURE_D3D9_H_ diff --git a/o3d/plugin/build.scons b/o3d/plugin/build.scons index 53d4dcf..d0a251c 100644 --- a/o3d/plugin/build.scons +++ b/o3d/plugin/build.scons @@ -194,6 +194,7 @@ inputs = AUTOGEN_CC_FILES + [ 'cross/np_v8_bridge.cc', 'cross/out_of_memory.cc', 'cross/stream_manager.cc', + 'cross/texture_static_glue.cc', 'cross/config_common.cc', ] diff --git a/o3d/plugin/cross/texture_static_glue.cc b/o3d/plugin/cross/texture_static_glue.cc new file mode 100644 index 0000000..2e34786 --- /dev/null +++ b/o3d/plugin/cross/texture_static_glue.cc @@ -0,0 +1,388 @@ +/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <vector>
+#include "core/cross/pointer_utils.h"
+#include "core/cross/error.h"
+#include "core/cross/math_utilities.h"
+#include "core/cross/texture.h"
+
+namespace {
+
+void SetRectCheck(o3d::Texture* self,
+ void* data,
+ int pitch,
+ o3d::Texture::Format format,
+ int destination_x,
+ int destination_y,
+ int texture_width,
+ int texture_height,
+ int source_width,
+ int source_height,
+ const std::vector<float>& values) {
+ unsigned num_components;
+ unsigned swizzle[4] = {2, 1, 0, 3};
+ switch (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;
+ }
+
+ // clip
+ int source_x = 0;
+ int source_y = 0;
+ int copy_width = source_width;
+ int copy_height = source_height;
+
+ if (destination_x < 0) {
+ copy_width += destination_x;
+ source_x -= destination_x;
+ destination_x = 0;
+ }
+ if (destination_x + copy_width > static_cast<int>(texture_width)) {
+ copy_width -= destination_x + copy_width - texture_width;
+ }
+
+ if (destination_y < 0) {
+ copy_height += destination_y;
+ source_y -= destination_y;
+ destination_y = 0;
+ }
+ if (destination_y + copy_height > static_cast<int>(texture_height)) {
+ copy_height -= destination_y + copy_height - texture_height;
+ }
+
+ if (copy_width <= 0 || copy_height <= 0) {
+ return;
+ }
+
+ const float* source =
+ &values[0] +
+ (source_y * source_width + source_x) * num_components;
+ unsigned source_stride = (source_width - copy_width) * num_components;
+ switch (format) {
+ case o3d::Texture::ABGR16F: {
+ uint16* dest_line =
+ static_cast<uint16*>(data) +
+ (destination_y * texture_width + destination_x) * num_components;
+ while (copy_height > 0) {
+ uint16* destination = dest_line;
+ for (int xx = 0; xx < copy_width; ++xx) {
+ for (unsigned element = 0; element < num_components; ++element) {
+ destination[element] = Vectormath::Aos::FloatToHalf(
+ source[swizzle[element]]);
+ }
+ destination += num_components;
+ source += num_components;
+ }
+ dest_line = o3d::AddPointerOffset<uint16*>(dest_line, pitch);
+ source += source_stride;
+ --copy_height;
+ }
+ break;
+ }
+ case o3d::Texture::R32F:
+ case o3d::Texture::ABGR32F: {
+ float* dest_line =
+ static_cast<float*>(data) +
+ (destination_y * texture_width + destination_x) * num_components;
+ while (copy_height > 0) {
+ float* destination = dest_line;
+ for (int xx = 0; xx < copy_width; ++xx) {
+ for (unsigned element = 0; element < num_components; ++element) {
+ destination[element] = source[swizzle[element]];
+ }
+ destination += num_components;
+ source += num_components;
+ }
+ dest_line = o3d::AddPointerOffset<float*>(dest_line, pitch);
+ source += source_stride;
+ --copy_height;
+ }
+ break;
+ }
+ default: {
+ uint8* dest_line =
+ static_cast<uint8*>(data) +
+ (destination_y * texture_width + destination_x) * 4;
+ while (copy_height > 0) {
+ uint8* destination = dest_line;
+ for (int xx = 0; xx < copy_width; ++xx) {
+ destination[0] = static_cast<unsigned char>(
+ source[swizzle[0]] * 255.0f);
+ destination[1] = static_cast<unsigned char>(
+ source[swizzle[1]] * 255.0f);
+ destination[2] = static_cast<unsigned char>(
+ source[swizzle[2]] * 255.0f);
+ destination[3] = (num_components == 4) ?
+ static_cast<unsigned char>(source[swizzle[3]] * 255.0f) : 255;
+ destination += 4;
+ source += num_components;
+ }
+ dest_line = o3d::AddPointerOffset<uint8*>(dest_line, pitch);
+ source += source_stride;
+ --copy_height;
+ }
+ break;
+ }
+ }
+}
+
+void SetRectCheck2D(o3d::Texture2D* self,
+ int level,
+ int destination_x,
+ int destination_y,
+ int source_width,
+ const std::vector<float>& values,
+ bool check_needed) {
+ if (level < 0 || level >= self->levels()) {
+ O3D_ERROR(self->service_locator())
+ << "level (" << level << " out of range";
+ return;
+ }
+ if (values.empty() || source_width <= 0) {
+ return;
+ }
+ unsigned num_values = values.size();
+ unsigned texture_width = std::max(self->width() >> level, 1);
+ unsigned texture_height = std::max(self->height() >> level, 1);
+ 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;
+ }
+ if (num_values % num_components != 0) {
+ O3D_ERROR(self->service_locator())
+ << "The number of elements passed in must be a multiple of "
+ << num_components;
+ }
+ unsigned num_elements = num_values / num_components;
+ if (num_elements % source_width != 0) {
+ O3D_ERROR(self->service_locator())
+ << "The number of elements passed in must be a multiple of the "
+ << "width";
+ return;
+ }
+ unsigned source_height = num_elements / source_width;
+ if (check_needed) {
+ unsigned needed = num_components * texture_width * texture_height;
+ if (num_values != needed) {
+ O3D_ERROR(self->service_locator())
+ << "needed " << needed << " values but " << num_values
+ << " passed in.";
+ return;
+ }
+ }
+ o3d::Texture2D::LockHelper helper(self, level);
+ void* data = helper.GetData();
+ if (!data) {
+ O3D_ERROR(self->service_locator()) << "could not lock texture";
+ return;
+ }
+
+ SetRectCheck(self, data, helper.pitch(), self->format(),
+ destination_x, destination_y,
+ texture_width, texture_height,
+ source_width, source_height,
+ values);
+}
+
+void SetRectCheckCUBE(o3d::TextureCUBE* self,
+ o3d::TextureCUBE::CubeFace face,
+ int level,
+ int destination_x,
+ int destination_y,
+ int source_width,
+ const std::vector<float>& values,
+ bool check_needed) {
+ if (level < 0 || level >= self->levels()) {
+ O3D_ERROR(self->service_locator())
+ << "level (" << level << " out of range";
+ return;
+ }
+ if (values.empty() || source_width <= 0) {
+ return;
+ }
+ unsigned num_values = values.size();
+ unsigned texture_width = std::max(self->edge_length() >> level, 1);
+ unsigned texture_height = texture_width;
+ 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;
+ }
+ if (num_values % num_components != 0) {
+ O3D_ERROR(self->service_locator())
+ << "The number of elements passed in must be a multiple of "
+ << num_components;
+ }
+ unsigned num_elements = num_values / num_components;
+ if (num_elements % source_width != 0) {
+ O3D_ERROR(self->service_locator())
+ << "The number of elements passed in must be a multiple of the "
+ << "width";
+ return;
+ }
+ unsigned source_height = num_elements / source_width;
+ if (check_needed) {
+ unsigned needed = num_components * texture_width * texture_height;
+ if (num_values != needed) {
+ O3D_ERROR(self->service_locator())
+ << "needed " << needed << " values but " << num_values
+ << " passed in.";
+ return;
+ }
+ }
+ o3d::TextureCUBE::LockHelper helper(self, face, level);
+ void* data = helper.GetData();
+ if (!data) {
+ O3D_ERROR(self->service_locator()) << "could not lock texture";
+ return;
+ }
+
+ SetRectCheck(self, data, helper.pitch(), self->format(),
+ destination_x, destination_y,
+ texture_width, texture_height,
+ source_width, source_height,
+ values);
+}
+
+} // anonymous namespace
+
+namespace glue {
+namespace namespace_o3d {
+namespace class_Texture2D {
+
+void userglue_method_SetRect(o3d::Texture2D* self,
+ int level,
+ int destination_x,
+ int destination_y,
+ int source_width,
+ const std::vector<float>& values) {
+ SetRectCheck2D(self,
+ level,
+ destination_x,
+ destination_y,
+ source_width,
+ values,
+ false);
+}
+void userglue_method_Set(o3d::Texture2D* self,
+ int level,
+ const std::vector<float>& values) {
+ SetRectCheck2D(self, level, 0, 0, self->width(), values, true);
+}
+
+} // namespace class_Texture2D
+
+namespace class_TextureCUBE {
+
+void userglue_method_SetRect(o3d::TextureCUBE* self,
+ o3d::TextureCUBE::CubeFace face,
+ int level,
+ int destination_x,
+ int destination_y,
+ int source_width,
+ const std::vector<float>& values) {
+ SetRectCheckCUBE(self,
+ face,
+ level,
+ destination_x,
+ destination_y,
+ source_width,
+ values,
+ false);
+}
+void userglue_method_Set(o3d::TextureCUBE* self,
+ o3d::TextureCUBE::CubeFace face,
+ int level,
+ const std::vector<float>& values) {
+ SetRectCheckCUBE(self, face, level, 0, 0, self->edge_length(), values, true);
+}
+
+
+} // namespace class_Texture2D
+
+} // namespace namespace_o3d
+} // namespace glue
+
diff --git a/o3d/plugin/idl/texture.idl b/o3d/plugin/idl/texture.idl index 81d1a5c..763518e 100644 --- a/o3d/plugin/idl/texture.idl +++ b/o3d/plugin/idl/texture.idl @@ -138,7 +138,7 @@ namespace o3d { %] RenderSurface? GetRenderSurface(int mip_level, Pack pack); - // TODO: Add Get, GetRect and/or expose Bitmap. + // TODO: Add Get, GetRect. %[ Sets the values of the data stored in the texture. @@ -166,7 +166,7 @@ namespace o3d { \param level the mip level to update. \param values Values to be stored in the buffer. %] - [nocpp, userglue, include="core/cross/math_utilities.h"] + [nocpp, userglue] void Set(int level, float[] values); %[ @@ -187,7 +187,7 @@ namespace o3d { \param values Values to be stored in the buffer. \see o3d.Texture2D.set %] - [nocpp, userglue, include="core/cross/math_utilities.h"] + [nocpp, userglue] void SetRect(int level, int destination_x, int destination_y, @@ -213,204 +213,6 @@ namespace o3d { int source_width, int source_height, int dest_x, int dest_y, int dest_width, int dest_height, int dest_mip); - - [verbatim=cpp_glue] %{ - void SetRectCheck(o3d::Texture2D* self, - int level, - int destination_x, - int destination_y, - int source_width, - const std::vector<float>& values, - bool check_needed) { - if (level < 0 || level >= self->levels()) { - O3D_ERROR(self->service_locator()) - << "level (" << level << " out of range"; - return; - } - if (values.empty() || source_width <= 0) { - return; - } - unsigned num_values = values.size(); - unsigned texture_width = std::max(self->width() >> level, 1); - unsigned texture_height = std::max(self->height() >> level, 1); - 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: - O3D_ERROR(self->service_locator()) - << "Texture::Set not supported for this type of texture"; - return; - } - if (num_values % num_components != 0) { - O3D_ERROR(self->service_locator()) - << "The number of elements passed in must be a multiple of " - << num_components; - } - unsigned num_elements = num_values / num_components; - if (num_elements % source_width != 0) { - O3D_ERROR(self->service_locator()) - << "The number of elements passed in must be a multiple of the " - << "width"; - return; - } - unsigned source_height = num_elements / source_width; - if (check_needed) { - unsigned needed = num_components * texture_width * texture_height; - if (num_values != needed) { - O3D_ERROR(self->service_locator()) - << "needed " << needed << " values but " << num_values - << " passed in."; - return; - } - } - - // clip - int source_x = 0; - int source_y = 0; - int copy_width = source_width; - int copy_height = source_height; - - if (destination_x < 0) { - copy_width += destination_x; - source_x -= destination_x; - destination_x = 0; - } - if (destination_x + copy_width > static_cast<int>(texture_width)) { - copy_width -= destination_x + copy_width - texture_width; - } - - if (destination_y < 0) { - copy_height += destination_y; - source_y -= destination_y; - destination_y = 0; - } - if (destination_y + copy_height > static_cast<int>(texture_height)) { - copy_height -= destination_y + copy_height - texture_height; - } - - if (copy_width <= 0 || copy_height <= 0) { - return; - } - - void* data; - if (!self->Lock(level, &data)) { - O3D_ERROR(self->service_locator()) << "could not lock texture"; - return; - } - const float* source = - &values[0] + - (source_y * source_width + source_x) * num_components; - unsigned source_stride = (source_width - copy_width) * num_components; - unsigned destination_stride = - (texture_width - copy_width) * num_components; - switch (self->format()) { - case o3d::Texture::ABGR16F: { - unsigned short* destination = - static_cast<unsigned short*>(data) + - (destination_y * texture_width + destination_x) * num_components; - while (copy_height > 0) { - for (int xx = 0; xx < copy_width; ++xx) { - for (unsigned element = 0; element < num_components; ++element) { - destination[element] = Vectormath::Aos::FloatToHalf( - source[swizzle[element]]); - } - destination += num_components; - source += num_components; - } - destination += destination_stride; - source += source_stride; - --copy_height; - } - break; - } - case o3d::Texture::R32F: - case o3d::Texture::ABGR32F: { - float* destination = - static_cast<float*>(data) + - (destination_y * texture_width + destination_x) * num_components; - while (copy_height > 0) { - for (int xx = 0; xx < copy_width; ++xx) { - for (unsigned element = 0; element < num_components; ++element) { - destination[element] = source[swizzle[element]]; - } - destination += num_components; - source += num_components; - } - destination += destination_stride; - source += source_stride; - --copy_height; - } - break; - } - default: { - destination_stride = (texture_width - copy_width) * 4; - uint8* destination = - static_cast<uint8*>(data) + - (destination_y * texture_width + destination_x) * 4; - while (copy_height > 0) { - for (int xx = 0; xx < copy_width; ++xx) { - destination[0] = static_cast<unsigned char>( - source[swizzle[0]] * 255.0f); - destination[1] = static_cast<unsigned char>( - source[swizzle[1]] * 255.0f); - destination[2] = static_cast<unsigned char>( - source[swizzle[2]] * 255.0f); - destination[3] = (num_components == 4) ? - static_cast<unsigned char>(source[swizzle[3]] * 255.0f) : 255; - destination += 4; - source += num_components; - } - destination += destination_stride; - source += source_stride; - --copy_height; - } - break; - } - } - if (!self->Unlock(level)) { - O3D_ERROR(self->service_locator()) << "could not unlock texture"; - } - } - void userglue_method_SetRect(o3d::Texture2D* self, - int level, - int destination_x, - int destination_y, - int source_width, - const std::vector<float>& values) { - SetRectCheck(self, - level, - destination_x, - destination_y, - source_width, - values, - false); - } - void userglue_method_Set(o3d::Texture2D* self, - int level, - const std::vector<float>& values) { - SetRectCheck(self, level, 0, 0, self->width(), values, true); - } - %} }; // Texture2D @@ -458,6 +260,48 @@ namespace o3d { RenderSurface? GetRenderSurface(CubeFace face, int mip_level, Pack pack); %[ + Sets the values of the data stored in the texture. + + It is not recommend that you call this for large textures but it is useful + for making simple ramps or noise textures for shaders. + + See o3d.Texture2D.set for details on formats. + + \param face the face to update. + \param level the mip level to update. + \param values Values to be stored in the buffer. + %] + [nocpp, userglue] + void Set(CubeFace face, int level, float[] values); + + %[ + Sets a rectangular area of values in a texture. + + Does clipping. In other words if you pass in a 10x10 pixel array + and give it destination of (-5, -5) it will only use the bottom 5x5 + pixels of the array you passed in to set the top 5x5 pixels of the + texture. + + See o3d.Texture2D.set for details on formats. + + \param face the face to update. + \param level the mip level to update. + \param destination_x The x coordinate of the area in the texture to affect. + \param destination_y The y coordinate of the area in the texture to affect. + \param source_width The width of the area to effect. The height is + determined by the size of the array passed in. + \param values Values to be stored in the buffer. + \see o3d.Texture2D.set + %] + [nocpp, userglue] + void SetRect(CubeFace face, + int level, + int destination_x, + int destination_y, + int source_width, + float[] values); + + %[ 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/plugin/plugin.gyp b/o3d/plugin/plugin.gyp index f241187..dc9d65b 100644 --- a/o3d/plugin/plugin.gyp +++ b/o3d/plugin/plugin.gyp @@ -61,6 +61,7 @@ 'cross/plugin_main.h', 'cross/stream_manager.cc', 'cross/stream_manager.h', + 'cross/texture_static_glue.cc', ], 'conditions' : [ ['OS != "linux"', diff --git a/o3d/tests/build.scons b/o3d/tests/build.scons index 7b4be1c..c9bc853 100644 --- a/o3d/tests/build.scons +++ b/o3d/tests/build.scons @@ -229,6 +229,8 @@ tests = [ 'core/cross/state_test.cc', 'core/cross/stream_bank_test.cc', 'core/cross/stream_test.cc', + 'core/cross/texture_base_test.cc', + 'core/cross/texture_test.cc', 'core/cross/transform_test.cc', 'core/cross/tree_traversal_test.cc', 'core/cross/vector_map_test.cc', |