chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FadingShadow.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.browser_ui.widget;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.view.View;

/**
 * This class draws a variable-sized shadow at the top or bottom edge of a view. This is just like
 * a fading edge, except that the shadow color can have an alpha component, whereas a fading edge
 * color must be opaque.
 */
public class FadingShadow {
    public static final int POSITION_TOP = 0;
    public static final int POSITION_BOTTOM = 1;

    private static final int SMOOTH_ALGORITHM_INTERPOLATION_POINTS_NUM = 8;

    private Paint mShadowPaint = new Paint();
    private Matrix mShadowMatrix = new Matrix();
    private Shader mShadowShader;

    /** @param shadowColor The color of the shadow, e.g. 0x11000000. */
    FadingShadow(int shadowColor) {
        final int n = SMOOTH_ALGORITHM_INTERPOLATION_POINTS_NUM;
        float[] positions = new float[n];
        int[] colors = new int[n];
        int transparentShadowColor = shadowColor & 0x00FFFFFF;
        int shadowAlpha = Color.alpha(shadowColor);

        // Piece-wise linear interpolation of the smooth cubic function below.
        for (int i = 0; i < n; ++i) {
            float x = (float) i / (n - 1);
            // Polynomial computation by Estrin's scheme.
            float value = (1.0f - 2.2f * x) + (1.8f - 0.6f * x) * (x * x);

            positions[i] = x;
            colors[i] = (Math.round(shadowAlpha * value) << 24) | transparentShadowColor;
        }

        mShadowShader = new LinearGradient(0, 0, 0, 1, colors, positions, Shader.TileMode.CLAMP);
    }

    /**
     * Draws a shadow at the top or bottom of a view. This should be called from dispatchDraw() so
     * the shadow is drawn on top of the view's children.
     *
     * @param view The View in which to draw the shadow.
     * @param canvas The canvas on which to draw.
     * @param position Where to draw the shadow: either POSITION_TOP or POSITION_BOTTOM.
     * @param shadowHeight The maximum height of the shadow, in pixels.
     * @param shadowStrength A value between 0 and 1 indicating the relative size of the shadow. 0
     *                       means no shadow at all. 1 means a full height shadow.
     */
    void drawShadow(
            View view, Canvas canvas, int position, float shadowHeight, float shadowStrength) {
        float scaledShadowHeight = Math.max(0.0f, Math.min(1.0f, shadowStrength)) * shadowHeight;
        if (scaledShadowHeight < 1.0f) return;

        int left = view.getScrollX();
        int right = left + view.getRight();

        if (position == POSITION_BOTTOM) {
            int bottom = view.getScrollY() + view.getBottom() - view.getTop();
            mShadowMatrix.setScale(1, scaledShadowHeight);
            mShadowMatrix.postRotate(180);
            mShadowMatrix.postTranslate(left, bottom);
            mShadowShader.setLocalMatrix(mShadowMatrix);
            mShadowPaint.setShader(mShadowShader);
            canvas.drawRect(left, bottom - scaledShadowHeight, right, bottom, mShadowPaint);
        } else if (position == POSITION_TOP) {
            int top = view.getScrollY();
            mShadowMatrix.setScale(1, scaledShadowHeight);
            mShadowMatrix.postTranslate(left, top);
            mShadowShader.setLocalMatrix(mShadowMatrix);
            mShadowPaint.setShader(mShadowShader);
            canvas.drawRect(left, top, right, top + scaledShadowHeight, mShadowPaint);
        }
    }
}