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

#include "SkPaint.h"
#include "SkPathEffect.h"
#include "SkPictureContentInfo.h"

bool SkPictureContentInfo::suitableForGpuRasterization(GrContext* context, const char **reason,
                                                       int sampleCount) const {
    // TODO: the heuristic used here needs to be refined
    static const int kNumPaintWithPathEffectUsesTol = 1;
    static const int kNumAAConcavePaths = 5;

    SkASSERT(fNumAAHairlineConcavePaths <= fNumAAConcavePaths);

    int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
                                  fNumFastPathDashEffects;

    bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
                           (numNonDashedPathEffects < kNumPaintWithPathEffectUsesTol
                            && 0 == sampleCount);

    bool ret = suitableForDash &&
                    (fNumAAConcavePaths - fNumAAHairlineConcavePaths - fNumAADFEligibleConcavePaths)
                    < kNumAAConcavePaths;
    if (!ret && reason) {
        if (!suitableForDash) {
            if (0 != sampleCount) {
                *reason = "Can't use multisample on dash effect.";
            } else {
                *reason = "Too many non dashed path effects.";
            }
        } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths - fNumAADFEligibleConcavePaths)
                    >= kNumAAConcavePaths) {
            *reason = "Too many anti-aliased concave paths.";
        } else {
            *reason = "Unknown reason for GPU unsuitability.";
        }
    }
    return ret;
}

void SkPictureContentInfo::onDrawPoints(size_t count, const SkPaint& paint) {
    if (paint.getPathEffect() != nullptr) {
        SkPathEffect::DashInfo info;
        SkPathEffect::DashType dashType = paint.getPathEffect()->asADash(&info);
        if (2 == count && SkPaint::kRound_Cap != paint.getStrokeCap() &&
            SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
            ++fNumFastPathDashEffects;
        }
    }
}

void SkPictureContentInfo::onDrawPath(const SkPath& path, const SkPaint& paint) {
    if (paint.isAntiAlias() && !path.isConvex()) {
        ++fNumAAConcavePaths;

        SkPaint::Style paintStyle = paint.getStyle();
        const SkRect& pathBounds = path.getBounds();
        if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
            ++fNumAAHairlineConcavePaths;
        } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
                   pathBounds.height() < 64.f && !path.isVolatile()) {
            ++fNumAADFEligibleConcavePaths;
        }
    }
}

void SkPictureContentInfo::onAddPaintPtr(const SkPaint* paint) {
    if (paint && paint->getPathEffect()) {
        ++fNumPaintWithPathEffectUses;
    }
}

void SkPictureContentInfo::onSaveLayer() {
    *fSaveStack.append() = kSaveLayer_Flag;
}

void SkPictureContentInfo::onSave() {
    *fSaveStack.append() = kSave_Flag;
}

void SkPictureContentInfo::onRestore() {
    SkASSERT(fSaveStack.count() > 0);

    bool containedSaveLayer = fSaveStack.top() & kContainedSaveLayer_Flag;

    if (fSaveStack.top() & kSaveLayer_Flag) {
        ++fNumLayers;
        if (containedSaveLayer) {
            ++fNumInteriorLayers;
        } else {
            ++fNumLeafLayers;
        }
        containedSaveLayer = true;
    }

    fSaveStack.pop();

    if (containedSaveLayer && fSaveStack.count() > 0) {
        fSaveStack.top() |= kContainedSaveLayer_Flag;
    }
}

void SkPictureContentInfo::rescindLastSave() {
    SkASSERT(fSaveStack.count() > 0);
    SkASSERT(fSaveStack.top() & kSave_Flag);

    bool containedSaveLayer = fSaveStack.top() & kContainedSaveLayer_Flag;

    fSaveStack.pop();

    if (containedSaveLayer && fSaveStack.count() > 0) {
        fSaveStack.top() |= kContainedSaveLayer_Flag;
    }
}

void SkPictureContentInfo::rescindLastSaveLayer() {
    SkASSERT(fSaveStack.count() > 0);
    SkASSERT(fSaveStack.top() & kSaveLayer_Flag);

    bool containedSaveLayer = fSaveStack.top() & kContainedSaveLayer_Flag;

    fSaveStack.pop();

    if (containedSaveLayer && fSaveStack.count() > 0) {
        fSaveStack.top() |= kContainedSaveLayer_Flag;
    }
}

void SkPictureContentInfo::set(const SkPictureContentInfo& src) {
    fNumOperations = src.fNumOperations;
    fNumTexts = src.fNumTexts;
    fNumPaintWithPathEffectUses = src.fNumPaintWithPathEffectUses;
    fNumFastPathDashEffects = src.fNumFastPathDashEffects;
    fNumAAConcavePaths = src.fNumAAConcavePaths;
    fNumAAHairlineConcavePaths = src.fNumAAHairlineConcavePaths;
    fNumAADFEligibleConcavePaths = src.fNumAADFEligibleConcavePaths;
    fNumLayers = src.fNumLayers;
    fNumInteriorLayers = src.fNumInteriorLayers;
    fNumLeafLayers = src.fNumLeafLayers;
    fSaveStack = src.fSaveStack;
}

void SkPictureContentInfo::reset() {
    fNumOperations = 0;
    fNumTexts = 0;
    fNumPaintWithPathEffectUses = 0;
    fNumFastPathDashEffects = 0;
    fNumAAConcavePaths = 0;
    fNumAAHairlineConcavePaths = 0;
    fNumAADFEligibleConcavePaths = 0;
    fNumLayers = 0;
    fNumInteriorLayers = 0;
    fNumLeafLayers = 0;
    fSaveStack.rewind();
}

void SkPictureContentInfo::swap(SkPictureContentInfo* other) {
    SkTSwap(fNumOperations, other->fNumOperations);
    SkTSwap(fNumTexts, other->fNumTexts);
    SkTSwap(fNumPaintWithPathEffectUses, other->fNumPaintWithPathEffectUses);
    SkTSwap(fNumFastPathDashEffects, other->fNumFastPathDashEffects);
    SkTSwap(fNumAAConcavePaths, other->fNumAAConcavePaths);
    SkTSwap(fNumAAHairlineConcavePaths, other->fNumAAHairlineConcavePaths);
    SkTSwap(fNumAADFEligibleConcavePaths, other->fNumAADFEligibleConcavePaths);
    SkTSwap(fNumLayers, other->fNumLayers);
    SkTSwap(fNumInteriorLayers, other->fNumInteriorLayers);
    SkTSwap(fNumLeafLayers, other->fNumLeafLayers);
    fSaveStack.swap(other->fSaveStack);
}