#include #include #include #include #include using namespace std; #include "Image.h" extern bool ReadImage(const char *filename, int &width, int &height, unsigned char *&rgb_data, unsigned char *&png_alpha); extern bool WriteImage(const char *filename, const int width, const int height, unsigned char * const rgb_data, unsigned char * const png_alpha, const int quality); Image::Image() : width_(0), height_(0), area_(0), rgbData_(NULL), pngAlpha_(NULL), quality_(80) { } Image::Image(const int w, const int h, const unsigned char *rgb, const unsigned char *alpha) : width_(w), height_(h), area_(w*h), quality_(80) { rgbData_ = (unsigned char *) malloc(3 * area_); memcpy(rgbData_, rgb, 3 * area_); if (alpha == NULL) { pngAlpha_ = NULL; } else { pngAlpha_ = (unsigned char *) malloc(area_); memcpy(pngAlpha_, alpha, area_); } } Image::~Image() { free(rgbData_); free(pngAlpha_); } bool Image::Read(const char *filename) { bool success = ReadImage(filename, width_, height_, rgbData_, pngAlpha_); area_ = width_ * height_; return(success); } bool Image::Write(const char *filename) { bool success = WriteImage(filename, width_, height_, rgbData_, pngAlpha_, quality_); return(success); } bool Image::Crop(const int x0, const int y0, const int x1, const int y1) { if (x1 <= x0 || y1 <= y0) return(false); const int w = x1 - x0; const int h = y1 - y0; const int new_area = w * h; unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area); memset(new_rgb, 0, 3 * new_area); unsigned char *new_alpha = NULL; if (pngAlpha_ != NULL) { new_alpha = (unsigned char *) malloc(new_area); memset(new_alpha, 0, new_area); } int ipos = 0; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { memcpy(new_rgb + ipos, rgbData_ + 3 * ((j + y0) * width_ + (i + x0)), 3); ipos += 3; } } free(rgbData_); free(pngAlpha_); width_ = w; height_ = h; area_ = new_area; rgbData_ = new_rgb; pngAlpha_ = new_alpha; return(true); } void Image::Reduce(const int factor) { if (factor < 1) return; int scale = 1; for (int i = 0; i < factor; i++) scale *= 2; double scale2 = scale*scale; int w = width_ / scale; int h = height_ / scale; int new_area = w * h; unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area); memset(new_rgb, 0, 3 * new_area); unsigned char *new_alpha = NULL; if (pngAlpha_ != NULL) { new_alpha = (unsigned char *) malloc(new_area); memset(new_alpha, 0, new_area); } int ipos = 0; for (int j = 0; j < height_; j++) { int js = j / scale; for (int i = 0; i < width_; i++) { int is = i/scale; for (int k = 0; k < 3; k++) new_rgb[3*(js * w + is) + k] += static_cast ((rgbData_[3*ipos + k] + 0.5) / scale2); if (pngAlpha_ != NULL) new_alpha[js * w + is] += static_cast (pngAlpha_[ipos]/scale2); ipos++; } } free(rgbData_); free(pngAlpha_); width_ = w; height_ = h; area_ = new_area; rgbData_ = new_rgb; pngAlpha_ = new_alpha; } void Image::Resize(const int w, const int h) { int new_area = w * h; unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area); unsigned char *new_alpha = NULL; if (pngAlpha_ != NULL) new_alpha = (unsigned char *) malloc(new_area); const double scale_x = ((double) w) / width_; const double scale_y = ((double) h) / height_; int ipos = 0; for (int j = 0; j < h; j++) { const double y = j / scale_y; for (int i = 0; i < w; i++) { const double x = i / scale_x; if (new_alpha == NULL) getPixel(x, y, new_rgb + 3*ipos); else getPixel(x, y, new_rgb + 3*ipos, new_alpha + ipos); ipos++; } } free(rgbData_); free(pngAlpha_); width_ = w; height_ = h; area_ = w * h; rgbData_ = new_rgb; pngAlpha_ = new_alpha; } // Slide the whole image to the right (for positive x) or to the left // (for negative x). The original pixel 0 column will be in the pixel // x column after this routine is called. void Image::Shift(const int x) { unsigned char *new_rgb = (unsigned char *) malloc(3 * area_); unsigned char *new_alpha = NULL; if (pngAlpha_ != NULL) new_alpha = (unsigned char *) malloc(area_); int shift = x; while (shift < 0) shift += width_; while (shift >= width_) shift -= width_; int ipos = 0; for (int j = 0; j < height_; j++) { for (int i = 0; i < width_; i++) { int ii = i + shift; if (ii < 0) ii += width_; if (ii >= width_) ii -= width_; int iipos = j * width_ + ii; memcpy(new_rgb + 3*ipos, rgbData_ + 3*iipos, 3); if (pngAlpha_ != NULL) new_alpha[ipos] = pngAlpha_[iipos]; ipos++; } } free(rgbData_); free(pngAlpha_); rgbData_ = new_rgb; pngAlpha_ = new_alpha; } // Find the color of the desired point using bilinear interpolation. // Assume the array indices refer to the denter of the pixel, so each // pixel has corners at (i - 0.5, j - 0.5) and (i + 0.5, j + 0.5) void Image::getPixel(double x, double y, unsigned char *pixel) { getPixel(x, y, pixel, NULL); } void Image::getPixel(double x, double y, unsigned char *pixel, unsigned char *alpha) { if (x < -0.5) x = -0.5; if (x >= width_ - 0.5) x = width_ - 0.5; if (y < -0.5) y = -0.5; if (y >= height_ - 0.5) y = height_ - 0.5; int ix0 = (int) (floor(x)); int ix1 = ix0 + 1; if (ix0 < 0) ix0 = width_ - 1; if (ix1 >= width_) ix1 = 0; int iy0 = (int) (floor(y)); int iy1 = iy0 + 1; if (iy0 < 0) iy0 = 0; if (iy1 >= height_) iy1 = height_ - 1; const double t = x - floor(x); const double u = 1 - (y - floor(y)); // Weights are from Numerical Recipes, 2nd Edition // weight[0] = (1 - t) * u; // weight[2] = (1-t) * (1-u); // weight[3] = t * (1-u); double weight[4]; weight[1] = t * u; weight[0] = u - weight[1]; weight[2] = 1 - t - u + weight[1]; weight[3] = t - weight[1]; unsigned char *pixels[4]; pixels[0] = rgbData_ + 3 * (iy0 * width_ + ix0); pixels[1] = rgbData_ + 3 * (iy0 * width_ + ix1); pixels[2] = rgbData_ + 3 * (iy1 * width_ + ix0); pixels[3] = rgbData_ + 3 * (iy1 * width_ + ix1); memset(pixel, 0, 3); for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) pixel[j] += (unsigned char) (weight[i] * pixels[i][j]); } if (alpha != NULL) { unsigned char pixels[4]; pixels[0] = pngAlpha_[iy0 * width_ + ix0]; pixels[1] = pngAlpha_[iy0 * width_ + ix1]; pixels[2] = pngAlpha_[iy0 * width_ + ix0]; pixels[3] = pngAlpha_[iy1 * width_ + ix1]; for (int i = 0; i < 4; i++) *alpha = (unsigned char) (weight[i] * pixels[i]); } }