/*
 *  This file is part of RawTherapee.
 *
 *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
 *
 *  RawTherapee is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  RawTherapee is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <cassert>
#include <cmath>

#include "../rtgui/multilangmgr.h"
#include "color.h"
#include "image8.h"
#include "median.h"
#include "opthelper.h"
#include "procparams.h"
#include "rawimage.h"
#include "rawimagesource.h"
#include "rt_math.h"
#include "sleef.h"
// #define BENCHMARK
#include "StopWatch.h"
#ifdef _OPENMP
#include <omp.h>
#endif

namespace rtengine {

extern const Settings *settings;

#undef fc
#define fc(row, col)                                                           \
    (ri->get_filters() >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)

#define FORCC for (unsigned int c = 0; c < colors; c++)

/*
   Patterned Pixel Grouping Interpolation by Alain Desbiolles
*/
void RawImageSource::ppg_demosaic()
{
    int width = W, height = H;
    int dir[5] = {1, width, -1, -width, 1};
    int row, col, diff[2] = {}, guess[2], c, d, i;
    float(*pix)[4];

    float(*image)[4];

    if (plistener) {
        // looks like ppg isn't supported anymore
        // plistener->setProgressStr
        // (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"),
        // RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::ppg)));
        plistener->setProgressStr(
            Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), "xxx"));
        plistener->setProgress(0.0);
    }

    image = (float(*)[4])calloc(H * W, sizeof *image);

    for (int ii = 0; ii < H; ii++)
        for (int jj = 0; jj < W; jj++) {
            image[ii * W + jj][fc(ii, jj)] = rawData[ii][jj];
        }

    border_interpolate(3, image);

    /*  Fill in the green layer with gradients and pattern recognition: */
    for (row = 3; row < height - 3; row++) {
        for (col = 3 + (FC(row, 3) & 1), c = FC(row, col); col < width - 3;
             col += 2) {
            pix = image + row * width + col;

            for (i = 0; (d = dir[i]) > 0; i++) {
                guess[i] = (pix[-d][1] + pix[0][c] + pix[d][1]) * 2 -
                           pix[-2 * d][c] - pix[2 * d][c];
                diff[i] = (ABS(pix[-2 * d][c] - pix[0][c]) +
                           ABS(pix[2 * d][c] - pix[0][c]) +
                           ABS(pix[-d][1] - pix[d][1])) *
                              3 +
                          (ABS(pix[3 * d][1] - pix[d][1]) +
                           ABS(pix[-3 * d][1] - pix[-d][1])) *
                              2;
            }

            d = dir[i = diff[0] > diff[1]];
            pix[0][1] = median(static_cast<float>(guess[i] >> 2), pix[d][1],
                               pix[-d][1]);
        }

        if (plistener) {
            plistener->setProgress(0.33 * row / (height - 3));
        }
    }

    /*  Calculate red and blue for each green pixel:        */
    for (row = 1; row < height - 1; row++) {
        for (col = 1 + (FC(row, 2) & 1), c = FC(row, col + 1); col < width - 1;
             col += 2) {
            pix = image + row * width + col;

            for (i = 0; (d = dir[i]) > 0; c = 2 - c, i++)
                pix[0][c] = CLIP(0.5 * (pix[-d][c] + pix[d][c] + 2 * pix[0][1] -
                                        pix[-d][1] - pix[d][1]));
        }

        if (plistener) {
            plistener->setProgress(0.33 + 0.33 * row / (height - 1));
        }
    }

    /*  Calculate blue for red pixels and vice versa:       */
    for (row = 1; row < height - 1; row++) {
        for (col = 1 + (FC(row, 1) & 1), c = 2 - FC(row, col); col < width - 1;
             col += 2) {
            pix = image + row * width + col;

            for (i = 0; (d = dir[i] + dir[i + 1]) > 0; i++) {
                diff[i] = ABS(pix[-d][c] - pix[d][c]) +
                          ABS(pix[-d][1] - pix[0][1]) +
                          ABS(pix[d][1] - pix[0][1]);
                guess[i] = pix[-d][c] + pix[d][c] + 2 * pix[0][1] - pix[-d][1] -
                           pix[d][1];
            }

            if (diff[0] != diff[1]) {
                pix[0][c] = CLIP(guess[diff[0] > diff[1]] / 2);
            } else {
                pix[0][c] = CLIP((guess[0] + guess[1]) / 4);
            }
        }

        if (plistener) {
            plistener->setProgress(0.67 + 0.33 * row / (height - 1));
        }
    }

    red(W, H);

    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++) {
            red[i][j] = image[i * W + j][0];
        }

    green(W, H);

    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++) {
            green[i][j] = image[i * W + j][1];
        }

    blue(W, H);

    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++) {
            blue[i][j] = image[i * W + j][2];
        }

    free(image);
}

void RawImageSource::border_interpolate(unsigned int border, float (*image)[4],
                                        unsigned int start, unsigned int end)
{
    unsigned row, col, y, x, f;
    float sum[8];
    unsigned int width = W, height = H;
    unsigned int colors = 3;

    if (end == 0) {
        end = H;
    }

    for (row = start; row < end; row++)
        for (col = 0; col < width; col++) {
            if (col == border && row >= border && row < height - border) {
                col = width - border;
            }

            memset(sum, 0, sizeof sum);

            for (y = row - 1; y != row + 2; y++)
                for (x = col - 1; x != col + 2; x++)
                    if (y < height && x < width) {
                        f = fc(y, x);
                        sum[f] += image[y * width + x][f];
                        sum[f + 4]++;
                    }

            f = fc(row, col);

            FORCC if (c != f && sum[c + 4])
            {
                image[row * width + col][c] = sum[c] / sum[c + 4];
            }
        }
}

void RawImageSource::border_interpolate2(int winw, int winh, int lborders,
                                         const array2D<float> &rawData,
                                         array2D<float> &red,
                                         array2D<float> &green,
                                         array2D<float> &blue)
{
    int bord = lborders;
    int width = winw;
    int height = winh;

    for (int i = 0; i < height; i++) {

        float sum[6];

        for (int j = 0; j < bord; j++) { // first few columns
            for (int c = 0; c < 6; c++) {
                sum[c] = 0;
            }

            for (int i1 = i - 1; i1 < i + 2; i1++)
                for (int j1 = j - 1; j1 < j + 2; j1++) {
                    if ((i1 > -1) && (i1 < height) && (j1 > -1)) {
                        int c = FC(i1, j1);
                        sum[c] += rawData[i1][j1];
                        sum[c + 3]++;
                    }
                }

            int c = FC(i, j);

            if (c == 1) {
                red[i][j] = sum[0] / sum[3];
                green[i][j] = rawData[i][j];
                blue[i][j] = sum[2] / sum[5];
            } else {
                green[i][j] = sum[1] / sum[4];

                if (c == 0) {
                    red[i][j] = rawData[i][j];
                    blue[i][j] = sum[2] / sum[5];
                } else {
                    red[i][j] = sum[0] / sum[3];
                    blue[i][j] = rawData[i][j];
                }
            }
        } // j

        for (int j = width - bord; j < width; j++) { // last few columns
            for (int c = 0; c < 6; c++) {
                sum[c] = 0;
            }

            for (int i1 = i - 1; i1 < i + 2; i1++)
                for (int j1 = j - 1; j1 < j + 2; j1++) {
                    if ((i1 > -1) && (i1 < height) && (j1 < width)) {
                        int c = FC(i1, j1);
                        sum[c] += rawData[i1][j1];
                        sum[c + 3]++;
                    }
                }

            int c = FC(i, j);

            if (c == 1) {
                red[i][j] = sum[0] / sum[3];
                green[i][j] = rawData[i][j];
                blue[i][j] = sum[2] / sum[5];
            } else {
                green[i][j] = sum[1] / sum[4];

                if (c == 0) {
                    red[i][j] = rawData[i][j];
                    blue[i][j] = sum[2] / sum[5];
                } else {
                    red[i][j] = sum[0] / sum[3];
                    blue[i][j] = rawData[i][j];
                }
            }
        } // j
    } // i

    for (int i = 0; i < bord; i++) {

        float sum[6];

        for (int j = bord; j < width - bord; j++) { // first few rows
            for (int c = 0; c < 6; c++) {
                sum[c] = 0;
            }

            for (int i1 = i - 1; i1 < i + 2; i1++)
                for (int j1 = j - 1; j1 < j + 2; j1++) {
                    if ((i1 > -1) && (i1 < height) && (j1 > -1)) {
                        int c = FC(i1, j1);
                        sum[c] += rawData[i1][j1];
                        sum[c + 3]++;
                    }
                }

            int c = FC(i, j);

            if (c == 1) {
                red[i][j] = sum[0] / sum[3];
                green[i][j] = rawData[i][j];
                blue[i][j] = sum[2] / sum[5];
            } else {
                green[i][j] = sum[1] / sum[4];

                if (c == 0) {
                    red[i][j] = rawData[i][j];
                    blue[i][j] = sum[2] / sum[5];
                } else {
                    red[i][j] = sum[0] / sum[3];
                    blue[i][j] = rawData[i][j];
                }
            }
        } // j
    }

    for (int i = height - bord; i < height; i++) {

        float sum[6];

        for (int j = bord; j < width - bord; j++) { // last few rows
            for (int c = 0; c < 6; c++) {
                sum[c] = 0;
            }

            for (int i1 = i - 1; i1 < i + 2; i1++)
                for (int j1 = j - 1; j1 < j + 2; j1++) {
                    if ((i1 > -1) && (i1 < height) && (j1 < width)) {
                        int c = FC(i1, j1);
                        sum[c] += rawData[i1][j1];
                        sum[c + 3]++;
                    }
                }

            int c = FC(i, j);

            if (c == 1) {
                red[i][j] = sum[0] / sum[3];
                green[i][j] = rawData[i][j];
                blue[i][j] = sum[2] / sum[5];
            } else {
                green[i][j] = sum[1] / sum[4];

                if (c == 0) {
                    red[i][j] = rawData[i][j];
                    blue[i][j] = sum[2] / sum[5];
                } else {
                    red[i][j] = sum[0] / sum[3];
                    blue[i][j] = rawData[i][j];
                }
            }
        } // j
    }
}

// Joint Demosaicing and Denoising using High Order Interpolation Techniques
// Revision 0.9.1a - 09/02/2010 - Contact info: luis.sanz.rodriguez@gmail.com
// Copyright Luis Sanz Rodriguez 2010
// Adapted to RawTherapee by Jacques Desmis 3/2013

void RawImageSource::jdl_interpolate_omp() // from "Lassus"
{
    int width = W, height = H;
    int row = 0, col = 0, c, d, i, u = width, v = 2 * u, w = 3 * u, x = 4 * u,
        y = 5 * u, z = 6 * u, indx, (*dif)[2], (*chr)[2];
    float f[4], g[4];
    float(*image)[4];
    image = (float(*)[4])calloc(width * height, sizeof *image);
    dif = (int(*)[2])calloc(width * height, sizeof *dif);
    chr = (int(*)[2])calloc(width * height, sizeof *chr);

    if (plistener) {
        // this function seems to be unused
        // plistener->setProgressStr
        // (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"),
        // RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::jdl)));
        plistener->setProgressStr(
            Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), "xxx"));
        plistener->setProgress(0.0);
    }

#ifdef _OPENMP
#pragma omp parallel shared(image, width, height, u, w, v, y, x, z, dif,       \
                                chr) private(row, col, f, g, indx, c, d, i)
#endif
    {
#ifdef _OPENMP
#pragma omp for
#endif

        for (int ii = 0; ii < height; ii++)
            for (int jj = 0; jj < width; jj++) {
                image[ii * width + jj][fc(ii, jj)] = rawData[ii][jj];
            }

        border_interpolate(6, image);

#ifdef _OPENMP
#pragma omp for
#endif

        for (row = 5; row < height - 5; row++)
            for (col = 5 + (FC(row, 1) & 1), indx = row * width + col,
                c = FC(row, col);
                 col < u - 5; col += 2, indx += 2) {
                f[0] = 1.f + abs(image[indx - u][1] - image[indx - w][1]) +
                       abs(image[indx - u][1] - image[indx + u][1]) +
                       abs(image[indx][c] - image[indx - v][c]) +
                       abs(image[indx - v][c] - image[indx - x][c]);
                f[1] = 1.f + abs(image[indx + 1][1] - image[indx + 3][1]) +
                       abs(image[indx + 1][1] - image[indx - 1][1]) +
                       abs(image[indx][c] - image[indx + 2][c]) +
                       abs(image[indx + 2][c] - image[indx + 4][c]);
                f[2] = 1.f + abs(image[indx - 1][1] - image[indx - 3][1]) +
                       abs(image[indx - 1][1] - image[indx + 1][1]) +
                       abs(image[indx][c] - image[indx - 2][c]) +
                       abs(image[indx - 2][c] - image[indx - 4][c]);
                f[3] = 1.f + abs(image[indx + u][1] - image[indx + w][1]) +
                       abs(image[indx + u][1] - image[indx - u][1]) +
                       abs(image[indx][c] - image[indx + v][c]) +
                       abs(image[indx + v][c] - image[indx + x][c]);
                g[0] = CLIP(
                    (22.f * image[indx - u][1] + 22.f * image[indx - w][1] +
                     2.f * image[indx - y][1] + 2.f * image[indx + u][1] +
                     40.f * image[indx][c] - 32.f * image[indx - v][c] -
                     8.f * image[indx - x][c]) /
                    48.f);
                g[1] = CLIP(
                    (22.f * image[indx + 1][1] + 22.f * image[indx + 3][1] +
                     2.f * image[indx + 5][1] + 2.f * image[indx - 1][1] +
                     40.f * image[indx][c] - 32.f * image[indx + 2][c] -
                     8.f * image[indx + 4][c]) /
                    48.f);
                g[2] = CLIP(
                    (22.f * image[indx - 1][1] + 22.f * image[indx - 3][1] +
                     2.f * image[indx - 5][1] + 2.f * image[indx + 1][1] +
                     40.f * image[indx][c] - 32.f * image[indx - 2][c] -
                     8.f * image[indx - 4][c]) /
                    48.f);
                g[3] = CLIP(
                    (22.f * image[indx + u][1] + 22.f * image[indx + w][1] +
                     2.f * image[indx + y][1] + 2.f * image[indx - u][1] +
                     40.f * image[indx][c] - 32.f * image[indx + v][c] -
                     8.f * image[indx + x][c]) /
                    48.f);
                dif[indx][0] =
                    CLIP((f[3] * g[0] + f[0] * g[3]) / (f[0] + f[3])) -
                    image[indx][c];
                dif[indx][1] =
                    CLIP((f[2] * g[1] + f[1] * g[2]) / (f[1] + f[2])) -
                    image[indx][c];
            }

#ifdef _OPENMP
#pragma omp for
#endif

        for (row = 6; row < height - 6; row++)
            for (col = 6 + (FC(row, 2) & 1), indx = row * width + col,
                c = FC(row, col) / 2;
                 col < u - 6; col += 2, indx += 2) {
                f[0] = 1.f + 78.f * SQR((float)dif[indx][0]) +
                       69.f * (SQR((float)dif[indx - v][0]) +
                               SQR((float)dif[indx + v][0])) +
                       51.f * (SQR((float)dif[indx - x][0]) +
                               SQR((float)dif[indx + x][0])) +
                       21.f * (SQR((float)dif[indx - z][0]) +
                               SQR((float)dif[indx + z][0])) -
                       6.f * SQR((float)dif[indx - v][0] + dif[indx][0] +
                                 dif[indx + v][0]) -
                       10.f * (SQR((float)dif[indx - x][0] + dif[indx - v][0] +
                                   dif[indx][0]) +
                               SQR((float)dif[indx][0] + dif[indx + v][0] +
                                   dif[indx + x][0])) -
                       7.f * (SQR((float)dif[indx - z][0] + dif[indx - x][0] +
                                  dif[indx - v][0]) +
                              SQR((float)dif[indx + v][0] + dif[indx + x][0] +
                                  dif[indx + z][0]));
                f[1] = 1.f + 78.f * SQR((float)dif[indx][1]) +
                       69.f * (SQR((float)dif[indx - 2][1]) +
                               SQR((float)dif[indx + 2][1])) +
                       51.f * (SQR((float)dif[indx - 4][1]) +
                               SQR((float)dif[indx + 4][1])) +
                       21.f * (SQR((float)dif[indx - 6][1]) +
                               SQR((float)dif[indx + 6][1])) -
                       6.f * SQR((float)dif[indx - 2][1] + dif[indx][1] +
                                 dif[indx + 2][1]) -
                       10.f * (SQR((float)dif[indx - 4][1] + dif[indx - 2][1] +
                                   dif[indx][1]) +
                               SQR((float)dif[indx][1] + dif[indx + 2][1] +
                                   dif[indx + 4][1])) -
                       7.f * (SQR((float)dif[indx - 6][1] + dif[indx - 4][1] +
                                  dif[indx - 2][1]) +
                              SQR((float)dif[indx + 2][1] + dif[indx + 4][1] +
                                  dif[indx + 6][1]));
                g[0] =
                    median(0.725f * dif[indx][0] + 0.1375f * dif[indx - v][0] +
                               0.1375f * dif[indx + v][0],
                           static_cast<float>(dif[indx - v][0]),
                           static_cast<float>(dif[indx + v][0]));
                g[1] =
                    median(0.725f * dif[indx][1] + 0.1375f * dif[indx - 2][1] +
                               0.1375f * dif[indx + 2][1],
                           static_cast<float>(dif[indx - 2][1]),
                           static_cast<float>(dif[indx + 2][1]));
                chr[indx][c] = (f[1] * g[0] + f[0] * g[1]) / (f[0] + f[1]);
            }

#ifdef _OPENMP
#pragma omp for
#endif

        for (row = 6; row < height - 6; row++)
            for (col = 6 + (FC(row, 2) & 1), indx = row * width + col,
                c = 1 - FC(row, col) / 2, d = 2 * c;
                 col < u - 6; col += 2, indx += 2) {
                f[0] = 1.f / (float)(1.f +
                                     fabs((float)chr[indx - u - 1][c] -
                                          chr[indx + u + 1][c]) +
                                     fabs((float)chr[indx - u - 1][c] -
                                          chr[indx - w - 3][c]) +
                                     fabs((float)chr[indx + u + 1][c] -
                                          chr[indx - w - 3][c]));
                f[1] = 1.f / (float)(1.f +
                                     fabs((float)chr[indx - u + 1][c] -
                                          chr[indx + u - 1][c]) +
                                     fabs((float)chr[indx - u + 1][c] -
                                          chr[indx - w + 3][c]) +
                                     fabs((float)chr[indx + u - 1][c] -
                                          chr[indx - w + 3][c]));
                f[2] = 1.f / (float)(1.f +
                                     fabs((float)chr[indx + u - 1][c] -
                                          chr[indx - u + 1][c]) +
                                     fabs((float)chr[indx + u - 1][c] -
                                          chr[indx + w + 3][c]) +
                                     fabs((float)chr[indx - u + 1][c] -
                                          chr[indx + w - 3][c]));
                f[3] = 1.f / (float)(1.f +
                                     fabs((float)chr[indx + u + 1][c] -
                                          chr[indx - u - 1][c]) +
                                     fabs((float)chr[indx + u + 1][c] -
                                          chr[indx + w - 3][c]) +
                                     fabs((float)chr[indx - u - 1][c] -
                                          chr[indx + w + 3][c]));
                g[0] = median(chr[indx - u - 1][c], chr[indx - w - 1][c],
                              chr[indx - u - 3][c]);
                g[1] = median(chr[indx - u + 1][c], chr[indx - w + 1][c],
                              chr[indx - u + 3][c]);
                g[2] = median(chr[indx + u - 1][c], chr[indx + w - 1][c],
                              chr[indx + u - 3][c]);
                g[3] = median(chr[indx + u + 1][c], chr[indx + w + 1][c],
                              chr[indx + u + 3][c]);
                chr[indx][c] =
                    (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
                    (f[0] + f[1] + f[2] + f[3]);
                image[indx][1] = CLIP(image[indx][2 - d] + chr[indx][1 - c]);
                image[indx][d] = CLIP(image[indx][1] - chr[indx][c]);
            }

#ifdef _OPENMP
#pragma omp for
#endif

        for (row = 6; row < height - 6; row++)
            for (col = 6 + (FC(row, 1) & 1), indx = row * width + col,
                c = FC(row, col + 1) / 2, d = 2 * c;
                 col < u - 6; col += 2, indx += 2)
                for (i = 0; i <= 1; c = 1 - c, d = 2 * c, i++) {
                    f[0] = 1.f / (float)(1.f +
                                         fabs((float)chr[indx - u][c] -
                                              chr[indx + u][c]) +
                                         fabs((float)chr[indx - u][c] -
                                              chr[indx - w][c]) +
                                         fabs((float)chr[indx + u][c] -
                                              chr[indx - w][c]));
                    f[1] = 1.f / (float)(1.f +
                                         fabs((float)chr[indx + 1][c] -
                                              chr[indx - 1][c]) +
                                         fabs((float)chr[indx + 1][c] -
                                              chr[indx + 3][c]) +
                                         fabs((float)chr[indx - 1][c] -
                                              chr[indx + 3][c]));
                    f[2] = 1.f / (float)(1.f +
                                         fabs((float)chr[indx - 1][c] -
                                              chr[indx + 1][c]) +
                                         fabs((float)chr[indx - 1][c] -
                                              chr[indx - 3][c]) +
                                         fabs((float)chr[indx + 1][c] -
                                              chr[indx - 3][c]));
                    f[3] = 1.f / (float)(1.f +
                                         fabs((float)chr[indx + u][c] -
                                              chr[indx - u][c]) +
                                         fabs((float)chr[indx + u][c] -
                                              chr[indx + w][c]) +
                                         fabs((float)chr[indx - u][c] -
                                              chr[indx + w][c]));
                    g[0] =
                        0.875f * chr[indx - u][c] + 0.125f * chr[indx - w][c];
                    g[1] =
                        0.875f * chr[indx + 1][c] + 0.125f * chr[indx + 3][c];
                    g[2] =
                        0.875f * chr[indx - 1][c] + 0.125f * chr[indx - 3][c];
                    g[3] =
                        0.875f * chr[indx + u][c] + 0.125f * chr[indx + w][c];
                    image[indx][d] =
                        CLIP(image[indx][1] - (f[0] * g[0] + f[1] * g[1] +
                                               f[2] * g[2] + f[3] * g[3]) /
                                                  (f[0] + f[1] + f[2] + f[3]));
                }

#ifdef _OPENMP
#pragma omp for
#endif

        for (int ii = 0; ii < height; ii++) {
            for (int jj = 0; jj < width; jj++) {
                red[ii][jj] = CLIP(image[ii * width + jj][0]);
                green[ii][jj] = CLIP(image[ii * width + jj][1]);
                blue[ii][jj] = CLIP(image[ii * width + jj][2]);
            }
        }
    } // End of parallelization
    free(image);
    free(dif);
    free(chr);
    // RawImageSource::refinement_lassus();
}

/***
 *
 *   Bayer CFA Demosaicing using Integrated Gaussian Vector on Color Differences
 *   Revision 1.0 - 2013/02/28
 *
 *   Copyright (c) 2007-2013 Luis Sanz Rodriguez
 *   Using High Order Interpolation technique by Jim S, Jimmy Li, and Sharmil
 *Randhawa
 *
 *   Contact info: luis.sanz.rodriguez@gmail.com
 *
 *   This code is distributed under a GNU General Public License, version 3.
 *   Visit <http://www.gnu.org/licenses/> for more information.
 *
 ***/
// Adapted to RawTherapee by Jacques Desmis 3/2013
// SSE version by Ingo Weyrich 5/2013
#ifdef ART_SIMD
void RawImageSource::igv_interpolate(int winw, int winh)
{
    static const float eps = 1e-5f,
                       epssq = 1e-5f; // mod epssq -10f =>-5f Jacques 3/2013 to
                                      // prevent artifact (divide by zero)

    static const int h1 = 1, h2 = 2, h3 = 3, h5 = 5;
    const int width = winw, height = winh;
    const int v1 = 1 * width, v2 = 2 * width, v3 = 3 * width, v5 = 5 * width;
    float *rgb[2];
    float *chr[4];
    float *rgbarray, *vdif, *hdif, *chrarray;
    rgbarray = (float(*))malloc((width * height) * sizeof(float));
    rgb[0] = rgbarray;
    rgb[1] = rgbarray + (width * height) / 2;

    vdif = (float(*))calloc(width * height / 2, sizeof *vdif);
    hdif = (float(*))calloc(width * height / 2, sizeof *hdif);

    chrarray = (float(*))calloc(width * height, sizeof(float));
    chr[0] = chrarray;
    chr[1] = chrarray + (width * height) / 2;

    // mapped chr[2] and chr[3] to hdif and hdif, because these are out of use,
    // when chr[2] and chr[3] are used
    chr[2] = hdif;
    chr[3] = vdif;

    if (plistener) {
        plistener->setProgressStr(Glib::ustring::compose(
            M("TP_RAW_DMETHOD_PROGRESSBAR"), M("TP_RAW_IGV")));
        plistener->setProgress(0.0);
    }

#ifdef _OPENMP
#pragma omp parallel shared(rgb, vdif, hdif, chr)
#endif
    {
        __m128 ngv, egv, wgv, sgv, nvv, evv, wvv, svv, nwgv, negv, swgv, segv,
            nwvv, nevv, swvv, sevv, tempv, temp1v, temp2v, temp3v, temp4v,
            temp5v, temp6v, temp7v, temp8v;
        __m128 epsv = _mm_set1_ps(eps);
        __m128 epssqv = _mm_set1_ps(epssq);
        __m128 c65535v = _mm_set1_ps(65535.f);
        __m128 c23v = _mm_set1_ps(23.f);
        __m128 c40v = _mm_set1_ps(40.f);
        __m128 c51v = _mm_set1_ps(51.f);
        __m128 c32v = _mm_set1_ps(32.f);
        __m128 c8v = _mm_set1_ps(8.f);
        __m128 c7v = _mm_set1_ps(7.f);
        __m128 c6v = _mm_set1_ps(6.f);
        __m128 c10v = _mm_set1_ps(10.f);
        __m128 c21v = _mm_set1_ps(21.f);
        __m128 c78v = _mm_set1_ps(78.f);
        __m128 c69v = _mm_set1_ps(69.f);
        __m128 c3145680v = _mm_set1_ps(3145680.f);
        __m128 onev = _mm_set1_ps(1.f);
        __m128 zerov = _mm_set1_ps(0.f);
        __m128 d725v = _mm_set1_ps(0.725f);
        __m128 d1375v = _mm_set1_ps(0.1375f);

        float *dest1, *dest2;
        float ng, eg, wg, sg, nv, ev, wv, sv, nwg, neg, swg, seg, nwv, nev, swv,
            sev;
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 0; row < height - 0; row++) {
            dest1 = rgb[FC(row, 0) & 1];
            dest2 = rgb[FC(row, 1) & 1];
            int col, indx;

            for (col = 0, indx = row * width + col; col < width - 7;
                 col += 8, indx += 8) {
                temp1v = LVFU(rawData[row][col]);
                temp1v = vmaxf(temp1v, ZEROV);
                temp2v = LVFU(rawData[row][col + 4]);
                temp2v = vmaxf(temp2v, ZEROV);
                tempv = _mm_shuffle_ps(temp1v, temp2v, _MM_SHUFFLE(2, 0, 2, 0));
                _mm_storeu_ps(&dest1[indx >> 1], tempv);
                tempv = _mm_shuffle_ps(temp1v, temp2v, _MM_SHUFFLE(3, 1, 3, 1));
                _mm_storeu_ps(&dest2[indx >> 1], tempv);
            }

            for (; col < width; col++, indx += 2) {
                dest1[indx >> 1] =
                    std::max(0.f, rawData[row][col]); // rawData = RT data
                col++;
                if (col < width)
                    dest2[indx >> 1] =
                        std::max(0.f, rawData[row][col]); // rawData = RT data
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.13);
            }
        }

#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 5; row < height - 5; row++) {
            int col, indx, indx1;

            for (col = 5 + (FC(row, 1) & 1), indx = row * width + col,
                indx1 = indx >> 1;
                 col < width - 12; col += 8, indx += 8, indx1 += 4) {
                // N,E,W,S Gradients
                ngv =
                    (epsv +
                     (vabsf(LVFU(rgb[1][(indx - v1) >> 1]) -
                            LVFU(rgb[1][(indx - v3) >> 1])) +
                      vabsf(LVFU(rgb[0][indx1]) - LVFU(rgb[0][(indx1 - v1)]))) /
                         c65535v);
                egv =
                    (epsv +
                     (vabsf(LVFU(rgb[1][(indx + h1) >> 1]) -
                            LVFU(rgb[1][(indx + h3) >> 1])) +
                      vabsf(LVFU(rgb[0][indx1]) - LVFU(rgb[0][(indx1 + h1)]))) /
                         c65535v);
                wgv =
                    (epsv +
                     (vabsf(LVFU(rgb[1][(indx - h1) >> 1]) -
                            LVFU(rgb[1][(indx - h3) >> 1])) +
                      vabsf(LVFU(rgb[0][indx1]) - LVFU(rgb[0][(indx1 - h1)]))) /
                         c65535v);
                sgv =
                    (epsv +
                     (vabsf(LVFU(rgb[1][(indx + v1) >> 1]) -
                            LVFU(rgb[1][(indx + v3) >> 1])) +
                      vabsf(LVFU(rgb[0][indx1]) - LVFU(rgb[0][(indx1 + v1)]))) /
                         c65535v);
                // N,E,W,S High Order Interpolation (Li & Randhawa)
                // N,E,W,S Hamilton Adams Interpolation
                //  (48.f * 65535.f) = 3145680.f
                tempv = c40v * LVFU(rgb[0][indx1]);
                nvv = vclampf(((c23v * LVFU(rgb[1][(indx - v1) >> 1]) +
                                c23v * LVFU(rgb[1][(indx - v3) >> 1]) +
                                LVFU(rgb[1][(indx - v5) >> 1]) +
                                LVFU(rgb[1][(indx + v1) >> 1]) + tempv -
                                c32v * LVFU(rgb[0][(indx1 - v1)]) -
                                c8v * LVFU(rgb[0][(indx1 - v2)]))) /
                                  c3145680v,
                              zerov, onev);
                evv = vclampf(((c23v * LVFU(rgb[1][(indx + h1) >> 1]) +
                                c23v * LVFU(rgb[1][(indx + h3) >> 1]) +
                                LVFU(rgb[1][(indx + h5) >> 1]) +
                                LVFU(rgb[1][(indx - h1) >> 1]) + tempv -
                                c32v * LVFU(rgb[0][(indx1 + h1)]) -
                                c8v * LVFU(rgb[0][(indx1 + h2)]))) /
                                  c3145680v,
                              zerov, onev);
                wvv = vclampf(((c23v * LVFU(rgb[1][(indx - h1) >> 1]) +
                                c23v * LVFU(rgb[1][(indx - h3) >> 1]) +
                                LVFU(rgb[1][(indx - h5) >> 1]) +
                                LVFU(rgb[1][(indx + h1) >> 1]) + tempv -
                                c32v * LVFU(rgb[0][(indx1 - h1)]) -
                                c8v * LVFU(rgb[0][(indx1 - h2)]))) /
                                  c3145680v,
                              zerov, onev);
                svv = vclampf(((c23v * LVFU(rgb[1][(indx + v1) >> 1]) +
                                c23v * LVFU(rgb[1][(indx + v3) >> 1]) +
                                LVFU(rgb[1][(indx + v5) >> 1]) +
                                LVFU(rgb[1][(indx - v1) >> 1]) + tempv -
                                c32v * LVFU(rgb[0][(indx1 + v1)]) -
                                c8v * LVFU(rgb[0][(indx1 + v2)]))) /
                                  c3145680v,
                              zerov, onev);
                // Horizontal and vertical color differences
                tempv = LVFU(rgb[0][indx1]) / c65535v;
                _mm_storeu_ps(&vdif[indx1],
                              (sgv * nvv + ngv * svv) / (ngv + sgv) - tempv);
                _mm_storeu_ps(&hdif[indx1],
                              (wgv * evv + egv * wvv) / (egv + wgv) - tempv);
            }

            // borders without SSE
            for (; col < width - 5; col += 2, indx += 2, indx1++) {
                // N,E,W,S Gradients
                ng = (eps + (fabsf(rgb[1][(indx - v1) >> 1] -
                                   rgb[1][(indx - v3) >> 1]) +
                             fabsf(rgb[0][indx1] - rgb[0][(indx1 - v1)])) /
                                65535.f);
                ;
                eg = (eps + (fabsf(rgb[1][(indx + h1) >> 1] -
                                   rgb[1][(indx + h3) >> 1]) +
                             fabsf(rgb[0][indx1] - rgb[0][(indx1 + h1)])) /
                                65535.f);
                wg = (eps + (fabsf(rgb[1][(indx - h1) >> 1] -
                                   rgb[1][(indx - h3) >> 1]) +
                             fabsf(rgb[0][indx1] - rgb[0][(indx1 - h1)])) /
                                65535.f);
                sg = (eps + (fabsf(rgb[1][(indx + v1) >> 1] -
                                   rgb[1][(indx + v3) >> 1]) +
                             fabsf(rgb[0][indx1] - rgb[0][(indx1 + v1)])) /
                                65535.f);
                // N,E,W,S High Order Interpolation (Li & Randhawa)
                // N,E,W,S Hamilton Adams Interpolation
                //  (48.f * 65535.f) = 3145680.f
                nv =
                    LIM(((23.0f * rgb[1][(indx - v1) >> 1] +
                          23.0f * rgb[1][(indx - v3) >> 1] +
                          rgb[1][(indx - v5) >> 1] + rgb[1][(indx + v1) >> 1] +
                          40.0f * rgb[0][indx1] - 32.0f * rgb[0][(indx1 - v1)] -
                          8.0f * rgb[0][(indx1 - v2)])) /
                            3145680.f,
                        0.0f, 1.0f);
                ev =
                    LIM(((23.0f * rgb[1][(indx + h1) >> 1] +
                          23.0f * rgb[1][(indx + h3) >> 1] +
                          rgb[1][(indx + h5) >> 1] + rgb[1][(indx - h1) >> 1] +
                          40.0f * rgb[0][indx1] - 32.0f * rgb[0][(indx1 + h1)] -
                          8.0f * rgb[0][(indx1 + h2)])) /
                            3145680.f,
                        0.0f, 1.0f);
                wv =
                    LIM(((23.0f * rgb[1][(indx - h1) >> 1] +
                          23.0f * rgb[1][(indx - h3) >> 1] +
                          rgb[1][(indx - h5) >> 1] + rgb[1][(indx + h1) >> 1] +
                          40.0f * rgb[0][indx1] - 32.0f * rgb[0][(indx1 - h1)] -
                          8.0f * rgb[0][(indx1 - h2)])) /
                            3145680.f,
                        0.0f, 1.0f);
                sv =
                    LIM(((23.0f * rgb[1][(indx + v1) >> 1] +
                          23.0f * rgb[1][(indx + v3) >> 1] +
                          rgb[1][(indx + v5) >> 1] + rgb[1][(indx - v1) >> 1] +
                          40.0f * rgb[0][indx1] - 32.0f * rgb[0][(indx1 + v1)] -
                          8.0f * rgb[0][(indx1 + v2)])) /
                            3145680.f,
                        0.0f, 1.0f);
                // Horizontal and vertical color differences
                vdif[indx1] =
                    (sg * nv + ng * sv) / (ng + sg) - (rgb[0][indx1]) / 65535.f;
                hdif[indx1] =
                    (wg * ev + eg * wv) / (eg + wg) - (rgb[0][indx1]) / 65535.f;
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.26);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++) {
            int col, d, indx1;

            for (col = 7 + (FC(row, 1) & 1), indx1 = (row * width + col) >> 1,
                d = FC(row, col) / 2;
                 col < width - 14; col += 8, indx1 += 4) {
                // H&V integrated gaussian vector over variance on color
                // differences Mod Jacques 3/2013
                ngv = vclampf(
                    epssqv + c78v * SQRV(LVFU(vdif[indx1])) +
                        c69v * (SQRV(LVFU(vdif[indx1 - v1])) +
                                SQRV(LVFU(vdif[indx1 + v1]))) +
                        c51v * (SQRV(LVFU(vdif[indx1 - v2])) +
                                SQRV(LVFU(vdif[indx1 + v2]))) +
                        c21v * (SQRV(LVFU(vdif[indx1 - v3])) +
                                SQRV(LVFU(vdif[indx1 + v3]))) -
                        c6v * SQRV(LVFU(vdif[indx1 - v1]) + LVFU(vdif[indx1]) +
                                   LVFU(vdif[indx1 + v1])) -
                        c10v *
                            (SQRV(LVFU(vdif[indx1 - v2]) +
                                  LVFU(vdif[indx1 - v1]) + LVFU(vdif[indx1])) +
                             SQRV(LVFU(vdif[indx1]) + LVFU(vdif[indx1 + v1]) +
                                  LVFU(vdif[indx1 + v2]))) -
                        c7v * (SQRV(LVFU(vdif[indx1 - v3]) +
                                    LVFU(vdif[indx1 - v2]) +
                                    LVFU(vdif[indx1 - v1])) +
                               SQRV(LVFU(vdif[indx1 + v1]) +
                                    LVFU(vdif[indx1 + v2]) +
                                    LVFU(vdif[indx1 + v3]))),
                    zerov, onev);
                egv = vclampf(
                    epssqv + c78v * SQRV(LVFU(hdif[indx1])) +
                        c69v * (SQRV(LVFU(hdif[indx1 - h1])) +
                                SQRV(LVFU(hdif[indx1 + h1]))) +
                        c51v * (SQRV(LVFU(hdif[indx1 - h2])) +
                                SQRV(LVFU(hdif[indx1 + h2]))) +
                        c21v * (SQRV(LVFU(hdif[indx1 - h3])) +
                                SQRV(LVFU(hdif[indx1 + h3]))) -
                        c6v * SQRV(LVFU(hdif[indx1 - h1]) + LVFU(hdif[indx1]) +
                                   LVFU(hdif[indx1 + h1])) -
                        c10v *
                            (SQRV(LVFU(hdif[indx1 - h2]) +
                                  LVFU(hdif[indx1 - h1]) + LVFU(hdif[indx1])) +
                             SQRV(LVFU(hdif[indx1]) + LVFU(hdif[indx1 + h1]) +
                                  LVFU(hdif[indx1 + h2]))) -
                        c7v * (SQRV(LVFU(hdif[indx1 - h3]) +
                                    LVFU(hdif[indx1 - h2]) +
                                    LVFU(hdif[indx1 - h1])) +
                               SQRV(LVFU(hdif[indx1 + h1]) +
                                    LVFU(hdif[indx1 + h2]) +
                                    LVFU(hdif[indx1 + h3]))),
                    zerov, onev);
                // Limit chrominance using H/V neighbourhood
                nvv = median(d725v * LVFU(vdif[indx1]) +
                                 d1375v * LVFU(vdif[indx1 - v1]) +
                                 d1375v * LVFU(vdif[indx1 + v1]),
                             LVFU(vdif[indx1 - v1]), LVFU(vdif[indx1 + v1]));
                evv = median(d725v * LVFU(hdif[indx1]) +
                                 d1375v * LVFU(hdif[indx1 - h1]) +
                                 d1375v * LVFU(hdif[indx1 + h1]),
                             LVFU(hdif[indx1 - h1]), LVFU(hdif[indx1 + h1]));
                // Chrominance estimation
                tempv = (egv * nvv + ngv * evv) / (ngv + egv);
                _mm_storeu_ps(&(chr[d][indx1]), tempv);
                // Green channel population
                temp1v = c65535v * tempv + LVFU(rgb[0][indx1]);
                _mm_storeu_ps(&(rgb[0][indx1]), temp1v);
            }

            for (; col < width - 7; col += 2, indx1++) {
                // H&V integrated gaussian vector over variance on color
                // differences Mod Jacques 3/2013
                ng = LIM(epssq + 78.0f * SQR(vdif[indx1]) +
                             69.0f * (SQR(vdif[indx1 - v1]) +
                                      SQR(vdif[indx1 + v1])) +
                             51.0f * (SQR(vdif[indx1 - v2]) +
                                      SQR(vdif[indx1 + v2])) +
                             21.0f * (SQR(vdif[indx1 - v3]) +
                                      SQR(vdif[indx1 + v3])) -
                             6.0f * SQR(vdif[indx1 - v1] + vdif[indx1] +
                                        vdif[indx1 + v1]) -
                             10.0f * (SQR(vdif[indx1 - v2] + vdif[indx1 - v1] +
                                          vdif[indx1]) +
                                      SQR(vdif[indx1] + vdif[indx1 + v1] +
                                          vdif[indx1 + v2])) -
                             7.0f * (SQR(vdif[indx1 - v3] + vdif[indx1 - v2] +
                                         vdif[indx1 - v1]) +
                                     SQR(vdif[indx1 + v1] + vdif[indx1 + v2] +
                                         vdif[indx1 + v3])),
                         0.f, 1.f);
                eg = LIM(epssq + 78.0f * SQR(hdif[indx1]) +
                             69.0f * (SQR(hdif[indx1 - h1]) +
                                      SQR(hdif[indx1 + h1])) +
                             51.0f * (SQR(hdif[indx1 - h2]) +
                                      SQR(hdif[indx1 + h2])) +
                             21.0f * (SQR(hdif[indx1 - h3]) +
                                      SQR(hdif[indx1 + h3])) -
                             6.0f * SQR(hdif[indx1 - h1] + hdif[indx1] +
                                        hdif[indx1 + h1]) -
                             10.0f * (SQR(hdif[indx1 - h2] + hdif[indx1 - h1] +
                                          hdif[indx1]) +
                                      SQR(hdif[indx1] + hdif[indx1 + h1] +
                                          hdif[indx1 + h2])) -
                             7.0f * (SQR(hdif[indx1 - h3] + hdif[indx1 - h2] +
                                         hdif[indx1 - h1]) +
                                     SQR(hdif[indx1 + h1] + hdif[indx1 + h2] +
                                         hdif[indx1 + h3])),
                         0.f, 1.f);
                // Limit chrominance using H/V neighbourhood
                nv = median(0.725f * vdif[indx1] + 0.1375f * vdif[indx1 - v1] +
                                0.1375f * vdif[indx1 + v1],
                            vdif[indx1 - v1], vdif[indx1 + v1]);
                ev = median(0.725f * hdif[indx1] + 0.1375f * hdif[indx1 - h1] +
                                0.1375f * hdif[indx1 + h1],
                            hdif[indx1 - h1], hdif[indx1 + h1]);
                // Chrominance estimation
                chr[d][indx1] = (eg * nv + ng * ev) / (ng + eg);
                // Green channel population
                rgb[0][indx1] = rgb[0][indx1] + 65535.f * chr[d][indx1];
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.39);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++) {
            int col, indx, c;

            for (col = 7 + (FC(row, 1) & 1), indx = row * width + col,
                c = 1 - FC(row, col) / 2;
                 col < width - 14; col += 8, indx += 8) {
                // NW,NE,SW,SE Gradients
                nwgv = onev / (epsv +
                               vabsf(LVFU(chr[c][(indx - v1 - h1) >> 1]) -
                                     LVFU(chr[c][(indx - v3 - h3) >> 1])) +
                               vabsf(LVFU(chr[c][(indx + v1 + h1) >> 1]) -
                                     LVFU(chr[c][(indx - v3 - h3) >> 1])));
                negv = onev / (epsv +
                               vabsf(LVFU(chr[c][(indx - v1 + h1) >> 1]) -
                                     LVFU(chr[c][(indx - v3 + h3) >> 1])) +
                               vabsf(LVFU(chr[c][(indx + v1 - h1) >> 1]) -
                                     LVFU(chr[c][(indx - v3 + h3) >> 1])));
                swgv = onev / (epsv +
                               vabsf(LVFU(chr[c][(indx + v1 - h1) >> 1]) -
                                     LVFU(chr[c][(indx + v3 + h3) >> 1])) +
                               vabsf(LVFU(chr[c][(indx - v1 + h1) >> 1]) -
                                     LVFU(chr[c][(indx + v3 - h3) >> 1])));
                segv = onev / (epsv +
                               vabsf(LVFU(chr[c][(indx + v1 + h1) >> 1]) -
                                     LVFU(chr[c][(indx + v3 - h3) >> 1])) +
                               vabsf(LVFU(chr[c][(indx - v1 - h1) >> 1]) -
                                     LVFU(chr[c][(indx + v3 + h3) >> 1])));
                // Limit NW,NE,SW,SE Color differences
                nwvv = median(LVFU(chr[c][(indx - v1 - h1) >> 1]),
                              LVFU(chr[c][(indx - v3 - h1) >> 1]),
                              LVFU(chr[c][(indx - v1 - h3) >> 1]));
                nevv = median(LVFU(chr[c][(indx - v1 + h1) >> 1]),
                              LVFU(chr[c][(indx - v3 + h1) >> 1]),
                              LVFU(chr[c][(indx - v1 + h3) >> 1]));
                swvv = median(LVFU(chr[c][(indx + v1 - h1) >> 1]),
                              LVFU(chr[c][(indx + v3 - h1) >> 1]),
                              LVFU(chr[c][(indx + v1 - h3) >> 1]));
                sevv = median(LVFU(chr[c][(indx + v1 + h1) >> 1]),
                              LVFU(chr[c][(indx + v3 + h1) >> 1]),
                              LVFU(chr[c][(indx + v1 + h3) >> 1]));
                // Interpolate chrominance: R@B and B@R
                tempv =
                    (nwgv * nwvv + negv * nevv + swgv * swvv + segv * sevv) /
                    (nwgv + negv + swgv + segv);
                _mm_storeu_ps(&(chr[c][indx >> 1]), tempv);
            }

            for (; col < width - 7; col += 2, indx += 2) {
                // NW,NE,SW,SE Gradients
                nwg = 1.0f / (eps +
                              fabsf(chr[c][(indx - v1 - h1) >> 1] -
                                    chr[c][(indx - v3 - h3) >> 1]) +
                              fabsf(chr[c][(indx + v1 + h1) >> 1] -
                                    chr[c][(indx - v3 - h3) >> 1]));
                neg = 1.0f / (eps +
                              fabsf(chr[c][(indx - v1 + h1) >> 1] -
                                    chr[c][(indx - v3 + h3) >> 1]) +
                              fabsf(chr[c][(indx + v1 - h1) >> 1] -
                                    chr[c][(indx - v3 + h3) >> 1]));
                swg = 1.0f / (eps +
                              fabsf(chr[c][(indx + v1 - h1) >> 1] -
                                    chr[c][(indx + v3 + h3) >> 1]) +
                              fabsf(chr[c][(indx - v1 + h1) >> 1] -
                                    chr[c][(indx + v3 - h3) >> 1]));
                seg = 1.0f / (eps +
                              fabsf(chr[c][(indx + v1 + h1) >> 1] -
                                    chr[c][(indx + v3 - h3) >> 1]) +
                              fabsf(chr[c][(indx - v1 - h1) >> 1] -
                                    chr[c][(indx + v3 + h3) >> 1]));
                // Limit NW,NE,SW,SE Color differences
                nwv = median(chr[c][(indx - v1 - h1) >> 1],
                             chr[c][(indx - v3 - h1) >> 1],
                             chr[c][(indx - v1 - h3) >> 1]);
                nev = median(chr[c][(indx - v1 + h1) >> 1],
                             chr[c][(indx - v3 + h1) >> 1],
                             chr[c][(indx - v1 + h3) >> 1]);
                swv = median(chr[c][(indx + v1 - h1) >> 1],
                             chr[c][(indx + v3 - h1) >> 1],
                             chr[c][(indx + v1 - h3) >> 1]);
                sev = median(chr[c][(indx + v1 + h1) >> 1],
                             chr[c][(indx + v3 + h1) >> 1],
                             chr[c][(indx + v1 + h3) >> 1]);
                // Interpolate chrominance: R@B and B@R
                chr[c][indx >> 1] =
                    (nwg * nwv + neg * nev + swg * swv + seg * sev) /
                    (nwg + neg + swg + seg);
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.65);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++) {
            int col, indx;

            for (col = 7 + (FC(row, 0) & 1), indx = row * width + col;
                 col < width - 14; col += 8, indx += 8) {
                // N,E,W,S Gradients
                ngv = onev / (epsv +
                              vabsf(LVFU(chr[0][(indx - v1) >> 1]) -
                                    LVFU(chr[0][(indx - v3) >> 1])) +
                              vabsf(LVFU(chr[0][(indx + v1) >> 1]) -
                                    LVFU(chr[0][(indx - v3) >> 1])));
                egv = onev / (epsv +
                              vabsf(LVFU(chr[0][(indx + h1) >> 1]) -
                                    LVFU(chr[0][(indx + h3) >> 1])) +
                              vabsf(LVFU(chr[0][(indx - h1) >> 1]) -
                                    LVFU(chr[0][(indx + h3) >> 1])));
                wgv = onev / (epsv +
                              vabsf(LVFU(chr[0][(indx - h1) >> 1]) -
                                    LVFU(chr[0][(indx - h3) >> 1])) +
                              vabsf(LVFU(chr[0][(indx + h1) >> 1]) -
                                    LVFU(chr[0][(indx - h3) >> 1])));
                sgv = onev / (epsv +
                              vabsf(LVFU(chr[0][(indx + v1) >> 1]) -
                                    LVFU(chr[0][(indx + v3) >> 1])) +
                              vabsf(LVFU(chr[0][(indx - v1) >> 1]) -
                                    LVFU(chr[0][(indx + v3) >> 1])));
                // Interpolate chrominance: R@G and B@G
                tempv = ((ngv * LVFU(chr[0][(indx - v1) >> 1]) +
                          egv * LVFU(chr[0][(indx + h1) >> 1]) +
                          wgv * LVFU(chr[0][(indx - h1) >> 1]) +
                          sgv * LVFU(chr[0][(indx + v1) >> 1])) /
                         (ngv + egv + wgv + sgv));
                _mm_storeu_ps(&chr[0 + 2][indx >> 1], tempv);
            }

            for (; col < width - 7; col += 2, indx += 2) {
                // N,E,W,S Gradients
                ng = 1.0f / (eps +
                             fabsf(chr[0][(indx - v1) >> 1] -
                                   chr[0][(indx - v3) >> 1]) +
                             fabsf(chr[0][(indx + v1) >> 1] -
                                   chr[0][(indx - v3) >> 1]));
                eg = 1.0f / (eps +
                             fabsf(chr[0][(indx + h1) >> 1] -
                                   chr[0][(indx + h3) >> 1]) +
                             fabsf(chr[0][(indx - h1) >> 1] -
                                   chr[0][(indx + h3) >> 1]));
                wg = 1.0f / (eps +
                             fabsf(chr[0][(indx - h1) >> 1] -
                                   chr[0][(indx - h3) >> 1]) +
                             fabsf(chr[0][(indx + h1) >> 1] -
                                   chr[0][(indx - h3) >> 1]));
                sg = 1.0f / (eps +
                             fabsf(chr[0][(indx + v1) >> 1] -
                                   chr[0][(indx + v3) >> 1]) +
                             fabsf(chr[0][(indx - v1) >> 1] -
                                   chr[0][(indx + v3) >> 1]));
                // Interpolate chrominance: R@G and B@G
                chr[0 + 2][indx >> 1] = ((ng * chr[0][(indx - v1) >> 1] +
                                          eg * chr[0][(indx + h1) >> 1] +
                                          wg * chr[0][(indx - h1) >> 1] +
                                          sg * chr[0][(indx + v1) >> 1]) /
                                         (ng + eg + wg + sg));
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.78);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++) {
            int col, indx;

            for (col = 7 + (FC(row, 0) & 1), indx = row * width + col;
                 col < width - 14; col += 8, indx += 8) {
                // N,E,W,S Gradients
                ngv = onev / (epsv +
                              vabsf(LVFU(chr[1][(indx - v1) >> 1]) -
                                    LVFU(chr[1][(indx - v3) >> 1])) +
                              vabsf(LVFU(chr[1][(indx + v1) >> 1]) -
                                    LVFU(chr[1][(indx - v3) >> 1])));
                egv = onev / (epsv +
                              vabsf(LVFU(chr[1][(indx + h1) >> 1]) -
                                    LVFU(chr[1][(indx + h3) >> 1])) +
                              vabsf(LVFU(chr[1][(indx - h1) >> 1]) -
                                    LVFU(chr[1][(indx + h3) >> 1])));
                wgv = onev / (epsv +
                              vabsf(LVFU(chr[1][(indx - h1) >> 1]) -
                                    LVFU(chr[1][(indx - h3) >> 1])) +
                              vabsf(LVFU(chr[1][(indx + h1) >> 1]) -
                                    LVFU(chr[1][(indx - h3) >> 1])));
                sgv = onev / (epsv +
                              vabsf(LVFU(chr[1][(indx + v1) >> 1]) -
                                    LVFU(chr[1][(indx + v3) >> 1])) +
                              vabsf(LVFU(chr[1][(indx - v1) >> 1]) -
                                    LVFU(chr[1][(indx + v3) >> 1])));
                // Interpolate chrominance: R@G and B@G
                tempv = ((ngv * LVFU(chr[1][(indx - v1) >> 1]) +
                          egv * LVFU(chr[1][(indx + h1) >> 1]) +
                          wgv * LVFU(chr[1][(indx - h1) >> 1]) +
                          sgv * LVFU(chr[1][(indx + v1) >> 1])) /
                         (ngv + egv + wgv + sgv));
                _mm_storeu_ps(&chr[1 + 2][indx >> 1], tempv);
            }

            for (; col < width - 7; col += 2, indx += 2) {
                // N,E,W,S Gradients
                ng = 1.0f / (eps +
                             fabsf(chr[1][(indx - v1) >> 1] -
                                   chr[1][(indx - v3) >> 1]) +
                             fabsf(chr[1][(indx + v1) >> 1] -
                                   chr[1][(indx - v3) >> 1]));
                eg = 1.0f / (eps +
                             fabsf(chr[1][(indx + h1) >> 1] -
                                   chr[1][(indx + h3) >> 1]) +
                             fabsf(chr[1][(indx - h1) >> 1] -
                                   chr[1][(indx + h3) >> 1]));
                wg = 1.0f / (eps +
                             fabsf(chr[1][(indx - h1) >> 1] -
                                   chr[1][(indx - h3) >> 1]) +
                             fabsf(chr[1][(indx + h1) >> 1] -
                                   chr[1][(indx - h3) >> 1]));
                sg = 1.0f / (eps +
                             fabsf(chr[1][(indx + v1) >> 1] -
                                   chr[1][(indx + v3) >> 1]) +
                             fabsf(chr[1][(indx - v1) >> 1] -
                                   chr[1][(indx + v3) >> 1]));
                // Interpolate chrominance: R@G and B@G
                chr[1 + 2][indx >> 1] = ((ng * chr[1][(indx - v1) >> 1] +
                                          eg * chr[1][(indx + h1) >> 1] +
                                          wg * chr[1][(indx - h1) >> 1] +
                                          sg * chr[1][(indx + v1) >> 1]) /
                                         (ng + eg + wg + sg));
            }
        }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.91);
            }
        }
        float *src1, *src2, *redsrc0, *redsrc1, *bluesrc0, *bluesrc1;
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++) {
            int col, indx, fc;
            fc = FC(row, 7) & 1;
            src1 = rgb[fc];
            src2 = rgb[fc ^ 1];
            redsrc0 = chr[fc << 1];
            redsrc1 = chr[(fc ^ 1) << 1];
            bluesrc0 = chr[(fc << 1) + 1];
            bluesrc1 = chr[((fc ^ 1) << 1) + 1];

            for (col = 7, indx = row * width + col; col < width - 14;
                 col += 8, indx += 8) {
                temp1v = LVFU(src1[indx >> 1]);
                temp2v = LVFU(src2[(indx + 1) >> 1]);
                tempv = _mm_shuffle_ps(temp1v, temp2v, _MM_SHUFFLE(1, 0, 1, 0));
                tempv = _mm_shuffle_ps(tempv, tempv, _MM_SHUFFLE(3, 1, 2, 0));
                _mm_storeu_ps(&green[row][col], vmaxf(tempv, ZEROV));
                temp5v = LVFU(redsrc0[indx >> 1]);
                temp6v = LVFU(redsrc1[(indx + 1) >> 1]);
                temp3v =
                    _mm_shuffle_ps(temp5v, temp6v, _MM_SHUFFLE(1, 0, 1, 0));
                temp3v =
                    _mm_shuffle_ps(temp3v, temp3v, _MM_SHUFFLE(3, 1, 2, 0));
                temp3v = vmaxf(tempv - c65535v * temp3v, ZEROV);
                _mm_storeu_ps(&red[row][col], temp3v);
                temp7v = LVFU(bluesrc0[indx >> 1]);
                temp8v = LVFU(bluesrc1[(indx + 1) >> 1]);
                temp4v =
                    _mm_shuffle_ps(temp7v, temp8v, _MM_SHUFFLE(1, 0, 1, 0));
                temp4v =
                    _mm_shuffle_ps(temp4v, temp4v, _MM_SHUFFLE(3, 1, 2, 0));
                temp4v = vmaxf(tempv - c65535v * temp4v, ZEROV);
                _mm_storeu_ps(&blue[row][col], temp4v);

                tempv = _mm_shuffle_ps(temp1v, temp2v, _MM_SHUFFLE(3, 2, 3, 2));
                tempv = _mm_shuffle_ps(tempv, tempv, _MM_SHUFFLE(3, 1, 2, 0));
                _mm_storeu_ps(&green[row][col + 4], vmaxf(tempv, ZEROV));

                temp3v =
                    _mm_shuffle_ps(temp5v, temp6v, _MM_SHUFFLE(3, 2, 3, 2));
                temp3v =
                    _mm_shuffle_ps(temp3v, temp3v, _MM_SHUFFLE(3, 1, 2, 0));
                temp3v = vmaxf(tempv - c65535v * temp3v, ZEROV);
                _mm_storeu_ps(&red[row][col + 4], temp3v);
                temp4v =
                    _mm_shuffle_ps(temp7v, temp8v, _MM_SHUFFLE(3, 2, 3, 2));
                temp4v =
                    _mm_shuffle_ps(temp4v, temp4v, _MM_SHUFFLE(3, 1, 2, 0));
                temp4v = vmaxf(tempv - c65535v * temp4v, ZEROV);
                _mm_storeu_ps(&blue[row][col + 4], temp4v);
            }

            for (; col < width - 7; col++, indx += 2) {
                red[row][col] = std::max(0.f, src1[indx >> 1] -
                                                  65535.f * redsrc0[indx >> 1]);
                green[row][col] = std::max(0.f, src1[indx >> 1]);
                blue[row][col] = std::max(
                    0.f, src1[indx >> 1] - 65535.f * bluesrc0[indx >> 1]);
                col++;
                red[row][col] =
                    std::max(0.f, src2[(indx + 1) >> 1] -
                                      65535.f * redsrc1[(indx + 1) >> 1]);
                green[row][col] = std::max(0.f, src2[(indx + 1) >> 1]);
                blue[row][col] =
                    std::max(0.f, src2[(indx + 1) >> 1] -
                                      65535.f * bluesrc1[(indx + 1) >> 1]);
            }
        }
    } // End of parallelization
    border_interpolate2(winw, winh, 8, rawData, red, green, blue);

    if (plistener) {
        plistener->setProgress(1.0);
    }

    free(chrarray);
    free(rgbarray);
    free(vdif);
    free(hdif);
}
#else
void RawImageSource::igv_interpolate(int winw, int winh)
{
    static const float eps = 1e-5f,
                       epssq = 1e-5f; // mod epssq -10f =>-5f Jacques 3/2013 to
                                      // prevent artifact (divide by zero)
    static const int h1 = 1, h2 = 2, h3 = 3, h4 = 4, h5 = 5, h6 = 6;
    const int width = winw, height = winh;
    const int v1 = 1 * width, v2 = 2 * width, v3 = 3 * width, v4 = 4 * width,
              v5 = 5 * width, v6 = 6 * width;
    float *rgb[3];
    float *chr[2];
    float *rgbarray, *vdif, *hdif, *chrarray;

    rgbarray = (float(*))calloc(width * height * 3, sizeof(float));
    rgb[0] = rgbarray;
    rgb[1] = rgbarray + (width * height);
    rgb[2] = rgbarray + 2 * (width * height);

    chrarray = (float(*))calloc(width * height * 2, sizeof(float));
    chr[0] = chrarray;
    chr[1] = chrarray + (width * height);

    vdif = (float(*))calloc(width * height / 2, sizeof *vdif);
    hdif = (float(*))calloc(width * height / 2, sizeof *hdif);

    if (plistener) {
        plistener->setProgressStr(
            Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"),
                                   RAWParams::BayerSensor::getMethodString(
                                       RAWParams::BayerSensor::Method::IGV)));
        plistener->setProgress(0.0);
    }

#ifdef _OPENMP
#pragma omp parallel shared(rgb, vdif, hdif, chr)
#endif
    {

        float ng, eg, wg, sg, nv, ev, wv, sv, nwg, neg, swg, seg, nwv, nev, swv,
            sev;

#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 0; row < height - 0; row++)
            for (int col = 0, indx = row * width + col; col < width - 0;
                 col++, indx++) {
                int c = FC(row, col);
                rgb[c][indx] =
                    std::max(0.f, rawData[row][col]); // rawData = RT data
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.13);
            }
        }

#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 5; row < height - 5; row++)
            for (int col = 5 + (FC(row, 1) & 1), indx = row * width + col,
                     c = FC(row, col);
                 col < width - 5; col += 2, indx += 2) {
                // N,E,W,S Gradients
                ng = (eps + (fabsf(rgb[1][indx - v1] - rgb[1][indx - v3]) +
                             fabsf(rgb[c][indx] - rgb[c][indx - v2])) /
                                65535.f);
                ;
                eg = (eps + (fabsf(rgb[1][indx + h1] - rgb[1][indx + h3]) +
                             fabsf(rgb[c][indx] - rgb[c][indx + h2])) /
                                65535.f);
                wg = (eps + (fabsf(rgb[1][indx - h1] - rgb[1][indx - h3]) +
                             fabsf(rgb[c][indx] - rgb[c][indx - h2])) /
                                65535.f);
                sg = (eps + (fabsf(rgb[1][indx + v1] - rgb[1][indx + v3]) +
                             fabsf(rgb[c][indx] - rgb[c][indx + v2])) /
                                65535.f);
                // N,E,W,S High Order Interpolation (Li & Randhawa)
                // N,E,W,S Hamilton Adams Interpolation
                //  (48.f * 65535.f) = 3145680.f
                nv = LIM(
                    ((23.0f * rgb[1][indx - v1] + 23.0f * rgb[1][indx - v3] +
                      rgb[1][indx - v5] + rgb[1][indx + v1] +
                      40.0f * rgb[c][indx] - 32.0f * rgb[c][indx - v2] -
                      8.0f * rgb[c][indx - v4])) /
                        3145680.f,
                    0.0f, 1.0f);
                ev = LIM(
                    ((23.0f * rgb[1][indx + h1] + 23.0f * rgb[1][indx + h3] +
                      rgb[1][indx + h5] + rgb[1][indx - h1] +
                      40.0f * rgb[c][indx] - 32.0f * rgb[c][indx + h2] -
                      8.0f * rgb[c][indx + h4])) /
                        3145680.f,
                    0.0f, 1.0f);
                wv = LIM(
                    ((23.0f * rgb[1][indx - h1] + 23.0f * rgb[1][indx - h3] +
                      rgb[1][indx - h5] + rgb[1][indx + h1] +
                      40.0f * rgb[c][indx] - 32.0f * rgb[c][indx - h2] -
                      8.0f * rgb[c][indx - h4])) /
                        3145680.f,
                    0.0f, 1.0f);
                sv = LIM(
                    ((23.0f * rgb[1][indx + v1] + 23.0f * rgb[1][indx + v3] +
                      rgb[1][indx + v5] + rgb[1][indx - v1] +
                      40.0f * rgb[c][indx] - 32.0f * rgb[c][indx + v2] -
                      8.0f * rgb[c][indx + v4])) /
                        3145680.f,
                    0.0f, 1.0f);
                // Horizontal and vertical color differences
                vdif[indx >> 1] =
                    (sg * nv + ng * sv) / (ng + sg) - (rgb[c][indx]) / 65535.f;
                hdif[indx >> 1] =
                    (wg * ev + eg * wv) / (eg + wg) - (rgb[c][indx]) / 65535.f;
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.26);
            }
        }

#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++)
            for (int col = 7 + (FC(row, 1) & 1), indx = row * width + col,
                     c = FC(row, col), d = c / 2;
                 col < width - 7; col += 2, indx += 2) {
                // H&V integrated gaussian vector over variance on color
                // differences Mod Jacques 3/2013
                ng = LIM(
                    epssq + 78.0f * SQR(vdif[indx >> 1]) +
                        69.0f * (SQR(vdif[(indx - v2) >> 1]) +
                                 SQR(vdif[(indx + v2) >> 1])) +
                        51.0f * (SQR(vdif[(indx - v4) >> 1]) +
                                 SQR(vdif[(indx + v4) >> 1])) +
                        21.0f * (SQR(vdif[(indx - v6) >> 1]) +
                                 SQR(vdif[(indx + v6) >> 1])) -
                        6.0f * SQR(vdif[(indx - v2) >> 1] + vdif[indx >> 1] +
                                   vdif[(indx + v2) >> 1]) -
                        10.0f * (SQR(vdif[(indx - v4) >> 1] +
                                     vdif[(indx - v2) >> 1] + vdif[indx >> 1]) +
                                 SQR(vdif[indx >> 1] + vdif[(indx + v2) >> 1] +
                                     vdif[(indx + v4) >> 1])) -
                        7.0f * (SQR(vdif[(indx - v6) >> 1] +
                                    vdif[(indx - v4) >> 1] +
                                    vdif[(indx - v2) >> 1]) +
                                SQR(vdif[(indx + v2) >> 1] +
                                    vdif[(indx + v4) >> 1] +
                                    vdif[(indx + v6) >> 1])),
                    0.f, 1.f);
                eg = LIM(
                    epssq + 78.0f * SQR(hdif[indx >> 1]) +
                        69.0f * (SQR(hdif[(indx - h2) >> 1]) +
                                 SQR(hdif[(indx + h2) >> 1])) +
                        51.0f * (SQR(hdif[(indx - h4) >> 1]) +
                                 SQR(hdif[(indx + h4) >> 1])) +
                        21.0f * (SQR(hdif[(indx - h6) >> 1]) +
                                 SQR(hdif[(indx + h6) >> 1])) -
                        6.0f * SQR(hdif[(indx - h2) >> 1] + hdif[indx >> 1] +
                                   hdif[(indx + h2) >> 1]) -
                        10.0f * (SQR(hdif[(indx - h4) >> 1] +
                                     hdif[(indx - h2) >> 1] + hdif[indx >> 1]) +
                                 SQR(hdif[indx >> 1] + hdif[(indx + h2) >> 1] +
                                     hdif[(indx + h4) >> 1])) -
                        7.0f * (SQR(hdif[(indx - h6) >> 1] +
                                    hdif[(indx - h4) >> 1] +
                                    hdif[(indx - h2) >> 1]) +
                                SQR(hdif[(indx + h2) >> 1] +
                                    hdif[(indx + h4) >> 1] +
                                    hdif[(indx + h6) >> 1])),
                    0.f, 1.f);
                // Limit chrominance using H/V neighbourhood
                nv = median(0.725f * vdif[indx >> 1] +
                                0.1375f * vdif[(indx - v2) >> 1] +
                                0.1375f * vdif[(indx + v2) >> 1],
                            vdif[(indx - v2) >> 1], vdif[(indx + v2) >> 1]);
                ev = median(0.725f * hdif[indx >> 1] +
                                0.1375f * hdif[(indx - h2) >> 1] +
                                0.1375f * hdif[(indx + h2) >> 1],
                            hdif[(indx - h2) >> 1], hdif[(indx + h2) >> 1]);
                // Chrominance estimation
                chr[d][indx] = (eg * nv + ng * ev) / (ng + eg);
                // Green channel population
                rgb[1][indx] = rgb[c][indx] + 65535.f * chr[d][indx];
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.39);
            }
        }

//  free(vdif); free(hdif);
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row += 2)
            for (int col = 7 + (FC(row, 1) & 1), indx = row * width + col,
                     c = 1 - FC(row, col) / 2;
                 col < width - 7; col += 2, indx += 2) {
                // NW,NE,SW,SE Gradients
                nwg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx - v1 - h1] - chr[c][indx - v3 - h3]) +
                       fabsf(chr[c][indx + v1 + h1] - chr[c][indx - v3 - h3]));
                neg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx - v1 + h1] - chr[c][indx - v3 + h3]) +
                       fabsf(chr[c][indx + v1 - h1] - chr[c][indx - v3 + h3]));
                swg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx + v1 - h1] - chr[c][indx + v3 + h3]) +
                       fabsf(chr[c][indx - v1 + h1] - chr[c][indx + v3 - h3]));
                seg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx + v1 + h1] - chr[c][indx + v3 - h3]) +
                       fabsf(chr[c][indx - v1 - h1] - chr[c][indx + v3 + h3]));
                // Limit NW,NE,SW,SE Color differences
                nwv = median(chr[c][indx - v1 - h1], chr[c][indx - v3 - h1],
                             chr[c][indx - v1 - h3]);
                nev = median(chr[c][indx - v1 + h1], chr[c][indx - v3 + h1],
                             chr[c][indx - v1 + h3]);
                swv = median(chr[c][indx + v1 - h1], chr[c][indx + v3 - h1],
                             chr[c][indx + v1 - h3]);
                sev = median(chr[c][indx + v1 + h1], chr[c][indx + v3 + h1],
                             chr[c][indx + v1 + h3]);
                // Interpolate chrominance: R@B and B@R
                chr[c][indx] = (nwg * nwv + neg * nev + swg * swv + seg * sev) /
                               (nwg + neg + swg + seg);
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.52);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 8; row < height - 7; row += 2)
            for (int col = 7 + (FC(row, 1) & 1), indx = row * width + col,
                     c = 1 - FC(row, col) / 2;
                 col < width - 7; col += 2, indx += 2) {
                // NW,NE,SW,SE Gradients
                nwg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx - v1 - h1] - chr[c][indx - v3 - h3]) +
                       fabsf(chr[c][indx + v1 + h1] - chr[c][indx - v3 - h3]));
                neg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx - v1 + h1] - chr[c][indx - v3 + h3]) +
                       fabsf(chr[c][indx + v1 - h1] - chr[c][indx - v3 + h3]));
                swg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx + v1 - h1] - chr[c][indx + v3 + h3]) +
                       fabsf(chr[c][indx - v1 + h1] - chr[c][indx + v3 - h3]));
                seg = 1.0f /
                      (eps +
                       fabsf(chr[c][indx + v1 + h1] - chr[c][indx + v3 - h3]) +
                       fabsf(chr[c][indx - v1 - h1] - chr[c][indx + v3 + h3]));
                // Limit NW,NE,SW,SE Color differences
                nwv = median(chr[c][indx - v1 - h1], chr[c][indx - v3 - h1],
                             chr[c][indx - v1 - h3]);
                nev = median(chr[c][indx - v1 + h1], chr[c][indx - v3 + h1],
                             chr[c][indx - v1 + h3]);
                swv = median(chr[c][indx + v1 - h1], chr[c][indx + v3 - h1],
                             chr[c][indx + v1 - h3]);
                sev = median(chr[c][indx + v1 + h1], chr[c][indx + v3 + h1],
                             chr[c][indx + v1 + h3]);
                // Interpolate chrominance: R@B and B@R
                chr[c][indx] = (nwg * nwv + neg * nev + swg * swv + seg * sev) /
                               (nwg + neg + swg + seg);
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.65);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++)
            for (int col = 7 + (FC(row, 0) & 1), indx = row * width + col;
                 col < width - 7; col += 2, indx += 2) {
                // N,E,W,S Gradients
                ng =
                    1.0f / (eps + fabsf(chr[0][indx - v1] - chr[0][indx - v3]) +
                            fabsf(chr[0][indx + v1] - chr[0][indx - v3]));
                eg =
                    1.0f / (eps + fabsf(chr[0][indx + h1] - chr[0][indx + h3]) +
                            fabsf(chr[0][indx - h1] - chr[0][indx + h3]));
                wg =
                    1.0f / (eps + fabsf(chr[0][indx - h1] - chr[0][indx - h3]) +
                            fabsf(chr[0][indx + h1] - chr[0][indx - h3]));
                sg =
                    1.0f / (eps + fabsf(chr[0][indx + v1] - chr[0][indx + v3]) +
                            fabsf(chr[0][indx - v1] - chr[0][indx + v3]));
                // Interpolate chrominance: R@G and B@G
                chr[0][indx] =
                    ((ng * chr[0][indx - v1] + eg * chr[0][indx + h1] +
                      wg * chr[0][indx - h1] + sg * chr[0][indx + v1]) /
                     (ng + eg + wg + sg));
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.78);
            }
        }
#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++)
            for (int col = 7 + (FC(row, 0) & 1), indx = row * width + col;
                 col < width - 7; col += 2, indx += 2) {

                // N,E,W,S Gradients
                ng =
                    1.0f / (eps + fabsf(chr[1][indx - v1] - chr[1][indx - v3]) +
                            fabsf(chr[1][indx + v1] - chr[1][indx - v3]));
                eg =
                    1.0f / (eps + fabsf(chr[1][indx + h1] - chr[1][indx + h3]) +
                            fabsf(chr[1][indx - h1] - chr[1][indx + h3]));
                wg =
                    1.0f / (eps + fabsf(chr[1][indx - h1] - chr[1][indx - h3]) +
                            fabsf(chr[1][indx + h1] - chr[1][indx - h3]));
                sg =
                    1.0f / (eps + fabsf(chr[1][indx + v1] - chr[1][indx + v3]) +
                            fabsf(chr[1][indx - v1] - chr[1][indx + v3]));
                // Interpolate chrominance: R@G and B@G
                chr[1][indx] =
                    ((ng * chr[1][indx - v1] + eg * chr[1][indx + h1] +
                      wg * chr[1][indx - h1] + sg * chr[1][indx + v1]) /
                     (ng + eg + wg + sg));
            }

#ifdef _OPENMP
#pragma omp single
#endif
        {
            if (plistener) {
                plistener->setProgress(0.91);
            }

            // Interpolate borders
            //  border_interpolate2(7, rgb);
        }
        /*
#ifdef _OPENMP
        #pragma omp for
#endif
            for (int row=0; row < height; row++)  //borders
                for (int col=0; col < width; col++) {
                    if (col==7 && row >= 7 && row < height-7)
                        col = width-7;
                    int indxc=row*width+col;
                    red  [row][col] = rgb[indxc][0];
                    green[row][col] = rgb[indxc][1];
                    blue [row][col] = rgb[indxc][2];
                }
        */

#ifdef _OPENMP
#pragma omp for
#endif

        for (int row = 7; row < height - 7; row++)
            for (int col = 7, indx = row * width + col; col < width - 7;
                 col++, indx++) {
                red[row][col] =
                    std::max(0.f, rgb[1][indx] - 65535.f * chr[0][indx]);
                green[row][col] = std::max(0.f, rgb[1][indx]);
                blue[row][col] =
                    std::max(0.f, rgb[1][indx] - 65535.f * chr[1][indx]);
            }
    } // End of parallelization
    border_interpolate2(winw, winh, 8, rawData, red, green, blue);

    if (plistener) {
        plistener->setProgress(1.0);
    }

    free(chrarray);
    free(rgbarray);
    free(vdif);
    free(hdif);
}
#endif

void RawImageSource::nodemosaic(bool bw)
{
    red(W, H);
    green(W, H);
    blue(W, H);
#ifdef _OPENMP
#pragma omp parallel for
#endif

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            if (bw) {
                red[i][j] = green[i][j] = blue[i][j] = rawData[i][j];
            } else if (ri->getSensorType() == ST_BAYER) {
                switch (FC(i, j)) {
                case 0:
                    red[i][j] = rawData[i][j];
                    green[i][j] = blue[i][j] = 0;
                    break;

                case 1:
                    green[i][j] = rawData[i][j];
                    red[i][j] = blue[i][j] = 0;
                    break;

                case 2:
                    blue[i][j] = rawData[i][j];
                    red[i][j] = green[i][j] = 0;
                    break;
                }
            } else if (ri->getSensorType() == ST_FUJI_XTRANS) {
                switch (ri->XTRANSFC(i, j)) {
                case 0:
                    red[i][j] = rawData[i][j];
                    green[i][j] = blue[i][j] = 0;
                    break;

                case 1:
                    green[i][j] = rawData[i][j];
                    red[i][j] = blue[i][j] = 0;
                    break;

                case 2:
                    blue[i][j] = rawData[i][j];
                    red[i][j] = green[i][j] = 0;
                    break;
                }
            } else {
                red[i][j] = rawData[i][j * 3 + 0];
                green[i][j] = rawData[i][j * 3 + 1];
                blue[i][j] = rawData[i][j * 3 + 2];
            }
        }
    }
}

/*
 *      Redistribution and use in source and binary forms, with or without
 *      modification, are permitted provided that the following conditions are
 *      met:
 *
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following disclaimer
 *        in the documentation and/or other materials provided with the
 *        distribution.
 *      * Neither the name of the author nor the names of its
 *        contributors may be used to endorse or promote products derived from
 *        this software without specific prior written permission.
 *
 *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *      LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *      A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *      OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *      LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *      DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *      THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *      (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *      OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// If you want to use the code, you need to display name of the original authors
// in your software!

/* DCB demosaicing by Jacek Gozdz (cuniek@kft.umcs.lublin.pl)
 * the code is open source (BSD licence)
 */

#define TILESIZE 192
#define TILEBORDER 10
#define CACHESIZE (TILESIZE + 2 * TILEBORDER)

inline void RawImageSource::dcb_initTileLimits(int &colMin, int &rowMin,
                                               int &colMax, int &rowMax, int x0,
                                               int y0, int border)
{
    rowMin = border;
    colMin = border;
    rowMax = CACHESIZE - border;
    colMax = CACHESIZE - border;

    if (!y0) {
        rowMin = TILEBORDER + border;
    }

    if (!x0) {
        colMin = TILEBORDER + border;
    }

    if (y0 + TILESIZE + TILEBORDER >= H - border) {
        rowMax = TILEBORDER + H - border - y0;
    }

    if (x0 + TILESIZE + TILEBORDER >= W - border) {
        colMax = TILEBORDER + W - border - x0;
    }
}

void RawImageSource::fill_raw(float (*cache)[3], int x0, int y0,
                              float **rawData)
{
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 0);

    for (int row = rowMin, y = y0 - TILEBORDER + rowMin; row < rowMax;
         row++, y++)
        for (int col = colMin, x = x0 - TILEBORDER + colMin,
                 indx = row * CACHESIZE + col;
             col < colMax; col++, x++, indx++) {
            cache[indx][fc(y, x)] = rawData[y][x];
        }
}

void RawImageSource::fill_border(float (*cache)[3], int border, int x0, int y0)
{
    unsigned f;
    float sum[8];
    constexpr unsigned int colors = 3; // used in FORCC

    for (int row = y0; row < y0 + TILESIZE + TILEBORDER && row < H; row++) {
        for (int col = x0; col < x0 + TILESIZE + TILEBORDER && col < W; col++) {
            if (col >= border && col < W - border && row >= border &&
                row < H - border) {
                col = W - border;

                if (col >= x0 + TILESIZE + TILEBORDER) {
                    break;
                }
            }

            memset(sum, 0, sizeof sum);

            for (int y = row - 1; y != row + 2; y++)
                for (int x = col - 1; x != col + 2; x++)
                    if (y < H && y < y0 + TILESIZE + TILEBORDER && x < W &&
                        x < x0 + TILESIZE + TILEBORDER) {
                        f = fc(y, x);
                        sum[f] += cache[(y - y0 + TILEBORDER) * CACHESIZE +
                                        TILEBORDER + x - x0][f];
                        sum[f + 4]++;
                    }

            f = fc(row, col);
            FORCC

            if (c != f && sum[c + 4] > 0) {
                cache[(row - y0 + TILEBORDER) * CACHESIZE + TILEBORDER + col -
                      x0][c] = sum[c] / sum[c + 4];
            }
        }
    }
}

// saves red and blue

// change buffer[3] -> buffer[2],  possibly to buffer[1] if split
// into two loops, one for R and another for B, could also be smaller because
// there is no need for green pixels pass
// this would decrease the amount of needed memory
// from megapixels*2 records to megapixels*0.5
// also don't know if float is needed as data is 1-65536 integer (I believe!!)
// comment from Ingo: float is needed because rawdata in rt is float
void RawImageSource::copy_to_buffer(float (*buffer)[2], float (*image)[3])
{
    for (int indx = 0; indx < CACHESIZE * CACHESIZE; indx++) {
        buffer[indx][0] = image[indx][0]; // R
        buffer[indx][1] = image[indx][2]; // B
    }
}

// restores red and blue

// other comments like in copy_to_buffer
void RawImageSource::restore_from_buffer(float (*image)[3], float (*buffer)[2])
{
    for (int indx = 0; indx < CACHESIZE * CACHESIZE; indx++) {
        image[indx][0] = buffer[indx][0]; // R
        image[indx][2] = buffer[indx][1]; // B
    }
}

// First pass green interpolation

// remove entirely: bufferH and bufferV
void RawImageSource::dcb_hid(float (*image)[3], int x0, int y0)
{
    const int u = CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2);

    // simple green bilinear in R and B pixels
    for (int row = rowMin; row < rowMax; row++)
        for (int col =
                     colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 indx = row * CACHESIZE + col;
             col < colMax; col += 2, indx += 2) {
            assert(indx - u - 1 >= 0 && indx + u + 1 < u * u);

            image[indx][1] = 0.25 * (image[indx - 1][1] + image[indx + 1][1] +
                                     image[indx - u][1] + image[indx + u][1]);
        }
}

// missing colours are interpolated
void RawImageSource::dcb_color(float (*image)[3], int x0, int y0)
{
    const int u = CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 1);

    // red in blue pixel, blue in red pixel
    for (int row = rowMin; row < rowMax; row++)
        for (int col =
                     colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 indx = row * CACHESIZE + col,
                 c = 2 - FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col);
             col < colMax; col += 2, indx += 2) {
            assert(indx >= 0 && indx < u * u && c >= 0 && c < 4);

            // Jacek comment: one multiplication less
            image[indx][c] =
                image[indx][1] +
                (image[indx + u + 1][c] + image[indx + u - 1][c] +
                 image[indx - u + 1][c] + image[indx - u - 1][c] -
                 (image[indx + u + 1][1] + image[indx + u - 1][1] +
                  image[indx - u + 1][1] + image[indx - u - 1][1])) *
                    0.25f;

            /* original
                        image[indx][c] = ( 4.f * image[indx][1]
                                           - image[indx + u + 1][1] - image[indx
               + u - 1][1] - image[indx - u + 1][1] - image[indx - u - 1][1]
                                           + image[indx + u + 1][c] + image[indx
               + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] ) *
               0.25f;
            */
        }

    // red or blue in green pixels
    for (int row = rowMin; row < rowMax; row++)
        for (int col = colMin + (FC(y0 - TILEBORDER + row,
                                    x0 - TILEBORDER + colMin + 1) &
                                 1),
                 indx = row * CACHESIZE + col,
                 c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1),
                 d = 2 - c;
             col < colMax; col += 2, indx += 2) {
            assert(indx >= 0 && indx < u * u && c >= 0 && c < 4);

            // Jacek comment: two multiplications (in total) less
            image[indx][c] =
                image[indx][1] + (image[indx + 1][c] + image[indx - 1][c] -
                                  (image[indx + 1][1] + image[indx - 1][1])) *
                                     0.5f;
            image[indx][d] =
                image[indx][1] + (image[indx + u][d] + image[indx - u][d] -
                                  (image[indx + u][1] + image[indx - u][1])) *
                                     0.5f;

            /* original
                        image[indx][c] = (2.f * image[indx][1] - image[indx +
               1][1] - image[indx - 1][1] + image[indx + 1][c] + image[indx -
               1][c]) * 0.5f; image[indx][d] = (2.f * image[indx][1] -
               image[indx + u][1] - image[indx - u][1] + image[indx + u][d] +
               image[indx - u][d]) * 0.5f;
            */
        }
}

// green correction
void RawImageSource::dcb_hid2(float (*image)[3], int x0, int y0)
{
    const int v = 2 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2);

    for (int row = rowMin; row < rowMax; row++) {
        for (int col =
                     colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 indx = row * CACHESIZE + col,
                 c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col);
             col < colMax; col += 2, indx += 2) {
            assert(indx - v >= 0 && indx + v < CACHESIZE * CACHESIZE);

            // Jacek comment: one multiplication less
            image[indx][1] =
                image[indx][c] + (image[indx + v][1] + image[indx - v][1] +
                                  image[indx - 2][1] + image[indx + 2][1] -
                                  (image[indx + v][c] + image[indx - v][c] +
                                   image[indx - 2][c] + image[indx + 2][c])) *
                                     0.25f;

            /* original
                        image[indx][1] = (image[indx + v][1] + image[indx -
               v][1] + image[indx - 2][1] + image[indx + 2][1]) * 0.25f +
                                         image[indx][c] - ( image[indx + v][c] +
               image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c]) *
               0.25f;
            */
        }
    }
}

// green is used to create
// an interpolation direction map
// 1 = vertical
// 0 = horizontal
// saved in image[][3]

// seems at least 2 persons implemented some code, as this one has different
// coding style, could be unified I don't know if *pix is faster than a loop
// working on image[] directly
void RawImageSource::dcb_map(float (*image)[3], uint8_t *map, int x0, int y0)
{
    const int u = 3 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2);

    for (int row = rowMin; row < rowMax; row++) {
        for (int col = colMin, indx = row * CACHESIZE + col; col < colMax;
             col++, indx++) {
            float *pix = &(image[indx][1]);

            assert(indx >= 0 && indx < u * u);

            // comparing 4 * a to (b+c+d+e) instead of a to (b+c+d+e)/4 is
            // faster because divisions are slow
            if (4 * (*pix) > ((pix[-3] + pix[+3]) + (pix[-u] + pix[+u]))) {
                map[indx] = ((min(pix[-3], pix[+3]) + (pix[-3] + pix[+3])) <
                             (min(pix[-u], pix[+u]) + (pix[-u] + pix[+u])));
            } else {
                map[indx] = ((max(pix[-3], pix[+3]) + (pix[-3] + pix[+3])) >
                             (max(pix[-u], pix[+u]) + (pix[-u] + pix[+u])));
            }
        }
    }
}

// interpolated green pixels are corrected using the map
void RawImageSource::dcb_correction(float (*image)[3], uint8_t *map, int x0,
                                    int y0)
{
    const int u = CACHESIZE, v = 2 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2);

    for (int row = rowMin; row < rowMax; row++) {
        for (int indx =
                 row * CACHESIZE + colMin +
                 (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1);
             indx < row * CACHESIZE + colMax; indx += 2) {
            //        for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 -
            //        TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col;
            //        col < colMax; col += 2, indx += 2) {
            float current = 4 * map[indx] +
                            2 * (map[indx + u] + map[indx - u] + map[indx + 1] +
                                 map[indx - 1]) +
                            map[indx + v] + map[indx - v] + map[indx + 2] +
                            map[indx - 2];

            assert(indx >= 0 && indx < u * u);
            image[indx][1] =
                ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1]) +
                 current * (image[indx - u][1] + image[indx + u][1])) *
                0.03125f;
            //            image[indx][1] = ((16.f - current) * (image[indx -
            //            1][1] + image[indx + 1][1]) * 0.5f + current *
            //            (image[indx - u][1] + image[indx + u][1]) * 0.5f ) *
            //            0.0625f;
        }
    }
}

// R and B smoothing using green contrast, all pixels except 2 pixel wide border

// again code with *pix, is this kind of calculating faster in C, than this what
// was commented?
void RawImageSource::dcb_pp(float (*image)[3], int x0, int y0)
{
    const int u = CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2);

    for (int row = rowMin; row < rowMax; row++)
        for (int col = colMin, indx = row * CACHESIZE + col; col < colMax;
             col++, indx++) {
            //            float r1 = image[indx-1][0] + image[indx+1][0] +
            //            image[indx-u][0] + image[indx+u][0] +
            //            image[indx-u-1][0] + image[indx+u+1][0] +
            //            image[indx-u+1][0] + image[indx+u-1][0]; float g1 =
            //            image[indx-1][1] + image[indx+1][1] + image[indx-u][1]
            //            + image[indx+u][1] + image[indx-u-1][1] +
            //            image[indx+u+1][1] + image[indx-u+1][1] +
            //            image[indx+u-1][1]; float b1 = image[indx-1][2] +
            //            image[indx+1][2] + image[indx-u][2] + image[indx+u][2]
            //            + image[indx-u-1][2] + image[indx+u+1][2] +
            //            image[indx-u+1][2] + image[indx+u-1][2];
            float(*pix)[3] = image + (indx - u - 1);
            float r1 = (*pix)[0];
            float g1 = (*pix)[1];
            float b1 = (*pix)[2];
            pix++;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix++;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix += CACHESIZE - 2;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix += 2;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix += CACHESIZE - 2;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix++;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            pix++;
            r1 += (*pix)[0];
            g1 += (*pix)[1];
            b1 += (*pix)[2];
            r1 *= 0.125f;
            g1 *= 0.125f;
            b1 *= 0.125f;
            r1 += (image[indx][1] - g1);
            b1 += (image[indx][1] - g1);

            assert(indx >= 0 && indx < u * u);
            image[indx][0] = r1;
            image[indx][2] = b1;
        }
}

// interpolated green pixels are corrected using the map
// with correction
void RawImageSource::dcb_correction2(float (*image)[3], uint8_t *map, int x0,
                                     int y0)
{
    const int u = CACHESIZE, v = 2 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 4);

    for (int row = rowMin; row < rowMax; row++) {
        for (int indx =
                     row * CACHESIZE + colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 c = FC(
                     y0 - TILEBORDER + row,
                     x0 - TILEBORDER + colMin +
                         (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) &
                          1));
             indx < row * CACHESIZE + colMax; indx += 2) {
            // map values are uint8_t either 0 or 1. Adding them using integer
            // instructions is perfectly valid and fast. Final result is
            // converted to float then
            float current = 4 * map[indx] +
                            2 * (map[indx + u] + map[indx - u] + map[indx + 1] +
                                 map[indx - 1]) +
                            map[indx + v] + map[indx - v] + map[indx + 2] +
                            map[indx - 2];

            assert(indx >= 0 && indx < u * u);

            // Jacek comment: works now, and has 3 float mults and 9 float adds
            image[indx][1] =
                image[indx][c] +
                ((16.f - current) *
                     (image[indx - 1][1] + image[indx + 1][1] -
                      (image[indx + 2][c] + image[indx - 2][c])) +
                 current * (image[indx - u][1] + image[indx + u][1] -
                            (image[indx + v][c] + image[indx - v][c]))) *
                    0.03125f;

            // 4 float mults and 9 float adds
            // Jacek comment: not mathematically identical to original
            /*            image[indx][1] = 16.f * image[indx][c] +
                                         ((16.f - current) * ((image[indx -
               1][1] + image[indx + 1][1])
                                                              - (image[indx +
               2][c] + image[indx - 2][c]))
                                          + current * ((image[indx - u][1] +
               image[indx + u][1]) - (image[indx + v][c] + image[indx - v][c])))
               * 0.03125f;
            */
            // 7 float mults and 10 float adds
            // original code
            /*
                        image[indx][1] = ((16.f - current) * ((image[indx -
               1][1] + image[indx + 1][1]) * 0.5f
                                                              + image[indx][c] -
               (image[indx + 2][c] + image[indx - 2][c]) * 0.5f)
                                          + current * ((image[indx - u][1] +
               image[indx + u][1]) * 0.5f + image[indx][c] - (image[indx + v][c]
               + image[indx - v][c]) * 0.5f)) * 0.0625f;
            */
        }
    }
}

// image refinement
void RawImageSource::dcb_refinement(float (*image)[3], uint8_t *map, int x0,
                                    int y0)
{
    const int u = CACHESIZE, v = 2 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 4);

    float f0, f1, f2, g1, h0, h1, h2, g2;

    for (int row = rowMin; row < rowMax; row++)
        for (int col =
                     colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 indx = row * CACHESIZE + col,
                 c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col);
             col < colMax; col += 2, indx += 2) {

            float current = 4 * map[indx] +
                            2 * (map[indx + u] + map[indx - u] + map[indx + 1] +
                                 map[indx - 1]) +
                            map[indx + v] + map[indx - v] + map[indx - 2] +
                            map[indx + 2];

            float currPix = image[indx][c];

            f0 = (float)(image[indx - u][1] + image[indx + u][1]) /
                 (1.f + 2.f * currPix);
            f1 =
                2.f * image[indx - u][1] / (1.f + image[indx - v][c] + currPix);
            f2 =
                2.f * image[indx + u][1] / (1.f + image[indx + v][c] + currPix);

            g1 = f0 + f1 + f2;

            h0 = (float)(image[indx - 1][1] + image[indx + 1][1]) /
                 (1.f + 2.f * currPix);
            h1 =
                2.f * image[indx - 1][1] / (1.f + image[indx - 2][c] + currPix);
            h2 =
                2.f * image[indx + 1][1] / (1.f + image[indx + 2][c] + currPix);

            g2 = h0 + h1 + h2;

            // new green value
            assert(indx >= 0 && indx < u * u);
            currPix *= (current * g1 + (16.f - current) * g2) / 48.f;

            // get rid of the overshot pixels
            float minVal =
                min(image[indx - 1][1],
                    min(image[indx + 1][1],
                        min(image[indx - u][1], image[indx + u][1])));
            float maxVal =
                max(image[indx - 1][1],
                    max(image[indx + 1][1],
                        max(image[indx - u][1], image[indx + u][1])));

            image[indx][1] = LIM(currPix, minVal, maxVal);
        }
}

// missing colours are interpolated using high quality algorithm by Luis Sanz
// Rodriguez
void RawImageSource::dcb_color_full(float (*image)[3], int x0, int y0,
                                    float (*chroma)[2])
{
    const int u = CACHESIZE, w = 3 * CACHESIZE;
    int rowMin, colMin, rowMax, colMax;
    dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 3);

    float f[4], g[4];

    for (int row = 1; row < CACHESIZE - 1; row++)
        for (int col = 1 + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + 1) & 1),
                 indx = row * CACHESIZE + col,
                 c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col),
                 d = c / 2;
             col < CACHESIZE - 1; col += 2, indx += 2) {
            assert(indx >= 0 && indx < u * u && c >= 0 && c < 4);
            chroma[indx][d] = image[indx][c] - image[indx][1];
        }

    for (int row = rowMin; row < rowMax; row++)
        for (int col =
                     colMin +
                     (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1),
                 indx = row * CACHESIZE + col,
                 c = 1 - FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col) / 2;
             col < colMax; col += 2, indx += 2) {
            f[0] = 1.f / (float)(1.f +
                                 fabs(chroma[indx - u - 1][c] -
                                      chroma[indx + u + 1][c]) +
                                 fabs(chroma[indx - u - 1][c] -
                                      chroma[indx - w - 3][c]) +
                                 fabs(chroma[indx + u + 1][c] -
                                      chroma[indx - w - 3][c]));
            f[1] = 1.f / (float)(1.f +
                                 fabs(chroma[indx - u + 1][c] -
                                      chroma[indx + u - 1][c]) +
                                 fabs(chroma[indx - u + 1][c] -
                                      chroma[indx - w + 3][c]) +
                                 fabs(chroma[indx + u - 1][c] -
                                      chroma[indx - w + 3][c]));
            f[2] = 1.f / (float)(1.f +
                                 fabs(chroma[indx + u - 1][c] -
                                      chroma[indx - u + 1][c]) +
                                 fabs(chroma[indx + u - 1][c] -
                                      chroma[indx + w + 3][c]) +
                                 fabs(chroma[indx - u + 1][c] -
                                      chroma[indx + w - 3][c]));
            f[3] = 1.f / (float)(1.f +
                                 fabs(chroma[indx + u + 1][c] -
                                      chroma[indx - u - 1][c]) +
                                 fabs(chroma[indx + u + 1][c] -
                                      chroma[indx + w - 3][c]) +
                                 fabs(chroma[indx - u - 1][c] -
                                      chroma[indx + w + 3][c]));
            g[0] = 1.325f * chroma[indx - u - 1][c] -
                   0.175f * chroma[indx - w - 3][c] -
                   0.075f * (chroma[indx - w - 1][c] + chroma[indx - u - 3][c]);
            g[1] = 1.325f * chroma[indx - u + 1][c] -
                   0.175f * chroma[indx - w + 3][c] -
                   0.075f * (chroma[indx - w + 1][c] + chroma[indx - u + 3][c]);
            g[2] = 1.325f * chroma[indx + u - 1][c] -
                   0.175f * chroma[indx + w - 3][c] -
                   0.075f * (chroma[indx + w - 1][c] + chroma[indx + u - 3][c]);
            g[3] = 1.325f * chroma[indx + u + 1][c] -
                   0.175f * chroma[indx + w + 3][c] -
                   0.075f * (chroma[indx + w + 1][c] + chroma[indx + u + 3][c]);

            //            g[0] = 1.325f * chroma[indx - u - 1][c] - 0.175f *
            //            chroma[indx - w - 3][c] - 0.075f * chroma[indx - w -
            //            1][c] - 0.075f * chroma[indx - u - 3][c]; g[1]
            //            = 1.325f * chroma[indx - u + 1][c] - 0.175f *
            //            chroma[indx - w + 3][c] - 0.075f * chroma[indx - w +
            //            1][c] - 0.075f * chroma[indx - u + 3][c]; g[2]
            //            = 1.325f * chroma[indx + u - 1][c] - 0.175f *
            //            chroma[indx + w - 3][c] - 0.075f * chroma[indx + w -
            //            1][c] - 0.075f * chroma[indx + u - 3][c]; g[3]
            //            = 1.325f * chroma[indx + u + 1][c] - 0.175f *
            //            chroma[indx + w + 3][c] - 0.075f * chroma[indx + w +
            //            1][c] - 0.075f * chroma[indx + u + 3][c];

            assert(indx >= 0 && indx < u * u && c >= 0 && c < 2);
            chroma[indx][c] =
                (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
                (f[0] + f[1] + f[2] + f[3]);
        }

    for (int row = rowMin; row < rowMax; row++)
        for (int col = colMin + (FC(y0 - TILEBORDER + row,
                                    x0 - TILEBORDER + colMin + 1) &
                                 1),
                 indx = row * CACHESIZE + col,
                 c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1) / 2;
             col < colMax; col += 2, indx += 2)
            for (int d = 0; d <= 1; c = 1 - c, d++) {
                f[0] = 1.f /
                       (1.f + fabs(chroma[indx - u][c] - chroma[indx + u][c]) +
                        fabs(chroma[indx - u][c] - chroma[indx - w][c]) +
                        fabs(chroma[indx + u][c] - chroma[indx - w][c]));
                f[1] = 1.f /
                       (1.f + fabs(chroma[indx + 1][c] - chroma[indx - 1][c]) +
                        fabs(chroma[indx + 1][c] - chroma[indx + 3][c]) +
                        fabs(chroma[indx - 1][c] - chroma[indx + 3][c]));
                f[2] = 1.f /
                       (1.f + fabs(chroma[indx - 1][c] - chroma[indx + 1][c]) +
                        fabs(chroma[indx - 1][c] - chroma[indx - 3][c]) +
                        fabs(chroma[indx + 1][c] - chroma[indx - 3][c]));
                f[3] = 1.f /
                       (1.f + fabs(chroma[indx + u][c] - chroma[indx - u][c]) +
                        fabs(chroma[indx + u][c] - chroma[indx + w][c]) +
                        fabs(chroma[indx - u][c] - chroma[indx + w][c]));

                g[0] = intp(0.875f, chroma[indx - u][c], chroma[indx - w][c]);
                g[1] = intp(0.875f, chroma[indx + 1][c], chroma[indx + 3][c]);
                g[2] = intp(0.875f, chroma[indx - 1][c], chroma[indx - 3][c]);
                g[3] = intp(0.875f, chroma[indx + u][c], chroma[indx + w][c]);

                //                g[0] = 0.875f * chroma[indx - u][c] + 0.125f *
                //                chroma[indx - w][c]; g[1] = 0.875f *
                //                chroma[indx + 1][c] + 0.125f * chroma[indx +
                //                3][c]; g[2] = 0.875f * chroma[indx - 1][c] +
                //                0.125f * chroma[indx - 3][c]; g[3] = 0.875f *
                //                chroma[indx + u][c] + 0.125f * chroma[indx +
                //                w][c];

                assert(indx >= 0 && indx < u * u && c >= 0 && c < 2);
                chroma[indx][c] =
                    (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) /
                    (f[0] + f[1] + f[2] + f[3]);
            }

    for (int row = rowMin; row < rowMax; row++)
        for (int col = colMin, indx = row * CACHESIZE + col; col < colMax;
             col++, indx++) {
            assert(indx >= 0 && indx < u * u);

            image[indx][0] = chroma[indx][0] + image[indx][1];
            image[indx][2] = chroma[indx][1] + image[indx][1];
        }
}

// DCB demosaicing main routine
void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance)
{
    BENCHFUN
    double currentProgress = 0.0;

    if (plistener) {
        plistener->setProgressStr(
            Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"),
                                   RAWParams::BayerSensor::getMethodString(
                                       RAWParams::BayerSensor::Method::DCB)));
        plistener->setProgress(currentProgress);
    }

    int wTiles = W / TILESIZE + (W % TILESIZE ? 1 : 0);
    int hTiles = H / TILESIZE + (H % TILESIZE ? 1 : 0);
    int numTiles = wTiles * hTiles;
    int tilesDone = 0;
    constexpr int cldf = 2; // factor to multiply cache line distance. 1 = 64
                            // bytes, 2 = 128 bytes ...

#ifdef _OPENMP
#pragma omp parallel
#endif
    {
        // assign working space
        char *buffer0 = (char *)malloc(
            5 * sizeof(float) * CACHESIZE * CACHESIZE +
            sizeof(uint8_t) * CACHESIZE * CACHESIZE + 3 * cldf * 64 + 63);
        // aligned to 64 byte boundary
        char *data = (char *)((uintptr_t(buffer0) + uintptr_t(63)) / 64 * 64);

        float(*tile)[3] = (float(*)[3])data;
        float(*buffer)[2] = (float(*)[2])(
            (char *)tile + sizeof(float) * CACHESIZE * CACHESIZE * 3 +
            cldf * 64);
        float(*chrm)[2] =
            (float(*)[2])(buffer); // No overlap in usage of buffer and chrm
                                   // means we can reuse buffer
        uint8_t *map =
            (uint8_t *)((char *)buffer +
                        sizeof(float) * CACHESIZE * CACHESIZE * 2 + cldf * 64);

#ifdef _OPENMP
#pragma omp for schedule(dynamic) nowait
#endif

        for (int iTile = 0; iTile < numTiles; iTile++) {
            int xTile = iTile % wTiles;
            int yTile = iTile / wTiles;
            int x0 = xTile * TILESIZE;
            int y0 = yTile * TILESIZE;

            memset(tile, 0, CACHESIZE * CACHESIZE * sizeof *tile);
            memset(map, 0, CACHESIZE * CACHESIZE * sizeof *map);

            fill_raw(tile, x0, y0, rawData);

            if (!xTile || !yTile || xTile == wTiles - 1 ||
                yTile == hTiles - 1) {
                fill_border(tile, 6, x0, y0);
            }

            copy_to_buffer(buffer, tile);
            dcb_hid(tile, x0, y0);

            for (int i = iterations; i > 0; i--) {
                dcb_hid2(tile, x0, y0);
                dcb_hid2(tile, x0, y0);
                dcb_hid2(tile, x0, y0);
                dcb_map(tile, map, x0, y0);
                dcb_correction(tile, map, x0, y0);
            }

            dcb_color(tile, x0, y0);
            dcb_pp(tile, x0, y0);
            dcb_map(tile, map, x0, y0);
            dcb_correction2(tile, map, x0, y0);
            dcb_map(tile, map, x0, y0);
            dcb_correction(tile, map, x0, y0);
            dcb_color(tile, x0, y0);
            dcb_map(tile, map, x0, y0);
            dcb_correction(tile, map, x0, y0);
            dcb_map(tile, map, x0, y0);
            dcb_correction(tile, map, x0, y0);
            dcb_map(tile, map, x0, y0);
            restore_from_buffer(tile, buffer);

            if (!dcb_enhance)
                dcb_color(tile, x0, y0);
            else {
                memset(chrm, 0, CACHESIZE * CACHESIZE * sizeof *chrm);
                dcb_refinement(tile, map, x0, y0);
                dcb_color_full(tile, x0, y0, chrm);
            }

            /*
                   dcb_hid(tile, buffer, buffer2, x0, y0);
                   dcb_color(tile, x0, y0);

                   copy_to_buffer(buffer, tile);

                   for (int i = iterations; i > 0; i--) {
                       dcb_hid2(tile, x0, y0);
                       dcb_hid2(tile, x0, y0);
                       dcb_hid2(tile, x0, y0);
                       dcb_map(tile, x0, y0);
                       dcb_correction(tile, x0, y0);
                   }

                   dcb_color(tile, x0, y0);
                   dcb_pp(tile, x0, y0);
                   dcb_map(tile, x0, y0);
                   dcb_correction2(tile, x0, y0);
                   dcb_map(tile, x0, y0);
                   dcb_correction(tile, x0, y0);
                   dcb_color(tile, x0, y0);
                   dcb_map(tile, x0, y0);
                   dcb_correction(tile, x0, y0);
                   dcb_map(tile, x0, y0);
                   dcb_correction(tile, x0, y0);
                   dcb_map(tile, x0, y0);
                   restore_from_buffer(tile, buffer);
                   dcb_color_full(tile, x0, y0, chrm);

                   if (dcb_enhance) {
                       dcb_refinement(tile, x0, y0);
                       dcb_color_full(tile, x0, y0, chrm);
                    }
           */
            for (int y = 0; y < TILESIZE && y0 + y < H; y++) {
                for (int j = 0; j < TILESIZE && x0 + j < W; j++) {
                    red[y0 + y][x0 + j] = std::max(
                        0.f,
                        tile[(y + TILEBORDER) * CACHESIZE + TILEBORDER + j][0]);
                    green[y0 + y][x0 + j] = std::max(
                        0.f,
                        tile[(y + TILEBORDER) * CACHESIZE + TILEBORDER + j][1]);
                    blue[y0 + y][x0 + j] = std::max(
                        0.f,
                        tile[(y + TILEBORDER) * CACHESIZE + TILEBORDER + j][2]);
                }
            }

#ifdef _OPENMP

            if (omp_get_thread_num() == 0)
#endif
            {
                if (plistener &&
                    double(tilesDone) / numTiles > currentProgress) {
                    currentProgress += 0.1; // Show progress each 10%
                    plistener->setProgress(currentProgress);
                }
            }

#ifdef _OPENMP
#pragma omp atomic
#endif
            tilesDone++;
        }
        free(buffer0);
    }

    border_interpolate2(W, H, 1, rawData, red, green, blue);
    if (plistener) {
        plistener->setProgress(1.0);
    }
}

#undef TILEBORDER
#undef TILESIZE
#undef CACHESIZE
} // namespace rtengine
