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

#include "GrBlend.h"

static inline GrBlendCoeff swap_coeff_src_dst(GrBlendCoeff coeff) {
    switch (coeff) {
        case kDC_GrBlendCoeff:
            return kSC_GrBlendCoeff;
        case kIDC_GrBlendCoeff:
            return kISC_GrBlendCoeff;
        case kDA_GrBlendCoeff:
            return kSA_GrBlendCoeff;
        case kIDA_GrBlendCoeff:
            return kISA_GrBlendCoeff;
        case kSC_GrBlendCoeff:
            return kDC_GrBlendCoeff;
        case kISC_GrBlendCoeff:
            return kIDC_GrBlendCoeff;
        case kSA_GrBlendCoeff:
            return kDA_GrBlendCoeff;
        case kISA_GrBlendCoeff:
            return kIDA_GrBlendCoeff;
        default:
            return coeff;
    }
}

static inline unsigned saturated_add(unsigned a, unsigned b) {
    SkASSERT(a <= 255);
    SkASSERT(b <= 255);
    unsigned sum = a + b;
    if (sum > 255) {
        sum = 255;
    }
    return sum;
}

static GrColor add_colors(GrColor src, GrColor dst) {
    unsigned r = saturated_add(GrColorUnpackR(src), GrColorUnpackR(dst));
    unsigned g = saturated_add(GrColorUnpackG(src), GrColorUnpackG(dst));
    unsigned b = saturated_add(GrColorUnpackB(src), GrColorUnpackB(dst));
    unsigned a = saturated_add(GrColorUnpackA(src), GrColorUnpackA(dst));
    return GrColorPackRGBA(r, g, b, a);
}

static inline bool valid_color(uint32_t compFlags) {
     return (kRGBA_GrColorComponentFlags & compFlags) == kRGBA_GrColorComponentFlags;
}

static GrColor simplify_blend_term(GrBlendCoeff* srcCoeff,
                                   GrColor srcColor, uint32_t srcCompFlags,
                                   GrColor dstColor, uint32_t dstCompFlags,
                                   GrColor constantColor) {

    SkASSERT(!GrBlendCoeffRefsSrc(*srcCoeff));
    SkASSERT(NULL != srcCoeff);

    // Check whether srcCoeff can be reduced to kOne or kZero based on known color inputs.
    // We could pick out the coeff r,g,b,a values here and use them to compute the blend term color,
    // if possible, below but that is not implemented now.
    switch (*srcCoeff) {
        case kIDC_GrBlendCoeff:
            dstColor = ~dstColor; // fallthrough
        case kDC_GrBlendCoeff:
            if (valid_color(dstCompFlags)) {
                if (0xffffffff == dstColor) {
                    *srcCoeff = kOne_GrBlendCoeff;
                } else if (0 == dstColor) {
                    *srcCoeff = kZero_GrBlendCoeff;
                }
            }
            break;

        case kIDA_GrBlendCoeff:
            dstColor = ~dstColor; // fallthrough
        case kDA_GrBlendCoeff:
            if (kA_GrColorComponentFlag & dstCompFlags) {
                if (0xff == GrColorUnpackA(dstColor)) {
                    *srcCoeff = kOne_GrBlendCoeff;
                } else if (0 == GrColorUnpackA(dstColor)) {
                    *srcCoeff = kZero_GrBlendCoeff;
                }
            }
            break;

        case kIConstC_GrBlendCoeff:
            constantColor = ~constantColor; // fallthrough
        case kConstC_GrBlendCoeff:
            if (0xffffffff == constantColor) {
                *srcCoeff = kOne_GrBlendCoeff;
            } else if (0 == constantColor) {
                *srcCoeff = kZero_GrBlendCoeff;
            }
            break;

        case kIConstA_GrBlendCoeff:
            constantColor = ~constantColor; // fallthrough
        case kConstA_GrBlendCoeff:
            if (0xff == GrColorUnpackA(constantColor)) {
                *srcCoeff = kOne_GrBlendCoeff;
            } else if (0 == GrColorUnpackA(constantColor)) {
                *srcCoeff = kZero_GrBlendCoeff;
            }
            break;

        default:
            break;
    }
    // We may have invalidated these above and shouldn't read them again.
    SkDEBUGCODE(dstColor = constantColor = GrColor_ILLEGAL;)

    if (kZero_GrBlendCoeff == *srcCoeff || (valid_color(srcCompFlags) && 0 == srcColor)) {
        *srcCoeff = kZero_GrBlendCoeff;
        return 0;
    }

    if (kOne_GrBlendCoeff == *srcCoeff && valid_color(srcCompFlags)) {
        return srcColor;
    } else {
        return GrColor_ILLEGAL;
    }
}

GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
                        GrBlendCoeff* dstCoeff,
                        GrColor srcColor, uint32_t srcCompFlags,
                        GrColor dstColor, uint32_t dstCompFlags,
                        GrColor constantColor) {
    GrColor srcTermColor = simplify_blend_term(srcCoeff,
                                               srcColor, srcCompFlags,
                                               dstColor, dstCompFlags,
                                               constantColor);

    // We call the same function to simplify the dst blend coeff. We trick it out by swapping the
    // src and dst.
    GrBlendCoeff spoofedCoeff = swap_coeff_src_dst(*dstCoeff);
    GrColor dstTermColor = simplify_blend_term(&spoofedCoeff,
                                               dstColor, dstCompFlags,
                                               srcColor, srcCompFlags,
                                               constantColor);
    *dstCoeff = swap_coeff_src_dst(spoofedCoeff);

    if (GrColor_ILLEGAL != srcTermColor && GrColor_ILLEGAL != dstTermColor) {
        return add_colors(srcTermColor, dstTermColor);
    } else {
        return GrColor_ILLEGAL;
    }
}