summaryrefslogtreecommitdiffstats
path: root/o3d/core/cross/bitmap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/core/cross/bitmap.cc')
-rw-r--r--o3d/core/cross/bitmap.cc293
1 files changed, 292 insertions, 1 deletions
diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc
index 947d68ea..d3d1499 100644
--- a/o3d/core/cross/bitmap.cc
+++ b/o3d/core/cross/bitmap.cc
@@ -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::OpenFile;
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,6 +277,178 @@ bool Bitmap::LoadFromRawData(RawData *raw_data,
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.
@@ -574,6 +762,105 @@ bool Bitmap::Scale(unsigned int src_width,
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 @@ bool Bitmap::CheckAlphaIsOne() const {
return true;
}
+ObjectBase::Ref Bitmap::Create(ServiceLocator* service_locator) {
+ return ObjectBase::Ref(new Bitmap(service_locator));
+}
+
} // namespace o3d