kopia lustrzana https://github.com/ryukoposting/Signal-Android
146 wiersze
5.2 KiB
Java
146 wiersze
5.2 KiB
Java
package org.thoughtcrime.securesms.components;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.LinearGradient;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.Point;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Shader;
|
|
import android.graphics.Xfermode;
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import kotlin.jvm.functions.Function2;
|
|
|
|
/**
|
|
* Drawable which renders a gradient at a specified angle. Note that this drawable does
|
|
* not implement drawable state, and all the baggage that comes with a normal Drawable
|
|
* override, so this may not work in every scenario.
|
|
*
|
|
* Essentially, this drawable creates a LinearGradient shader using the given colors and
|
|
* positions, but makes it larger than the bounds, such that it can be rotated and still
|
|
* fill the bounds with a gradient.
|
|
*
|
|
* If you wish to apply clipping to this drawable, it is recommended to either use it with
|
|
* a CardView or utilize {@link org.thoughtcrime.securesms.util.CustomDrawWrapperKt#customizeOnDraw(Drawable, Function2)}
|
|
*/
|
|
public final class RotatableGradientDrawable extends Drawable {
|
|
|
|
/**
|
|
* From investigation into how Gradients are rendered vs how they are rendered in
|
|
* designs, in order to match spec, we need to rotate gradients by 225 degrees. (180 + 45)
|
|
*
|
|
* This puts 0 at the bottom (0, -1) of the surface area.
|
|
*/
|
|
private static final float DEGREE_OFFSET = 225f;
|
|
|
|
private final float degrees;
|
|
private final int[] colors;
|
|
private final float[] positions;
|
|
|
|
private final Rect fillRect = new Rect();
|
|
private final Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
|
|
|
|
/**
|
|
* @param degrees Gradient rotation in degrees, relative to a vector pointed from the center to bottom center
|
|
* @param colors The colors of the gradient
|
|
* @param positions The positions of the colors. Values should be between 0f and 1f and this array should be the
|
|
* same length as colors.
|
|
*/
|
|
public RotatableGradientDrawable(float degrees, int[] colors, @Nullable float[] positions) {
|
|
this.degrees = degrees + DEGREE_OFFSET;
|
|
this.colors = colors;
|
|
this.positions = positions;
|
|
}
|
|
|
|
@Override
|
|
public void setBounds(int left, int top, int right, int bottom) {
|
|
super.setBounds(left, top, right, bottom);
|
|
|
|
Point topLeft = new Point(left, top);
|
|
Point topRight = new Point(right, top);
|
|
Point bottomLeft = new Point(left, bottom);
|
|
Point bottomRight = new Point(right, bottom);
|
|
Point origin = new Point(getBounds().width() / 2, getBounds().height() / 2);
|
|
|
|
Point rotationTopLeft = cornerPrime(origin, topLeft, degrees);
|
|
Point rotationTopRight = cornerPrime(origin, topRight, degrees);
|
|
Point rotationBottomLeft = cornerPrime(origin, bottomLeft, degrees);
|
|
Point rotationBottomRight = cornerPrime(origin, bottomRight, degrees);
|
|
|
|
fillRect.left = Integer.MAX_VALUE;
|
|
fillRect.top = Integer.MAX_VALUE;
|
|
fillRect.right = Integer.MIN_VALUE;
|
|
fillRect.bottom = Integer.MIN_VALUE;
|
|
|
|
for (Point point : Arrays.asList(topLeft, topRight, bottomLeft, bottomRight, rotationTopLeft, rotationTopRight, rotationBottomLeft, rotationBottomRight)) {
|
|
if (point.x < fillRect.left) {
|
|
fillRect.left = point.x;
|
|
}
|
|
|
|
if (point.x > fillRect.right) {
|
|
fillRect.right = point.x;
|
|
}
|
|
|
|
if (point.y < fillRect.top) {
|
|
fillRect.top = point.y;
|
|
}
|
|
|
|
if (point.y > fillRect.bottom) {
|
|
fillRect.bottom = point.y;
|
|
}
|
|
}
|
|
|
|
fillPaint.setShader(new LinearGradient(fillRect.left, fillRect.top, fillRect.right, fillRect.bottom, colors, positions, Shader.TileMode.CLAMP));
|
|
}
|
|
|
|
public void setXfermode(@NonNull Xfermode xfermode) {
|
|
fillPaint.setXfermode(xfermode);
|
|
}
|
|
|
|
private static Point cornerPrime(@NonNull Point origin, @NonNull Point corner, float degrees) {
|
|
return new Point(xPrime(origin, corner, Math.toRadians(degrees)), yPrime(origin, corner, Math.toRadians(degrees)));
|
|
}
|
|
|
|
private static int xPrime(@NonNull Point origin, @NonNull Point corner, double theta) {
|
|
return (int) Math.ceil(((corner.x - origin.x) * Math.cos(theta)) - ((corner.y - origin.y) * Math.sin(theta)) + origin.x);
|
|
}
|
|
|
|
private static int yPrime(@NonNull Point origin, @NonNull Point corner, double theta) {
|
|
return (int) Math.ceil(((corner.x - origin.x) * Math.sin(theta)) + ((corner.y - origin.y) * Math.cos(theta)) + origin.y);
|
|
}
|
|
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
int save = canvas.save();
|
|
canvas.rotate(degrees, getBounds().width() / 2f, getBounds().height() / 2f);
|
|
|
|
int height = fillRect.height();
|
|
int width = fillRect.width();
|
|
canvas.drawRect(fillRect.left - width, fillRect.top - height, fillRect.right + width, fillRect.bottom + height, fillPaint);
|
|
|
|
canvas.restoreToCount(save);
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
// Not supported
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
|
// Not supported
|
|
}
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return PixelFormat.OPAQUE;
|
|
}
|
|
}
|