/* This file was generated by scramble.py. */
#include "image.h"
#include "scramble.h"
void image_move(Image *image, int x, int y, int w, int h)
{
    image->rowbytes = image->parent->rowbytes;
    image->row = image->parent->row;
    image->data.b = image->parent->data.b + y * image->rowbytes + x * image->bytes;
    image->w = w;
    image->h = h;
}
Image * image_window(Image *parent, int x, int y, int w, int h)
{
    Image *window = image_new(parent->data.b, w, h, parent->type);
    window->parent = parent;
    image_move(window, x, y, w, h);
    return window;
}
Image * image_new(B *data, int w, int h, ImageType type)
{
    Image *i = ALLOCATE0(sizeof *i);
    i->type = type;
    int ts = 1;
    switch (type)
    {
        case IT_FLOAT: ts = sizeof(float); break;
        case IT_INT: ts = sizeof(int); break;
        case IT_BYTE: ts = 1; break;
        case IT_RGB: ts = sizeof(Color); break;
    }
    if (data)
    {
        i->data.b = data;
    }
    else
    {
        i->data.b = ALLOCATE(w * h * ts);
    }
    i->bytes = ts;
    i->rowbytes = w * ts;
    i->w = i->row = w;
    i->h = h;
    return i;
}
Image * image_half_v(Image const *src)
{
    Image *i = image_new(None, src->w / 2, src->h / 2, src->type);
    for (int y = 0; y < i->h; y++)
    {
        for (int x = 0; x < i->w; x++)
        {
            V a = src->data.v[(y * 2 + 0) * src->row + (x * 2 + 0)];
            V b = src->data.v[(y * 2 + 0) * src->row + (x * 2 + 1)];
            V c = src->data.v[(y * 2 + 1) * src->row + (x * 2 + 1)];
            V d = src->data.v[(y * 2 + 1) * src->row + (x * 2 + 0)];
            i->data.v[y * i->row + x] = (a + b + c + d) / 4;
        }
    }
    return i;
}
Image * image_copy(Image const *src)
{
    Image *i = image_new(None, src->w, src->h, src->type);
    /* Should work for all types since rowbytes includes the size. */
    memcpy(i->data.b, src->data.b, i->rowbytes * i->h);
    if (src->name)
    {
        i->name = strdup(src->name);
    }
    return i;
}
void image_transfer(Image const *src, Image *target, int sx, int sy, int tx, int ty, int w, int h)
{
    /* 
     * Note: Only works if the target has the same format, for now. */
    B *src_ptr = src->data.b + src->rowbytes * sy + src->bytes * sx;
    B *target_ptr = target->data.b + target->rowbytes * ty + target->bytes * tx;
    for (int i = 0; i < h; i++)
    /* Should work for all types since rowbytes includes the size. */
    {
        memcpy(target_ptr, src_ptr, w * src->bytes);
        src_ptr += src->rowbytes;
        target_ptr += target->rowbytes;
    }
}
Image * image_copy_crop(Image const *src, int x, int y, int w, int h)
{
    Image *i = image_new(None, w, h, src->type);
    for (int row = y; row < y + h; row++)
    {
        if (row >= 0 && row < src->h)
        {
            memcpy(i->data.b + i->rowbytes * (row - y), src->data.b + src->rowbytes * row + src->bytes * x, i->bytes * w);
        }
        else
        {
            memset(i->data.b + i->rowbytes * (row - y), 0, i->bytes * w);
        }
    }
    if (src->name)
    {
        i->name = strdup(src->name);
    }
    return i;
}
void image_del(Image *image)
{
    if (! image->parent)
    {
        if (image->data.b)
        {
            FREE(image->data.b);
        }
    }
    if (image->name)
    {
        FREE(image->name);
    }
    if (image->path)
    {
        FREE(image->path);
    }
    FREE(image);
}
void image_set_path(Image *image, char const *path, int auto_name)
{
    image->path = strdup(path);
    if (auto_name)
    /* 0        9    14   19 */
    /* |        |    |    | */
    /* /tmp/blah/name.jpeg */
    /* S    D */
    /* a  b */
    /* a = 10 */
    /* b = 13 */
    {
        char const *slash = strrchr(path, '/');
        char const *dot = strrchr(path, '.');
        int a, b;
        if (slash)
        {
            a = slash + 1 - path;
        }
        else
        {
            a = 0;
        }
        if (dot)
        {
            b = dot - 1 - path;
        }
        else
        {
            b = strlen(path) - 1;
        }
        image->name = malloc(1 + b - a + 1);
        strncpy(image->name, path + a, 1 + b - a);
        image->name[1 + b - a] = '\0';
    }
}
Image * image_new_b(B *data, int w, int h)
{
    return image_new(data, w, h, IT_BYTE);
}
Image * image_new_undef_b(int w, int h)
{
    return image_new(None, w, h, IT_BYTE);
}
Image * image_new_undef_i(int w, int h)
{
    return image_new(None, w, h, IT_INT);
}
Image * image_new_undef_f(int w, int h)
{
    return image_new(None, w, h, IT_FLOAT);
}
Image * image_new_undef_rgb(int w, int h)
{
    return image_new(None, w, h, IT_RGB);
}
V image_mean_area(Image *im, int x, int y, int w, int h)
{
    V sum = 0;
    for (int j = y; j < y + h; j++)
    {
        for (int i = x; i < x + w; i++)
        {
            sum += im->data.v[j * im->w + i];
        }
    }
    V n = w * h;
    return sum / n;
}
V image_variance_area(Image *im, int x, int y, int w, int h)
{
    V e = image_mean_area(im, x, y, w, h);
    V sum = 0;
    for (int j = y; j < y + h; j++)
    {
        for (int i = x; i < x + w; i++)
        {
            V v = im->data.v[j * im->w + i];
            v = (v - e) * (v - e);
            sum += v;
        }
    }
    V n = w * h;
    return sum / n;
}
B get_near_pixel(Image *image, int x, int y)
{
    if (x < 0)
    {
        x = 0;
    }
    if (y < 0)
    {
        y = 0;
    }
    if (x > image->w - 1)
    {
        x = image->w - 1;
    }
    if (y > image->h - 1)
    {
        y = image->h - 1;
    }
    return image->data.b[x + y * image->w];
}
B getpixel_bilinear(Image *image, double x, double y)
{
    /* 
     * We compute the pixel value such that it consists of the values of all
     * pixels at offset t weighted by:
     * (1 - t) if t in [0, 1]
     * (1 + t) if t in [-1, 0]
     * 1       if t = 0
     * 0       if t < -1 or t > 1
     * 
     * This means that for any given position, we only need to consider two
     * neighbors per dimension or 4 pixels. (Pixels have a grid distance of 1
     * already,  so there can only be 4 of them in a square of radius 1.) */
    int ix = x;
    int iy = y;
    double xp = x - ix;
    double yp = y - iy;
    B c1 = get_near_pixel(image, ix, iy);
    B c2 = get_near_pixel(image, ix + 1, iy);
    B c3 = get_near_pixel(image, ix + 1, iy + 1);
    B c4 = get_near_pixel(image, ix, iy + 1);
    double c = c1 * (1 - xp) * (1 - yp) + c2 * xp * (1 - yp);
    c += c3 * xp * yp + c4 * (1 - xp) * yp;
    if (c < 0)
    {
        return 0;
    }
    if (c > 255)
    {
        return 255;
    }
    return c;
}
B getpixel_bicubic(Image *image, double x, double y)
{
    /* 
     * We compute the pixel value such that it consists of the values of all
     * pixels at offset t weighted by:
     * 1 - 2 |t|^2 + |t|^3          if |t| <= 1
     * 4 - 8 |t| + 5 |t|^2 - |t|^3  if 1 < |t| < 2
     * 0                            if t >= 2
     * note:
     * 1                            if t = 0
     * 0                            if t = 1
     * 
     * Since no pixels with offset >= 2 are considered, we only need to look at
     * 4 neighbors per dimenstion or 16 pixels. */
    int ix = x;
    int iy = y;
    double xp = x - ix;
    double yp = y - iy;
    B c0 = get_near_pixel(image, ix - 1, iy - 1);
    B c1 = get_near_pixel(image, ix - 0, iy - 1);
    B c2 = get_near_pixel(image, ix + 1, iy - 1);
    B c3 = get_near_pixel(image, ix + 2, iy - 1);
    B c4 = get_near_pixel(image, ix - 1, iy - 0);
    B c5 = get_near_pixel(image, ix - 0, iy - 0);
    B c6 = get_near_pixel(image, ix + 1, iy - 0);
    B c7 = get_near_pixel(image, ix + 2, iy - 0);
    B c8 = get_near_pixel(image, ix - 1, iy + 1);
    B c9 = get_near_pixel(image, ix - 0, iy + 1);
    B ca = get_near_pixel(image, ix + 1, iy + 1);
    B cb = get_near_pixel(image, ix + 2, iy + 1);
    B cc = get_near_pixel(image, ix - 1, iy + 2);
    B cd = get_near_pixel(image, ix - 0, iy + 2);
    B ce = get_near_pixel(image, ix + 1, iy + 2);
    B cf = get_near_pixel(image, ix + 2, iy + 2);
    double s0 = 4 - 8 * (1 + xp) + 5 * pow(1 + xp, 2) - pow(1 + xp, 3);
    double s1 = 1 - 2 * pow(xp, 2) + pow(xp, 3);
    double s2 = 1 - 2 * pow(1 - xp, 2) + pow(fabs(1 - xp), 3);
    double s3 = 4 - 8 * (2 - xp) + 5 * pow(2 - xp, 2) - pow(2 - xp, 3);
    double t0 = 4 - 8 * (1 + yp) + 5 * pow(1 + yp, 2) - pow(1 + yp, 3);
    double t1 = 1 - 2 * pow(yp, 2) + pow(yp, 3);
    double t2 = 1 - 2 * pow(1 - yp, 2) + pow(fabs(1 - yp), 3);
    double t3 = 4 - 8 * (2 - yp) + 5 * pow(2 - yp, 2) - pow(2 - yp, 3);
    double c = c0 * s0 * t0 + c1 * s1 * t0 + c2 * s2 * t0 + c3 * s3 * t0 + c4 * s0 * t1 + c5 * s1 * t1 + c6 * s2 * t1 + c7 * s3 * t1 + c8 * s0 * t2 + c9 * s1 * t2 + ca * s2 * t2 + cb * s3 * t2 + cc * s0 * t3 + cd * s1 * t3 + ce * s2 * t3 + cf * s3 * t3;
    if (c < 0)
    {
        return 0;
    }
    if (c > 255)
    {
        return 255;
    }
    return c;
}
Image * scale_image(Image *image, int neww, int newh, ScaleType how)
{
    Image *scaled = image_new_undef_b(neww, newh);
    if (how == IST_NEAREST)
    {
        for (int j = 0; j < newh; j++)
        {
            for (int i = 0; i < neww; i++)
            {
                int nx = i * image->w / neww;
                int ny = j * image->h / newh;
                B c = image->data.b[nx + ny * image->w];
                scaled->data.b[i + j * neww] = c;
            }
        }
    }
    else if (how == IST_BILINEAR)
    {
        for (int j = 0; j < newh; j++)
        {
            for (int i = 0; i < neww; i++)
            {
                double nx = (double)i * (image->w - 1) / (neww - 1);
                double ny = (double)j * (image->h - 1) / (newh - 1);
                B c = getpixel_bilinear(image, nx, ny);
                scaled->data.b[i + j * neww] = c;
            }
        }
    }
    else if (how == IST_BICUBIC)
    {
        for (int j = 0; j < newh; j++)
        {
            for (int i = 0; i < neww; i++)
            {
                double nx = (double)i * (image->w - 1) / (neww - 1);
                double ny = (double)j * (image->h - 1) / (newh - 1);
                B c = getpixel_bicubic(image, nx, ny);
                scaled->data.b[i + j * neww] = c;
                /* elif how == IST_AVERAGE: */
            }
        }
    }
    return scaled;
    /* def shift_multiply_image_inplace_f(V *image, int w, h, V shift, factor): */
    /* for int j = 0; j < h; j++: */
    /* for int i = 0; i < w; i++: */
    /* image[i + w * j] *= factor */
    /* image[i + w * j] += shift */
}
V * duplicate_image(V *image, int w, int h)
{
    V *result = ALLOCATE(w * h * sizeof *image);
    memcpy(result, image, w * h * sizeof *image);
    return result;
}
void shift_multiply_image(Image *image, V shift, V factor)
{
    /* 
     * Given an image, execute v = shift + v * factor for each pixel. */
    int w = image->w;
    int h = image->h;
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            if (image->type == IT_FLOAT)
            {
                V v = image->data.v[i + j * w];
                v *= factor;
                v += shift;
                image->data.v[i + j * w] = v;
            }
            else
            {
                V v = image->data.b[i + j * w];
                v *= factor;
                v += shift;
                int c = v;
                if (v < 0)
                {
                    v = 0;
                }
                if (v > 255)
                {
                    v = 255;
                }
                image->data.b[i + j * w] = c;
            }
        }
    }
}
void threshold_below(Image *image, int threshold, int value)
{
    /* 
     * Any pixels at or below the threshold are set to the given value. */
    int w = image->w;
    int h = image->h;
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            int c = image->data.b[i + j * w];
            if (c <= threshold)
            {
                image->data.b[i + j * w] = value;
            }
        }
    }
}
void threshold_above(Image *image, int threshold, int value)
{
    /* 
     * Any pixels ar or above the given threshold are set to the given value. */
    int w = image->w;
    int h = image->h;
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            int c = image->data.b[i + j * w];
            if (c >= threshold)
            {
                image->data.b[i + j * w] = value;
            }
        }
    }
}
void invert(Image *image)
{
    int w = image->w;
    int h = image->h;
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            int c = image->data.b[i + j * w];
            image->data.b[i + j * w] = 255 - c;
        }
    }
}
Image * cumulate(Image *image, int cw, int ch)
{
    /* 
     * Make light areas lighter. */
    int w = image->w;
    int h = image->h;
    B *data = image->data.b;
    Image *result = image_new_undef_b(w, h);
    B *a = result->data.b;
    int ox = cw / 2;
    int oy = ch / 2;
    for (int y = 0; y < h; y++)
    {
        int ay, by;
        if (y < oy)
        {
            ay = oy - y;
        }
        else
        {
            ay = 0;
        }
        if (y + ch - oy > h)
        {
            by = h + oy - y;
        }
        else
        {
            by = ch;
        }
        for (int x = 0; x < w; x++)
        {
            int sum = 0;
            int ax, bx;
            if (x < ox)
            {
                ax = ox - x;
            }
            else
            {
                ax = 0;
            }
            if (x + cw - ox > w)
            {
                bx = w + ox - x;
            }
            else
            {
                bx = cw;
            }
            for (int j = ay; j < by; j++)
            {
                for (int i = ax; i < bx; i++)
                {
                    sum += data[(y + j - oy) * w + (x + i - ox)];
                }
            }
            if (sum > 255)
            {
                sum = 255;
            }
            a[y * w + x] = sum;
        }
    }
    return result;
}
int bounds_bigger_than_threshold(Image *image, int threshold, int *x, int *y, int *x_, int *y_)
{
    int w = image->w;
    int h = image->h;
    B *data = image->data.b;
    int n= 0;
    *x = w - 1;
    *y = h - 1;
    *x_ = 0;
    *y_ = 0;
    for (int j = 0; j < h ; j++)
    {
        for (int i = 0; i < w; i++)
        {
            B c = data[j * w + i];
            if (c > threshold)
            {
                if (i < *x)
                {
                    *x = i;
                }
                if (i > *x_)
                {
                    *x_ = i;
                }
                if (j < *y)
                {
                    *y = j;
                }
                if (j > *y_)
                {
                    *y_ = j;
                }
                n++;
            }
        }
    }
    return n;
}
V * new_image(int w, int h)
{
    V *image = ALLOCATE(w * h * sizeof *image);
    return image;
}
V * binarize(V *image, int w, int h, V threshold)
{
    V *b = malloc(w * h * sizeof *b);
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            V c = image[i + w * j];
            if (c >= threshold)
            {
                c = 255;
            }
            else
            {
                c = 0;
            }
            b[i + w * j] = c;
        }
    }
    return b;
}
void stretch_min_max(Image *image, V newmin, V newmax)
{
    if (image->type == IT_FLOAT)
    {
        V *ptr = image->data.v;
        V minv = 1000000;
        V maxv = -1000000;
        for (int j = 0; j < image->h; j++)
        {
            for (int i = 0; i < image->w; i++)
            {
                V v = *ptr;
                if (v < minv)
                {
                    minv = v;
                }
                if (v > maxv)
                {
                    maxv = v;
                }
                ptr++;
            }
            ptr += image->row - image->w;
        }
        printf("stretching from: [%f..%f]\n", minv, maxv);
        ptr = image->data.v;
        for (int j = 0; j < image->h; j++)
        {
            for (int i = 0; i < image->w; i++)
            {
                V v = *ptr;
                *ptr = newmin + (newmax - newmin) * (v - minv) / (maxv - minv);
                ptr++;
            }
            ptr += image->row - image->w;
        }
    }
}
Image * BtoV(Image const *image)
{
    int w = image->w;
    int h = image->h;
    Image *out = image_new_undef_f(w, h);
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            out->data.v[i + w * j] = image->data.b[i + w * j];
        }
    }
    return out;
}
Image * BtoRGB(Image const *image)
{
    int w = image->w;
    int h = image->h;
    Image *out = image_new_undef_rgb(w, h);
    out->name = strdup(image->name);
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            int b = image->data.b[i + w * j];
            Color c = {b, b, b};
            out->data.rgb[i + w * j] = c;
        }
    }
    return out;
}
Image * VtoB(Image const *image)
{
    int w = image->w;
    int h = image->h;
    Image *raster = image_new_undef_b(w, h);
    for (int j = 0; j < h; j++)
    {
        for (int i = 0; i < w; i++)
        {
            int32_t c = image->data.v[i + w * j];
            if (c < 0 || c > 255)
            {
                fprintf(stderr, "Pixel %d truncated at %d/%d.\n", c, i, j);
                if (c < 0)
                {
                    c = 0;
                }
                if (c > 255)
                {
                    c = 255;
                }
            }
            raster->data.b[i + w * j] = c;
        }
    }
    return raster;
}
Image * image_load_b(char const *filename)
{
    char const *dot = strrchr(filename, '.');
    if (! dot)
    {
        return None;
    }
    if (! strcmp(dot, ".tiff") || ! strcmp(dot, ".tif"))
    {
        return load_tiff_luminance(filename);
    }
    else if (! strcmp(dot, ".jpeg") || ! strcmp(dot, ".jpg"))
    {
        return load_jpeg_luminance(filename);
    }
    return None;
}
int longest_horizontal_run(Image *image, int border, int threshold)
{
    int w = image->w;
    int h = image->h;
    int maxrun = 0;
    for (int j = border; j < h - border; j++)
    {
        int run = 0;
        for (int i = border; i < w - border; i++)
        {
            if (image->data.b[i + j * w] <= threshold)
            {
                run++;
                if (run > maxrun)
                {
                    maxrun = run;
                }
            }
            else
            {
                run = 0;
            }
        }
    }
    return maxrun;
}
int longest_vertical_run(Image *image, int border, int threshold)
{
    int w = image->w;
    int h = image->h;
    int maxrun = 0;
    for (int i = border; i < w - border; i++)
    {
        int run = 0;
        for (int j = border; j < h - border; j++)
        {
            if (image->data.b[i + j * w] <= threshold)
            {
                run++;
                if (run > maxrun)
                {
                    maxrun = run;
                }
            }
            else
            {
                run = 0;
            }
        }
    }
    return maxrun;
}
static char prefix[MAX_PATH] = "./";
static char basename[MAX_PATH] = "";
void set_output_prefix(char const *new_prefix)
{
    strcpy(prefix, new_prefix);
}
void set_output_basename(char const *new_basename)
{
    strcpy(basename, new_basename);
}
void get_image_name(Image const *image, char const *name, char *out)
{
    char const *base = image->name;
    if (basename[0])
    {
        base = basename;
    }
    sprintf(out, "%s/%s_%s.tiff", prefix, base, name);
}
void get_image_name_algo_ext(Image const *image, char const *name, char const *algo, char const *ext, char *out)
{
    char const *base = image->name;
    if (basename[0])
    {
        base = basename;
    }
    if (algo && algo[0])
    {
        sprintf(out, "%s/%s_%s_%s.%s", prefix, base, name, algo, ext);
    }
    else
    {
        get_image_name(image, name, out);
    }
}
void get_image_name_algo(Image const *image, char const *name, char const *algo, char *out)
{
    return get_image_name_algo_ext(image, name, algo, "tiff", out);
}
void get_image_name_algo_key_ext(Image const *image, char const *name, char const *algo, char const *key, char const *ext, char *out)
{
    char const *base = image->name;
    if (basename[0])
    {
        base = basename;
    }
    if (key && key[0])
    {
        sprintf(out, "%s/%s_%s_%s:%s.%s", prefix, base, name, algo, key, ext);
    }
    else
    {
        get_image_name_algo_ext(image, name, algo, ext, out);
    }
}
void get_image_name_algo_key(Image const *image, char const *name, char const *algo, char const *key, char *out)
{
    return get_image_name_algo_key_ext(image, name, algo, key, "tiff", out);
}
/* This file was generated by scramble.py. */
