That fuck shit the fascists are using
1package org.tm.archive.components;
2
3import android.graphics.Canvas;
4import android.graphics.ColorFilter;
5import android.graphics.LinearGradient;
6import android.graphics.Paint;
7import android.graphics.PixelFormat;
8import android.graphics.Point;
9import android.graphics.Rect;
10import android.graphics.Shader;
11import android.graphics.Xfermode;
12import android.graphics.drawable.Drawable;
13
14import androidx.annotation.NonNull;
15import androidx.annotation.Nullable;
16
17import java.util.Arrays;
18
19import kotlin.jvm.functions.Function2;
20
21/**
22 * Drawable which renders a gradient at a specified angle. Note that this drawable does
23 * not implement drawable state, and all the baggage that comes with a normal Drawable
24 * override, so this may not work in every scenario.
25 *
26 * Essentially, this drawable creates a LinearGradient shader using the given colors and
27 * positions, but makes it larger than the bounds, such that it can be rotated and still
28 * fill the bounds with a gradient.
29 *
30 * If you wish to apply clipping to this drawable, it is recommended to either use it with
31 * a MaterialCardView or utilize {@link org.tm.archive.util.CustomDrawWrapperKt#customizeOnDraw(Drawable, Function2)}
32 */
33public final class RotatableGradientDrawable extends Drawable {
34
35 /**
36 * From investigation into how Gradients are rendered vs how they are rendered in
37 * designs, in order to match spec, we need to rotate gradients by 225 degrees. (180 + 45)
38 *
39 * This puts 0 at the bottom (0, -1) of the surface area.
40 */
41 private static final float DEGREE_OFFSET = 225f;
42
43 private final float degrees;
44 private final int[] colors;
45 private final float[] positions;
46
47 private final Rect fillRect = new Rect();
48 private final Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
49
50 /**
51 * @param degrees Gradient rotation in degrees, relative to a vector pointed from the center to bottom center
52 * @param colors The colors of the gradient
53 * @param positions The positions of the colors. Values should be between 0f and 1f and this array should be the
54 * same length as colors.
55 */
56 public RotatableGradientDrawable(float degrees, int[] colors, @Nullable float[] positions) {
57 this.degrees = degrees + DEGREE_OFFSET;
58 this.colors = colors;
59 this.positions = positions;
60 }
61
62 @Override
63 public void setBounds(int left, int top, int right, int bottom) {
64 super.setBounds(left, top, right, bottom);
65
66 Point topLeft = new Point(left, top);
67 Point topRight = new Point(right, top);
68 Point bottomLeft = new Point(left, bottom);
69 Point bottomRight = new Point(right, bottom);
70 Point origin = new Point(getBounds().width() / 2, getBounds().height() / 2);
71
72 Point rotationTopLeft = cornerPrime(origin, topLeft, degrees);
73 Point rotationTopRight = cornerPrime(origin, topRight, degrees);
74 Point rotationBottomLeft = cornerPrime(origin, bottomLeft, degrees);
75 Point rotationBottomRight = cornerPrime(origin, bottomRight, degrees);
76
77 fillRect.left = Integer.MAX_VALUE;
78 fillRect.top = Integer.MAX_VALUE;
79 fillRect.right = Integer.MIN_VALUE;
80 fillRect.bottom = Integer.MIN_VALUE;
81
82 for (Point point : Arrays.asList(topLeft, topRight, bottomLeft, bottomRight, rotationTopLeft, rotationTopRight, rotationBottomLeft, rotationBottomRight)) {
83 if (point.x < fillRect.left) {
84 fillRect.left = point.x;
85 }
86
87 if (point.x > fillRect.right) {
88 fillRect.right = point.x;
89 }
90
91 if (point.y < fillRect.top) {
92 fillRect.top = point.y;
93 }
94
95 if (point.y > fillRect.bottom) {
96 fillRect.bottom = point.y;
97 }
98 }
99
100 fillPaint.setShader(new LinearGradient(fillRect.left, fillRect.top, fillRect.right, fillRect.bottom, colors, positions, Shader.TileMode.CLAMP));
101 }
102
103 public void setXfermode(@NonNull Xfermode xfermode) {
104 fillPaint.setXfermode(xfermode);
105 }
106
107 private static Point cornerPrime(@NonNull Point origin, @NonNull Point corner, float degrees) {
108 return new Point(xPrime(origin, corner, Math.toRadians(degrees)), yPrime(origin, corner, Math.toRadians(degrees)));
109 }
110
111 private static int xPrime(@NonNull Point origin, @NonNull Point corner, double theta) {
112 return (int) Math.ceil(((corner.x - origin.x) * Math.cos(theta)) - ((corner.y - origin.y) * Math.sin(theta)) + origin.x);
113 }
114
115 private static int yPrime(@NonNull Point origin, @NonNull Point corner, double theta) {
116 return (int) Math.ceil(((corner.x - origin.x) * Math.sin(theta)) + ((corner.y - origin.y) * Math.cos(theta)) + origin.y);
117 }
118
119 @Override
120 public void draw(Canvas canvas) {
121 int save = canvas.save();
122 canvas.rotate(degrees, getBounds().width() / 2f, getBounds().height() / 2f);
123
124 int height = fillRect.height();
125 int width = fillRect.width();
126 canvas.drawRect(fillRect.left - width, fillRect.top - height, fillRect.right + width, fillRect.bottom + height, fillPaint);
127
128 canvas.restoreToCount(save);
129 }
130
131 @Override
132 public void setAlpha(int alpha) {
133 // Not supported
134 }
135
136 @Override
137 public void setColorFilter(@Nullable ColorFilter colorFilter) {
138 // Not supported
139 }
140
141 @Override
142 public int getOpacity() {
143 return PixelFormat.OPAQUE;
144 }
145}