Index: core/cross/bitmap.cc |
=================================================================== |
--- core/cross/bitmap.cc (revision 20557) |
+++ core/cross/bitmap.cc (working copy) |
@@ -37,9 +37,10 @@ |
// The precompiled header must appear before anything else. |
#include "core/cross/precompile.h" |
+#include "core/cross/bitmap.h" |
#include <cstring> |
+#include <cmath> |
#include <sys/stat.h> |
-#include "core/cross/bitmap.h" |
#include "utils/cross/file_path_utils.h" |
#include "base/file_path.h" |
#include "base/file_util.h" |
@@ -51,8 +52,23 @@ |
using file_util::CloseFile; |
using file_util::GetFileSize; |
+namespace { |
+static const double kEpsilon = 0.0001; |
+} // anonymous namespace. |
+ |
namespace o3d { |
+ O3D_DEFN_CLASS(Bitmap, ParamObject); |
+ |
+Bitmap::Bitmap(ServiceLocator* service_locator) |
+ : ParamObject(service_locator), |
+ image_data_(NULL), |
+ format_(Texture::UNKNOWN_FORMAT), |
+ width_(0), |
+ height_(0), |
+ num_mipmaps_(0), |
+ is_cubemap_(false) {} |
+ |
// Gets the size of the buffer containing a an image, given its width, height |
// and format. |
unsigned int Bitmap::GetBufferSize(unsigned int width, |
@@ -261,7 +277,179 @@ |
return LoadFromStream(&stream, filename, file_type, generate_mipmaps); |
} |
+void Bitmap::DrawImage(Bitmap* src_img, |
+ int src_x, int src_y, |
+ int src_width, int src_height, |
+ int dst_x, int dst_y, |
+ int dst_width, int dst_height) { |
+ DCHECK(src_img->image_data()); |
+ DCHECK(image_data()); |
+ // Clip source and destination rectangles to |
+ // source and destination bitmaps. |
+ // if src or dest rectangle is out of boundary, |
+ // do nothing and return. |
+ if (!AdjustDrawImageBoundary(&src_x, &src_y, |
+ &src_width, &src_height, |
+ src_img->width_, src_img->height_, |
+ &dst_x, &dst_y, |
+ &dst_width, &dst_height, |
+ 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."; |
+ return; |
+ } |
+ // 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 && |
+ 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()); |
+ 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 { |
+ O3D_ERROR(service_locator()) << "DrawImage does not support format: " |
+ << src_img->format_ << " unless src and " |
+ << "dest images are in the same size and " |
+ << "copying the entire bitmap"; |
+ return; |
+ } |
+ |
+ unsigned char* src_img_data = src_img->image_data(); |
+ unsigned char* dst_img_data = image_data(); |
+ |
+ // crop part of image from src img, scale it in |
+ // bilinear interpolation fashion, and paste it |
+ // on dst img. |
+ BilinearInterpolateScale(src_img_data, src_x, src_y, |
+ src_width, src_height, |
+ src_img->width_, src_img->height_, |
+ dst_img_data, dst_x, dst_y, |
+ dst_width, dst_height, |
+ width_, height_, components); |
+} |
+ |
+// static utility function used by DrawImage in bitmap and textures. |
+// in this function, positions are converted to 4th-quadrant, which |
+// means origin locates left-up corner. |
+void Bitmap::BilinearInterpolateScale(const uint8* src_img_data, |
+ int src_x, int src_y, |
+ int src_width, int src_height, |
+ int src_img_width, int src_img_height, |
+ uint8* dest_img_data, |
+ int dest_x, int dest_y, |
+ int dest_width, int dest_height, |
+ int dest_img_width, int dest_img_height, |
+ int components) { |
+ for (int i = 0; i < std::abs(dest_width); i++) { |
+ // x is the iterator of dest_width in dest_img. |
+ // change x to negative if dest_width is negative. |
+ int x = i; |
+ if (dest_width < 0) |
+ x = -i; |
+ |
+ // calculate corresponding coordinate in src_img. |
+ double base_x = i * (std::abs(src_width) - 1) / |
+ static_cast<double>(std::abs(dest_width) - 1); |
+ // base_floor_x is the iterator of src_width in src_img. |
+ // change base_x to negative if src_width is negative. |
+ if (src_width < 0) |
+ base_x = -base_x; |
+ int base_floor_x = static_cast<int>(std::floor(base_x)); |
+ |
+ for (int j = 0; j < std::abs(dest_height); j++) { |
+ // y is the iterator of dest_height in dest_img. |
+ // change y to negative if dest_height is negative. |
+ int y = j; |
+ if (dest_height < 0) |
+ y = -j; |
+ |
+ // calculate coresponding coordinate in src_img. |
+ double base_y = j * (std::abs(src_height) - 1) / |
+ static_cast<double>(std::abs(dest_height) - 1); |
+ // change base_y to negative if src_height is negative. |
+ if (src_height < 0) |
+ base_y = -base_y; |
+ int base_floor_y = static_cast<int>(std::floor(base_y)); |
+ |
+ for (unsigned int c = 0; c < components; c++) { |
+ // if base_x and base_y are integers, which means this point |
+ // exists in src_img, just copy the original values. |
+ if (base_x - base_floor_x < kEpsilon && |
+ base_y - base_floor_y < kEpsilon) { |
+ dest_img_data[((dest_img_height - (y + dest_y) - 1) * |
+ dest_img_width + dest_x + x) * components + c] = |
+ src_img_data[((src_img_height - (base_floor_y + src_y) - 1) * |
+ src_img_width + src_x + base_floor_x) * components + c]; |
+ continue; |
+ } |
+ |
+ // get four nearest neighbors of point (base_x, base_y) from src img. |
+ uint8 src_neighbor_11, src_neighbor_21, |
+ src_neighbor_12, src_neighbor_22; |
+ src_neighbor_11 = src_img_data[((src_img_height - (base_floor_y + |
+ src_y) - 1) * src_img_width + src_x + |
+ base_floor_x) * components + c]; |
+ // if base_x exists in src img. set src_neighbor_21 to src_neighbor_11 |
+ // so the interpolation result would remain src_neighbor_11. |
+ if (base_x - base_floor_x < kEpsilon) |
+ src_neighbor_21 = src_neighbor_11; |
+ else |
+ src_neighbor_21 = src_img_data[((src_img_height - (base_floor_y + |
+ src_y) - 1) * src_img_width + src_x + |
+ base_floor_x + 1) * components + c]; |
+ // if base_y exists in src img. set src_neighbor_12 to src_neighbor_11 |
+ // so the interpolation result would remain src_neighbor_11. |
+ if (base_y - base_floor_y < kEpsilon) |
+ src_neighbor_12 = src_neighbor_11; |
+ else |
+ src_neighbor_12 = src_img_data[((src_img_height - (base_floor_y + |
+ src_y) - 2) * src_img_width + src_x + |
+ base_floor_x) * components + c]; |
+ |
+ if (base_x - base_floor_x < kEpsilon) |
+ src_neighbor_22 = src_neighbor_21; |
+ else if (base_y - base_floor_y < kEpsilon) |
+ src_neighbor_22 = src_neighbor_12; |
+ else |
+ src_neighbor_22 = src_img_data[((src_img_height - (base_floor_y + |
+ src_y) - 2) * src_img_width + src_x + |
+ base_floor_x + 1) * components + c]; |
+ |
+ // calculate interpolated value. |
+ double interpolatedValue = (1 - (base_y - base_floor_y)) * |
+ ((base_x - base_floor_x) * |
+ src_neighbor_21 + |
+ (1 - (base_x - base_floor_x)) * |
+ src_neighbor_11) + |
+ (base_y - base_floor_y) * |
+ ((base_x - base_floor_x) * |
+ src_neighbor_22 + |
+ (1 - (base_x - base_floor_x)) * |
+ src_neighbor_12); |
+ |
+ // assign the nearest integer of interpolatedValue to dest_img_data. |
+ dest_img_data[((dest_img_height - (y + dest_y) - 1) * dest_img_width + |
+ dest_x + x) * components + c] = |
+ static_cast<uint8>(interpolatedValue + 0.5); |
+ } |
+ } |
+ } |
+} |
+ |
Bitmap::ImageFileType Bitmap::GetFileTypeFromFilename(const char *filename) { |
// Convert the filename to lower case for matching. |
// NOTE: Surprisingly, "tolower" is not in the std namespace. |
@@ -574,6 +762,105 @@ |
return true; |
} |
+// Adjust boundaries when using DrawImage function in bitmap or texture. |
+bool Bitmap::AdjustDrawImageBoundary(int* src_x, int* src_y, |
+ int* src_width, int* src_height, |
+ int src_bmp_width, int src_bmp_height, |
+ int* dest_x, int* dest_y, |
+ int* dest_width, int* dest_height, |
+ 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) || |
+ (*src_y < 0 && *src_y + *src_height <= 0) || |
+ (*dest_x < 0 && *dest_x + *dest_width <= 0) || |
+ (*dest_y < 0 && *dest_y + *dest_height <= 0) || |
+ (*src_x >= src_bmp_width && |
+ *src_x + *src_width >= src_bmp_width - 1) || |
+ (*src_y >= src_bmp_height && |
+ *src_y + *src_height >= src_bmp_height - 1) || |
+ (*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)) |
+ return false; |
+ |
+ // if start points are negative. |
+ // check whether src_x is negative. |
+ if (!AdjustDrawImageBoundHelper(src_x, dest_x, |
+ src_width, dest_width, src_bmp_width)) |
+ return false; |
+ // check whether dest_x is negative. |
+ if (!AdjustDrawImageBoundHelper(dest_x, src_x, |
+ dest_width, src_width, dest_bmp_width)) |
+ return false; |
+ // check whether src_y is negative. |
+ if (!AdjustDrawImageBoundHelper(src_y, dest_y, |
+ src_height, dest_height, src_bmp_height)) |
+ return false; |
+ // check whether dest_y is negative. |
+ if (!AdjustDrawImageBoundHelper(dest_y, src_y, |
+ dest_height, src_height, dest_bmp_height)) |
+ return false; |
+ |
+ // check any width or height becomes negative after adjustment. |
+ if (*src_width == 0 || *src_height == 0 || |
+ *dest_width == 0 || *dest_height == 0) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+// utility function called in AdjustDrawImageBoundary. |
+// help to adjust a specific dimension, |
+// if start point or ending point is out of boundary. |
+bool Bitmap::AdjustDrawImageBoundHelper(int* src_a, int* dest_a, |
+ int* src_length, int* dest_length, |
+ int src_bmp_length) { |
+ 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. |
+ if (*src_a < 0) { |
+ int src_length_delta = 0 - *src_a; |
+ *dest_a = *dest_a + (*dest_length) * src_length_delta / (*src_length); |
+ *dest_length = *dest_length - (*dest_length) * |
+ src_length_delta / (*src_length); |
+ *src_length = *src_length - src_length_delta; |
+ *src_a = 0; |
+ } |
+ // if src_a >= src_bmp_width, src_length must be negative. |
+ if (*src_a >= src_bmp_length) { |
+ int src_length_delta = *src_a - (src_bmp_length - 1); |
+ *dest_a = *dest_a - (*dest_length) * src_length_delta / (*src_length); |
+ *dest_length = *dest_length - (*dest_length) * |
+ src_length_delta / *src_length; |
+ *src_length = *src_length - src_length_delta; |
+ *src_a = src_bmp_length - 1; |
+ } |
+ |
+ 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) { |
+ int src_length_delta = *src_length - (src_bmp_length - *src_a); |
+ *dest_length = *dest_length - (*dest_length) * |
+ src_length_delta / (*src_length); |
+ *src_length = *src_length - src_length_delta; |
+ } |
+ // if src_a + src_length < -1, src_length must be negative. |
+ if (*src_a + *src_length < -1) { |
+ int src_length_delta = 0 - (*src_a + *src_length); |
+ *dest_length = *dest_length + (*dest_length) * |
+ src_length_delta / (*src_length); |
+ *src_length = *src_length + src_length_delta; |
+ } |
+ |
+ return true; |
+} |
+ |
// Checks that all the alpha values are 1.0 |
bool Bitmap::CheckAlphaIsOne() const { |
if (!image_data()) |
@@ -682,4 +969,8 @@ |
return true; |
} |
+ObjectBase::Ref Bitmap::Create(ServiceLocator* service_locator) { |
+ return ObjectBase::Ref(new Bitmap(service_locator)); |
+} |
+ |
} // namespace o3d |