|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
#define IMG_PRECISION (1e-8)
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
typedef struct {
|
|
|
608c44 |
int w, h;
|
|
|
608c44 |
double *data;
|
|
|
608c44 |
} Img;
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void setRgb(double *rgb, double r, double g, double b)
|
|
|
608c44 |
{ rgb[0] = r; rgb[1] = g; rgb[2] = b; }
|
|
|
608c44 |
|
|
|
608c44 |
int colorMin(const double *rgb)
|
|
|
608c44 |
{ return rgb[0] < rgb[1] ? (rgb[0] < rgb[2] ? 0 : 2) : (rgb[1] < rgb[2] ? 1 : 2); }
|
|
|
608c44 |
int colorMax(const double *rgb)
|
|
|
608c44 |
{ return rgb[0] < rgb[1] ? (rgb[1] < rgb[2] ? 2 : 1) : (rgb[0] < rgb[2] ? 2 : 0); }
|
|
|
608c44 |
|
|
|
608c44 |
void rgbToHsv(double *hsv, const double *rgb) {
|
|
|
608c44 |
int cmin = colorMin(rgb);
|
|
|
608c44 |
int cmax = colorMax(rgb);
|
|
|
608c44 |
double d = rgb[cmax] - rgb[cmin];
|
|
|
608c44 |
|
|
|
608c44 |
double h = 0;
|
|
|
608c44 |
if (d > IMG_PRECISION) {
|
|
|
608c44 |
double k = 1.0/d;
|
|
|
608c44 |
switch(cmax){
|
|
|
608c44 |
case 0: h = (rgb[1] - rgb[2])*k + 0; break;
|
|
|
608c44 |
case 1: h = (rgb[2] - rgb[0])*k + 2; break;
|
|
|
608c44 |
case 2: h = (rgb[0] - rgb[1])*k + 4; break;
|
|
|
608c44 |
}
|
|
|
608c44 |
h /= 6; h -= floor(h); h *= 360;
|
|
|
608c44 |
}
|
|
|
608c44 |
double s = rgb[cmax] > IMG_PRECISION ? d/rgb[cmax] : 0;
|
|
|
608c44 |
double v = rgb[cmax];
|
|
|
608c44 |
setRgb(hsv, h, s, v);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void hsvToRgb(double *rgb, const double *hsv) {
|
|
|
608c44 |
double h = hsv[0], s = hsv[1], v = hsv[2];
|
|
|
608c44 |
h -= floor(h/360)*360;
|
|
|
608c44 |
h /= 60.0;
|
|
|
608c44 |
int i = (int)h;
|
|
|
608c44 |
double f = h - i;
|
|
|
608c44 |
double p = v*(1 - s);
|
|
|
608c44 |
double q = v*(1 - s*f);
|
|
|
608c44 |
double t = v*(1 - s*(1 - f));
|
|
|
608c44 |
switch(i) {
|
|
|
608c44 |
case 0: setRgb(rgb, v, t, p); return;
|
|
|
608c44 |
case 1: setRgb(rgb, q, v, p); return;
|
|
|
608c44 |
case 2: setRgb(rgb, p, v, t); return;
|
|
|
608c44 |
case 3: setRgb(rgb, p, q, v); return;
|
|
|
608c44 |
case 4: setRgb(rgb, t, p, v); return;
|
|
|
608c44 |
case 5: setRgb(rgb, v, p, q); return;
|
|
|
608c44 |
}
|
|
|
608c44 |
return setRgb(rgb, v, t, p);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double cubicInterpolation(double p0, double p1, double p2, double p3, double l) {
|
|
|
608c44 |
double ll = l*l;
|
|
|
608c44 |
double lll = ll*l;
|
|
|
608c44 |
return 0.5*( p0*( -lll + 2*ll - l)
|
|
|
608c44 |
+ p1*( 3*lll - 5*ll + 2)
|
|
|
608c44 |
+ p2*(-3*lll + 4*ll + l)
|
|
|
608c44 |
+ p3*( lll - ll ) );
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
double cubicRow(double *row, int count, double x) {
|
|
|
608c44 |
if (count < 0) return 0;
|
|
|
608c44 |
if (!(x > 0)) x = 0;
|
|
|
608c44 |
if (!(x < count)) x = count;
|
|
|
608c44 |
double xi = floor(x);
|
|
|
608c44 |
double l = x - xi;
|
|
|
608c44 |
|
|
|
608c44 |
int i = (int)x - 1;
|
|
|
608c44 |
double p0 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
|
|
|
608c44 |
double p1 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
|
|
|
608c44 |
double p2 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
|
|
|
608c44 |
double p3 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
|
|
|
608c44 |
|
|
|
608c44 |
return cubicInterpolation(p0, p1, p2, p3, l);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgDestroy(Img *img) {
|
|
|
608c44 |
free(img->data);
|
|
|
608c44 |
img->data = NULL;
|
|
|
608c44 |
img->w = img->h = 0;
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgSwap(Img *imgA, Img *imgB) {
|
|
|
608c44 |
Img imgC;
|
|
|
608c44 |
memcpy(&imgC, imgA, sizeof(imgC));
|
|
|
608c44 |
memcpy(imgA, imgB, sizeof(imgC));
|
|
|
608c44 |
memcpy(imgB, &imgC, sizeof(imgC));
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
int imgInit(Img *img, int w, int h) {
|
|
|
608c44 |
imgDestroy(img);
|
|
|
608c44 |
if (w < 0 || h < 0) return FALSE;
|
|
|
608c44 |
img->data = calloc(1, sizeof(double)*4*w*h);
|
|
|
608c44 |
if (!img->data) return FALSE;
|
|
|
608c44 |
img->w = w;
|
|
|
608c44 |
img->h = h;
|
|
|
608c44 |
return TRUE;
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double* imgPixel(const Img *img, int x, int y) {
|
|
|
608c44 |
if (!img->data) return NULL;
|
|
|
608c44 |
if (x >= img->w) x = img->w - 1;
|
|
|
608c44 |
if (y >= img->h) y = img->h - 1;
|
|
|
608c44 |
if (x < 0) x = 0;
|
|
|
608c44 |
if (y < 0) y = 0;
|
|
|
608c44 |
return img->data + (img->w*y + x)*4;
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double* imgNearest(const Img *img, double x, double y)
|
|
|
608c44 |
{ return imgPixel(img, (int)round(x), (int)round(y)); }
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double imgCubicHCh(const Img *img, int ch, double x, int y) {
|
|
|
608c44 |
if (!img->data) return 0;
|
|
|
608c44 |
if (!(x > 0)) x = 0;
|
|
|
608c44 |
if (!(x < img->w)) x = img->w;
|
|
|
608c44 |
double xi = floor(x);
|
|
|
608c44 |
int i = (int)xi - 1;
|
|
|
608c44 |
double p[] = {
|
|
|
608c44 |
imgPixel(img, i+0, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+1, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+2, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+3, y)[ch] };
|
|
|
608c44 |
return cubicInterpolation(p[0], p[1], p[2], p[3], x - xi);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double imgCubicVCh(const Img *img, int ch, int x, double y) {
|
|
|
608c44 |
if (!img->data) return 0;
|
|
|
608c44 |
if (!(y > 0)) x = 0;
|
|
|
608c44 |
if (!(y < img->h)) y = img->h;
|
|
|
608c44 |
double yi = floor(y);
|
|
|
608c44 |
int i = (int)yi - 1;
|
|
|
608c44 |
double p[] = {
|
|
|
608c44 |
imgPixel(img, i+0, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+1, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+2, y)[ch],
|
|
|
608c44 |
imgPixel(img, i+3, y)[ch] };
|
|
|
608c44 |
return cubicInterpolation(p[0], p[1], p[2], p[3], y - yi);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgCubicH(const Img *img, double x, int y, double *pix) {
|
|
|
608c44 |
if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
|
|
|
608c44 |
if (!(x > 0)) x = 0;
|
|
|
608c44 |
if (!(x < img->w)) x = img->w;
|
|
|
608c44 |
double xi = floor(x);
|
|
|
608c44 |
double l = x - xi;
|
|
|
608c44 |
|
|
|
608c44 |
int i = (int)xi - 1;
|
|
|
608c44 |
double *p[] = {
|
|
|
608c44 |
imgPixel(img, i+0, y),
|
|
|
608c44 |
imgPixel(img, i+1, y),
|
|
|
608c44 |
imgPixel(img, i+2, y),
|
|
|
608c44 |
imgPixel(img, i+3, y) };
|
|
|
608c44 |
|
|
|
608c44 |
pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
|
|
|
608c44 |
pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
|
|
|
608c44 |
pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
|
|
|
608c44 |
pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgCubicV(const Img *img, int x, double y, double *pix) {
|
|
|
608c44 |
if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
|
|
|
608c44 |
if (!(y > 0)) y = 0;
|
|
|
608c44 |
if (!(y < img->h)) y = img->h;
|
|
|
608c44 |
double yi = floor(y);
|
|
|
608c44 |
double l = y - yi;
|
|
|
608c44 |
|
|
|
608c44 |
int i = (int)yi - 1;
|
|
|
608c44 |
double *p[] = {
|
|
|
608c44 |
imgPixel(img, x, i+0),
|
|
|
608c44 |
imgPixel(img, x, i+1),
|
|
|
608c44 |
imgPixel(img, x, i+2),
|
|
|
608c44 |
imgPixel(img, x, i+3) };
|
|
|
608c44 |
|
|
|
608c44 |
pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
|
|
|
608c44 |
pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
|
|
|
608c44 |
pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
|
|
|
608c44 |
pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
double imgCubicCh(const Img *img, int ch, double x, double y) {
|
|
|
608c44 |
if (!img->data) return 0;
|
|
|
608c44 |
if (!(y > 0)) y = 0;
|
|
|
608c44 |
if (!(y < img->h)) y = img->h;
|
|
|
608c44 |
double yi = floor(y);
|
|
|
608c44 |
double l = y - yi;
|
|
|
608c44 |
|
|
|
608c44 |
int i = (int)yi - 1;
|
|
|
608c44 |
double p[] = {
|
|
|
608c44 |
imgCubicHCh(img, ch, x, i+0),
|
|
|
608c44 |
imgCubicHCh(img, ch, x, i+1),
|
|
|
608c44 |
imgCubicHCh(img, ch, x, i+2),
|
|
|
608c44 |
imgCubicHCh(img, ch, x, i+3) };
|
|
|
608c44 |
|
|
|
608c44 |
return cubicInterpolation(p[0], p[1], p[2], p[3], l);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgCubic(const Img *img, double x, double y, double *pix) {
|
|
|
608c44 |
if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
|
|
|
608c44 |
if (!(y > 0)) y = 0;
|
|
|
608c44 |
if (!(y < img->h)) y = img->h;
|
|
|
608c44 |
double yi = floor(y);
|
|
|
608c44 |
double l = y - yi;
|
|
|
608c44 |
|
|
|
608c44 |
int i = (int)yi - 1;
|
|
|
608c44 |
double p[4][4];
|
|
|
608c44 |
imgCubicH(img, x, i+0, p[0]);
|
|
|
608c44 |
imgCubicH(img, x, i+1, p[1]);
|
|
|
608c44 |
imgCubicH(img, x, i+2, p[2]);
|
|
|
608c44 |
imgCubicH(img, x, i+3, p[3]);
|
|
|
608c44 |
|
|
|
608c44 |
pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
|
|
|
608c44 |
pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
|
|
|
608c44 |
pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
|
|
|
608c44 |
pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
void imgFromInt(Img *img, int w, int h, const unsigned char *data) {
|
|
|
608c44 |
imgInit(img, w, h);
|
|
|
608c44 |
if (!img->data || !data) return;
|
|
|
608c44 |
for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; ++p)
|
|
|
608c44 |
*p = (*data++)/255.0;
|
|
|
608c44 |
}
|
|
|
608c44 |
|
|
|
608c44 |
|
|
|
608c44 |
unsigned char* imgToInt(Img *img) {
|
|
|
608c44 |
if (!img->data) return NULL;
|
|
|
608c44 |
unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char)*4*img->w*img->h), *dp = data;
|
|
|
608c44 |
for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; ++p)
|
|
|
608c44 |
*dp++ = *p > 0 ? (*p < 1 ? (unsigned char)floor(*p*255.9999999) : 255) : 0;
|
|
|
608c44 |
return data;
|
|
|
608c44 |
}
|
|
|
608c44 |
|