/*
 * 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 "SkColorMatrixFilter.h"
#include "SkColorSpace.h"
#include "SkColorSpaceXformer.h"
#if SK_SUPPORT_GPU
    #include "GrFragmentProcessor.h"
#endif

static SkScalar byte_to_scale(U8CPU byte) {
    if (0xFF == byte) {
        // want to get this exact
        return 1;
    } else {
        return byte * 0.00392156862745f;
    }
}

// If we can't reduce to a mode filter in MakeLightingFilter(), this is the general case.
// We operate as a matrix color filter, but remember our input colors in case we're asked
// to onMakeColorSpace() a new filter.
class SkLightingColorFilter : public SkColorFilter {
public:
    SkLightingColorFilter(SkColor mul, SkColor add) : fMul(mul), fAdd(add) {
        SkColorMatrix matrix;
        matrix.setScale(byte_to_scale(SkColorGetR(mul)),
                        byte_to_scale(SkColorGetG(mul)),
                        byte_to_scale(SkColorGetB(mul)),
                        1);
        matrix.postTranslate(SkIntToScalar(SkColorGetR(add)),
                             SkIntToScalar(SkColorGetG(add)),
                             SkIntToScalar(SkColorGetB(add)),
                             0);
        fMatrixFilter = SkColorFilter::MakeMatrixFilterRowMajor255(matrix.fMat);
    }

    // Overriding this method is the class' raison d'etre.
    sk_sp<SkColorFilter> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
        SkColor add = xformer->apply(fAdd);
        if (add != fAdd) {
            return sk_make_sp<SkLightingColorFilter>(fMul, add);
        }
        return this->INHERITED::onMakeColorSpace(xformer);
    }

    // Let fMatrixFilter handle all the other calls directly.

    uint32_t getFlags() const override {
        return fMatrixFilter->getFlags();
    }
    bool asColorMatrix(SkScalar matrix[20]) const override {
        return fMatrixFilter->asColorMatrix(matrix);
    }
    void onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
                        bool shaderIsOpaque) const override {
        fMatrixFilter->appendStages(p, cs, alloc, shaderIsOpaque);
    }

    // TODO: might want to remember we're a lighting color filter through serialization?
    void flatten(SkWriteBuffer& buf) const override { return fMatrixFilter->flatten(buf); }
    Factory getFactory() const override             { return fMatrixFilter->getFactory(); }

#if SK_SUPPORT_GPU
    std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
            GrContext* ctx, const GrColorSpaceInfo& csi) const override {
        return fMatrixFilter->asFragmentProcessor(ctx, csi);
    }
#endif

#ifndef SK_IGNORE_TO_STRING
    void toString(SkString* str) const override { fMatrixFilter->toString(str); }
#endif

private:
    SkColor              fMul, fAdd;
    sk_sp<SkColorFilter> fMatrixFilter;

    typedef SkColorFilter INHERITED;
};

sk_sp<SkColorFilter> SkColorMatrixFilter::MakeLightingFilter(SkColor mul, SkColor add) {
    const SkColor opaqueAlphaMask = SK_ColorBLACK;
    // omit the alpha and compare only the RGB values
    if (0 == (add & ~opaqueAlphaMask)) {
        return SkColorFilter::MakeModeFilter(mul | opaqueAlphaMask, SkBlendMode::kModulate);
    }
    return sk_make_sp<SkLightingColorFilter>(mul, add);
}
