dniz cc73c2a630 Update TODOs
PiperOrigin-RevId: 259535844
2019-07-23 11:21:39 -04:00

179 lines
5.4 KiB
Java

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.material.shadow;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.graphics.Shader;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.ColorUtils;
/**
* A helper class to draw linear or radial shadows using gradient shaders.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
public class ShadowRenderer {
/** Gradient start color of 68 which evaluates to approximately 26% opacity. */
private static final int COLOR_ALPHA_START = 0x44;
/** Gradient start color of 20 which evaluates to approximately 8% opacity. */
private static final int COLOR_ALPHA_MIDDLE = 0x14;
private static final int COLOR_ALPHA_END = 0;
private final Paint shadowPaint;
private final Paint cornerShadowPaint;
private final Paint edgeShadowPaint;
private int shadowStartColor;
private int shadowMiddleColor;
private int shadowEndColor;
private static final int[] edgeColors = new int[3];
/** Start, middle of shadow, and end of shadow positions */
private static final float[] edgePositions = new float[] {0f, .5f, 1f};
private static final int[] cornerColors = new int[4];
/** Start, beginning of corner, middle of shadow, and end of shadow positions */
private static final float[] cornerPositions = new float[] {0f, 0f, .5f, 1f};
private final Path scratch = new Path();
public ShadowRenderer() {
this(Color.BLACK);
}
public ShadowRenderer(int color) {
setShadowColor(color);
cornerShadowPaint = new Paint(Paint.DITHER_FLAG);
cornerShadowPaint.setStyle(Paint.Style.FILL);
shadowPaint = new Paint();
shadowPaint.setColor(shadowStartColor);
edgeShadowPaint = new Paint(cornerShadowPaint);
}
public void setShadowColor(int color) {
shadowStartColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_START);
shadowMiddleColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_MIDDLE);
shadowEndColor = ColorUtils.setAlphaComponent(color, COLOR_ALPHA_END);
}
/** Draws an edge shadow on the canvas in the current bounds with the matrix transform applied. */
public void drawEdgeShadow(Canvas canvas, Matrix transform, RectF bounds, int elevation) {
bounds.bottom += elevation;
bounds.offset(0, -elevation);
edgeColors[0] = shadowEndColor;
edgeColors[1] = shadowMiddleColor;
edgeColors[2] = shadowStartColor;
edgeShadowPaint.setShader(
new LinearGradient(
bounds.left,
bounds.top,
bounds.left,
bounds.bottom,
edgeColors,
edgePositions,
Shader.TileMode.CLAMP));
canvas.save();
canvas.concat(transform);
canvas.drawRect(bounds, edgeShadowPaint);
canvas.restore();
}
/**
* Draws a corner shadow on the canvas in the current bounds with the matrix transform applied.
*/
public void drawCornerShadow(
Canvas canvas,
Matrix matrix,
RectF bounds,
int elevation,
float startAngle,
float sweepAngle) {
boolean drawShadowInsideBounds = sweepAngle < 0;
Path arcBounds = scratch;
if (drawShadowInsideBounds) {
cornerColors[0] = 0;
cornerColors[1] = shadowEndColor;
cornerColors[2] = shadowMiddleColor;
cornerColors[3] = shadowStartColor;
} else {
// Calculate the arc bounds to prevent drawing shadow in the same part of the arc.
arcBounds.rewind();
arcBounds.moveTo(bounds.centerX(), bounds.centerY());
arcBounds.arcTo(bounds, startAngle, sweepAngle);
arcBounds.close();
bounds.inset(-elevation, -elevation);
cornerColors[0] = 0;
cornerColors[1] = shadowStartColor;
cornerColors[2] = shadowMiddleColor;
cornerColors[3] = shadowEndColor;
}
float startRatio = 1f - (elevation / (bounds.width() / 2f));
float midRatio = startRatio + ((1f - startRatio) / 2f);
cornerPositions[1] = startRatio;
cornerPositions[2] = midRatio;
cornerShadowPaint.setShader(
new RadialGradient(
bounds.centerX(),
bounds.centerY(),
bounds.width() / 2,
cornerColors,
cornerPositions,
Shader.TileMode.CLAMP));
// TODO(b/117606382): handle oval bounds by scaling the canvas.
canvas.save();
canvas.concat(matrix);
if (!drawShadowInsideBounds) {
canvas.clipPath(arcBounds, Op.DIFFERENCE);
}
canvas.drawArc(bounds, startAngle, sweepAngle, true, cornerShadowPaint);
canvas.restore();
}
public Paint getShadowPaint() {
return shadowPaint;
}
}