Sponsor heart: airbrushed multicolor blend matching sponsor.png
Replace the hard gloss streak with soft overlapping color blobs (violet top-left, blue center, orange/coral right and bottom) that drift slightly for a subtle shimmer, matching the GhostCloud sponsor.png reference.
This commit is contained in:
parent
e9e9711f0b
commit
e2f2ceb26d
1 changed files with 49 additions and 85 deletions
|
|
@ -1,9 +1,7 @@
|
|||
package tw.nekomimi.nekogram.helpers;
|
||||
|
||||
import android.graphics.BlurMaskFilter;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
|
|
@ -17,9 +15,10 @@ import androidx.annotation.NonNull;
|
|||
import org.telegram.messenger.AndroidUtilities;
|
||||
|
||||
/**
|
||||
* A glossy 3D-looking heart badge with a soft multicolor gradient (purple →
|
||||
* blue → orange) and a thin curved glossy streak ("blik") that slowly sweeps
|
||||
* diagonally across it, like light reflecting off a shiny sticker.
|
||||
* A soft, airbrushed 3D heart badge matching the GhostCloud "sponsor.png":
|
||||
* violet in the upper-left, blue through the centre, warm orange/coral on the
|
||||
* right and bottom, all blended smoothly with a gentle glossy highlight. The
|
||||
* colour blobs drift slightly so the badge subtly shimmers.
|
||||
*
|
||||
* It self-invalidates each frame, so when attached to a view via
|
||||
* {@code setRightDrawable(...)} / {@code setRightDrawable2(...)} the host keeps
|
||||
|
|
@ -29,17 +28,14 @@ import org.telegram.messenger.AndroidUtilities;
|
|||
public class ShimmerHeartDrawable extends Drawable {
|
||||
|
||||
private final Paint basePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint tintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint shadePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint glossDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint streakPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint blobPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint highlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Path heart = new Path();
|
||||
private final Path streak = new Path();
|
||||
|
||||
private int lastWidth = -1;
|
||||
private int lastHeight = -1;
|
||||
|
||||
private static final long CYCLE_MS = 3600L;
|
||||
private static final long CYCLE_MS = 5200L;
|
||||
private final int size;
|
||||
|
||||
public ShimmerHeartDrawable() {
|
||||
|
|
@ -49,11 +45,8 @@ public class ShimmerHeartDrawable extends Drawable {
|
|||
public ShimmerHeartDrawable(int sizePx) {
|
||||
this.size = sizePx;
|
||||
basePaint.setStyle(Paint.Style.FILL);
|
||||
tintPaint.setStyle(Paint.Style.FILL);
|
||||
shadePaint.setStyle(Paint.Style.FILL);
|
||||
glossDotPaint.setStyle(Paint.Style.FILL);
|
||||
streakPaint.setStyle(Paint.Style.STROKE);
|
||||
streakPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
blobPaint.setStyle(Paint.Style.FILL);
|
||||
highlightPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
private void buildHeart(Rect b) {
|
||||
|
|
@ -71,50 +64,22 @@ public class ShimmerHeartDrawable extends Drawable {
|
|||
heart.close();
|
||||
}
|
||||
|
||||
private void buildShaders(Rect b) {
|
||||
float w = b.width();
|
||||
float h = b.height();
|
||||
float l = b.left;
|
||||
float t = b.top;
|
||||
|
||||
// Base diagonal gradient matching the reference: violet (top-left) →
|
||||
// blue → cyan → warm orange (bottom-right).
|
||||
basePaint.setShader(new LinearGradient(
|
||||
l + w * 0.15f, t, l + w * 0.9f, t + h,
|
||||
new int[]{0xFF8E2DE2, 0xFF5B5BFF, 0xFF2F9BFF, 0xFFFFB24D, 0xFFFF8A3D},
|
||||
new float[]{0f, 0.30f, 0.55f, 0.85f, 1f},
|
||||
/** A soft circular blob of one colour fading to transparent. */
|
||||
private void drawBlob(Canvas canvas, float cx, float cy, float r, int color) {
|
||||
blobPaint.setShader(new RadialGradient(
|
||||
cx, cy, Math.max(1f, r),
|
||||
new int[]{color, color & 0x00FFFFFF},
|
||||
new float[]{0f, 1f},
|
||||
Shader.TileMode.CLAMP));
|
||||
|
||||
// Soft violet glow on the upper-left lobe for depth.
|
||||
tintPaint.setShader(new RadialGradient(
|
||||
l + w * 0.32f, t + h * 0.28f, w * 0.6f,
|
||||
new int[]{0x999B30FF, 0x00000000},
|
||||
null, Shader.TileMode.CLAMP));
|
||||
|
||||
// Inner shade at the bottom tip for a rounded 3D feel.
|
||||
shadePaint.setShader(new RadialGradient(
|
||||
l + w * 0.55f, t + h * 0.92f, w * 0.55f,
|
||||
new int[]{0x55401E7A, 0x00000000},
|
||||
null, Shader.TileMode.CLAMP));
|
||||
|
||||
// Fixed small specular dot, top-left (glossy sticker look).
|
||||
glossDotPaint.setShader(new RadialGradient(
|
||||
l + w * 0.33f, t + h * 0.27f, w * 0.16f,
|
||||
new int[]{0xE6FFFFFF, 0x00FFFFFF},
|
||||
null, Shader.TileMode.CLAMP));
|
||||
|
||||
streakPaint.setStrokeWidth(Math.max(1f, w * 0.10f));
|
||||
streakPaint.setMaskFilter(new BlurMaskFilter(Math.max(1f, w * 0.06f), BlurMaskFilter.Blur.NORMAL));
|
||||
|
||||
lastWidth = b.width();
|
||||
lastHeight = b.height();
|
||||
canvas.drawRect(getBounds(), blobPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(@NonNull Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
buildHeart(bounds);
|
||||
buildShaders(bounds);
|
||||
lastWidth = bounds.width();
|
||||
lastHeight = bounds.height();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -125,7 +90,8 @@ public class ShimmerHeartDrawable extends Drawable {
|
|||
}
|
||||
if (lastWidth != b.width() || lastHeight != b.height()) {
|
||||
buildHeart(b);
|
||||
buildShaders(b);
|
||||
lastWidth = b.width();
|
||||
lastHeight = b.height();
|
||||
}
|
||||
|
||||
float w = b.width();
|
||||
|
|
@ -133,43 +99,41 @@ public class ShimmerHeartDrawable extends Drawable {
|
|||
float l = b.left;
|
||||
float t = b.top;
|
||||
|
||||
// Subtle drift so the colours softly shimmer.
|
||||
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
|
||||
double a = phase * 2 * Math.PI;
|
||||
float dx = (float) Math.cos(a) * w * 0.05f;
|
||||
float dy = (float) Math.sin(a) * h * 0.05f;
|
||||
|
||||
int save = canvas.save();
|
||||
canvas.clipPath(heart);
|
||||
|
||||
// Base colors + violet glow + bottom shade.
|
||||
canvas.drawPath(heart, basePaint);
|
||||
canvas.drawPath(heart, tintPaint);
|
||||
canvas.drawPath(heart, shadePaint);
|
||||
// Light lavender base so blends stay airy.
|
||||
basePaint.setShader(null);
|
||||
basePaint.setColor(0xFFB9A8F0);
|
||||
canvas.drawRect(b, basePaint);
|
||||
|
||||
// Moving curved glossy streak sweeping diagonally across the heart.
|
||||
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
|
||||
// travel from top-left (off-screen) to bottom-right (off-screen)
|
||||
float p = -0.4f + phase * 1.8f;
|
||||
float dx = w * p; // horizontal offset of the streak
|
||||
streak.reset();
|
||||
// A gently curved (bowed) vertical-ish line, slanted diagonally.
|
||||
float x0 = l + dx + w * 0.10f;
|
||||
float y0 = t - h * 0.10f;
|
||||
float xc = l + dx + w * 0.45f;
|
||||
float yc = t + h * 0.50f;
|
||||
float x1 = l + dx + w * 0.30f;
|
||||
float y1 = t + h * 1.10f;
|
||||
streak.moveTo(x0, y0);
|
||||
streak.quadTo(xc, yc, x1, y1);
|
||||
// Soft overlapping colour blobs (airbrushed look).
|
||||
// Violet — upper-left.
|
||||
drawBlob(canvas, l + w * (0.30f) + dx, t + h * (0.28f) + dy, w * 0.62f, 0xFF7A2FE0);
|
||||
// Blue — centre-left.
|
||||
drawBlob(canvas, l + w * (0.40f) - dx, t + h * (0.55f) + dy, w * 0.55f, 0xFF4F6BFF);
|
||||
// Cyan/blue glow — centre.
|
||||
drawBlob(canvas, l + w * (0.52f) + dy, t + h * (0.45f) - dx, w * 0.42f, 0xCC59B7FF);
|
||||
// Warm orange — right.
|
||||
drawBlob(canvas, l + w * (0.82f) - dx, t + h * (0.40f) - dy, w * 0.60f, 0xFFFF9A3D);
|
||||
// Coral/pink — bottom-right.
|
||||
drawBlob(canvas, l + w * (0.70f) + dx, t + h * (0.80f) + dy, w * 0.55f, 0xFFFF7E5A);
|
||||
// Deep violet — bottom-left corner for contrast.
|
||||
drawBlob(canvas, l + w * (0.22f) + dx, t + h * (0.82f) - dy, w * 0.45f, 0xCC6A2BC8);
|
||||
|
||||
// Fade the streak in/out at the edges of the sweep.
|
||||
float edge = Math.min(1f, Math.min(phase, 1f - phase) * 3f);
|
||||
int alpha = (int) (200 * Math.max(0f, edge));
|
||||
int whiteCore = (alpha << 24) | 0x00FFFFFF;
|
||||
streakPaint.setShader(new LinearGradient(
|
||||
x0, y0, x1, y1,
|
||||
new int[]{0x00FFFFFF, whiteCore, 0x00FFFFFF},
|
||||
new float[]{0f, 0.5f, 1f},
|
||||
// Gentle glossy highlight, upper-centre.
|
||||
highlightPaint.setShader(new RadialGradient(
|
||||
l + w * 0.45f + dx, t + h * 0.30f + dy, w * 0.40f,
|
||||
new int[]{0x80FFFFFF, 0x00FFFFFF},
|
||||
new float[]{0f, 1f},
|
||||
Shader.TileMode.CLAMP));
|
||||
canvas.drawPath(streak, streakPaint);
|
||||
|
||||
// Fixed specular dot.
|
||||
canvas.drawPath(heart, glossDotPaint);
|
||||
canvas.drawRect(b, highlightPaint);
|
||||
|
||||
canvas.restoreToCount(save);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue