blob: 3579eb7f80b7745a4337839a11e6beb16f26ddf5 [file] [log] [blame]
// Copyright 2018 The Feed Authors.
//
// 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.libraries.feed.piet.ui;
import static com.google.android.libraries.feed.common.Validators.checkState;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.ShapeDrawable;
import androidx.annotation.VisibleForTesting;
import com.google.android.libraries.feed.common.functional.Supplier;
/** Generates a linear gradient according to CSS behavior */
class GradientShader extends ShapeDrawable.ShaderFactory {
private final int[] colors;
private final float[] stops;
@VisibleForTesting final double angleRadians;
@VisibleForTesting final double angleRadiansSmall;
@VisibleForTesting /*@Nullable*/ final Supplier<Boolean> rtLSupplier;
GradientShader(int[] colors, float[] stops, int angle, /*@Nullable*/ Supplier<Boolean> rtLSupplier) {
checkState(
colors.length == stops.length,
"Mismatch: got %s colors and %s stops",
colors.length,
stops.length);
this.colors = colors;
this.stops = stops;
this.angleRadians = Math.toRadians(angle % 360);
this.angleRadiansSmall = Math.toRadians(angle % 90);
this.rtLSupplier = rtLSupplier;
}
@Override
public Shader resize(int width, int height) {
RectF line = calculateGradientLine(width, height, angleRadians, angleRadiansSmall, isRtL());
return new android.graphics.LinearGradient(
line.left, line.top, line.right, line.bottom, colors, stops, TileMode.CLAMP);
}
private boolean isRtL() {
return rtLSupplier != null && rtLSupplier.get();
}
/** Returns a RectF as a shorthand for two points - start coords and end coords */
@VisibleForTesting
static RectF calculateGradientLine(
int width, int height, double angleRadians, double angleRadiansSmall, boolean isRtL) {
// See documentation on how CSS gradients work at:
// https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient
// This is Math. It's kind of like Magic.
// You put numbers in, and the right gradients come out.
// Note that on Android, the Y axis starts at the top and increases to the bottom, so some of
// the math is backward.
double length =
Math.hypot(width / 2.0d, height / 2.0d)
* Math.cos((Math.PI / 2) - Math.atan2(height, width) - angleRadiansSmall);
float rtLmultiplier = isRtL ? -1 : 1;
float startX = (float) ((width / 2.0f) - length * rtLmultiplier * Math.sin(angleRadians));
float endX = (float) ((width / 2.0f) + length * rtLmultiplier * Math.sin(angleRadians));
float startY = (float) ((height / 2.0f) + length * Math.cos(angleRadians));
float endY = (float) ((height / 2.0f) - length * Math.cos(angleRadians));
return new RectF(startX, startY, endX, endY);
}
}