diff options
author | gman@google.com <gman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-03 22:56:18 +0000 |
---|---|---|
committer | gman@google.com <gman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-03 22:56:18 +0000 |
commit | aa078b67d3d8e976ffeee26e2dcccb6e938b7008 (patch) | |
tree | 3f9edf829005027d107a5f20527a78780386f135 /o3d | |
parent | 04c2262254185379aa762a69ab4410f3aedab9af (diff) | |
download | chromium_src-aa078b67d3d8e976ffeee26e2dcccb6e938b7008.zip chromium_src-aa078b67d3d8e976ffeee26e2dcccb6e938b7008.tar.gz chromium_src-aa078b67d3d8e976ffeee26e2dcccb6e938b7008.tar.bz2 |
Add SetRect to Texture2d and TextureCUBE
Originally I was going to replace Lock/Unlock with
only SetRect (and maybe GetRect) and I did that.
But then as I was going through the code things started
not being so cool. The JavaScript texture.SetRect where
as before it allocated no memory, now it needed to allocate
a temporary buffer just to be able to write the texture.
DrawImage also would have have required copying the original
texture out using GetRect, then modifying it, then copying it
back even in D3D or it would have required a temporary buffer.
That's what happens under the hood in the GL implementation
of Texture2D but why should D3D suffer because of the of GL?
Or maybe it's a reasonable compomise the D3D should go slower
so that GL isn't twice as slow as it it could be?
So, I left Lock/Unlock and added SetRect as well.
This CL should help the video guys and we can decide if
we want to get rid of Lock/Unlock later.
SetRect is really only there because trying to make GL
act like D3D makes GL slow since we had to get a copy
of the texture from GL before Locking. SetRect, since
it is not exposing the original data doesn't need to do
that.
So, now there are both. If need to read (and write) to
the texture use Lock. If you only need to write use
SetRect.
I also fixed Lock/Unlock so they return a pitch which
is required for D3D. While I did that I made Lock/Unlock
private so you have to use the TextureXXXLockHelpers
to access the data.
I didn't do the command buffer versions.
I also added stubs for texture tests. We need to add
more tests.
I feel like a lot of clean up needs to happen.
It seesm like Bitmap should be split into
Bitmap2D
BitmapCUBE
BitmapVolume
Possibly. And maybe BitmapCube should effectively
just be typedef Bitmap2D BitmapCUBE[6] and
BitmapVolume would be std::vector<Bitmap2D>
Then we wouldn't need face and volume versions
of various functions. You'd just get the face or
the slice Bitma2D and then use the 2D functions.
We should decide if that's what we want before
pushing a new release. Otherwise we should remove
bitmap.idl from idl_scons.lst and the one sample
And ship without exposing Bitmap until we are
sure they API we are exposing is the one we want.
Review URL: http://codereview.chromium.org/159725
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22332 0039d316-1c4b-4281-b951-d872f2087c98
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', |