Created
February 27, 2026 04:46
-
-
Save setanarut/7129a3839e5c096d42686d664dc68342 to your computer and use it in GitHub Desktop.
Krita Halftone script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from krita import Krita | |
| import array, math, time | |
| # ─── AYARLAR ────────────────────────────────────────────────── | |
| WAVE_COUNT = 24 | |
| TOTAL_FRAMES = 10 | |
| WAVEFORM = "sawtooth" # "triangle" | "sawtooth" | "sine" | |
| REVERSE = False | |
| FPS = 10 | |
| SRC_LAYER = "dem" # kaynak katman adı | |
| ANIM_LAYER = "anim" # hedef animasyon katmanı adı | |
| # ────────────────────────────────────────────────────────────── | |
| app = Krita.instance() | |
| doc = app.activeDocument() | |
| w, h = doc.width(), doc.height() | |
| depth = doc.colorDepth() | |
| model = doc.colorModel() | |
| MAX_VAL = 65535 if depth == "U16" else 255 | |
| fmt = "H" if depth == "U16" else "B" | |
| WAVE_SPACING = MAX_VAL / WAVE_COUNT | |
| CH_COUNT = {"GRAYA": 2, "RGBA": 4, "CMYKA": 5}.get(model, 4) | |
| CHANNEL_IDX = {"GRAYA": 0, "RGBA": 2, "CMYKA": 0}.get(model, 0) | |
| ALPHA_IDX = {"GRAYA": 1, "RGBA": 3, "CMYKA": 4}.get(model, CH_COUNT - 1) | |
| print(f"Model: {model}, Derinlik: {depth}, Kanal: {CH_COUNT}") | |
| # ─── KATMAN BUL ─────────────────────────────────────────────── | |
| def find_layer(name, node=None): | |
| if node is None: | |
| node = doc.rootNode() | |
| for child in node.childNodes(): | |
| if child.name() == name: | |
| return child | |
| found = find_layer(name, child) | |
| if found: | |
| return found | |
| return None | |
| anim_layer = find_layer(ANIM_LAYER) | |
| src_layer = find_layer(SRC_LAYER) | |
| if anim_layer is None: | |
| print(f"HATA: '{ANIM_LAYER}' katmanı bulunamadı.") | |
| raise SystemExit | |
| if src_layer is None: | |
| print(f"HATA: '{SRC_LAYER}' katmanı bulunamadı.") | |
| raise SystemExit | |
| print(f"Anim: '{anim_layer.name()}', Kaynak: '{src_layer.name()}'") | |
| # ─── KAYNAK OKU ─────────────────────────────────────────────── | |
| raw = src_layer.pixelData(0, 0, w, h) | |
| vals = array.array(fmt) | |
| vals.frombytes(raw) | |
| print(f"Kaynak sıfırdan farklı: {sum(1 for v in vals if v != 0)}") | |
| # ─── KEYFRAME TESPİT ────────────────────────────────────────── | |
| keyframes = [i for i in range(TOTAL_FRAMES) if anim_layer.hasKeyframeAtTime(i)] | |
| print(f"Bulunan keyframe'ler: {keyframes}") | |
| if not keyframes: | |
| print(f"HATA: '{ANIM_LAYER}' katmanında hiç keyframe yok.") | |
| raise SystemExit | |
| if len(keyframes) != TOTAL_FRAMES: | |
| print(f"UYARI: {TOTAL_FRAMES} beklendi, {len(keyframes)} bulundu.") | |
| # ─── ZAMAN ADIMLARI ─────────────────────────────────────────── | |
| n = len(keyframes) | |
| step = 1.0 / n | |
| frames_t = [i * step for i in range(n)] | |
| if REVERSE: | |
| frames_t = list(reversed(frames_t)) | |
| # ─── ALGORİTMA ──────────────────────────────────────────────── | |
| def fract(x): | |
| return x - math.floor(x) | |
| def wave(frac): | |
| if WAVEFORM == "triangle": | |
| return 1.0 - abs(2.0 * frac - 1.0) | |
| elif WAVEFORM == "sawtooth": | |
| return frac | |
| elif WAVEFORM == "sine": | |
| return 0.5 + 0.5 * math.sin(frac * 2 * math.pi) | |
| return frac | |
| def get_frame(vals, t): | |
| out = array.array(fmt, [0] * (w * h * CH_COUNT)) | |
| for i in range(w * h): | |
| v = vals[i * CH_COUNT + CHANNEL_IDX] | |
| frac = fract(v / WAVE_SPACING + t) | |
| val = round(wave(frac) * MAX_VAL) | |
| base = i * CH_COUNT | |
| for ch in range(CH_COUNT): | |
| out[base + ch] = MAX_VAL if ch == ALPHA_IDX else val | |
| return out.tobytes() | |
| # ─── KARE KARE YAZ ──────────────────────────────────────────── | |
| doc.setFramesPerSecond(FPS) | |
| doc.setFullClipRangeEndTime(TOTAL_FRAMES - 1) | |
| doc.setPlayBackRange(0, TOTAL_FRAMES - 1) | |
| total_start = time.time() | |
| for idx, frame_num in enumerate(keyframes): | |
| now = time.time() | |
| t = frames_t[idx] | |
| doc.setCurrentTime(frame_num) | |
| doc.waitForDone() | |
| anim_layer.setPixelData(get_frame(vals, t), 0, 0, w, h) | |
| doc.waitForDone() | |
| print(f"[{idx+1}/{n}] frame:{frame_num} t={t:.3f} {time.time()-now:.1f}s") | |
| doc.setCurrentTime(0) | |
| doc.refreshProjection() | |
| print(f"\nToplam: {time.time()-total_start:.1f}s") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment