diff --git a/CLAUDE.md b/CLAUDE.md index b4065ec..a0d3503 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,11 +58,14 @@ ui/player/ MiniPlayer, NowPlayingScreen, QueueScreen, GlassPlayButton, Fi ui/common/ shared pieces: LargeTitleScaffold, SongRow, AlbumCard, Loadable, ArtworkColors, SharedElements, FastScrollBar, AppLocals (cover URLs, playback hooks) ui/theme/ SyncWaveTheme, ArtworkTheme, colors, type -res/mipmap-*/ic_launcher_* app icon from syncwave-icon-1024.png (project root), whole and untrimmed as - the foreground + a brightness mask as monochrome; background #080812. -res/drawable-*/splash_icon.png launch screen (Theme.SyncWave.Starting): same image with the grey - spotlight faded to #080812 so only the wave and its glow show (no "plate"). - Regenerate all these PNGs if the source changes. +res/mipmap-*/ic_launcher_* app icon from "SyncWave Sv2.png" (project root, glossy purple S): whole + image at 94% as the foreground (S inside the 33dp safe zone), edge blended into + background #0E0B14; monochrome = solid S silhouette. +res/drawable-*/splash_icon.png launch screen (Theme.SyncWave.Starting): only the S on #0E0B14 (no + textured square, so the round crop shows no edge). + Regenerate all these PNGs with tools/make_icons.py (needs Pillow + numpy): + py tools/make_icons.py "SyncWave Sv2.png" app/src/main/res + syncwave-icon-1024.png is the previous logo (no longer used). ``` Key rules: diff --git a/SyncWave Sv2.png b/SyncWave Sv2.png new file mode 100644 index 0000000..5fe63f2 Binary files /dev/null and b/SyncWave Sv2.png differ diff --git a/app/src/main/res/drawable-hdpi/splash_icon.png b/app/src/main/res/drawable-hdpi/splash_icon.png index 7a6f70c..ca3336c 100644 Binary files a/app/src/main/res/drawable-hdpi/splash_icon.png and b/app/src/main/res/drawable-hdpi/splash_icon.png differ diff --git a/app/src/main/res/drawable-mdpi/splash_icon.png b/app/src/main/res/drawable-mdpi/splash_icon.png index d149cc3..5d425c8 100644 Binary files a/app/src/main/res/drawable-mdpi/splash_icon.png and b/app/src/main/res/drawable-mdpi/splash_icon.png differ diff --git a/app/src/main/res/drawable-xhdpi/splash_icon.png b/app/src/main/res/drawable-xhdpi/splash_icon.png index f68bc1c..9fa4062 100644 Binary files a/app/src/main/res/drawable-xhdpi/splash_icon.png and b/app/src/main/res/drawable-xhdpi/splash_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/splash_icon.png b/app/src/main/res/drawable-xxhdpi/splash_icon.png index b655b29..f604e42 100644 Binary files a/app/src/main/res/drawable-xxhdpi/splash_icon.png and b/app/src/main/res/drawable-xxhdpi/splash_icon.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/splash_icon.png b/app/src/main/res/drawable-xxxhdpi/splash_icon.png index 30accb2..6a2db81 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/splash_icon.png and b/app/src/main/res/drawable-xxxhdpi/splash_icon.png differ diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml index cf71b8a..899daca 100644 --- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -1,9 +1,10 @@ diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml index cf71b8a..899daca 100644 --- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -1,9 +1,10 @@ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 29066e3..688871f 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png index 528e890..3787345 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index 54e6499..7ab1dc7 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png index d088fdf..5f8c998 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index f991042..4ee6be2 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png index a0afe0b..49ff605 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index e1cde9b..cdb3949 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png index 091eae3..7e3b6fc 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index fcb8d86..b928a71 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png index 8a21cac..f43bead 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9b9bd07..620f30f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,6 +6,6 @@ #FFA693FF #FFFFFFFF #FF000000 - - #FF080812 + + #FF0E0B14 diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 63f7ccc..5be14ae 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -18,8 +18,9 @@ --> diff --git a/tools/make_icons.py b/tools/make_icons.py new file mode 100644 index 0000000..c742920 --- /dev/null +++ b/tools/make_icons.py @@ -0,0 +1,87 @@ +"""Builds the SyncWave launcher icon, themed icon and splash image from the source logo. + +Usage: py tools/make_icons.py "SyncWave Sv2.png" app/src/main/res +Writes mipmap-*/ic_launcher_foreground.png, mipmap-*/ic_launcher_monochrome.png, +drawable-*/splash_icon.png, and icon_preview.png in the preview dir. +""" +import sys, os +import numpy as np +from PIL import Image, ImageDraw, ImageFilter + +src_path, res_dir, preview_dir = sys.argv[1], sys.argv[2], sys.argv[3] +BG = (14, 11, 20) # #0E0B14, average of the logo's outer border +SCALE = 0.94 # S reaches 34.6dp of 54 -> 32.5dp, inside the 33dp safe zone +N = 2048 + +src = Image.open(src_path).convert("RGB") +assert src.size == (N, N), src.size +a = np.asarray(src).astype(np.float32) + +# --- Mask of the S: colourful/bright pixels; then everything not connected to the border +# background counts as S (fills darker spots inside the glass). +mx, mn = a.max(2), a.min(2) +core = ((mx - mn) > 60) & (mx > 70) +m = Image.fromarray(np.where(core, 255, 0).astype(np.uint8)) +m = m.filter(ImageFilter.MaxFilter(9)).filter(ImageFilter.MinFilter(9)) # close small gaps +ImageDraw.floodfill(m, (0, 0), 128) # outside -> 128 +s_mask = Image.fromarray(np.where(np.asarray(m) == 128, 0, 255).astype(np.uint8)) +s_mask = s_mask.filter(ImageFilter.GaussianBlur(2.5)) # soft anti-aliased edge + +# --- 1. Foreground: whole logo at 94% on BG, with its outer edge blended into BG. +def scaled_on_bg(img, alpha=None): + size = round(N * SCALE) + small = img.resize((size, size), Image.LANCZOS) + canvas = Image.new("RGB", (N, N), BG) + off = (N - size) // 2 + if alpha is None: + # Feather the outer 3% of the image into the background. + ramp = np.ones((size, size), np.float32) + f = int(size * 0.03) + lin = np.linspace(0, 1, f, dtype=np.float32) + for i in range(f): + ramp[i, :] = np.minimum(ramp[i, :], lin[i]); ramp[-1 - i, :] = np.minimum(ramp[-1 - i, :], lin[i]) + ramp[:, i] = np.minimum(ramp[:, i], lin[i]); ramp[:, -1 - i] = np.minimum(ramp[:, -1 - i], lin[i]) + alpha = Image.fromarray((ramp * 255).astype(np.uint8)) + else: + alpha = alpha.resize((size, size), Image.LANCZOS) + canvas.paste(small, (off, off), alpha) + return canvas + +foreground = scaled_on_bg(src) + +# --- 2. Monochrome: white S silhouette (Android tints it). +size = round(N * SCALE); off = (N - size) // 2 +mono = Image.new("RGBA", (N, N), (255, 255, 255, 0)) +mono_alpha = Image.new("L", (N, N), 0) +mono_alpha.paste(s_mask.resize((size, size), Image.LANCZOS), (off, off)) +mono.putalpha(mono_alpha) + +# --- 3. Splash: only the glossy S (no textured square) on BG. +splash = scaled_on_bg(src, alpha=s_mask) + +densities = {"mdpi": 108, "hdpi": 162, "xhdpi": 216, "xxhdpi": 324, "xxxhdpi": 432} +for d, px in densities.items(): + foreground.resize((px, px), Image.LANCZOS).save(os.path.join(res_dir, f"mipmap-{d}", "ic_launcher_foreground.png"), optimize=True) + mono.resize((px, px), Image.LANCZOS).save(os.path.join(res_dir, f"mipmap-{d}", "ic_launcher_monochrome.png"), optimize=True) + splash.resize((px, px), Image.LANCZOS).save(os.path.join(res_dir, f"drawable-{d}", "splash_icon.png"), optimize=True) + +# --- Previews: icon in a circle mask (the 72dp visible area), themed icon, splash in its circle. +def circle_preview(img, visible_dp, bg=(255, 255, 255)): + px = 432 + im = img.convert("RGBA").resize((px, px), Image.LANCZOS) + r = px * visible_dp / 108 / 2 + out = Image.new("RGBA", (px, px), bg + (255,)) + circ = Image.new("L", (px * 4, px * 4), 0) + ImageDraw.Draw(circ).ellipse([(px * 2 - r * 4), (px * 2 - r * 4), (px * 2 + r * 4), (px * 2 + r * 4)], fill=255) + out.paste(im, (0, 0), circ.resize((px, px), Image.LANCZOS)) + return out + +themed_bg = Image.new("RGB", (N, N), (40, 34, 60)) +themed_fg = Image.new("RGB", (N, N), (205, 190, 255)) +themed = Image.composite(themed_fg, themed_bg, mono_alpha) +row = [circle_preview(foreground, 72), circle_preview(foreground, 66), circle_preview(themed, 72), circle_preview(splash, 108 * 192 / 288, bg=BG)] +sheet = Image.new("RGB", (432 * 4 + 60, 472), (255, 255, 255)) +for i, p in enumerate(row): + sheet.paste(p, (i * 452, 20), p) +sheet.save(os.path.join(preview_dir, "icon_preview.png")) +print("done")