diff --git a/Config/Config.qml b/Config/Config.qml index 9455d7d..ef5fb65 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -169,6 +169,7 @@ Singleton { color: { wallust: general.color.wallust, mode: general.color.mode, + smart: general.color.smart, schemeGeneration: general.color.schemeGeneration, scheduleDarkStart: general.color.scheduleDarkStart, scheduleDarkEnd: general.color.scheduleDarkEnd, diff --git a/Config/General.qml b/Config/General.qml index 8b00553..9169d31 100644 --- a/Config/General.qml +++ b/Config/General.qml @@ -23,6 +23,7 @@ JsonObject { property int scheduleDarkEnd: 0 property int scheduleDarkStart: 0 property bool schemeGeneration: true + property bool smart: false property bool wallust: false } component Idle: JsonObject { diff --git a/cli/src/zshell/subcommands/scheme.py b/cli/src/zshell/subcommands/scheme.py index e2073ad..2b6e3fd 100644 --- a/cli/src/zshell/subcommands/scheme.py +++ b/cli/src/zshell/subcommands/scheme.py @@ -12,6 +12,7 @@ from materialyoucolor.quantize import QuantizeCelebi from materialyoucolor.score.score import Score from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors from materialyoucolor.hct.hct import Hct +from materialyoucolor.utils.color_utils import argb_from_rgb from materialyoucolor.utils.math_utils import difference_degrees, rotation_direction, sanitize_degrees_double app = typer.Typer() @@ -47,35 +48,38 @@ def generate( raise typer.BadParameter( "Use either --image-path or --preset, not both.") - if scheme is None: - with CONFIG.open() as f: - scheme = json.load(f)["colors"]["schemeType"] - - match scheme: - case "fruit-salad": - from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme - case 'expressive': - from materialyoucolor.scheme.scheme_expressive import SchemeExpressive as Scheme - case 'monochrome': - from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome as Scheme - case 'rainbow': - from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow as Scheme - case 'tonal-spot': - from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot as Scheme - case 'neutral': - from materialyoucolor.scheme.scheme_neutral import SchemeNeutral as Scheme - case 'fidelity': - from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity as Scheme - case 'content': - from materialyoucolor.scheme.scheme_content import SchemeContent as Scheme - case 'vibrant': - from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant as Scheme - case _: - from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme - - if mode is None: - with CONFIG.open() as f: - mode = json.load(f)["general"]["color"]["mode"] + def get_scheme_class(scheme_name: str): + match scheme_name: + case "fruit-salad": + from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad + return SchemeFruitSalad + case "expressive": + from materialyoucolor.scheme.scheme_expressive import SchemeExpressive + return SchemeExpressive + case "monochrome": + from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome + return SchemeMonochrome + case "rainbow": + from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow + return SchemeRainbow + case "tonal-spot": + from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot + return SchemeTonalSpot + case "neutral": + from materialyoucolor.scheme.scheme_neutral import SchemeNeutral + return SchemeNeutral + case "fidelity": + from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity + return SchemeFidelity + case "content": + from materialyoucolor.scheme.scheme_content import SchemeContent + return SchemeContent + case "vibrant": + from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant + return SchemeVibrant + case _: + from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad + return SchemeFruitSalad def hex_to_hct(hex_color: str) -> Hct: s = hex_color.strip() @@ -226,6 +230,16 @@ def generate( except (PermissionError, OSError, BlockingIOError): pass + def smart_mode(image_path: Path) -> str: + is_dark = "" + + with Image.open(image_path) as img: + img.thumbnail((1, 1), Image.LANCZOS) + hct = Hct.from_int(argb_from_rgb(*img.getpixel((0, 0)))) + is_dark = "light" if hct.tone > 50 else "dark" + + return is_dark + def build_template_context( *, colors: dict[str, str], @@ -375,11 +389,11 @@ def generate( raise typer.BadParameter( f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}") - def generate_color_scheme(seed: Hct, mode: str) -> dict[str, str]: + def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]: is_dark = mode.lower() == "dark" - scheme = Scheme( + scheme = scheme_class( seed, is_dark, 0.0 @@ -398,36 +412,38 @@ def generate( return "#{:06X}".format(argb_int & 0xFFFFFF) try: + with CONFIG.open() as f: + config = json.load(f) + + scheme = scheme or config["colors"]["schemeType"] + config_mode = config["general"]["color"]["mode"] + smart = bool(config["general"]["color"].get("smart", False)) + scheme_class = get_scheme_class(scheme) + if preset: seed = seed_from_preset(preset) - colors = generate_color_scheme(seed, mode) + effective_mode = mode or config_mode name, flavor = preset.split(":") - elif image_path: + else: + image_path = image_path or Path(WALL_PATH) generate_thumbnail(image_path, str(THUMB_PATH)) seed = seed_from_image(THUMB_PATH) - colors = generate_color_scheme(seed, mode) name = "dynamic" flavor = "default" - elif mode: - generate_thumbnail(WALL_PATH, str(THUMB_PATH)) - seed = seed_from_image(THUMB_PATH) - colors = generate_color_scheme(seed, mode) - name = "dynamic" - flavor = "default" - elif scheme: - with OUTPUT.open() as f: - js = json.load(f) - seed = Hct.from_int(js["seed"]) - mode = str(js["mode"]) - colors = generate_color_scheme(seed, mode) - name = "dynamic" - flavor = "default" + if smart: + effective_mode = smart_mode(THUMB_PATH) + elif mode is not None: + effective_mode = mode + else: + effective_mode = config_mode + + colors = generate_color_scheme(seed, effective_mode, scheme_class) output_dict = { "name": name, "flavor": flavor, - "mode": mode, + "mode": effective_mode, "variant": scheme, "colors": colors, "seed": seed.to_int() @@ -438,7 +454,7 @@ def generate( ctx = build_template_context( colors=colors, seed=seed, - mode=mode, + mode=effective_mode, wallpaper_path=wp, name=name, flavor=flavor,