Files
nixconfig/modules/home/gui/theme/wallpaper-gen.nix
soraefir 1590a1cf31 fix ui
2026-06-05 01:57:27 +02:00

151 lines
4.1 KiB
Nix

{ pkgs, config, lib ? pkgs.lib }:
let
colors = config.colorScheme.palette;
mediaImages = config.syscfg.media.main;
mediaNames = map (image: builtins.baseNameOf (toString image)) mediaImages;
mediaSourceDir = pkgs.linkFarm "wallpaper-media" (
map (image: {
name = builtins.baseNameOf (toString image);
path = image;
}) mediaImages
);
dither = "atkinson"; # none | floyd-steinberg | atkinson | jjn | burkes | sierra | sierra-lite
paletteSize = 0;
hexChars = "0123456789abcdef";
hexMap = {
"0" = 0; "1" = 1; "2" = 2; "3" = 3;
"4" = 4; "5" = 5; "6" = 6; "7" = 7;
"8" = 8; "9" = 9; "a" = 10; "b" = 11;
"c" = 12; "d" = 13; "e" = 14; "f" = 15;
};
baseColors = [
colors.base00
colors.base01
colors.base02
colors.base03
colors.base04
colors.base05
colors.base06
colors.base07
colors.base08
colors.base09
colors.base0A
colors.base0B
colors.base0C
colors.base0D
colors.base0E
colors.base0F
];
round = x: builtins.floor (x + 0.5);
clamp = x:
if x < 0 then 0 else if x > 255 then 255 else x;
parseHexByte = byte:
let
hi = hexMap.${builtins.substring 0 1 byte};
lo = hexMap.${builtins.substring 1 1 byte};
in
hi * 16 + lo;
hexToRgb = hex:
let
clean = lib.toLower (lib.removePrefix "#" hex);
in
{
r = parseHexByte (builtins.substring 0 2 clean);
g = parseHexByte (builtins.substring 2 2 clean);
b = parseHexByte (builtins.substring 4 2 clean);
};
componentToHex = value:
let
bounded = clamp value;
hi = builtins.div bounded 16;
lo = bounded - hi * 16;
in
"${builtins.substring hi 1 hexChars}${builtins.substring lo 1 hexChars}";
rgbToHex = color: "${componentToHex color.r}${componentToHex color.g}${componentToHex color.b}";
getTint = c: weight: round (c + (255 - c) * weight);
getShade = c: weight: round (c * weight);
tint = color: weight: {
r = getTint color.r weight;
g = getTint color.g weight;
b = getTint color.b weight;
};
shade = color: weight: {
r = getShade color.r weight;
g = getShade color.g weight;
b = getShade color.b weight;
};
genPalette = color:
let
tints =
if paletteSize == 0
then [ ]
else lib.genList (i: tint color ((i + 1.0) / paletteSize)) paletteSize;
shades =
if paletteSize == 0
then [ ]
else lib.genList (i: shade color (i * 1.0 / paletteSize)) paletteSize;
in
lib.reverseList tints ++ [ color ] ++ lib.reverseList shades;
keepColor = color:
let
sum = color.r + color.g + color.b;
in
sum > 0 && sum < 765;
paletteColors = lib.concatMap (hex: lib.filter keepColor (genPalette (hexToRgb hex))) baseColors;
paletteHex = lib.concatStringsSep "," (map rgbToHex paletteColors);
gifPaletteFile = pkgs.writeText "wallpaper-gifpalette.txt" (
lib.concatMapStringsSep "\n" (color: "${toString color.r} ${toString color.g} ${toString color.b}") paletteColors
);
buildCommands =
lib.concatMapStringsSep "\n" (name:
let
source = "${mediaSourceDir}/${name}";
target = "build/${name}";
in
if lib.hasSuffix ".gif" (lib.toLower name) then ''
gifsicle --use-colormap ${lib.escapeShellArg (toString gifPaletteFile)} < ${lib.escapeShellArg source} > ${lib.escapeShellArg target}
'' else ''
repalette ${lib.escapeShellArg source} ${lib.escapeShellArg target} -p ${lib.escapeShellArg paletteHex} --dither ${lib.escapeShellArg dither}
''
) mediaNames;
in
assert lib.assertMsg
(builtins.length mediaNames == builtins.length (lib.unique mediaNames))
"syscfg.media.main contains duplicate basenames, which would collide in generated wallpaper output.";
pkgs.stdenv.mkDerivation {
pname = "generated-wallpaper";
version = "local";
dontUnpack = true;
nativeBuildInputs = with pkgs; [
custom.repalette
gifsicle
];
buildPhase = ''
runHook preBuild
mkdir -p build
${buildCommands}
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/share/wallpaper
cp -r build/. $out/share/wallpaper/
runHook postInstall
'';
}