feat(release): Packaging für Zorin OS 18 (.deb & .AppImage), In-App Gitea Updater und CI/CD Workflow
Build & Publish Release / Build Packages & Publish to Gitea (push) Successful in 5m26s
Build & Publish Release / Build Packages & Publish to Gitea (push) Successful in 5m26s
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Erzeugt ein 256x256 PNG-Icon für ink-notes mit Python Standard-Bibliothek (ohne externe Module).
|
||||
"""
|
||||
|
||||
import math
|
||||
import os
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
WIDTH = 256
|
||||
HEIGHT = 256
|
||||
|
||||
|
||||
def make_png(width, height, rgba_data):
|
||||
def chunk(tag, data):
|
||||
return (
|
||||
struct.pack(">I", len(data))
|
||||
+ tag
|
||||
+ data
|
||||
+ struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF)
|
||||
)
|
||||
|
||||
header = b"\x89PNG\r\n\x1a\n"
|
||||
ihdr = chunk(tag=b"IHDR", data=struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0))
|
||||
|
||||
raw = bytearray()
|
||||
for y in range(height):
|
||||
raw.append(0) # Filter type 0 (None)
|
||||
raw.extend(rgba_data[y * width * 4 : (y + 1) * width * 4])
|
||||
|
||||
idat = chunk(tag=b"IDAT", data=zlib.compress(bytes(raw), 9))
|
||||
iend = chunk(tag=b"IEND", data=b"")
|
||||
return header + ihdr + idat + iend
|
||||
|
||||
|
||||
def render_icon():
|
||||
buffer = bytearray(WIDTH * HEIGHT * 4)
|
||||
|
||||
def set_pixel(x, y, r, g, b, a):
|
||||
if 0 <= x < WIDTH and 0 <= y < HEIGHT:
|
||||
idx = (y * WIDTH + x) * 4
|
||||
src_a = a / 255.0
|
||||
dst_a = buffer[idx + 3] / 255.0
|
||||
out_a = src_a + dst_a * (1.0 - src_a)
|
||||
if out_a > 1e-5:
|
||||
out_r = (r * src_a + buffer[idx] * dst_a * (1.0 - src_a)) / out_a
|
||||
out_g = (
|
||||
g * src_a + buffer[idx + 1] * dst_a * (1.0 - src_a)
|
||||
) / out_a
|
||||
out_b = (
|
||||
b * src_a + buffer[idx + 2] * dst_a * (1.0 - src_a)
|
||||
) / out_a
|
||||
buffer[idx] = int(round(out_r))
|
||||
buffer[idx + 1] = int(round(out_g))
|
||||
buffer[idx + 2] = int(round(out_b))
|
||||
buffer[idx + 3] = int(round(out_a * 255.0))
|
||||
|
||||
# 1. Abgerundete Squircle-Kachel (Zorin OS Stil)
|
||||
cx, cy = WIDTH / 2.0, HEIGHT / 2.0
|
||||
half_size = 114.0
|
||||
radius = 52.0
|
||||
|
||||
for y in range(HEIGHT):
|
||||
for x in range(WIDTH):
|
||||
dx = abs(x - cx)
|
||||
dy = abs(y - cy)
|
||||
# Distance from rounded box
|
||||
qx = max(0.0, dx - (half_size - radius))
|
||||
qy = max(0.0, dy - (half_size - radius))
|
||||
dist = math.sqrt(qx * qx + qy * qy)
|
||||
|
||||
if dx <= half_size and dy <= half_size and dist <= radius:
|
||||
# Gradient von Dunkelblau (#1e2638) nach Nachtblau (#0f1422)
|
||||
t = (x + y) / (WIDTH + HEIGHT)
|
||||
r = int(30 * (1 - t) + 12 * t)
|
||||
g = int(38 * (1 - t) + 16 * t)
|
||||
b = int(58 * (1 - t) + 28 * t)
|
||||
|
||||
# Rand-Antialiasing
|
||||
edge_dist = radius - dist
|
||||
alpha = min(255, max(0, int(edge_dist * 255)))
|
||||
|
||||
# Subtiler Rahmen
|
||||
if edge_dist < 2.5:
|
||||
r, g, b = 60, 75, 110
|
||||
|
||||
set_pixel(x, y, r, g, b, alpha)
|
||||
|
||||
# 2. Subtile Gitterlinien
|
||||
for gy in range(80, 200, 30):
|
||||
for gx in range(40, 216):
|
||||
set_pixel(gx, gy, 140, 160, 190, 25)
|
||||
for gx in range(70, 200, 30):
|
||||
for gy in range(50, 216):
|
||||
set_pixel(gx, gy, 140, 160, 190, 25)
|
||||
|
||||
# 3. Tintenspur (Bézier-Kurve mit Glow)
|
||||
pts = []
|
||||
for t_i in range(150):
|
||||
t = t_i / 150.0
|
||||
# P0=(55, 195), P1=(90, 215), P2=(130, 190), P3=(120, 155), P4=(140, 110), P5=(170, 110)
|
||||
# 2 Teilkurven
|
||||
if t <= 0.5:
|
||||
u = t * 2.0
|
||||
bx = (
|
||||
(1 - u) ** 3 * 55
|
||||
+ 3 * (1 - u) ** 2 * u * 90
|
||||
+ 3 * (1 - u) * u**2 * 130
|
||||
+ u**3 * 120
|
||||
)
|
||||
by = (
|
||||
(1 - u) ** 3 * 195
|
||||
+ 3 * (1 - u) ** 2 * u * 215
|
||||
+ 3 * (1 - u) * u**2 * 190
|
||||
+ u**3 * 155
|
||||
)
|
||||
else:
|
||||
u = (t - 0.5) * 2.0
|
||||
bx = (
|
||||
(1 - u) ** 3 * 120
|
||||
+ 3 * (1 - u) ** 2 * u * 110
|
||||
+ 3 * (1 - u) * u**2 * 140
|
||||
+ u**3 * 170
|
||||
)
|
||||
by = (
|
||||
(1 - u) ** 3 * 155
|
||||
+ 3 * (1 - u) ** 2 * u * 120
|
||||
+ 3 * (1 - u) * u**2 * 95
|
||||
+ u**3 * 110
|
||||
)
|
||||
pts.append((bx, by))
|
||||
|
||||
for px, py in pts:
|
||||
for ox in range(-7, 8):
|
||||
for oy in range(-7, 8):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 7.0:
|
||||
a = int((1.0 - d / 7.0) * 110)
|
||||
set_pixel(int(px + ox), int(py + oy), 59, 130, 246, a)
|
||||
for ox in range(-3, 4):
|
||||
for oy in range(-3, 4):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 3.0:
|
||||
a = int((1.0 - d / 3.0) * 230)
|
||||
set_pixel(int(px + ox), int(py + oy), 147, 197, 253, a)
|
||||
|
||||
# 4. Füllfederhalter (diagonale Geometrie)
|
||||
# Stift-Mittelachse von (120, 55) nach (170, 110)
|
||||
pen_dir = (math.cos(math.radians(45)), math.sin(math.radians(45)))
|
||||
norm = (-pen_dir[1], pen_dir[0])
|
||||
|
||||
# Spitze bei (110, 195)
|
||||
tip_x, tip_y = 55.0, 195.0
|
||||
for l in range(0, 120):
|
||||
pos_x = tip_x + pen_dir[0] * l
|
||||
pos_y = tip_y - pen_dir[1] * l
|
||||
# Radius entlang des Stifts
|
||||
if l < 20:
|
||||
# Goldene Feder
|
||||
w = (l / 20.0) * 10.0
|
||||
color = (245, 158, 11) # Gold
|
||||
elif l < 32:
|
||||
# Griffzone
|
||||
w = 11.0
|
||||
color = (30, 41, 59) # Dunkelgrau
|
||||
else:
|
||||
# Schaft
|
||||
w = 12.0
|
||||
color = (59, 130, 246) # Blau
|
||||
|
||||
for s in range(int(-w), int(w) + 1):
|
||||
px = int(pos_x + norm[0] * s)
|
||||
py = int(pos_y + norm[1] * s)
|
||||
set_pixel(px, py, color[0], color[1], color[2], 255)
|
||||
|
||||
# 5. Glanzpunkt auf der Spitze
|
||||
for ox in range(-4, 5):
|
||||
for oy in range(-4, 5):
|
||||
d = math.sqrt(ox * ox + oy * oy)
|
||||
if d <= 4:
|
||||
set_pixel(int(tip_x + ox), int(tip_y + oy), 255, 255, 255, 255)
|
||||
|
||||
return make_png(WIDTH, HEIGHT, buffer)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.makedirs("assets", exist_ok=True)
|
||||
png_data = render_icon()
|
||||
with open("assets/ink-notes.png", "wb") as f:
|
||||
f.write(png_data)
|
||||
print("PNG-Icon erfolgreich generiert: assets/ink-notes.png (256x256)")
|
||||
Reference in New Issue
Block a user