Blob Blame Raw


#define IMG_PRECISION (1e-8)


typedef struct {
  int w, h;
  double *data;
} Img;


void setRgb(double *rgb, double r, double g, double b)
  { rgb[0] = r; rgb[1] = g; rgb[2] = b; }

int colorMin(const double *rgb)
  { return rgb[0] < rgb[1] ? (rgb[0] < rgb[2] ? 0 : 2) : (rgb[1] < rgb[2] ? 1 : 2); }
int colorMax(const double *rgb)
  { return rgb[0] < rgb[1] ? (rgb[1] < rgb[2] ? 2 : 1) : (rgb[0] < rgb[2] ? 2 : 0); }

void rgbToHsv(double *hsv, const double *rgb) {
  int cmin = colorMin(rgb);
  int cmax = colorMax(rgb);
  double d = rgb[cmax] - rgb[cmin];

  double h = 0;
  if (d > IMG_PRECISION) {
    double k = 1.0/d;
    switch(cmax){
    case 0: h = (rgb[1] - rgb[2])*k + 0; break;
    case 1: h = (rgb[2] - rgb[0])*k + 2; break;
    case 2: h = (rgb[0] - rgb[1])*k + 4; break;
    }
    h /= 6; h -= floor(h); h *= 360;
  }
  double s = rgb[cmax] > IMG_PRECISION ? d/rgb[cmax] : 0;
  double v = rgb[cmax];
  setRgb(hsv, h, s, v);
}


void hsvToRgb(double *rgb, const double *hsv) {
  double h = hsv[0], s = hsv[1], v = hsv[2];
  h -= floor(h/360)*360;
  h /= 60.0;
  int i = (int)h;
  double f = h - i;
  double p = v*(1 - s);
  double q = v*(1 - s*f);
  double t = v*(1 - s*(1 - f));
  switch(i) {
  case 0: setRgb(rgb, v, t, p); return;
  case 1: setRgb(rgb, q, v, p); return;
  case 2: setRgb(rgb, p, v, t); return;
  case 3: setRgb(rgb, p, q, v); return;
  case 4: setRgb(rgb, t, p, v); return;
  case 5: setRgb(rgb, v, p, q); return;
  }
  return setRgb(rgb, v, t, p);
}


double cubicInterpolation(double p0, double p1, double p2, double p3, double l) {
  double ll = l*l;
  double lll = ll*l;
  return 0.5*( p0*(  -lll + 2*ll - l)
             + p1*( 3*lll - 5*ll + 2)
             + p2*(-3*lll + 4*ll + l)
             + p3*(   lll -   ll    ) );
}

double cubicRow(double *row, int count, double x) {
  if (count < 0) return 0;
  if (!(x > 0)) x = 0;
  if (!(x < count)) x = count;
  double xi = floor(x);
  double l = x - xi;

  int i = (int)x - 1;
  double p0 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
  double p1 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
  double p2 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;
  double p3 = i < 0 ? row[0] : i >= count ? row[count-1] : row[i]; ++i;

  return cubicInterpolation(p0, p1, p2, p3, l);
}



void imgDestroy(Img *img) {
  free(img->data);
  img->data = NULL;
  img->w = img->h = 0;
}


void imgSwap(Img *imgA, Img *imgB) {
  Img imgC;
  memcpy(&imgC, imgA, sizeof(imgC));
  memcpy(imgA, imgB, sizeof(imgC));
  memcpy(imgB, &imgC, sizeof(imgC));
}


int imgInit(Img *img, int w, int h) {
  imgDestroy(img);
  if (w < 0 || h < 0) return FALSE;
  img->data = calloc(1, sizeof(double)*4*w*h);
  if (!img->data) return FALSE;
  img->w = w;
  img->h = h;
  return TRUE;
}


double* imgPixel(const Img *img, int x, int y) {
  if (!img->data) return NULL;
  if (x >= img->w) x = img->w - 1;
  if (y >= img->h) y = img->h - 1;
  if (x < 0) x = 0;
  if (y < 0) y = 0;
  return img->data + (img->w*y + x)*4;
}


double* imgNearest(const Img *img, double x, double y)
  { return imgPixel(img, (int)round(x), (int)round(y)); }


double imgCubicHCh(const Img *img, int ch, double x, int y) {
  if (!img->data) return 0;
  if (!(x > 0)) x = 0;
  if (!(x < img->w)) x = img->w;
  double xi = floor(x);
  int i = (int)xi - 1;
  double p[] = {
    imgPixel(img, i+0, y)[ch],
    imgPixel(img, i+1, y)[ch],
    imgPixel(img, i+2, y)[ch],
    imgPixel(img, i+3, y)[ch] };
  return cubicInterpolation(p[0], p[1], p[2], p[3], x - xi);
}


double imgCubicVCh(const Img *img, int ch, int x, double y) {
  if (!img->data) return 0;
  if (!(y > 0)) x = 0;
  if (!(y < img->h)) y = img->h;
  double yi = floor(y);
  int i = (int)yi - 1;
  double p[] = {
    imgPixel(img, i+0, y)[ch],
    imgPixel(img, i+1, y)[ch],
    imgPixel(img, i+2, y)[ch],
    imgPixel(img, i+3, y)[ch] };
  return cubicInterpolation(p[0], p[1], p[2], p[3], y - yi);
}


void imgCubicH(const Img *img, double x, int y, double *pix) {
  if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
  if (!(x > 0)) x = 0;
  if (!(x < img->w)) x = img->w;
  double xi = floor(x);
  double l = x - xi;

  int i = (int)xi - 1;
  double *p[] = {
    imgPixel(img, i+0, y),
    imgPixel(img, i+1, y),
    imgPixel(img, i+2, y),
    imgPixel(img, i+3, y) };

  pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
  pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
  pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
  pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
}


void imgCubicV(const Img *img, int x, double y, double *pix) {
  if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
  if (!(y > 0)) y = 0;
  if (!(y < img->h)) y = img->h;
  double yi = floor(y);
  double l = y - yi;

  int i = (int)yi - 1;
  double *p[] = {
    imgPixel(img, x, i+0),
    imgPixel(img, x, i+1),
    imgPixel(img, x, i+2),
    imgPixel(img, x, i+3) };

  pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
  pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
  pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
  pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
}


double imgCubicCh(const Img *img, int ch, double x, double y) {
  if (!img->data) return 0;
  if (!(y > 0)) y = 0;
  if (!(y < img->h)) y = img->h;
  double yi = floor(y);
  double l = y - yi;

  int i = (int)yi - 1;
  double p[] = {
    imgCubicHCh(img, ch, x, i+0),
    imgCubicHCh(img, ch, x, i+1),
    imgCubicHCh(img, ch, x, i+2),
    imgCubicHCh(img, ch, x, i+3) };

  return cubicInterpolation(p[0], p[1], p[2], p[3], l);
}


void imgCubic(const Img *img, double x, double y, double *pix) {
  if (!img->data) { pix[0] = pix[1] = pix[2] = pix[3] = 0; return; }
  if (!(y > 0)) y = 0;
  if (!(y < img->h)) y = img->h;
  double yi = floor(y);
  double l = y - yi;

  int i = (int)yi - 1;
  double p[4][4];
  imgCubicH(img, x, i+0, p[0]);
  imgCubicH(img, x, i+1, p[1]);
  imgCubicH(img, x, i+2, p[2]);
  imgCubicH(img, x, i+3, p[3]);

  pix[0] = cubicInterpolation(p[0][0], p[1][0], p[2][0], p[3][0], l);
  pix[1] = cubicInterpolation(p[0][1], p[1][1], p[2][1], p[3][1], l);
  pix[2] = cubicInterpolation(p[0][2], p[1][2], p[2][2], p[3][2], l);
  pix[3] = cubicInterpolation(p[0][3], p[1][3], p[2][3], p[3][3], l);
}


void imgFromInt(Img *img, int w, int h, const unsigned char *data) {
  imgInit(img, w, h);
  if (!img->data || !data) return;
  for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; ++p)
    *p = (*data++)/255.0;
}


unsigned char* imgToInt(Img *img) {
  if (!img->data) return NULL;
  unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char)*4*img->w*img->h), *dp = data;
  for(double *p = img->data, *e = p + 4*img->w*img->h; p < e; ++p)
    *dp++ = *p > 0 ? (*p < 1 ? (unsigned char)floor(*p*255.9999999) : 255) : 0;
  return data;
}