diff options
27 files changed, 1360 insertions, 333 deletions
diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc index 8f82dc1..f2c96a0 100644 --- a/o3d/core/cross/bitmap.cc +++ b/o3d/core/cross/bitmap.cc @@ -74,6 +74,13 @@ size_t Bitmap::GetMipSize(unsigned int level) const { return image::ComputeMipChainSize(mip_width, mip_height, format(), 1); } +size_t Bitmap::ComputeMaxSize( + unsigned width, unsigned height, Texture::Format format) { + return image::ComputeMipChainSize( + width, height, format, + image::ComputeMipMapCount(width, height)); +} + void Bitmap::SetContents(Texture::Format format, unsigned int num_mipmaps, unsigned int width, @@ -138,6 +145,15 @@ uint8 *Bitmap::GetMipData(unsigned int level) const { return data + GetMipChainSize(level); } +uint8 *Bitmap::GetPixelData( + unsigned int level, unsigned int x, unsigned int y) const { + uint8* data = GetMipData(level); + if (data) { + data += GetMipPitch(level) * y + image::ComputePitch(format(), 1) * x; + } + return data; +} + void Bitmap::SetRect( int level, unsigned dst_left, @@ -157,10 +173,7 @@ void Bitmap::SetRect( src_width == mip_width && src_height == mip_height; DCHECK(!compressed || entire_rect); - uint8* dst = - GetMipData(level) + - image::ComputePitch(format(), mip_width) * dst_top + - image::ComputePitch(format(), dst_left); + uint8* dst = GetPixelData(level, dst_left, dst_top); const uint8* src = static_cast<const uint8*>(src_data); if (!compressed) { @@ -322,26 +335,37 @@ bool Bitmap::LoadFromRawData(RawData *raw_data, } void Bitmap::DrawImage(const Bitmap& src_img, + int src_level, int src_x, int src_y, int src_width, int src_height, + int dst_level, int dst_x, int dst_y, int dst_width, int dst_height) { DCHECK(src_img.image_data()); DCHECK(image_data()); + if (dst_level < 0 || dst_level >= num_mipmaps()) { + O3D_ERROR(service_locator()) << "Destination Mip out of range"; + } + + if (src_level < 0 || src_level >= src_img.num_mipmaps()) { + O3D_ERROR(service_locator()) << "Source Mip out of range"; + } + // Clip source and destination rectangles to // source and destination bitmaps. // if src or dest rectangle is out of boundary, // do nothing and return. if (!image::AdjustDrawImageBoundary(&src_x, &src_y, &src_width, &src_height, + src_level, src_img.width_, src_img.height_, &dst_x, &dst_y, &dst_width, &dst_height, + dst_level, width_, height_)) return; - unsigned int components = 0; // check formats of source and dest images. // format of source and dest should be the same. if (src_img.format_ != format_) { @@ -355,16 +379,13 @@ void Bitmap::DrawImage(const Bitmap& src_img, src_img.width_ == width_ && src_img.height_ == height_ && src_width == src_img.width_ && src_height == src_img.height_ && dst_width == width_ && dst_height == height_) { - memcpy(image_data(), src_img.image_data(), GetTotalSize()); + SetRect(dst_level, 0, 0, dst_width, dst_height, + src_img.GetMipData(src_level), src_img.GetMipPitch(src_level)); return; } - // if drawImage is not copying the whole bitmap, we need to check - // the format. currently only support XRGB8 and ARGB8 - if (src_img.format_ == Texture::XRGB8 || - src_img.format_ == Texture::ARGB8) { - components = 4; - } else { + unsigned int components = image::GetNumComponentsForFormat(format_); + if (components == 0) { O3D_ERROR(service_locator()) << "DrawImage does not support format: " << src_img.format_ << " unless src and " << "dest images are in the same size and " @@ -372,22 +393,30 @@ void Bitmap::DrawImage(const Bitmap& src_img, return; } - uint8* src_img_data = src_img.image_data(); - uint8* dst_img_data = image_data(); + int src_pitch = src_img.GetMipPitch(src_level); + if (image::AdjustForSetRect(&src_y, src_width, src_height, &src_pitch, + &dst_y, dst_width, &dst_height)) { + SetRect(dst_level, dst_x, dst_y, dst_width, dst_height, + src_img.GetPixelData(src_level, src_x, src_y), + src_pitch); + return; + } // crop part of image from src img, scale it in // bilinear interpolation fashion, and paste it // on dst img. - image::LanczosScale(src_img_data, src_x, src_y, + image::LanczosScale(src_img.format_, + src_img.GetMipData(src_level), + src_img.GetMipPitch(src_level), + src_x, src_y, src_width, src_height, - src_img.width_, src_img.height_, - dst_img_data, width_ * components, + GetMipData(dst_level), + GetMipPitch(dst_level), dst_x, dst_y, dst_width, dst_height, - width_, height_, components); + components); } - void Bitmap::GenerateMips(int source_level, int num_levels) { if (source_level >= static_cast<int>(num_mipmaps()) || source_level < 0) { O3D_ERROR(service_locator()) << "source level out of range."; diff --git a/o3d/core/cross/bitmap.h b/o3d/core/cross/bitmap.h index 2188fc9c..4710cb6 100644 --- a/o3d/core/cross/bitmap.h +++ b/o3d/core/cross/bitmap.h @@ -149,6 +149,11 @@ class Bitmap : public ParamObject { // level: mip level to get.
uint8 *GetMipData(unsigned int level) const;
+ // Gets the address of a particular pixel.
+ // Parameters:
+ // level: mip level to get.
+ uint8 *GetPixelData(unsigned int level, unsigned int x, unsigned int y) const;
+
// Gets the size of mip.
size_t GetMipSize(unsigned int level) const;
@@ -208,16 +213,20 @@ class Bitmap : public ParamObject { // and dest do not match.
// Parameters:
// source_img: source bitmap which would be drawn.
+ // source_level: level to draw.
// source_x: x-coordinate of the starting pixel in the source image.
// source_x: y-coordinate of the starting pixel in the source image.
// source_width: width of the source image to draw.
// source_height: Height of the source image to draw.
+ // dest_level: level to target.
// dest_x: x-coordinate of the starting pixel in the dest image.
// dest_y: y-coordinate of the starting pixel in the dest image.
// dest_width: width of the dest image to draw.
// dest_height: height of the dest image to draw.
- void DrawImage(const Bitmap& source_img, int source_x, int source_y,
+ void DrawImage(const Bitmap& source_img, int source_level,
+ int source_x, int source_y,
int source_width, int source_height,
+ int dest_level,
int dest_x, int dest_y,
int dest_width, int dest_height);
@@ -296,6 +305,9 @@ class Bitmap : public ParamObject { return GetMipChainSize(image::ComputeMipMapCount(width_, height_));
}
+ static size_t ComputeMaxSize(
+ unsigned width, unsigned height, Texture::Format format);
+
// pointer to the raw bitmap data
// NOTE: image_data_ is either NULL or it has space for the maximum number
// of mips for the current size bitmap, even if they are not used.
diff --git a/o3d/core/cross/bitmap_dds.cc b/o3d/core/cross/bitmap_dds.cc index 2c4f8a9..5625ed2 100644 --- a/o3d/core/cross/bitmap_dds.cc +++ b/o3d/core/cross/bitmap_dds.cc @@ -449,8 +449,9 @@ bool Bitmap::LoadFromDDSStream(ServiceLocator* service_locator, } unsigned int num_bitmaps = is_cubemap ? 6 : 1; - size_t face_size = image::ComputeMipChainSize( - dds_width, dds_height, format, mip_count); + // Bitmap requires we allocate enough memory for all mips even if we don't use + // them. + size_t face_size = Bitmap::ComputeMaxSize(dds_width, dds_height, format); BitmapRefArray temp_bitmaps; diff --git a/o3d/core/cross/bitmap_jpg.cc b/o3d/core/cross/bitmap_jpg.cc index 7b84490..21bc59a5 100644 --- a/o3d/core/cross/bitmap_jpg.cc +++ b/o3d/core/cross/bitmap_jpg.cc @@ -200,8 +200,9 @@ bool Bitmap::LoadFromJPEGStream(ServiceLocator* service_locator, } unsigned int image_components = 4; Texture::Format format = Texture::XRGB8; - // Allocate storage for the pixels. - size_t image_size = image::ComputeMipChainSize(width, height, format, 1); + // Allocate storage for the pixels. Bitmap requires we allocate enough + // memory for all mips even if we don't use them. + size_t image_size = Bitmap::ComputeMaxSize(width, height, format); image_data.reset(new uint8[image_size]); if (image_data.get() == NULL) { DLOG(ERROR) << "JPEG memory allocation error \"" << filename << "\""; @@ -249,13 +250,13 @@ bool Bitmap::LoadFromJPEGStream(ServiceLocator* service_locator, // copy the scanline to its final destination for (unsigned int i = 0; i < width; ++i) { // RGB -> BGRX - image_write_ptr[i*image_components+0] = - buffer[0][i*cinfo.output_components+2]; - image_write_ptr[i*image_components+1] = - buffer[0][i*cinfo.output_components+1]; - image_write_ptr[i*image_components+2] = - buffer[0][i*cinfo.output_components+0]; - image_write_ptr[i*image_components+3] = 0xff; + image_write_ptr[i * image_components + 0] = + buffer[0][i * cinfo.output_components + 2]; + image_write_ptr[i * image_components + 1] = + buffer[0][i * cinfo.output_components + 1]; + image_write_ptr[i * image_components + 2] = + buffer[0][i * cinfo.output_components + 0]; + image_write_ptr[i * image_components + 3] = 0xff; } } diff --git a/o3d/core/cross/bitmap_png.cc b/o3d/core/cross/bitmap_png.cc index 9e30011..fc2b56a 100644 --- a/o3d/core/cross/bitmap_png.cc +++ b/o3d/core/cross/bitmap_png.cc @@ -226,9 +226,9 @@ bool Bitmap::LoadFromPNGStream(ServiceLocator* service_locator, // selected such a transform above). png_read_update_info(png_ptr, info_ptr); - // Allocate storage for the pixels. - size_t png_image_size = - image::ComputeMipChainSize(png_width, png_height, format, 1); + // Allocate storage for the pixels. Bitmap requires we allocate enough + // memory for all mips even if we don't use them. + size_t png_image_size = Bitmap::ComputeMaxSize(png_width, png_height, format); image_data.reset(new uint8[png_image_size]); if (image_data.get() == NULL) { DLOG(ERROR) << "PNG image memory allocation error \"" << filename << "\""; diff --git a/o3d/core/cross/bitmap_test.cc b/o3d/core/cross/bitmap_test.cc index 7bebafb..b094188 100644 --- a/o3d/core/cross/bitmap_test.cc +++ b/o3d/core/cross/bitmap_test.cc @@ -740,20 +740,20 @@ static uint8 kpng_8x4_drawImage[128] = { static uint8 kpng_8x4_drawImage_top_left[128] = { // expected result of drawimage on top left corner of dest image. - 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, - 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x36, 0x6c, 0xd8, 0xff, 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, - 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, - 0x2e, 0x2e, 0x2e, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x26, 0x4c, 0x98, 0xff, 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, - 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, - 0x2a, 0x2a, 0x2a, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x22, 0x22, 0x22, 0xff, 0x16, 0x2c, 0x58, 0xff, 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, - 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, - 0x26, 0x26, 0x26, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, }; @@ -860,20 +860,20 @@ static uint8 kpng_8x4_drawImage_bottom[128] = { static uint8 kpng_8x4_drawImage_bottom_left[128] = { // expected result of drawimage on bottom left corner of dest image. - 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, - 0x2a, 0x2a, 0x2a, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, - 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, - 0x26, 0x26, 0x26, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x2e, 0x2e, 0x2e, 0xff, 0x26, 0x4c, 0x98, 0xff, 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, - 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, - 0x22, 0x22, 0x22, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x16, 0x2c, 0x58, 0xff, 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, - 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, - 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x06, 0x0c, 0x18, 0xff, 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, }; @@ -940,22 +940,22 @@ static uint8 kpng_8x4_drawImage_scale_down[128] = { static uint8 kpng_8x4_drawImage_scale_out[128] = { // expected result of scale src image larger than dest image. - 0x64, 0x64, 0x64, 0xff, 0x66, 0x66, 0x66, 0xff, - 0x68, 0x68, 0x68, 0xff, 0x6a, 0x6a, 0x6a, 0xff, - 0x6d, 0x6d, 0x6d, 0xff, 0x6f, 0x6f, 0x6f, 0xff, - 0x71, 0x71, 0x71, 0xff, 0x73, 0x73, 0x73, 0xff, - 0x58, 0x58, 0x58, 0xff, 0x5a, 0x5a, 0x5a, 0xff, - 0x5c, 0x5c, 0x5c, 0xff, 0x5e, 0x5e, 0x5e, 0xff, - 0x61, 0x61, 0x61, 0xff, 0x63, 0x63, 0x63, 0xff, - 0x65, 0x65, 0x65, 0xff, 0x67, 0x67, 0x67, 0xff, - 0x3e, 0x3e, 0x3e, 0xff, 0x40, 0x40, 0x40, 0xff, - 0x42, 0x42, 0x42, 0xff, 0x44, 0x44, 0x44, 0xff, - 0x47, 0x47, 0x47, 0xff, 0x49, 0x49, 0x49, 0xff, - 0x4b, 0x4b, 0x4b, 0xff, 0x4d, 0x4d, 0x4d, 0xff, - 0x32, 0x32, 0x32, 0xff, 0x34, 0x34, 0x34, 0xff, - 0x36, 0x36, 0x36, 0xff, 0x38, 0x38, 0x38, 0xff, - 0x3b, 0x3b, 0x3b, 0xff, 0x3d, 0x3d, 0x3d, 0xff, - 0x3f, 0x3f, 0x3f, 0xff, 0x41, 0x41, 0x41, 0xff, + 0x7c, 0x7c, 0x7c, 0xff, 0x7e, 0x7e, 0x7e, 0xff, + 0x80, 0x80, 0x80, 0xff, 0x82, 0x82, 0x82, 0xff, + 0x85, 0x85, 0x85, 0xff, 0x87, 0x87, 0x87, 0xff, + 0x89, 0x89, 0x89, 0xff, 0x8b, 0x8b, 0x8b, 0xff, + 0x70, 0x70, 0x70, 0xff, 0x72, 0x72, 0x72, 0xff, + 0x74, 0x74, 0x74, 0xff, 0x76, 0x76, 0x76, 0xff, + 0x79, 0x79, 0x79, 0xff, 0x7b, 0x7b, 0x7b, 0xff, + 0x7d, 0x7d, 0x7d, 0xff, 0x7f, 0x7f, 0x7f, 0xff, + 0x56, 0x56, 0x56, 0xff, 0x58, 0x58, 0x58, 0xff, + 0x5a, 0x5a, 0x5a, 0xff, 0x5c, 0x5c, 0x5c, 0xff, + 0x5f, 0x5f, 0x5f, 0xff, 0x61, 0x61, 0x61, 0xff, + 0x63, 0x63, 0x63, 0xff, 0x65, 0x65, 0x65, 0xff, + 0x4a, 0x4a, 0x4a, 0xff, 0x4c, 0x4c, 0x4c, 0xff, + 0x4e, 0x4e, 0x4e, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x53, 0x53, 0x53, 0xff, 0x55, 0x55, 0x55, 0xff, + 0x57, 0x57, 0x57, 0xff, 0x59, 0x59, 0x59, 0xff, }; static uint8 kpng_8x4_drawImage_flip[128] = { @@ -1042,7 +1042,8 @@ TEST_F(BitmapTest, DrawImage) { // test whether the raw image is loaded correctly or not. EXPECT_TRUE(bitmap_dest_top_left->image_data() != NULL); EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_left, kpng_8x4_drawImage)); - bitmap_dest_top_left->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, -1, -1, 4, 4); + bitmap_dest_top_left->DrawImage( + *bitmap_4x4_src, 0, 0, 0, 4, 4, 0, -1, -1, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_left, kpng_8x4_drawImage_top_left)); @@ -1053,7 +1054,7 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_top(bitmaps[0]); - bitmap_dest_top->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 2, -2, 4, 4); + bitmap_dest_top->DrawImage(*bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 2, -2, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_top, kpng_8x4_drawImage_top)); // test draw image on top right boundary. @@ -1063,7 +1064,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_top_right(bitmaps[0]); - bitmap_dest_top_right->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 5, -1, 4, 4); + bitmap_dest_top_right->DrawImage( + *bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 5, -1, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_right, kpng_8x4_drawImage_top_right)); @@ -1074,7 +1076,7 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_right(bitmaps[0]); - bitmap_dest_right->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 5, 0, 4, 4); + bitmap_dest_right->DrawImage(*bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 5, 0, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_right, kpng_8x4_drawImage_right)); // test draw image on bottom right boundary. @@ -1084,7 +1086,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_bottom_right(bitmaps[0]); - bitmap_dest_bottom_right->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 5, 1, 4, 4); + bitmap_dest_bottom_right->DrawImage( + *bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 5, 1, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom_right, kpng_8x4_drawImage_bottom_right)); @@ -1095,7 +1098,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_bottom(bitmaps[0]); - bitmap_dest_bottom->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 2, 1, 4, 4); + bitmap_dest_bottom->DrawImage( + *bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 2, 1, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom, kpng_8x4_drawImage_bottom)); // test draw image on bottom left boundary. @@ -1105,7 +1109,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_bottom_left(bitmaps[0]); - bitmap_dest_bottom_left->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, -1, 1, 4, 4); + bitmap_dest_bottom_left->DrawImage( + *bitmap_4x4_src, 0, 0, 0, 4, 4, 0, -1, 1, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom_left, kpng_8x4_drawImage_bottom_left)); @@ -1116,7 +1121,7 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_left(bitmaps[0]); - bitmap_dest_left->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, -1, 0, 4, 4); + bitmap_dest_left->DrawImage(*bitmap_4x4_src, 0, 0, 0, 4, 4, 0, -1, 0, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_left, kpng_8x4_drawImage_left)); // test scale up. @@ -1126,7 +1131,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_scale_up(bitmaps[0]); - bitmap_dest_scale_up->DrawImage(*bitmap_2x2_src, 0, 0, 2, 2, 0, 0, 8, 4); + bitmap_dest_scale_up->DrawImage( + *bitmap_2x2_src, 0, 0, 0, 2, 2, 0, 0, 0, 8, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_up, kpng_8x4_drawImage_scale_up)); @@ -1137,7 +1143,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_scale_down(bitmaps[0]); - bitmap_dest_scale_down->DrawImage(*bitmap_8x8_src, 0, 0, 8, 8, 0, 0, 4, 4); + bitmap_dest_scale_down->DrawImage( + *bitmap_8x8_src, 0, 0, 0, 8, 8, 0, 0, 0, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_down, kpng_8x4_drawImage_scale_down)); @@ -1148,7 +1155,8 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_scale_out(bitmaps[0]); - bitmap_dest_scale_out->DrawImage(*bitmap_8x8_src, 0, 0, 8, 8, -2, -4, 12, 12); + bitmap_dest_scale_out->DrawImage( + *bitmap_8x8_src, 0, 0, 0, 8, 8, 0, -2, -4, 12, 12); EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_out, kpng_8x4_drawImage_scale_out)); @@ -1159,7 +1167,7 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_dest_flip(bitmaps[0]); - bitmap_dest_flip->DrawImage(*bitmap_4x4_src, 0, 0, 4, 4, 5, 3, -4, -4); + bitmap_dest_flip->DrawImage(*bitmap_4x4_src, 0, 0, 0, 4, 4, 0, 5, 3, -4, -4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_flip, kpng_8x4_drawImage_flip)); // test draw image on argb8 format. @@ -1181,8 +1189,58 @@ TEST_F(BitmapTest, DrawImage) { image::PNG, &bitmaps)); ASSERT_EQ(1u, bitmaps.size()); Bitmap::Ref bitmap_src_argb8(bitmaps[0]); - bitmap_dest_argb8->DrawImage(*bitmap_src_argb8, 0, 0, 4, 4, 0, 0, 4, 4); + bitmap_dest_argb8->DrawImage(*bitmap_src_argb8, 0, 0, 0, 4, 4, 0, 0, 0, 4, 4); EXPECT_TRUE(TestBitmapData(*bitmap_dest_argb8, kpng_8x4_drawImage_argb8)); } +TEST_F(BitmapTest, SetRect) { + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + const int kWidth = 8; + const int kHeight = 8; + const int kLevels = 2; + const int kDestMip = 1; + const unsigned kDestX = 1u; + const unsigned kDestY = 1u; + bitmap->Allocate(o3d::Texture::R32F, kWidth, kHeight, kLevels, Bitmap::IMAGE); + const float* pixels = + reinterpret_cast<const float*>(bitmap->GetMipData(kDestMip)); + static const float kExpected1[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + }; + EXPECT_EQ(0, memcmp(pixels, kExpected1, sizeof(kExpected1))); + const int kSrcWidth = 2; + const int kSrcHeight = 2; + static const float kSourcePixels[] = { + 0.123f, 0.456f, + 0.789f, 123.0f, + }; + const int kSourcePitch = sizeof(kSourcePixels[0]) * kSrcWidth; + // normal copy + bitmap->SetRect(kDestMip, kDestX, kDestY, + kSrcWidth, kSrcHeight, kSourcePixels, kSourcePitch); + static const float kExpected2[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.123f, 0.456f, 0.0f, + 0.0f, 0.789f, 123.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + }; + EXPECT_EQ(0, memcmp(pixels, kExpected2, sizeof(kExpected2))); + // flipped copy + bitmap->SetRect( + kDestMip, kDestX, kDestY, + kSrcWidth, kSrcHeight, + reinterpret_cast<const uint8*>(kSourcePixels) + kSourcePitch, + -kSourcePitch); + static const float kExpected3[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.789f, 123.0f, 0.0f, + 0.0f, 0.123f, 0.456f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + }; + EXPECT_EQ(0, memcmp(pixels, kExpected3, sizeof(kExpected3))); +} + } // namespace diff --git a/o3d/core/cross/bitmap_tga.cc b/o3d/core/cross/bitmap_tga.cc index e31736a..883f7a3 100644 --- a/o3d/core/cross/bitmap_tga.cc +++ b/o3d/core/cross/bitmap_tga.cc @@ -111,8 +111,9 @@ bool Bitmap::LoadFromTGAStream(ServiceLocator* service_locator, unsigned int pixel_count = tga_width * tga_height; // Allocate storage for the pixels. Texture::Format format = components == 3 ? Texture::XRGB8 : Texture::ARGB8; - size_t image_size = - image::ComputeMipChainSize(tga_width, tga_height, format, 1); + // Allocate storage for the pixels. Bitmap requires we allocate enough + // memory for all mips even if we don't use them. + size_t image_size = Bitmap::ComputeMaxSize(tga_width, tga_height, format); scoped_array<uint8> image_data(new uint8[image_size]); if (image_data.get() == NULL) { DLOG(ERROR) << "Targa file memory allocation error \"" << filename << "\""; diff --git a/o3d/core/cross/canvas.cc b/o3d/core/cross/canvas.cc index 7dccb2d..a0dea81 100644 --- a/o3d/core/cross/canvas.cc +++ b/o3d/core/cross/canvas.cc @@ -38,6 +38,7 @@ #include "core/cross/canvas_utils.h" #include "core/cross/client.h" #include "core/cross/error.h" +#include "core/cross/features.h" #include "third_party/skia/include/core/SkPath.h" @@ -49,6 +50,10 @@ Canvas::Canvas(ServiceLocator* service_locator) : ParamObject(service_locator), width_(0), height_(0) { + Features* features = service_locator->GetService<Features>(); + DCHECK(features); + flip_ = features->flip_textures(); + // Initialize a 0x0 bitmap sk_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 0, 0); sk_canvas_.setBitmapDevice(sk_bitmap_); @@ -67,11 +72,14 @@ bool Canvas::SetSize(int width, int height) { return false; } sk_canvas_.setBitmapDevice(sk_bitmap_); - // Translate and flip our canvas to change from o3d coordinates - // (where the lower left is (0,0)) to skia coordinates (where the - // upper left is (0,0)) - sk_canvas_.translate(0, SkIntToScalar(sk_bitmap_.height())); - sk_canvas_.scale(SK_Scalar1, -SK_Scalar1); + + if (flip_) { + // Translate and flip our canvas to change from o3d coordinates + // (where the lower left is (0,0)) to skia coordinates (where the + // upper left is (0,0)) + sk_canvas_.translate(0, SkIntToScalar(sk_bitmap_.height())); + sk_canvas_.scale(SK_Scalar1, -SK_Scalar1); + } return true; } @@ -205,12 +213,10 @@ void Canvas::DrawBitmap(Texture2D* texture2d, } // Now copy from the temporary bitmap to the canvas bitmap. - // Note that we scale Y by -1 to flip the image vertically. The reason is - // that in O3D textures the first byte is the bottom left corner whereas - // in Skia the first byte is the top left of a bitmap. SaveMatrix(); - Scale(1, -1); - + if (flip_) { + Scale(1, -1); + } sk_canvas_.drawBitmap(bitmap, SkFloatToScalar(left), SkFloatToScalar(-bottom), @@ -231,7 +237,7 @@ void Canvas::Translate(float dx, float dy) { } // Copy the contents of the local bitmap to a Texture object. -bool Canvas::CopyToTexture(Texture2D* texture_2d) { +bool Canvas::CopyToTexture(Texture2D* texture_2d) const { DCHECK(texture_2d); if (texture_2d->width() != sk_bitmap_.width() || diff --git a/o3d/core/cross/canvas.h b/o3d/core/cross/canvas.h index 5c9c62e..0c86be9 100644 --- a/o3d/core/cross/canvas.h +++ b/o3d/core/cross/canvas.h @@ -50,7 +50,7 @@ class CanvasPaint; // The Canvas class provides an API for drawing text and 2D primitives onto // a 2D bitmap surface whose contents can be transfered to a compatible -// Texture2D object via the CopyToTexture() method. Each Canvas object +// Texture2D object via the CopyToTexture() method. Each Canvas object // maintains a stack of 2D transformation matrices which allow fine control over // the placement of drawable elements. Both geometry and drawing coordinates // provided to every draw call are transformed by the concatenation of @@ -115,6 +115,7 @@ class Canvas : public ParamObject { // Draws the contents of the specified texture onto the canvas surface. // The bottom left corner of the bitmap will be at (x, y) and transformed by // the current matrix. + // DEPRECATED // Parameters: // texture: Pointer to Texture2D object where the bitmap is extracted from // left: The position of the left side of the bitmap. @@ -153,15 +154,27 @@ class Canvas : public ParamObject { // Copies the contents of the Canvas bitmap to a Texture2D object. The // texture object must have the same size as the canvas and a ARGB8 or XRGB8 // format. All mip levels of the the texture will be filled. + // DEPRECATED // Parameters: // texture_2d: The texture object to copy the bitmap to. - bool CopyToTexture(Texture2D* texture_2d); + bool CopyToTexture(Texture2D* texture_2d) const; // Returns the width of the canvas bitmap. - int width() { return width_; } + int width() const { return width_; } // Returns the height of the canvas bitmap. - int height() { return height_; } + int height() const { return height_; } + + // Returns the actual pixels of the canvas + const uint8* GetPixelData(int x, int y) const { + return static_cast<const uint8*>(sk_bitmap_.getPixels()) + + y * GetPitch() + x * 4; + } + + // Returns the pitch of the pixel data + int GetPitch() const { + return width_ * 4; + } protected: explicit Canvas(ServiceLocator* service_locator); @@ -178,6 +191,9 @@ class Canvas : public ParamObject { int width_; int height_; + // For backward compatibility. Whether to flip the bitmap. + bool flip_; + O3D_DECL_CLASS(Canvas, ParamObject) DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/o3d/core/cross/draw_pass.h b/o3d/core/cross/draw_pass.h index 51e8878..60bb8e9 100644 --- a/o3d/core/cross/draw_pass.h +++ b/o3d/core/cross/draw_pass.h @@ -35,8 +35,9 @@ #ifndef O3D_CORE_CROSS_DRAW_PASS_H_ #define O3D_CORE_CROSS_DRAW_PASS_H_ -#include "core/cross/render_node.h" +#include "core/cross/draw_context.h" #include "core/cross/draw_list.h" +#include "core/cross/render_node.h" namespace o3d { diff --git a/o3d/core/cross/image_utils.cc b/o3d/core/cross/image_utils.cc index ca1eec4..4a94aad 100644 --- a/o3d/core/cross/image_utils.cc +++ b/o3d/core/cross/image_utils.cc @@ -96,8 +96,8 @@ size_t ComputeMipChainSize(unsigned int base_width, bool ScaleUpToPOT(unsigned int width, unsigned int height, Texture::Format format, - const uint8 *src, - uint8 *dst, + const void *src, + void *dst, int dst_pitch) { DCHECK(CheckImageDimensions(width, height)); switch (format) { @@ -164,10 +164,10 @@ uint8 Safe8Round(float f) { template <typename T> void PointScale( unsigned components, - const uint8* src, + const void* src, unsigned src_width, unsigned src_height, - uint8* dst, + void* dst, int dst_pitch, unsigned dst_width, unsigned dst_height) { @@ -198,10 +198,10 @@ void PointScale( bool Scale(unsigned int src_width, unsigned int src_height, Texture::Format format, - const uint8 *src, + const void *src, unsigned int dst_width, unsigned int dst_height, - uint8 *dst, + void *dst, int dst_pitch) { DCHECK(CheckImageDimensions(src_width, src_height)); DCHECK(CheckImageDimensions(dst_width, dst_height)); @@ -243,8 +243,9 @@ namespace { bool AdjustDrawImageBoundHelper(int* src_a, int* dest_a, int* src_length, int* dest_length, int src_bmp_length) { - if (*src_length == 0 || *dest_length == 0) + if (*src_length == 0 || *dest_length == 0) { return false; + } // check if start point is out of boundary. // if src_a < 0, src_length must be positive. @@ -266,8 +267,9 @@ bool AdjustDrawImageBoundHelper(int* src_a, int* dest_a, *src_a = src_bmp_length - 1; } - if (*src_length == 0 || *dest_length == 0) + if (*src_length == 0 || *dest_length == 0) { return false; + } // check whether start point + related length is out of boundary. // if src_a + src_length > src_bmp_length, src_length must be positive. if (*src_a + *src_length > src_bmp_length) { @@ -287,15 +289,16 @@ bool AdjustDrawImageBoundHelper(int* src_a, int* dest_a, return true; } -void LanczosResize1D(const uint8* src, int src_x, int src_y, +template <typename OriginalType, + float convert_to_float(OriginalType value), + OriginalType convert_to_original(float)> +void LanczosResize1D(const void* src_data, int src_pitch, + int src_x, int src_y, int width, int height, - int src_bmp_width, int src_bmp_height, - uint8* out, int dest_pitch, + void* dest_data, 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; + bool is_width, int components) { // calculate scale factor and init the weight array for lanczos filter. float scale = fabs(static_cast<float>(width) / nwidth); float support = kFilterSize * scale; @@ -306,10 +309,14 @@ void LanczosResize1D(const uint8* src, int src_x, int src_y, // center is the corresponding coordinate of i in original img. float center = (i + 0.5f) * scale; // boundary of weight array in original img. - int xmin = static_cast<int>(floor(center - support)); - if (xmin < 0) xmin = 0; - int xmax = static_cast<int>(ceil(center + support)); - if (xmax >= abs(width)) xmax = abs(width) - 1; + int xmin = static_cast<int>(floorf(center - support)); + if (xmin < 0) { + xmin = 0; + } + int xmax = static_cast<int>(ceilf(center + support)); + if (xmax >= abs(width)) { + xmax = abs(width) - 1; + } // fill up weight array by lanczos filter. float wsum = 0.0; @@ -341,57 +348,95 @@ void LanczosResize1D(const uint8* src, int src_x, int src_y, // of the image, we can apply that filter to all pixels in that // column. // calculate coordinate in new img. - int x = i; - if (nwidth < 0) - x = -1 * x; + int x = nwidth >= 0 ? i : -i; // lower bound of coordinate in original img. - if (width < 0) + if (width < 0) { xmin = -1 * xmin; + } for (int j = 0; j < abs(height); ++j) { // coordinate in height, same in src and dest img. - int base_y = j; - if (height < 0) - base_y = -1 * base_y; + int base_y = height >= 0 ? j : -j; // TODO(yux): fix the vertical flip problem and merge this if-else // statement coz at that time, there would be no need to check // which measure we are scaling. - if (isWidth) { - 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) * - pitch + dest_x + x) * components; - int step = components; - if (width < 0) - step = -1 * step; + if (is_width) { + const OriginalType* inrow = PointerFromVoidPointer<const OriginalType*>( + src_data, (src_y + base_y) * src_pitch) + + (src_x + xmin) * components; + OriginalType* outpix = PointerFromVoidPointer<OriginalType*>( + dest_data, (dest_y + base_y) * dest_pitch) + + (dest_x + x) * components; + int step = width >= 0 ? components : -components; for (int b = 0; b < components; ++b) { float sum = 0.0; - for (int k = 0, xk = b; k < wcount; ++k, xk += step) - sum += weight[k] * inrow[xk]; - - outpix[b] = Safe8Round(sum); + for (int k = 0, xk = b; k < wcount; ++k, xk += step) { + sum += weight[k] * convert_to_float(inrow[xk]); + } + outpix[b] = convert_to_original(sum); } } else { - const uint8* inrow = src + (src_x + base_y + (src_bmp_height - - (src_y + xmin) - 1) * src_bmp_width) * - components; - uint8* outpix = out + (dest_x + base_y + (dest_bmp_height - - (dest_y + x) - 1) * pitch) * components; - - int step = src_bmp_width * components; - if (width < 0) - step = -1 * step; + const OriginalType* inrow = PointerFromVoidPointer<const OriginalType*>( + src_data, + (src_y + xmin) * src_pitch) + + (src_x + base_y) * components; + OriginalType* outpix = PointerFromVoidPointer<OriginalType*>( + dest_data, + (dest_y + x) * dest_pitch) + + (dest_x + base_y) * components; + int step = width >= 0 ? src_pitch : -src_pitch; for (int b = 0; b < components; ++b) { float sum = 0.0; - for (int k = 0, xk = b; k < wcount; ++k, xk -= step) - sum += weight[k] * inrow[xk]; - - outpix[b] = Safe8Round(sum); + const OriginalType* work = inrow + b; + for (int k = 0; k < wcount; ++k) { + sum += weight[k] * convert_to_float(*work); + work = AddPointerOffset<const OriginalType*>(work, step); + } + outpix[b] = convert_to_original(sum); } } } } } +template <typename OriginalType, + float convert_to_float(OriginalType value), + OriginalType convert_to_original(float)> +void TypedLanczosScale(const void* src, int src_pitch, + int src_x, int src_y, + int src_width, int src_height, + void* dest, int dest_pitch, + int dest_x, int dest_y, + int dest_width, int dest_height, + int components) { + // Scale the image horizontally to a temp buffer. + int temp_img_width = abs(dest_width); + int temp_img_height = abs(src_height); + int temp_width = dest_width; + int temp_height = src_height; + int temp_x = 0, temp_y = 0; + if (temp_width < 0) + temp_x = abs(temp_width) - 1; + if (temp_height < 0) + temp_y = abs(temp_height) - 1; + + scoped_array<OriginalType> temp( + new OriginalType[temp_img_width * temp_img_height * components]); + + LanczosResize1D<OriginalType, convert_to_float, convert_to_original>( + src, src_pitch, src_x, src_y, src_width, src_height, + temp.get(), temp_img_width * components * sizeof(OriginalType), + temp_x, temp_y, temp_width, + true, components); + + // Scale the temp buffer vertically to get the final result. + LanczosResize1D<OriginalType, convert_to_float, convert_to_original>( + temp.get(), temp_img_width * components * sizeof(OriginalType), + temp_x, temp_y, temp_height, temp_width, + dest, dest_pitch, + dest_x, dest_y, dest_height, + false, components); +} + // Compute a texel, filtered from several source texels. This function assumes // minification. // Parameters: @@ -400,9 +445,11 @@ void LanczosResize1D(const uint8* src, int src_x, int src_y, // dst_width: width of the destination image // dst_height: height of the destination image // dst_data: address of the destination image data +// dst_pitch: the number of bytes per row of the destination. // src_width: width of the source image // src_height: height of the source image // src_data: address of the source image data +// src_pitch: the number of bytes per row of the source. // components: number of components in the image. template <typename OriginalType, typename WorkType, @@ -428,14 +475,6 @@ void FilterTexel(unsigned int x, DCHECK_LE(static_cast<int>(src_width), src_pitch); DCHECK_LE(static_cast<int>(dst_width), dst_pitch); - const OriginalType* src = static_cast<const OriginalType*>(src_data); - OriginalType* dst = static_cast<OriginalType*>(dst_data); - - DCHECK_EQ(src_pitch % (components * sizeof(*src)), 0u); - DCHECK_EQ(dst_pitch % (components * sizeof(*dst)), 0u); - - src_pitch /= components; - dst_pitch /= components; // the texel at (x, y) represents the square of texture coordinates // [x/dst_w, (x+1)/dst_w) x [y/dst_h, (y+1)/dst_h). // This takes contributions from the texels: @@ -477,7 +516,7 @@ void FilterTexel(unsigned int x, // source texel is across the left border of the footprint of the // destination texel. x_contrib = (src_x + 1) * dst_width - x * src_width; - } else if ((src_x + 1) * dst_width > (x+1) * src_width) { + } else if ((src_x + 1) * dst_width > (x + 1) * src_width) { // source texel is across the right border of the footprint of the // destination texel. x_contrib = (x+1) * src_width - src_x * dst_width; @@ -489,23 +528,27 @@ void FilterTexel(unsigned int x, // source texel is across the top border of the footprint of the // destination texel. y_contrib = (src_y + 1) * dst_height - y * src_height; - } else if ((src_y + 1) * dst_height > (y+1) * src_height) { + } else if ((src_y + 1) * dst_height > (y + 1) * src_height) { // source texel is across the bottom border of the footprint of the // destination texel. - y_contrib = (y+1) * src_height - src_y * dst_height; + y_contrib = (y + 1) * src_height - src_y * dst_height; } DCHECK(y_contrib > 0); DCHECK(y_contrib <= dst_height); WorkType contrib = static_cast<WorkType>(x_contrib * y_contrib); + const OriginalType* src = PointerFromVoidPointer<const OriginalType*>( + src_data, src_y * src_pitch); for (unsigned int c = 0; c < components; ++c) { accum[c] += contrib * - convert_to_work(src[(src_y * src_pitch + src_x) * components + c]); + convert_to_work(src[src_x * components + c]); } } } + OriginalType* dst = PointerFromVoidPointer<OriginalType*>( + dst_data, y * dst_pitch); for (unsigned int c = 0; c < components; ++c) { WorkType value = accum[c] / static_cast<WorkType>(src_height * src_width); - dst[(y * dst_pitch + x) * components + c] = convert_to_original(value); + dst[x * components + c] = convert_to_original(value); } } @@ -526,27 +569,24 @@ void GenerateMip(unsigned int components, unsigned int mip_width = std::max(1U, src_width >> 1); unsigned int mip_height = std::max(1U, src_height >> 1); - const OriginalType* src = static_cast<const OriginalType*>(src_data); - OriginalType* dst = static_cast<OriginalType*>(dst_data); - if (mip_width * 2 == src_width && mip_height * 2 == src_height) { - DCHECK_EQ(src_pitch % (components * sizeof(*src)), 0u); - DCHECK_EQ(dst_pitch % (components * sizeof(*dst)), 0u); - src_pitch /= components; - dst_pitch /= components; // Easy case: every texel maps to exactly 4 texels in the previous level. for (unsigned int y = 0; y < mip_height; ++y) { + const OriginalType* src0 = PointerFromVoidPointer<const OriginalType*>( + src_data, y * 2 * src_pitch); + const OriginalType* src1 = + AddPointerOffset<const OriginalType*>(src0, src_pitch); + OriginalType* dst = PointerFromVoidPointer<OriginalType*>( + dst_data, y * dst_pitch); for (unsigned int x = 0; x < mip_width; ++x) { for (unsigned int c = 0; c < components; ++c) { // Average the 4 texels. - unsigned int offset = (y * 2 * src_pitch + x * 2) * components + c; - WorkType value = convert_to_work(src[offset]); // (2x, 2y) - value += convert_to_work(src[offset + components]); // (2x+1, 2y) - value += convert_to_work(src[offset + src_width * components]); - // (2x, 2y+1) - value += convert_to_work(src[offset + (src_width + 1) * components]); - // (2x+1, 2y+1) - dst[(y * dst_pitch + x) * components + c] = + unsigned int offset = x * 2 * components + c; + WorkType value = convert_to_work(src0[offset]); // (2x, 2y) + value += convert_to_work(src0[offset + components]); // (2x+1, 2y) + value += convert_to_work(src1[offset]); // (2x, 2y+1) + value += convert_to_work(src1[offset + components]); // (2x+1, 2y+1) + dst[x * components + c] = convert_from_work(value / static_cast<WorkType>(4)); } } @@ -581,6 +621,10 @@ uint8 UInt64ToUInt8(uint64 value) { return static_cast<uint8>(value); }; +float UInt8ToFloat(uint8 value) { + return static_cast<float>(value); +}; + float FloatToFloat(float value) { return value; } @@ -611,12 +655,44 @@ uint16 DoubleToHalf(double value) { } // anonymous namespace +bool AdjustForSetRect(int* src_y, + int src_width, + int src_height, + int* src_pitch, + int* dst_y, + int dst_width, + int* dst_height) { + if (src_width != dst_width || abs(src_height) != abs(*dst_height) || + src_width < 0) { + return false; + } + + if (*dst_height < 0) { + *dst_y = *dst_y + *dst_height + 1; + *dst_height = -*dst_height; + if (src_height < 0) { + *src_y = *src_y + src_height + 1; + } else { + *src_y = *src_y + src_height - 1; + *src_pitch = -*src_pitch; + } + } else { + if (src_height < 0) { + *src_pitch = -*src_pitch; + } + } + + return true; +} + // Adjust boundaries when using DrawImage function in bitmap or texture. bool AdjustDrawImageBoundary(int* src_x, int* src_y, int* src_width, int* src_height, + int src_bmp_level, int src_bmp_width, int src_bmp_height, int* dest_x, int* dest_y, int* dest_width, int* dest_height, + int dest_bmp_level, int dest_bmp_width, int dest_bmp_height) { // if src or dest rectangle is out of boundaries, do nothing. if ((*src_x < 0 && *src_x + *src_width <= 0) || @@ -630,25 +706,35 @@ bool AdjustDrawImageBoundary(int* src_x, int* src_y, (*dest_x >= dest_bmp_width && *dest_x + *dest_width >= dest_bmp_width - 1) || (*dest_y >= dest_bmp_height && - *dest_y + *dest_height >= dest_bmp_height - 1)) + *dest_y + *dest_height >= dest_bmp_height - 1) || + (src_bmp_level < 0) || (dest_bmp_level < 0)) return false; + int src_mip_width = static_cast<int>( + image::ComputeMipDimension(src_bmp_level, src_bmp_width)); + int src_mip_height = static_cast<int>( + image::ComputeMipDimension(src_bmp_level, src_bmp_height)); + int dest_mip_width = static_cast<int>( + image::ComputeMipDimension(dest_bmp_level, dest_bmp_width)); + int dest_mip_height = static_cast<int>( + image::ComputeMipDimension(dest_bmp_level, dest_bmp_height)); + // if start points are negative. // check whether src_x is negative. if (!AdjustDrawImageBoundHelper(src_x, dest_x, - src_width, dest_width, src_bmp_width)) + src_width, dest_width, src_mip_width)) return false; // check whether dest_x is negative. if (!AdjustDrawImageBoundHelper(dest_x, src_x, - dest_width, src_width, dest_bmp_width)) + dest_width, src_width, dest_mip_width)) return false; // check whether src_y is negative. if (!AdjustDrawImageBoundHelper(src_y, dest_y, - src_height, dest_height, src_bmp_height)) + src_height, dest_height, src_mip_height)) return false; // check whether dest_y is negative. if (!AdjustDrawImageBoundHelper(dest_y, src_y, - dest_height, src_height, dest_bmp_height)) + dest_height, src_height, dest_mip_height)) return false; // check any width or height becomes negative after adjustment. @@ -660,49 +746,46 @@ bool AdjustDrawImageBoundary(int* src_x, int* src_y, return true; } -void LanczosScale(const uint8* src, +void LanczosScale(Texture::Format format, const void* src, int src_pitch, int src_x, int src_y, int src_width, int src_height, - int src_img_width, int src_img_height, - uint8* dest, int dest_pitch, + void* dest, int dest_pitch, int dest_x, int dest_y, int dest_width, int dest_height, - int dest_img_width, int dest_img_height, int components) { - // Scale the image horizontally to a temp buffer. - int temp_img_width = abs(dest_width); - int temp_img_height = abs(src_height); - int temp_width = dest_width; - int temp_height = src_height; - int temp_x = 0, temp_y = 0; - if (temp_width < 0) - temp_x = abs(temp_width) - 1; - if (temp_height < 0) - temp_y = abs(temp_height) - 1; - - scoped_array<uint8> temp(new uint8[temp_img_width * - temp_img_height * components]); - - LanczosResize1D(src, src_x, src_y, src_width, src_height, - src_img_width, src_img_height, - 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_pitch, - dest_x, dest_y, dest_height, - dest_img_width, dest_img_height, false, components); + switch (format) { + case Texture::ARGB8: + case Texture::XRGB8: + TypedLanczosScale<uint8, UInt8ToFloat, Safe8Round>( + src, src_pitch, src_x, src_y, src_width, src_height, + dest, dest_pitch, dest_x, dest_y, dest_width, dest_height, + components); + break; + case Texture::ABGR16F: + TypedLanczosScale<uint16, HalfToFloat, FloatToHalf>( + src, src_pitch, src_x, src_y, src_width, src_height, + dest, dest_pitch, dest_x, dest_y, dest_width, dest_height, + components); + break; + case Texture::ABGR32F: + case Texture::R32F: + TypedLanczosScale<float, FloatToFloat, FloatToFloat>( + src, src_pitch, src_x, src_y, src_width, src_height, + dest, dest_pitch, dest_x, dest_y, dest_width, dest_height, + components); + break; + default: + DLOG(ERROR) << "Mip-map generation not supported for format: " << format; + return; + } } bool GenerateMipmap(unsigned int src_width, unsigned int src_height, Texture::Format format, - const uint8 *src_data, + const void *src_data, int src_pitch, - uint8 *dst_data, + void *dst_data, int dst_pitch) { unsigned int components = GetNumComponentsForFormat(format); if (components == 0) { @@ -737,7 +820,6 @@ bool GenerateMipmap(unsigned int src_width, default: DLOG(ERROR) << "Mip-map generation not supported for format: " << format; return false; - break; } return true; } diff --git a/o3d/core/cross/image_utils.h b/o3d/core/cross/image_utils.h index cb2f761..ef3e75b 100644 --- a/o3d/core/cross/image_utils.h +++ b/o3d/core/cross/image_utils.h @@ -122,35 +122,31 @@ size_t ComputeBufferSize(unsigned int width, // and paste in dest image. Utility function for all DrawImage
// function in bitmap and textures. Scale operation is based on
// Lanczos resampling.
-// Note: this doesn't work for DXTC, or floating-point images.
+// Note: this doesn't work for DXTC.
//
// Parameters:
+// format: The format of the images.
// src: source image which would be copied from.
+// src_pitch: The number of bytes per row in the src image.
// src_x: x-coordinate of the starting pixel in the src image.
// src_y: y-coordinate of the starting pixel in the src image.
// src_width: width of the part in src image to be croped.
// src_height: height of the part in src image to be croped.
-// src_img_width: width of the src image.
-// src_img_height: height of the src image.
// dest: dest image which would be copied to.
+// dest_pitch: The number of bytes per row in the dest image.
// dest_x: x-coordinate of the starting pixel in the dest image.
// dest_y: y-coordinate of the starting pixel in the dest image.
// dest_width: width of the part in dest image to be pasted to.
// dest_height: height of the part in dest image to be pasted to.
-// dest_img_width: width of the dest image.
-// dest_img_height: height of the src image.
-// component: size of each pixel in terms of array element.
-// Returns:
-// true if crop and scale succeeds.
-void LanczosScale(const uint8* src,
+// components: number of components per pixel.
+void LanczosScale(Texture::Format format,
+ const void* src, int src_pitch,
int src_x, int src_y,
int src_width, int src_height,
- int src_img_width, int src_img_height,
- uint8* dest, int dest_pitch,
+ void* dest, int dest_pitch,
int dest_x, int dest_y,
int dest_width, int dest_height,
- int dest_img_width, int dest_img_height,
- int component);
+ int components);
// Detects the type of image file based on the filename.
ImageFileType GetFileTypeFromFilename(const char *filename);
@@ -167,7 +163,7 @@ void XYZToXYZA(uint8 *image_data, int pixel_count); void RGBAToBGRA(uint8 *image_data, int pixel_count);
// Generates a mip-map for 1 level.
-// NOTE: this doesn't work for DXTC, or floating-point images.
+// NOTE: this doesn't work for DXTC images.
//
// Parameters:
// src_width: the width of the source image.
@@ -182,13 +178,13 @@ void RGBAToBGRA(uint8 *image_data, int pixel_count); bool GenerateMipmap(unsigned int src_width,
unsigned int src_height,
Texture::Format format,
- const uint8 *src_data,
+ const void *src_data,
int src_pitch,
- uint8 *dst_data,
+ void *dst_data,
int dst_pitch);
// Scales an image up to power-of-two textures, using point filtering.
-// NOTE: this doesn't work for DXTC, or floating-point images.
+// NOTE: this doesn't work for DXTC images.
//
// Parameters:
// width: the non-power-of-two width of the original image.
@@ -202,12 +198,12 @@ bool GenerateMipmap(unsigned int src_width, bool ScaleUpToPOT(unsigned int width,
unsigned int height,
Texture::Format format,
- const uint8 *src,
- uint8 *dst,
+ const void *src,
+ void *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.
+// NOTE: this doesn't work for DXTC images.
//
// Parameters:
// src_width: the width of the original image.
@@ -223,10 +219,10 @@ bool ScaleUpToPOT(unsigned int width, bool Scale(unsigned int src_width,
unsigned int src_height,
Texture::Format format,
- const uint8 *src,
+ const void *src,
unsigned int dst_width,
unsigned int dst_height,
- uint8 *dst,
+ void *dst,
int dst_pitch);
// adjust start points and boundaries when using DrawImage data
@@ -236,23 +232,55 @@ bool Scale(unsigned int src_width, // src_y: y-coordinate of the starting pixel in the source image.
// src_width: width of the source image to draw.
// src_height: height of the source image to draw.
+// src_bmp_level: which mip in source.
// src_bmp_width: original width of source bitmap.
// src_bmp_height: original height of source bitmap.
// dest_x: x-coordinate of the starting pixel in the dest image.
// dest_y: y-coordinate of the starting pixel in the dest image.
// dest_width: width of the dest image to draw.
// dest_height: height of the dest image to draw.
+// dest_bmp_level: which mip in dest.
// dest_bmp_width: original width of dest bitmap.
// dest_bmp_height: original height of dest bitmap.
// Returns:
// false if src or dest rectangle is out of boundaries.
bool AdjustDrawImageBoundary(int* src_x, int* src_y,
int* src_width, int* src_height,
+ int src_level,
int src_bmp_width, int src_bmp_height,
int* dest_x, int* dest_y,
int* dest_width, int* dest_height,
+ int dest_level,
int dest_bmp_width, int dest_bmp_height);
+// Checks whether or not we can call SetRect and adjust the inputs
+// accordingly so SetRect will work.
+//
+// Assumes that both the source and destination rectangles are within the
+// bounds of their respective images.
+//
+// Parameters:
+// src_y: A pointer to an int holding the Y position of the source
+// rect. Will be adjusted if SetRect can be called.
+// src_width: The width of the source rect.
+// src_height: The height of the source rect.
+// src_pitch: A pointer to an int holding the pitch of the source. Will be
+// adjusted if SetRect can be called.
+// dst_y: A pointer to an int holding the Y position of the dest rect. Will be
+// adjusted if SetRect can be called.
+// dst_width: The width of the dest rect.
+// dst_height: A pointer to an int holding the height of the dest rect. Will
+// adjusted if SetRect can be called.
+// Returns:
+// True if SetRect can be called.
+bool AdjustForSetRect(int* src_y,
+ int src_width,
+ int src_height,
+ int* src_pitch,
+ int* dst_y,
+ int dst_width,
+ int* dst_height);
+
} // namespace image
} // namespace o3d
diff --git a/o3d/core/cross/image_utils_test.cc b/o3d/core/cross/image_utils_test.cc index ae069e0..a2a237c 100644 --- a/o3d/core/cross/image_utils_test.cc +++ b/o3d/core/cross/image_utils_test.cc @@ -35,12 +35,40 @@ #include "tests/common/win/testing_common.h" #include "base/file_path.h" #include "utils/cross/file_path_utils.h" +#include "core/cross/math_utilities.h" namespace o3d { +namespace { + +void ConvertToHalf(const float* src, size_t count, uint16* dst) { + for (; count != 0; --count) { + *dst++ = Vectormath::Aos::FloatToHalf(*src++); + } +} + +} // anonymous namespace. + class ImageTest : public testing::Test { }; +TEST_F(ImageTest, GetNumComponentsForFormat) { + EXPECT_EQ(4, image::GetNumComponentsForFormat(Texture::XRGB8)); + EXPECT_EQ(4, image::GetNumComponentsForFormat(Texture::ARGB8)); + EXPECT_EQ(4, image::GetNumComponentsForFormat(Texture::ABGR16F)); + EXPECT_EQ(4, image::GetNumComponentsForFormat(Texture::ABGR32F)); + EXPECT_EQ(1, image::GetNumComponentsForFormat(Texture::R32F)); + EXPECT_EQ(0, image::GetNumComponentsForFormat(Texture::DXT1)); + EXPECT_EQ(0, image::GetNumComponentsForFormat(Texture::DXT3)); + EXPECT_EQ(0, image::GetNumComponentsForFormat(Texture::DXT5)); +} + +TEST_F(ImageTest, IsPOT) { + EXPECT_TRUE(image::IsPOT(2, 2)); + EXPECT_FALSE(image::IsPOT(3, 2)); + EXPECT_FALSE(image::IsPOT(2, 3)); +} + TEST_F(ImageTest, CheckImageDimensions) {
EXPECT_TRUE(image::CheckImageDimensions(1u, 1u));
EXPECT_TRUE(image::CheckImageDimensions(image::kMaxImageDimension,
@@ -148,7 +176,6 @@ TEST_F(ImageTest, ScaleUpToPOT) { EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size)); } - // NOTE: untested ffile types are: // png grayscale textures // dds cube maps @@ -176,7 +203,7 @@ static const uint8 kMipmapDataPOT[] = { // Generates mip-maps from a known power-of-two image, compare with expected // results. -TEST_F(ImageTest, GenerateMipmapsPOT) { +TEST_F(ImageTest, GenerateMipmapsPOTUInt8) { const unsigned int kWidth = 4; const unsigned int kHeight = 4; const Texture::Format format = Texture::ARGB8; @@ -206,6 +233,97 @@ TEST_F(ImageTest, GenerateMipmapsPOT) { EXPECT_EQ(0, memcmp(data.get(), kMipmapDataPOT, size)); } +TEST_F(ImageTest, GenerateMipmapsPOTFloat) { + static const float original[] = { 0.0f, 2.0f, 3.0f, 5.0f, }; + static const float expected_mip1[] = { 1.0f, 4.0f, }; + static const float expected_mip2[] = { 2.5f, }; + + const unsigned int kWidth = 4; + const unsigned int kHeight = 1; + const Texture::Format kFormat = Texture::R32F; + unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight); + EXPECT_EQ(3u, mipmaps); + float mip1[2 + 1]; + float mip2[1 + 1]; + // Put sentinels at the ends + const float kSentinel = 123.12345f; + mip1[2] = kSentinel; + mip2[1] = kSentinel; + image::GenerateMipmap( + kWidth, kHeight, kFormat, + original, image::ComputeMipPitch(kFormat, 0, kWidth), + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth)); + image::GenerateMipmap( + image::ComputeMipDimension(1, kWidth), + image::ComputeMipDimension(1, kHeight), + kFormat, + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth), + mip2, + image::ComputeMipPitch(kFormat, 2, kWidth)); + // Check the result. + EXPECT_EQ(0, memcmp(mip1, expected_mip1, sizeof(expected_mip1))); + EXPECT_EQ(0, memcmp(mip2, expected_mip2, sizeof(expected_mip2))); + EXPECT_EQ(mip1[2], kSentinel); + EXPECT_EQ(mip2[1], kSentinel); +} + +TEST_F(ImageTest, GenerateMipmapsPOTHalf) { + static const float original_f[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 2.0f, 2.0f, 2.0f, 2.0f, + 3.0f, 3.0f, 3.0f, 3.0f, + 5.0f, 5.0f, 5.0f, 5.0f, + }; + static const float expected_mip1_f[] = { + 1.0f, 1.0f, 1.0f, 1.0f, + 4.0f, 4.0f, 4.0f, 4.0f, + }; + static const float expected_mip2_f[] = { + 2.5f, 2.5f, 2.5f, 2.5f, + }; + + uint16 original[arraysize(original_f)]; + uint16 expected_mip1[arraysize(expected_mip1_f)]; + uint16 expected_mip2[arraysize(expected_mip2_f)]; + + ConvertToHalf(original_f, arraysize(original_f), original); + ConvertToHalf(expected_mip1_f, arraysize(expected_mip1_f), expected_mip1); + ConvertToHalf(expected_mip2_f, arraysize(expected_mip2_f), expected_mip2); + + const unsigned int kWidth = 4; + const unsigned int kHeight = 1; + const Texture::Format kFormat = Texture::ABGR16F; + unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight); + EXPECT_EQ(3u, mipmaps); + uint16 mip1[2 * 4 + 1]; + uint16 mip2[1 * 4 + 1]; + // Put sentinels at the ends + const float kSentinel = 123.12345f; + uint16 sentinel = Vectormath::Aos::FloatToHalf(kSentinel); + mip1[2 * 4] = sentinel; + mip2[1 * 4] = sentinel; + image::GenerateMipmap( + kWidth, kHeight, kFormat, + original, image::ComputeMipPitch(kFormat, 0, kWidth), + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth)); + image::GenerateMipmap( + image::ComputeMipDimension(1, kWidth), + image::ComputeMipDimension(1, kHeight), + kFormat, + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth), + mip2, + image::ComputeMipPitch(kFormat, 2, kWidth)); + // Check the result. + EXPECT_EQ(0, memcmp(mip1, expected_mip1, sizeof(expected_mip1))); + EXPECT_EQ(0, memcmp(mip2, expected_mip2, sizeof(expected_mip2))); + EXPECT_EQ(mip1[2 * 4], sentinel); + EXPECT_EQ(mip2[1 * 4], sentinel); +} + static const uint8 kMipmapDataNPOT[] = { // This is a 7x7 image 0x0d, 0x16, 0x68, 0x1b, 0xe6, 0x09, 0x89, 0x55, @@ -302,6 +420,147 @@ TEST_F(ImageTest, GetFileTypeFromMimeType) { image::GetFileTypeFromFilename("application/x-123")); } +TEST_F(ImageTest, LanczosScaleFloat) { + static const float original[] = { 0.0f, 2.0f, 3.0f, 5.0f, }; + static const float expected_mip1[] = { 0.84352076f, 4.1564794f, }; + static const float expected_mip2[] = { 2.5f, }; + + const unsigned int kWidth = 4; + const unsigned int kHeight = 1; + const Texture::Format kFormat = Texture::R32F; + unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight); + EXPECT_EQ(3u, mipmaps); + float mip1[2 + 1]; + float mip2[1 + 1]; + // Put sentinels at the ends + const float kSentinel = 123.12345f; + mip1[2] = kSentinel; + mip2[1] = kSentinel; + image::LanczosScale( + kFormat, + original, image::ComputeMipPitch(kFormat, 0, kWidth), + 0, 0, 4, 1, + mip1, image::ComputeMipPitch(kFormat, 1, kWidth), + 0, 0, 2, 1, + 1); + image::LanczosScale( + kFormat, + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth), + 0, 0, 2, 1, + mip2, image::ComputeMipPitch(kFormat, 2, kWidth), + 0, 0, 1, 1, + 1); + // Check the result. + EXPECT_EQ(0, memcmp(mip1, expected_mip1, sizeof(expected_mip1))); + EXPECT_EQ(0, memcmp(mip2, expected_mip2, sizeof(expected_mip2))); + EXPECT_EQ(mip1[2], kSentinel); + EXPECT_EQ(mip2[1], kSentinel); +} + +TEST_F(ImageTest, LanczosScaleHalf) { + static const uint16 original[] = { + 0x0000, 0x0000, 0x0000, 0x0000, + 0x4000, 0x4000, 0x4000, 0x4000, + 0x4200, 0x4200, 0x4200, 0x4200, + 0x4500, 0x4500, 0x4500, 0x4500, + }; + static const uint16 expected_mip1[] = { + 0x3abf, 0x3abf, 0x3abf, 0x3abf, + 0x4428, 0x4428, 0x4428, 0x4428, + }; + static const uint16 expected_mip2[] = { + 0x40ff, 0x40ff, 0x40ff, 0x40ff, + }; + + const unsigned int kWidth = 4; + const unsigned int kHeight = 1; + const Texture::Format kFormat = Texture::ABGR16F; + unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight); + EXPECT_EQ(3u, mipmaps); + uint16 mip1[2 * 4 + 1]; + uint16 mip2[1 * 4 + 1]; + // Put sentinels at the ends + const float kSentinel = 123.12345f; + uint16 sentinel = Vectormath::Aos::FloatToHalf(kSentinel); + mip1[2 * 4] = sentinel; + mip2[1 * 4] = sentinel; + image::LanczosScale( + kFormat, + original, image::ComputeMipPitch(kFormat, 0, kWidth), + 0, 0, 4, 1, + mip1, image::ComputeMipPitch(kFormat, 1, kWidth), + 0, 0, 2, 1, + 4); + image::LanczosScale( + kFormat, + mip1, + image::ComputeMipPitch(kFormat, 1, kWidth), + 0, 0, 2, 1, + mip2, image::ComputeMipPitch(kFormat, 2, kWidth), + 0, 0, 1, 1, + 4); + // Check the result. + EXPECT_EQ(0, memcmp(mip1, expected_mip1, sizeof(expected_mip1))); + EXPECT_EQ(0, memcmp(mip2, expected_mip2, sizeof(expected_mip2))); + EXPECT_EQ(mip1[2 * 4], sentinel); + EXPECT_EQ(mip2[1 * 4], sentinel); +} + +TEST_F(ImageTest, AdjustForSetRect) { + int src_y = 1; + int src_pitch = 2; + int dst_y = 3; + int dst_height = 10; + // Different widths + EXPECT_FALSE(image::AdjustForSetRect(&src_y, 10, 10, &src_pitch, + &dst_y, 11, &dst_height)); + // Different heights + EXPECT_FALSE(image::AdjustForSetRect(&src_y, 10, 11, &src_pitch, + &dst_y, 10, &dst_height)); + // width < 0 + EXPECT_FALSE(image::AdjustForSetRect(&src_y, -10, 10, &src_pitch, + &dst_y, -10, &dst_height)); + // SH > 0, DH > 0 + EXPECT_TRUE(image::AdjustForSetRect(&src_y, 10, 10, &src_pitch, + &dst_y, 10, &dst_height)); + EXPECT_EQ(1, src_y); + EXPECT_EQ(2, src_pitch); + EXPECT_EQ(3, dst_y); + EXPECT_EQ(10, dst_height); + // SH > 0, DH < 0 + dst_y = 9; + dst_height = -10; + EXPECT_TRUE(image::AdjustForSetRect(&src_y, 10, 10, &src_pitch, + &dst_y, 10, &dst_height)); + EXPECT_EQ(10, src_y); + EXPECT_EQ(-2, src_pitch); + EXPECT_EQ(0, dst_y); + EXPECT_EQ(10, dst_height); + // SH < 0, DH < 0 + src_y = 10; + src_pitch = 2; + dst_y = 15; + dst_height = -10; + EXPECT_TRUE(image::AdjustForSetRect(&src_y, 10, -10, &src_pitch, + &dst_y, 10, &dst_height)); + EXPECT_EQ(1, src_y); + EXPECT_EQ(2, src_pitch); + EXPECT_EQ(6, dst_y); + EXPECT_EQ(10, dst_height); + // SH < 0, DH > 0 + src_y = 10; + src_pitch = 2; + dst_y = 3; + dst_height = 10; + EXPECT_TRUE(image::AdjustForSetRect(&src_y, 10, -10, &src_pitch, + &dst_y, 10, &dst_height)); + EXPECT_EQ(10, src_y); + EXPECT_EQ(-2, src_pitch); + EXPECT_EQ(3, dst_y); + EXPECT_EQ(10, dst_height); +} + } // namespace diff --git a/o3d/core/cross/render_node.h b/o3d/core/cross/render_node.h index c51bf79..bbb1004 100644 --- a/o3d/core/cross/render_node.h +++ b/o3d/core/cross/render_node.h @@ -37,7 +37,6 @@ #include <vector> #include "core/cross/param_object.h" -#include "core/cross/draw_context.h" #include "core/cross/render_context.h" namespace o3d { diff --git a/o3d/core/cross/texture.cc b/o3d/core/cross/texture.cc index 10e1289..560eb6c 100644 --- a/o3d/core/cross/texture.cc +++ b/o3d/core/cross/texture.cc @@ -36,6 +36,7 @@ #include <cmath> #include "core/cross/texture.h" #include "core/cross/bitmap.h" +#include "core/cross/canvas.h" #include "core/cross/renderer.h" #include "core/cross/client_info.h" #include "core/cross/error.h" @@ -89,18 +90,21 @@ Texture2D::~Texture2D() { } void Texture2D::DrawImage(const Bitmap& src_img, + int src_mip, int src_x, int src_y, int src_width, int src_height, + int dst_mip, int dst_x, int dst_y, - int dst_width, int dst_height, int dest_mip) { + int dst_width, int dst_height) { DCHECK(src_img.image_data()); - if (dest_mip < 0 || dest_mip >= levels()) { + if (dst_mip < 0 || dst_mip >= levels()) { O3D_ERROR(service_locator()) << "Mip out of range"; } - unsigned int mip_width = std::max(1, width() >> dest_mip); - unsigned int mip_height = std::max(1, height() >> dest_mip); + if (src_mip < 0 || src_mip >= src_img.num_mipmaps()) { + O3D_ERROR(service_locator()) << "Source Mip out of range"; + } // Clip source and destination rectangles to // source and destination bitmaps. @@ -108,21 +112,25 @@ void Texture2D::DrawImage(const Bitmap& src_img, // do nothing and return. if (!image::AdjustDrawImageBoundary(&src_x, &src_y, &src_width, &src_height, + src_mip, src_img.width(), src_img.height(), &dst_x, &dst_y, &dst_width, &dst_height, - mip_width, mip_height)) { + dst_mip, + width(), height())) { return; } - unsigned int components = 0; // check formats of source and dest images. // format of source and dest should be the same. if (src_img.format() != format()) { - O3D_ERROR(service_locator()) << "DrawImage does not support " - << "different formats."; + O3D_ERROR(service_locator()) << "formats must be the same."; return; } + + unsigned int mip_width = image::ComputeMipDimension(dst_mip, width()); + unsigned int mip_height = image::ComputeMipDimension(dst_mip, height()); + // if src and dest are in the same size and drawImage is copying // the entire bitmap on dest image, just perform memcpy. if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && @@ -131,15 +139,14 @@ void Texture2D::DrawImage(const 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) { - SetRect(dest_mip, 0, 0, mip_width, mip_height, - src_img.image_data(), - src_img.GetMipPitch(0)); + SetRect(dst_mip, 0, 0, mip_width, mip_height, + src_img.GetMipData(src_mip), + src_img.GetMipPitch(src_mip)); return; } - if (src_img.format() == Texture::XRGB8 || - src_img.format() == Texture::ARGB8) { - components = 4; - } else { + + unsigned int components = image::GetNumComponentsForFormat(format()); + if (components == 0) { O3D_ERROR(service_locator()) << "DrawImage does not support format: " << src_img.format() << " unless src and " << "dest images are in the same size and " @@ -147,21 +154,93 @@ void Texture2D::DrawImage(const Bitmap& src_img, return; } - LockHelper helper(this, dest_mip); + int src_pitch = src_img.GetMipPitch(src_mip); + if (image::AdjustForSetRect(&src_y, src_width, src_height, &src_pitch, + &dst_y, dst_width, &dst_height)) { + SetRect(dst_mip, dst_x, dst_y, dst_width, dst_height, + src_img.GetPixelData(src_mip, src_x, src_y), + src_pitch); + return; + } + + LockHelper helper(this, dst_mip); uint8* mip_data = helper.GetDataAs<uint8>(); if (!mip_data) { return; } - uint8* src_img_data = src_img.image_data(); + image::LanczosScale(src_img.format(), + src_img.GetMipData(src_mip), + src_img.GetMipPitch(src_mip), + src_x, src_y, + src_width, src_height, + mip_data, helper.pitch(), + dst_x, dst_y, + dst_width, dst_height, + components); +} + +void Texture2D::DrawImage(const Canvas& src_img, + int src_x, int src_y, + int src_width, int src_height, + int dst_mip, + int dst_x, int dst_y, + int dst_width, int dst_height) { + if (dst_mip < 0 || dst_mip >= levels()) { + O3D_ERROR(service_locator()) << "Mip out of range"; + } - image::LanczosScale(src_img_data, src_x, src_y, + // Clip source and destination rectangles to + // source and destination bitmaps. + // if src or dest rectangle is out of boundary, + // do nothing and return. + if (!image::AdjustDrawImageBoundary(&src_x, &src_y, + &src_width, &src_height, + 0, + src_img.width(), src_img.height(), + &dst_x, &dst_y, + &dst_width, &dst_height, + dst_mip, + width(), height())) { + return; + } + + // check formats of source and dest images. + // format of source and dest should be the same. + if (format() != Texture::ARGB8 && format() != Texture::XRGB8) { + O3D_ERROR(service_locator()) << "format must be ARGB8 or XRGB8."; + return; + } + + unsigned int mip_width = image::ComputeMipDimension(dst_mip, width()); + unsigned int mip_height = image::ComputeMipDimension(dst_mip, height()); + unsigned int components = image::GetNumComponentsForFormat(format()); + DCHECK(components > 0); + + int src_pitch = src_img.GetPitch(); + if (image::AdjustForSetRect(&src_y, src_width, src_height, &src_pitch, + &dst_y, dst_width, &dst_height)) { + SetRect(dst_mip, dst_x, dst_y, dst_width, dst_height, + src_img.GetPixelData(src_x, src_y), + src_pitch); + return; + } + + LockHelper helper(this, dst_mip); + uint8* mip_data = helper.GetDataAs<uint8>(); + if (!mip_data) { + return; + } + + image::LanczosScale(format(), + src_img.GetPixelData(0, 0), + src_img.GetPitch(), + src_x, src_y, src_width, src_height, - src_img.width(), src_img.height(), mip_data, helper.pitch(), dst_x, dst_y, dst_width, dst_height, - mip_width, mip_height, components); + components); } void Texture2D::SetFromBitmap(const Bitmap& bitmap) { @@ -291,12 +370,12 @@ ObjectBase::Ref TextureCUBE::Create(ServiceLocator* service_locator) { return ObjectBase::Ref(); } -void TextureCUBE::DrawImage(const Bitmap& src_img, +void TextureCUBE::DrawImage(const Bitmap& src_img, int src_mip, int src_x, int src_y, int src_width, int src_height, + CubeFace dest_face, int dest_mip, int dst_x, int dst_y, - int dst_width, int dst_height, - CubeFace dest_face, int dest_mip) { + int dst_width, int dst_height) { DCHECK(src_img.image_data()); if (dest_face >= NUMBER_OF_FACES) { @@ -305,10 +384,12 @@ void TextureCUBE::DrawImage(const Bitmap& src_img, } if (dest_mip < 0 || dest_mip >= levels()) { - O3D_ERROR(service_locator()) << "Mip out of range"; + O3D_ERROR(service_locator()) << "Destination Mip out of range"; } - unsigned int mip_length = std::max(1, edge_length() >> dest_mip); + if (src_mip < 0 || src_mip >= src_img.num_mipmaps()) { + O3D_ERROR(service_locator()) << "Source Mip out of range"; + } // Clip source and destination rectangles to // source and destination bitmaps. @@ -316,14 +397,15 @@ void TextureCUBE::DrawImage(const Bitmap& src_img, // do nothing and return true. if (!image::AdjustDrawImageBoundary(&src_x, &src_y, &src_width, &src_height, + src_mip, src_img.width(), src_img.height(), &dst_x, &dst_y, &dst_width, &dst_height, - mip_length, mip_length)) { + dest_mip, + edge_length(), edge_length())) { return; } - unsigned int components = 0; // check formats of source and dest images. // format of source and dest should be the same. if (src_img.format() != format()) { @@ -331,6 +413,9 @@ void TextureCUBE::DrawImage(const Bitmap& src_img, << "different formats."; return; } + + unsigned int mip_length = image::ComputeMipDimension(dest_mip, edge_length()); + // if src and dest are in the same size and drawImage is copying // the entire bitmap on dest image, just perform memcpy. if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && @@ -341,13 +426,12 @@ void TextureCUBE::DrawImage(const Bitmap& src_img, static_cast<unsigned int>(dst_height) == mip_length) { SetRect(dest_face, dest_mip, 0, 0, mip_length, mip_length, src_img.image_data(), - src_img.GetMipPitch(0)); + src_img.GetMipPitch(src_mip)); return; } - if (src_img.format() == Texture::XRGB8 || - src_img.format() == Texture::ARGB8) { - components = 4; - } else { + + unsigned int components = image::GetNumComponentsForFormat(format()); + if (components == 0) { O3D_ERROR(service_locator()) << "DrawImage does not support format: " << src_img.format() << " unless src and " << "dest images are in the same size and " @@ -355,21 +439,95 @@ void TextureCUBE::DrawImage(const Bitmap& src_img, return; } + int src_pitch = src_img.GetMipPitch(src_mip); + if (image::AdjustForSetRect(&src_y, src_width, src_height, &src_pitch, + &dst_y, dst_width, &dst_height)) { + SetRect(dest_face, dest_mip, dst_x, dst_y, dst_width, dst_height, + src_img.GetPixelData(src_mip, src_x, src_y), + src_pitch); + } + 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(); + image::LanczosScale(src_img.format(), src_img.GetMipData(src_mip), + src_img.GetMipPitch(src_mip), + src_x, src_y, + src_width, src_height, + mip_data, helper.pitch(), + dst_x, dst_y, + dst_width, dst_height, + components); +} + +void TextureCUBE::DrawImage(const Canvas& src_img, + int src_x, int src_y, + int src_width, int src_height, + CubeFace dest_face, int dest_mip, + int dst_x, int dst_y, + int dst_width, int dst_height) { + if (dest_face >= NUMBER_OF_FACES) { + O3D_ERROR(service_locator()) << "Invalid face specification"; + return; + } + + if (dest_mip < 0 || dest_mip >= levels()) { + O3D_ERROR(service_locator()) << "Destination Mip out of range"; + } + + // Clip source and destination rectangles to + // source and destination bitmaps. + // if src or dest rectangle is out of boundary, + // do nothing and return true. + if (!image::AdjustDrawImageBoundary(&src_x, &src_y, + &src_width, &src_height, + 0, + src_img.width(), src_img.height(), + &dst_x, &dst_y, + &dst_width, &dst_height, + dest_mip, + edge_length(), edge_length())) { + return; + } + + // check formats of source and dest images. + // format of source and dest should be the same. + if (format() != Texture::ARGB8 && format() != Texture::XRGB8) { + O3D_ERROR(service_locator()) << "format must be ARGB8 or XRGB8."; + return; + } + + unsigned int mip_length = image::ComputeMipDimension(dest_mip, edge_length()); + unsigned int components = image::GetNumComponentsForFormat(format()); + DCHECK(components > 0); + + int src_pitch = src_img.GetPitch(); + if (image::AdjustForSetRect(&src_y, src_width, src_height, &src_pitch, + &dst_y, dst_width, &dst_height)) { + SetRect(dest_face, dest_mip, dst_x, dst_y, dst_width, dst_height, + src_img.GetPixelData(src_x, src_y), + src_pitch); + return; + } + + LockHelper helper(this, dest_face, dest_mip); + uint8* mip_data = helper.GetDataAs<uint8>(); + if (!mip_data) { + return; + } - image::LanczosScale(src_img_data, src_x, src_y, + image::LanczosScale(format(), + src_img.GetPixelData(0, 0), + src_img.GetPitch(), + src_x, src_y, src_width, src_height, - src_img.width(), src_img.height(), mip_data, helper.pitch(), dst_x, dst_y, dst_width, dst_height, - mip_length, mip_length, components); + components); } void TextureCUBE::SetFromBitmap(CubeFace face, const Bitmap& bitmap) { diff --git a/o3d/core/cross/texture.h b/o3d/core/cross/texture.h index c16c884..79ddae2 100644 --- a/o3d/core/cross/texture.h +++ b/o3d/core/cross/texture.h @@ -42,6 +42,7 @@ namespace o3d { class Pack; class Bitmap; +class Canvas; // An abstract class for 2D textures that defines the interface for getting // the dimensions of the texture and number of mipmap levels. @@ -130,19 +131,43 @@ class Texture2D : public Texture { // Scales if the width and height of source and dest do not match. // Parameters: // source_img: source bitmap which would be drawn. + // source_mip: source mip to draw. // source_x: x-coordinate of the starting pixel in the source image. // source_y: y-coordinate of the starting pixel in the source image. // source_width: width of the source image to draw. // source_height: Height of the source image to draw. + // dest_mip: on which mip level the sourceImg would be drawn. // dest_x: x-coordinate of the starting pixel in the dest image. // dest_y: y-coordinate of the starting pixel in the dest image. // dest_width: width of the dest image. // dest_height: height of the dest image. - // dest_mip: on which mip level the sourceImg would be drawn. - void DrawImage(const Bitmap& source_img, int source_x, int source_y, + void DrawImage(const Bitmap& source_img, int src_mip, + int source_x, int source_y, + int source_width, int source_height, + int dest_mip, + int dest_x, int dest_y, + int dest_width, int dest_height); + + // Copy pixels from source bitmap to certain mip level. + // Scales if the width and height of source and dest do not match. + // Parameters: + // source_img: source canvas to draw. + // source_x: x-coordinate of the starting pixel in the source image. + // source_y: y-coordinate of the starting pixel in the source image. + // source_width: width of the source image to draw. + // source_height: Height of the source image to draw. + // dest_mip: the dest mip to draw to. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image. + // dest_height: height of the dest image. + void DrawImage(const Canvas& source_img, + int source_x, int source_y, int source_width, int source_height, + int dest_mip, int dest_x, int dest_y, - int dest_width, int dest_height, int dest_mip); + int dest_width, int dest_height); + // Sets the contents of the texture from a Bitmap. void SetFromBitmap(const Bitmap& bitmap); @@ -293,21 +318,45 @@ class TextureCUBE : public Texture { // Copy pixels from source bitmap to certain mip level. // Scales if the width and height of source and dest do not match. // Parameters: - // source_img: source bitmap which would be drawn. + // source_img: source bitmap. + // source_mip: source mip to draw. // source_x: x-coordinate of the starting pixel in the source image. // source_y: y-coordinate of the starting pixel in the source image. // source_width: width of the source image to draw. // source_height: Height of the source image to draw. + // face: face to draw to. + // dest_mip: mip to draw to. // dest_x: x-coordinate of the starting pixel in the dest image. // dest_y: y-coordinate of the starting pixel in the dest image. // dest_width: width of the dest image. // dest_height: height of the dest image. - // face: on which face the sourceImg would be drawn. - // dest_mip: on which mip level the sourceImg would be drawn. - void DrawImage(const Bitmap& source_img, int source_x, int source_y, + void DrawImage(const Bitmap& source_img, int source_mip, + int source_x, int source_y, + int source_width, int source_height, + CubeFace face, int dest_mip, + int dest_x, int dest_y, int dest_width, + int dest_height); + + // Copy pixels from source canvas to certain mip level. + // Scales if the width and height of source and dest do not match. + // Parameters: + // source_img: source canvas. + // source_x: x-coordinate of the starting pixel in the source image. + // source_y: y-coordinate of the starting pixel in the source image. + // source_width: width of the source image to draw. + // source_height: Height of the source image to draw. + // face: face to draw to. + // dest_mip: mip to draw to. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image. + // dest_height: height of the dest image. + void DrawImage(const Canvas& source_img, + int source_x, int source_y, int source_width, int source_height, + CubeFace face, int dest_mip, int dest_x, int dest_y, int dest_width, - int dest_height, CubeFace face, int dest_mip); + int dest_height); // Sets the contents of the texture from a Bitmap. void SetFromBitmap(CubeFace face, const Bitmap& bitmap); diff --git a/o3d/plugin/idl/param.idl b/o3d/plugin/idl/param.idl index e57f8fe..49aa369 100644 --- a/o3d/plugin/idl/param.idl +++ b/o3d/plugin/idl/param.idl @@ -316,7 +316,7 @@ typedef Param[] ParamVector; %[ A Param which stores a DrawList. %] -[nocpp, include="core/cross/param.h"] class ParamDrawList : Param { +[nocpp, include="core/cross/draw_context.h"] class ParamDrawList : Param { %[ The DrawList stored by the Param. %] diff --git a/o3d/plugin/idl/texture.idl b/o3d/plugin/idl/texture.idl index dcaf4e7..0cd27c0 100644 --- a/o3d/plugin/idl/texture.idl +++ b/o3d/plugin/idl/texture.idl @@ -242,34 +242,74 @@ namespace o3d { Copy pixels from source bitmap to certain mip level. Scales if the width and height of source and dest do not match. - \param source_img source bitmap which would be drawn. + \param source_img The source bitmap. + \param source_mip which mip from the source to copy from. \param source_x x-coordinate of the starting pixel in the source image. \param source_y y-coordinate of the starting pixel in the source image. \param source_width width of the source image to draw. \param source_height Height of the source image to draw. - \param dest_x x-coordinate of the starting pixel in the dest image. - \param dest_y y-coordinate of the starting pixel in the dest image. + \param dest_mip on which mip level to draw to. + \param dest_x x-coordinate of the starting pixel in the destination texture. + \param dest_y y-coordinate of the starting pixel in the destination texture. \param dest_width width of the dest image. \param dest_height height of the dest image. - \param dest_mip on which mip level the sourceImg would be drawn. %] [userglue] - void DrawImage(Bitmap source_img, int source_x, int source_y, + void DrawImage(Bitmap source_img, int source_mip, + int source_x, int source_y, int source_width, int source_height, + int dest_mip, int dest_x, int dest_y, - int dest_width, int dest_height, int dest_mip); + int dest_width, int dest_height); + + %[ + Copy pixels from source canvas to certain mip level. + Scales if the width and height of source and dest do not match. + + \param source_img The source canvas. + \param source_x x-coordinate of the starting pixel in the source image. + \param source_y y-coordinate of the starting pixel in the source image. + \param source_width width of the source image to draw. + \param source_height Height of the source image to draw. + \param dest_mip on which mip level to draw to. + \param dest_x x-coordinate of the starting pixel in the destination texture. + \param dest_y y-coordinate of the starting pixel in the destination texture. + \param dest_width width of the dest image. + \param dest_height height of the dest image. + %] + [userglue, include="core/cross/canvas.h"] + void DrawImage(Canvas source_img, + int source_x, int source_y, + int source_width, int source_height, + int dest_mip, + int dest_x, int dest_y, + int dest_width, int dest_height); + [verbatim=cpp_glue] %{ void userglue_method_DrawImage( o3d::Texture2D* self, - o3d::Bitmap* source_img, int source_x, int source_y, + o3d::Bitmap* source_img, int source_mip, int source_x, int source_y, + int source_width, int source_height, + int dest_mip, + int dest_x, int dest_y, + int dest_width, int dest_height) { + self->DrawImage(*source_img, source_mip, source_x, source_y, + source_width, source_height, + dest_mip, dest_x, dest_y, + dest_width, dest_height); + } + void userglue_method_DrawImage( + o3d::Texture2D* self, + o3d::Canvas* source_img, int source_x, int source_y, int source_width, int source_height, + int dest_mip, int dest_x, int dest_y, - int dest_width, int dest_height, int dest_mip) { + int dest_width, int dest_height) { self->DrawImage(*source_img, source_x, source_y, source_width, source_height, - dest_x, dest_y, - dest_width, dest_height, dest_mip); + dest_mip, dest_x, dest_y, + dest_width, dest_height); } %} }; // Texture2D @@ -402,40 +442,78 @@ namespace o3d { %} %[ - Copy pixels from source bitmap to certain mip level. + Copy pixels from source bitmap to certain face and mip level. Scales if the width and height of source and dest do not match. - \param source_img source bitmap which would be drawn. + \param source_img The source bitmap. + \param source_mip which mip of the source to copy from. \param source_x x-coordinate of the starting pixel in the source image. \param source_y y-coordinate of the starting pixel in the source image. \param source_width width of the source image to draw. \param source_height Height of the source image to draw. - \param dest_x x-coordinate of the starting pixel in the dest image. - \param dest_y y-coordinate of the starting pixel in the dest image. - \param dest_width width of the dest image. - \param dest_height height of the dest image. - \param face on which face the sourceImg would be drawn. - \param dest_mip on which mip level the sourceImg would be drawn. + \param face on which face to draw on. + \param dest_mip on which mip level to draw on. + \param dest_x x-coordinate of the starting pixel in the destination texture. + \param dest_y y-coordinate of the starting pixel in the destination texture. + \param dest_width width of the destination image. + \param dest_height height of the destination image. %] [userglue] - void DrawImage(Bitmap source_img, int source_x, int source_y, + void DrawImage(Bitmap source_img, int source_mip, int source_x, int source_y, + int source_width, int source_height, + CubeFace face, int dest_mip, + int dest_x, int dest_y, + int dest_width, int dest_height); + + %[ + Copy pixels from source canvas to certain face and mip level. + Scales if the width and height of source and dest do not match. + + \param source_img The source canvas. + \param source_x x-coordinate of the starting pixel in the source image. + \param source_y y-coordinate of the starting pixel in the source image. + \param source_width width of the source image to draw. + \param source_height Height of the source image to draw. + \param face on which face to draw on. + \param dest_mip on which mip level to draw on. + \param dest_x x-coordinate of the starting pixel in the destination texture. + \param dest_y y-coordinate of the starting pixel in the destination texture. + \param dest_width width of the destination image. + \param dest_height height of the destination image. + %] + [userglue, include="core/cross/canvas.h"] + void DrawImage(Canvas source_img, int source_x, int source_y, int source_width, int source_height, + CubeFace face, int dest_mip, int dest_x, int dest_y, - int dest_width, int dest_height, - CubeFace face, int dest_mip); + int dest_width, int dest_height); [verbatim=cpp_glue] %{ void userglue_method_DrawImage( o3d::TextureCUBE* self, - o3d::Bitmap* source_img, int source_x, int source_y, + o3d::Bitmap* source_img, int source_mip, int source_x, int source_y, + int source_width, int source_height, + o3d::TextureCUBE::CubeFace dest_face, int dest_mip, + int dest_x, int dest_y, + int dest_width, int dest_height) { + self->DrawImage(*source_img, source_mip, source_x, source_y, + source_width, source_height, + dest_face, dest_mip, + dest_x, dest_y, + dest_width, dest_height); + } + void userglue_method_DrawImage( + o3d::TextureCUBE* self, + o3d::Canvas* source_img, int source_x, int source_y, int source_width, int source_height, + o3d::TextureCUBE::CubeFace dest_face, int dest_mip, int dest_x, int dest_y, - int dest_width, int dest_height, - o3d::TextureCUBE::CubeFace dest_face, int dest_mip) { + int dest_width, int dest_height) { self->DrawImage(*source_img, source_x, source_y, source_width, source_height, + dest_face, dest_mip, dest_x, dest_y, - dest_width, dest_height, dest_face, dest_mip); + dest_width, dest_height); } %} }; // TextureCUBE diff --git a/o3d/samples/MANIFEST b/o3d/samples/MANIFEST index d7b0963..b17fd66 100644 --- a/o3d/samples/MANIFEST +++ b/o3d/samples/MANIFEST @@ -198,6 +198,7 @@ o3djs/serialization.js o3djs/shape.js o3djs/simple.js o3djs/test.js +o3djs/texture.js o3djs/util.js pingpong/instructions.gif pingpong/logo.gif diff --git a/o3d/samples/archive-textures.html b/o3d/samples/archive-textures.html index 2e33a3e..7b7b4ea 100644 --- a/o3d/samples/archive-textures.html +++ b/o3d/samples/archive-textures.html @@ -49,6 +49,7 @@ o3djs.require('o3djs.io'); o3djs.require('o3djs.rendergraph'); o3djs.require('o3djs.primitives'); o3djs.require('o3djs.effect'); +o3djs.require('o3djs.texture'); // Events // Run the init() once the page has finished loading. @@ -173,7 +174,7 @@ function createArchiveRequest(effect) { g_streamingStarted = true; // Create a texture from the RawData object that was just made available. - var texture = g_pack.createTextureFromRawData(rawData, true); + var texture = o3djs.texture.createTextureFromRawData(g_pack, rawData, true); // Free the raw data object immediately since we're done with it. // If we don't call this the RawData will stay around so we can use it diff --git a/o3d/samples/bitmap-draw-image.html b/o3d/samples/bitmap-draw-image.html index 1e3bac0..e4a5208 100644 --- a/o3d/samples/bitmap-draw-image.html +++ b/o3d/samples/bitmap-draw-image.html @@ -177,28 +177,33 @@ function initStep2(clientElements) { function callback(archiveInfo, exception) {
if (!exception) {
var rawdata1 = archiveInfo.getFileByURI('shaving_cream_300x300.jpg', true);
- var bitmap1 = g_pack.createBitmapFromRawData(rawdata1);
+ var bitmap1 = g_pack.createBitmapsFromRawData(rawdata1)[0];
+ bitmap1.flipVertically();
var rawdata2 = archiveInfo.getFileByURI('four_pixel.png', true);
- var bitmap2 = g_pack.createBitmapFromRawData(rawdata2);
+ var bitmap2 = g_pack.createBitmapsFromRawData(rawdata2)[0];
+ bitmap2.flipVertically();
var rawdata_hi = archiveInfo.getFileByURI('hi.jpg', true);
- var bitmap_hi = g_pack.createBitmapFromRawData(rawdata_hi);
+ var bitmapHi = g_pack.createBitmapsFromRawData(rawdata_hi)[0];
+ bitmapHi.flipVertically();
var texture = g_pack.createTexture2D(300, 300, g_o3d.Texture.XRGB8, 0,
false);
// draw image on bitmap.
// scale down on top left corner.
- texture.drawImage(bitmap1, 0, 0, 300, 300, 0, 0, 150, 150, 0);
+ texture.drawImage(bitmap1, 0, 0, 0, 300, 300, 0, 0, 0, 150, 150);
// scale up on top right corner.
- texture.drawImage(bitmap1, 0, 0, 100, 100, 150, 0, 150, 150, 0);
+ texture.drawImage(bitmap1, 0, 0, 156, 100, 100, 0, 150, 0, 150, 150);
// flip and draw part of the img on bottom left.
- texture.drawImage(bitmap1, 150, 0, 150, 150, 149, 299, -150, -150, 0);
+ texture.drawImage(
+ bitmap1, 0, 150, 100, 150, 150, 0, 149, 299, -150, -150);
// draw out of boundary.
- texture.drawImage(bitmap1, 0, 0, 300, 300, 150, 150, 300, 300, 0);
+ texture.drawImage(bitmap1, 0, 0, 135, 300, 300, 0, 150, 150, 300, 300);
// draw o3d.
- texture.drawImage(bitmap_hi, 0, 0, 100, 50, 100, 125, 100, 50, 0);
+ texture.drawImage(bitmapHi, 0, 0, 0, 100, 50, 0, 100, 125, 100, 50);
// draw image on different mip-maps.
- texture.drawImage(bitmap1, 0, 0, 300, 300, 0, 0, 150, 150, 1);
- texture.drawImage(bitmap2, 0, 0, 2, 2, 0, 0, 75, 75, 2);
+ texture.drawImage(bitmap1, 0, 0, 0, 300, 300, 1, 0, 0, 150, 150);
+ texture.drawImage(bitmap2, 0, 0, 0, 2, 2, 2, 0, 0, 75, 75);
+ texture.setRect(0, 0, 0, 1, [1, 0, 0]);
makeShape(texture, effect);
}
diff --git a/o3d/samples/o3djs/canvas.js b/o3d/samples/o3djs/canvas.js index 3c264b7..b2f6247 100644 --- a/o3d/samples/o3djs/canvas.js +++ b/o3d/samples/o3djs/canvas.js @@ -355,7 +355,10 @@ o3djs.canvas.CanvasQuad = function(canvasInfo, * been issued to the CanvasQuad's Canvas object. */ o3djs.canvas.CanvasQuad.prototype.updateTexture = function() { - this.canvas.copyToTexture(this.texture); + var width = this.texture.width; + var height = this.texture.height; + this.texture.drawImage(this.canvas, 0, height - 1, width, -height, + 0, 0, 0, width, height); }; /** diff --git a/o3d/samples/o3djs/io.js b/o3d/samples/o3djs/io.js index 690bd2c..f575eff 100644 --- a/o3d/samples/o3djs/io.js +++ b/o3d/samples/o3djs/io.js @@ -36,6 +36,9 @@ o3djs.provide('o3djs.io'); +o3djs.require('o3djs.texture'); + + /** * A Module with various io functions and classes. * @namespace @@ -543,11 +546,10 @@ o3djs.io.loadArchiveAdvanced = function(pack, * texture is loaded. It will be passed the texture and an exception on * error or null on success. * @return {!o3djs.io.LoadInfo} A LoadInfo to track progress. - * @see o3djs.io.createLoader + * @see o3djs.loader.createLoader */ o3djs.io.loadTexture = function(pack, url, callback) { - // TODO: change this to get use RawData and Bitmap - var request = pack.createFileRequest('TEXTURE'); + var request = pack.createFileRequest('RAWDATA'); var loadInfo = o3djs.io.createLoadInfo( /** @type {!o3d.FileRequest} */ (request), false); @@ -557,9 +559,13 @@ o3djs.io.loadTexture = function(pack, url, callback) { */ request.onreadystatechange = function() { if (request.done) { - var texture = request.texture; + var rawData = /** @type {!o3d.RawData} */ request.data; var success = request.success; var exception = request.error; + var texture = null; + if (success) { + texture = o3djs.texture.createTextureFromRawData(pack, rawData, true); + } loadInfo.finish(); pack.removeObject(request); if (!success && !exception) { @@ -572,4 +578,3 @@ o3djs.io.loadTexture = function(pack, url, callback) { return loadInfo; }; - diff --git a/o3d/samples/o3djs/js_list.scons b/o3d/samples/o3djs/js_list.scons index f590330..c5fad39 100644 --- a/o3d/samples/o3djs/js_list.scons +++ b/o3d/samples/o3djs/js_list.scons @@ -55,6 +55,7 @@ O3D_JS_SOURCES = [ 'shape.js', 'simple.js', 'test.js', + 'texture.js', 'util.js', ] diff --git a/o3d/samples/o3djs/serialization.js b/o3d/samples/o3djs/serialization.js index bc459a3..2514bd7 100644 --- a/o3d/samples/o3djs/serialization.js +++ b/o3d/samples/o3djs/serialization.js @@ -39,6 +39,7 @@ o3djs.provide('o3djs.serialization'); o3djs.require('o3djs.error'); +o3djs.require('o3djs.texture'); /** * A Module for handling events related to o3d and various browsers. @@ -162,9 +163,7 @@ o3djs.serialization.Deserializer = function(pack, json) { if (!rawData) { throw 'Could not find texture ' + uri + ' in the archive'; } - return deserializer.pack.createTextureFromRawData( - rawData, - true); + return o3djs.texture.createTextureFromRawData(pack, rawData, true); } else { return deserializer.pack.createTexture2D( json.custom.width, @@ -182,9 +181,7 @@ o3djs.serialization.Deserializer = function(pack, json) { if (!rawData) { throw 'Could not find texture ' + uri + ' in the archive'; } - return deserializer.pack.createTextureFromRawData( - rawData, - true); + return o3djs.texture.createTextureFromRawData(pack, rawData, true); } else { return deserializer.pack.createTextureCUBE( json.custom.edgeLength, @@ -683,7 +680,7 @@ o3djs.serialization.deserialize = function(pack, json) { /** * Deserializes a single json object named 'scene.json' from a loaded * o3djs.io.ArchiveInfo. - * @param {!o3djs.io.archiveInfo} archiveInfo Archive to load from. + * @param {!o3djs.io.ArchiveInfo} archiveInfo Archive to load from. * @param {string} sceneJsonUri The relative URI of the scene JSON file within * the archive. * @param {!o3d.Client} client An O3D client object. diff --git a/o3d/samples/o3djs/texture.js b/o3d/samples/o3djs/texture.js new file mode 100644 index 0000000..6571503 --- /dev/null +++ b/o3d/samples/o3djs/texture.js @@ -0,0 +1,236 @@ +/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains functions helping to manipulate and manage
+ * textures.
+ */
+
+o3djs.provide('o3djs.texture');
+
+/**
+ * A Module for bitmaps.
+ * @namespace
+ */
+o3djs.texture = o3djs.texture || {};
+
+/**
+ * The maximum dimension of a texture.
+ * @type {number}
+ */
+o3djs.texture.MAX_TEXTURE_DIMENSION = 2048;
+
+/**
+ * Computes the maximum number of levels of mips a given width and height could
+ * use.
+ * @param {number} width Width of texture.
+ * @param {number} height Height of texture.
+ * @return {number} The maximum number of levels for the given width and height.
+ */
+o3djs.texture.computeNumLevels = function(width, height) {
+ if (width == 0 || height == 0) {
+ return 0;
+ }
+ var max = Math.max(width, height);
+ var levels = 0;
+ while (max > 0) {
+ ++levels;
+ max = max >> 1;
+ }
+ return levels;
+};
+
+/**
+ * Creates a texture from a RawData object.
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {!o3d.RawData} rawData The raw data to create the texture from.
+ * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
+ * can not be generated for DXT textures although they will be loaded if they
+ * exist in the RawData.
+ * @param {boolean} opt_flip Whether or not to flip the texture. Most DCC tools
+ * Like Maya, Max, etc expect the textures to be flipped. Note that only
+ * 2D (image) textures will be flipped. Cube textures will not be flipped.
+ * Default = true.
+ * @param {number} opt_maxWidth The maximum width of the texture. If the RawData
+ * is larger than this size it will be scaled down to this size. Note that
+ * DXT format textures can not be scaled. Default = 2048.
+ * @param {number} opt_maxHeight The maximum width of the texture. If the
+ * RawData is larger than this size it will be scaled down to this size. Note
+ * that DXT format textures can not be scaled. Default = 2048.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createTextureFromRawData = function(
+ pack,
+ rawData,
+ opt_generateMips,
+ opt_flip,
+ opt_maxWidth,
+ opt_maxHeight) {
+ // Make a bitmaps from the raw data.
+ var bitmaps = pack.createBitmapsFromRawData(rawData);
+ if (opt_flip || typeof opt_flip === 'undefined') {
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ var bitmap = bitmaps[ii];
+ if (bitmap.semantic == o3djs.base.o3d.Bitmap.IMAGE) {
+ bitmaps[ii].flipVertically();
+ }
+ }
+ }
+
+ // Create a texture from the bitmaps.
+ var texture = o3djs.texture.createTextureFromBitmaps(
+ pack, bitmaps, opt_generateMips);
+
+ // Delete the bitmaps.
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ pack.removeObject(bitmaps[ii]);
+ }
+
+ return texture;
+};
+
+/**
+ * Returns whether or not a given texture format can be scaled.
+ * @param {!o3d.Texture.Format} format The format to check.
+ * @return {boolean} True if you can scale and make mips for the given format.
+ */
+o3djs.texture.canMakeMipsAndScale = function(format) {
+ switch (format) {
+ case o3djs.base.o3d.Texture.XRGB8:
+ case o3djs.base.o3d.Texture.ARGB8:
+ case o3djs.base.o3d.Texture.ABGR16F:
+ case o3djs.base.o3d.Texture.R32F:
+ case o3djs.base.o3d.Texture.ABGR32F:
+ return true;
+ case o3djs.base.o3d.Texture.DXT1:
+ case o3djs.base.o3d.Texture.DXT3:
+ case o3djs.base.o3d.Texture.DXT5:
+ return false;
+ }
+ return false;
+};
+
+/**
+ * Creates a Texture from an array of bitmaps.
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {!Array.<!o3d.Bitmap>} bitmaps An array of bitmaps to create the
+ * texture from. For a 2D texture this would be 1 bitmap. For a cubemap this
+ * would be 6 bitmaps.
+ * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
+ * can not be generated for DXT textures although they will be loaded if they
+ * exist in the RawData.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createTextureFromBitmaps = function(
+ pack,
+ bitmaps,
+ opt_generateMips) {
+
+ if (bitmaps.length == 0) {
+ throw 'no bitmaps';
+ }
+
+ var srcWidth = bitmaps[0].width;
+ var srcHeight = bitmaps[0].height;
+ var format = bitmaps[0].format;
+ var mipMaps = bitmaps[0].numMipmaps;
+ var maxMips = o3djs.texture.computeNumLevels(srcWidth, srcHeight);
+ var targetMips = mipMaps;
+ var dstWidth = srcWidth;
+ var dstHeight = srcHeight;
+ if (opt_generateMips && o3djs.texture.canMakeMipsAndScale(format) &&
+ mipMaps == 1 && maxMips > 1) {
+ targetMips = maxMips;
+ }
+
+ // Check that all the bitmaps are the same size and make mips
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ var bitmap = bitmaps[ii];
+ if (bitmap.width != srcWidth ||
+ bitmap.height != srcHeight ||
+ bitmap.format != format ||
+ bitmap.numMipmaps != mipMaps) {
+ throw 'bitmaps must all be the same width, height, mips and format';
+ }
+ if (targetMips != mipMaps) {
+ bitmap.generateMips(0, targetMips - 1);
+ }
+ }
+
+ var levels = bitmap.numMipmaps > 1 ? bitmap.numMipmaps :
+ o3djs.texture.computeNumLevels(dstWidth, dstHeight);
+ var texture;
+ if (bitmaps.length == 6 &&
+ bitmaps[0].semantic != o3djs.base.o3d.Bitmap.SLICE) {
+ if (srcWidth != srcHeight ||
+ srcWidth != dstWidth ||
+ srcHeight != dstHeight) {
+ throw 'Cubemaps must be square';
+ }
+ texture = pack.createTextureCUBE(dstWidth, format, targetMips, false);
+ for (var ii = 0; ii < 6; ++ii) {
+ texture.setFromBitmap(ii, bitmaps[ii]);
+ }
+ } else if (bitmaps.length == 1) {
+ texture = pack.createTexture2D(
+ dstWidth, dstHeight, format, targetMips, false);
+ texture.setFromBitmap(bitmaps[0]);
+ }
+
+ return texture;
+};
+
+/**
+ * Creates a TextureCUBE from 6 bitmaps. The bitmaps do not have to be the same
+ * size thought they do have to be the same format.
+ *
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {number} edgeLength The size of the cubemap.
+ * @param {!Array.<!o3d.Bitmap>} bitmaps An array of 6 bitmaps in the order
+ * FACE_POSITIVE_X, FACE_NEGATIVE_X, FACE_POSITIVE_Y, FACE_NEGATIVE_Y,
+ * FACE_POSITIVE_Z, FACE_NEGATIVE_Z.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createCubeTextureFrom6Bitmaps = function(
+ pack, edgeLength, bitmaps) {
+ var numMips = o3djs.texture.computeNumLevels(edgeLength, edgeLength);
+ var texture = pack.createTextureCUBE(
+ edgeLength, bitmaps[0].format, numMips, false);
+ for (var ii = 0; ii < 6; ++ii) {
+ var bitmap = bitmaps[ii];
+ texture.drawImage(bitmap, 0, 0, 0, bitmap.width, bitmap.height,
+ ii, 0, 0, edgeLength, edgeLength);
+ }
+ texture.generateMips(0, numMips - 1);
+ return texture;
+};
+
diff --git a/o3d/samples/o3djs/util.js b/o3d/samples/o3djs/util.js index 66722e1..6df3e93 100644 --- a/o3d/samples/o3djs/util.js +++ b/o3d/samples/o3djs/util.js @@ -60,7 +60,7 @@ o3djs.util.PLUGIN_NAME = 'O3D Plugin'; * utility libraries. * @type {string} */ -o3djs.util.REQUIRED_VERSION = '0.1.38.0'; +o3djs.util.REQUIRED_VERSION = '0.1.40.0'; /** * A URL at which to download the client. |