/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/effects/SkColorMatrix.h"
#include "src/core/SkYUVMath.h"

enum SkYUVColorSpace : int;

SkColorMatrix SkColorMatrix::RGBtoYUV(SkYUVColorSpace cs) {
    SkColorMatrix m;
    SkColorMatrix_RGB2YUV(cs, m.fMat.data());
    return m;
}

SkColorMatrix SkColorMatrix::YUVtoRGB(SkYUVColorSpace cs) {
    SkColorMatrix m;
    SkColorMatrix_YUV2RGB(cs, m.fMat.data());
    return m;
}

enum {
    kR_Scale = 0,
    kG_Scale = 6,
    kB_Scale = 12,
    kA_Scale = 18,

    kR_Trans = 4,
    kG_Trans = 9,
    kB_Trans = 14,
    kA_Trans = 19,
};

static void set_concat(float result[20], const float outer[20], const float inner[20]) {
    float    tmp[20];
    float*   target;

    if (outer == result || inner == result) {
        target = tmp;   // will memcpy answer when we're done into result
    } else {
        target = result;
    }

    int index = 0;
    for (int j = 0; j < 20; j += 5) {
        for (int i = 0; i < 4; i++) {
            target[index++] =   outer[j + 0] * inner[i + 0] +
                                outer[j + 1] * inner[i + 5] +
                                outer[j + 2] * inner[i + 10] +
                                outer[j + 3] * inner[i + 15];
        }
        target[index++] =   outer[j + 0] * inner[4] +
                            outer[j + 1] * inner[9] +
                            outer[j + 2] * inner[14] +
                            outer[j + 3] * inner[19] +
                            outer[j + 4];
    }

    if (target != result) {
        std::copy_n(target, 20, result);
    }
}

void SkColorMatrix::setIdentity() {
    fMat.fill(0.0f);
    fMat[kR_Scale] = fMat[kG_Scale] = fMat[kB_Scale] = fMat[kA_Scale] = 1;
}

void SkColorMatrix::setScale(float rScale, float gScale, float bScale, float aScale) {
    fMat.fill(0.0f);
    fMat[kR_Scale] = rScale;
    fMat[kG_Scale] = gScale;
    fMat[kB_Scale] = bScale;
    fMat[kA_Scale] = aScale;
}

void SkColorMatrix::postTranslate(float dr, float dg, float db, float da) {
    fMat[kR_Trans] += dr;
    fMat[kG_Trans] += dg;
    fMat[kB_Trans] += db;
    fMat[kA_Trans] += da;
}

///////////////////////////////////////////////////////////////////////////////

void SkColorMatrix::setConcat(const SkColorMatrix& matA, const SkColorMatrix& matB) {
    set_concat(fMat.data(), matA.fMat.data(), matB.fMat.data());
}

///////////////////////////////////////////////////////////////////////////////

static void setrow(float row[], float r, float g, float b) {
    row[0] = r;
    row[1] = g;
    row[2] = b;
}

static const float kHueR = 0.213f;
static const float kHueG = 0.715f;
static const float kHueB = 0.072f;

void SkColorMatrix::setSaturation(float sat) {
    fMat.fill(0.0f);

    const float R = kHueR * (1 - sat);
    const float G = kHueG * (1 - sat);
    const float B = kHueB * (1 - sat);

    setrow(fMat.data() +  0, R + sat, G, B);
    setrow(fMat.data() +  5, R, G + sat, B);
    setrow(fMat.data() + 10, R, G, B + sat);
    fMat[kA_Scale] = 1;
}
