diff --git a/bcc.spec b/bcc.spec index 6eb5854..d0cfa2a 100644 --- a/bcc.spec +++ b/bcc.spec @@ -98,7 +98,7 @@ else: debug=False, bootloader_ignore_signals=False, strip=False, - upx=True, + upx=False, # UPX can strip icon resources from the PE on Windows upx_exclude=[], runtime_tmpdir=None, console=False, diff --git a/icons/app.ico b/icons/app.ico index 62a9f2c..2448f48 100644 Binary files a/icons/app.ico and b/icons/app.ico differ diff --git a/scripts/build_icons.py b/scripts/build_icons.py index b62496c..7d4923d 100644 --- a/scripts/build_icons.py +++ b/scripts/build_icons.py @@ -32,22 +32,79 @@ def build_ico(): print("Pillow not installed — skipping .ico generation (pip install pillow)") return - sizes = [16, 32, 48, 64, 128, 256] - imgs = [] + import io, struct + + sizes = [16, 24, 32, 48, 64, 128, 256] + images: list[tuple[int, object]] = [] for s in sizes: p = SRC / f"icon-{s}.png" if not p.exists(): print(f" Missing {p.name}, skipping") continue - imgs.append(Image.open(p).convert("RGBA")) + images.append((s, Image.open(p).convert("RGBA"))) - if not imgs: + if not images: print(" No source PNGs found — cannot build .ico") return + # Write the ICO manually so that entries ≤ 128 px use uncompressed BMP DIB + # and the 256 px entry uses PNG. Pillow's ICO saver stores all sizes as + # PNG-compressed ("Vista icon" format), which PyInstaller's Windows + # resource-updater cannot embed — it silently falls back to its default icon. + + def bmp_dib(img: object) -> bytes: + """Return a BMP DIB (BITMAPINFOHEADER + BGRA rows + AND mask).""" + w, h = img.size + # biHeight is doubled: top half = XOR mask (color), bottom = AND mask + hdr = struct.pack( + " bytes: + buf = io.BytesIO() + img.save(buf, format="PNG") + return buf.getvalue() + + blobs: list[tuple[int, bytes]] = [] + for s, img in images: + blobs.append((s, bmp_dib(img) if s < 256 else png_bytes(img))) + + n = len(blobs) + dir_offset = 6 + n * 16 # ICONDIR (6) + n × ICONDIRENTRY (16) + + out = bytearray() + out += struct.pack("