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

#include "modules/svg/include/SkSVGMask.h"

#include "include/core/SkCanvas.h"
#include "include/core/SkColorFilter.h"
#include "include/effects/SkLumaColorFilter.h"
#include "modules/svg/include/SkSVGRenderContext.h"

bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) {
    return INHERITED::parseAndSetAttribute(n, v) ||
           this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) ||
           this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) ||
           this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", n, v)) ||
           this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", n, v)) ||
           this->setMaskUnits(
                SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskUnits", n, v)) ||
           this->setMaskContentUnits(
                SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskContentUnits", n, v));
}

SkRect SkSVGMask::bounds(const SkSVGRenderContext& ctx) const {
    return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits);
}

void SkSVGMask::renderMask(const SkSVGRenderContext& ctx) const {
    // https://www.w3.org/TR/SVG11/masking.html#Masking

    // Propagate any inherited properties that may impact mask effect behavior (e.g.
    // color-interpolation). We call this explicitly here because the SkSVGMask
    // nodes do not participate in the normal onRender path, which is when property
    // propagation currently occurs.
    // The local context also restores the filter layer created below on scope exit.
    SkSVGRenderContext lctx(ctx);
    this->onPrepareToRender(&lctx);

    const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation;
    auto ci_filter = (ci == SkSVGColorspace::kLinearRGB)
            ? SkColorFilters::SRGBToLinearGamma()
            : nullptr;

    SkPaint mask_filter;
    mask_filter.setColorFilter(
                SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter)));

    // Mask color filter layer.
    // Note: We could avoid this extra layer if we invert the stacking order
    // (mask/content -> content/mask, kSrcIn -> kDstIn) and apply the filter
    // via the top (mask) layer paint.  That requires deferring mask rendering
    // until after node content, which introduces extra state/complexity.
    // Something to consider if masking performance ever becomes an issue.
    lctx.canvas()->saveLayer(nullptr, &mask_filter);

    const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits);
    lctx.canvas()->translate(obbt.offset.x, obbt.offset.y);
    lctx.canvas()->scale(obbt.scale.x, obbt.scale.y);

    for (const auto& child : fChildren) {
        child->render(lctx);
    }
}
