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.cc257
1 files changed, 150 insertions, 107 deletions
diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc
index 98863cf..6487fbd 100644
--- a/o3d/core/cross/bitmap.cc
+++ b/o3d/core/cross/bitmap.cc
@@ -54,6 +54,19 @@ using file_util::GetFileSize;
namespace {
static const double kEpsilon = 0.0001;
+static const double kPi = 3.14159265358979;
+static const int kFilterSize = 3;
+
+// utility function, round double numbers into 0 to 255 integers.
+uint8 Safe8Round(double f) {
+ f += 0.5;
+ if (f < 0.0) {
+ return 0;
+ } else if (!(f < 255.0)) {
+ return 255;
+ }
+ return static_cast<uint8>(f);
+}
} // anonymous namespace.
namespace o3d {
@@ -333,117 +346,147 @@ void Bitmap::DrawImage(Bitmap* src_img,
// 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);
+ LanczosScale(src_img_data, src_x, src_y,
+ src_width, src_height,
+ src_img->width_, src_img->height_,
+ dst_img_data, dst_x, dst_y,
+ dst_width, dst_height,
+ width_, height_, components);
+}
+
+void Bitmap::LanczosScale(const uint8* src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int src_img_width, int src_img_height,
+ uint8* dest,
+ 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_x, temp_y, temp_width,
+ temp_img_width, temp_img_height, true, components);
+
+ // Scale the temp buffer vertically to get the final result.
+ LanczosResize1D(temp.get(), temp_x, temp_y, temp_height, temp_width,
+ temp_img_width, temp_img_height,
+ dest, dest_x, dest_y, dest_height,
+ dest_img_width, dest_img_height, false, 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.
+void Bitmap::LanczosResize1D(const uint8* src, int src_x, int src_y,
+ int width, int height,
+ int src_bmp_width, int src_bmp_height,
+ uint8* out, int dest_x, int dest_y,
+ int nwidth,
+ int dest_bmp_width, int dest_bmp_height,
+ bool isWidth, 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;
+ scoped_array<float> weight(new float[static_cast<int>(support * 2) + 4]);
+ // we assume width is the dimension we are scaling, and height stays
+ // the same.
+ for (int i = 0; i < abs(nwidth); ++i) {
+ // center is the corresponding coordinate of i in original img.
+ float center = (i + 0.5) * 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;
+
+ // fill up weight array by lanczos filter.
+ float wsum = 0.0;
+ for (int ox = xmin; ox <= xmax; ++ox) {
+ float wtemp;
+ float dx = ox + 0.5 - center;
+ // lanczos filter
+ if (dx <= -kFilterSize || dx >= kFilterSize) {
+ wtemp = 0.0;
+ } else if (dx == 0.0) {
+ wtemp = 1.0;
+ } else {
+ wtemp = kFilterSize * sin(kPi * dx) * sin(kPi / kFilterSize * dx) /
+ (kPi * kPi * dx * dx);
+ }
+
+ weight[ox - xmin] = wtemp;
+ wsum += wtemp;
+ }
+ int wcount = xmax - xmin + 1;
+
+ // Normalize the weights.
+ if (fabs(wsum) > kEpsilon) {
+ for (int k = 0; k < wcount; ++k) {
+ weight[k] /= wsum;
+ }
+ }
+ // Now that we've computed the filter weights for this x-position
+ // of the image, we can apply that filter to all pixels in that
+ // column.
+ // calculate coordinate in new img.
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 (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;
+ if (nwidth < 0)
+ x = -1 * x;
+ // lower bound of coordinate in original img.
+ 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;
+ // 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) *
+ dest_bmp_width + dest_x + x) * components;
+ int step = components;
+ if (width < 0)
+ step = -1 * step;
+ 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);
+ }
+ } 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) * dest_bmp_width) * components;
+
+ int step = src_bmp_width * components;
+ if (width < 0)
+ step = -1 * step;
+ 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);
}
-
- // 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);
}
}
}