#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
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<unsigned char> ((rgbData_[3*ipos + k] + 0.5) / scale2);
if (pngAlpha_ != NULL)
new_alpha[js * w + is] += static_cast<unsigned char> (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]);
}
}
syntax highlighted by Code2HTML, v. 0.9.1