# jdl-effects.avsi -- Functions for creating effects. # Also includes miscellaneous, exotic, and specialized # functions. # # Last modified: 2008-09-13 # # Written by James D. Lin (stickboy) and assigned to the public domain. # # The latest version of this file can be downloaded from: # # JDL_Blackout # # Blacks out a clip. Does not affect the audio track. # # USAGE: # Intended to be used with ApplyRange. For example: # # ApplyRange(0, 10, "JDL_Blackout", "") # Black out frames [0, 10] # function JDL_Blackout(clip c, string "unused") { # return AudioDub(c.BlankClip(color=$000000), c) return c.Levels(0, 1.0, 255, 0, 0) } # JDL_Silence # # Silences a clip. Does not affect the video track. # # USAGE: # Intended to be used with ApplyRange. For example: # # ApplyRange(0, 10, "JDL_Silence", "") # Silence frames [0, 10] # function JDL_Silence(clip c, string "unused") { # return AudioDub(c, c.BlankClip()) return c.Amplify(0.0) } # JDL_FadeIO # # Fades a clip. # # Unlike FadeIO/FadeIn/FadeOut, does not add additional frames. # # DEPRECATED: # AviSynth 2.5.6 adds FadeIn0/FadeOut0/FadeIO0 functions that should do # the same thing. # # PARAMETERS: # "start" : If specified, the number of frames to fade at the beginning # of the clip. # (Default: 0) # "end" : if specified, the number of frames to fade at the end of the # clip. # (Default: 0) # # REQUIRES: # jdl-util.avsi (Trim2) # function JDL_FadeIO(clip c, int "start", int "end") { start = Default(start, 0) end = Default(end, 0) Assert(start != 0 || end != 0, "JDL_FadeIO: no frames specified") Assert(start >= 0 && end >= 0, "JDL_FadeIO: invalid number of frames to fade") c = (start == 0) ? c : c.Trim2(1).FadeIn(start) c = (end == 0) ? c : c.Trim2(0, -1).FadeOut(end) return c } # JDL_FadeAudio # # Fades audio in a clip. Does not affect the video track. # # Unlike FadeIO/FadeIn/FadeOut, does not add additional frames. # # PARAMETERS: # "start" : If specified, the number of frames to fade at the beginning # of the clip. # (Default: 0) # "end" : If specified, the number of frames to fade at the end of the # clip. # (Default: 0) # function JDL_FadeAudio(clip c, int "start", int "end") { return AudioDub(c, c.JDL_FadeIO(start, end)) } # JDL_FadeVideo # # Fades the video in a clip. Does not affect the audio track. # # Unlike FadeIO/FadeIn/FadeOut, does not add additional frames. # # PARAMETERS: # "start" : If specified, the number of frames to fade at the beginning # of the clip. # (Default: 0) # "end" : If specified, the number of frames to fade at the end of the # clip. # (Default: 0) # function JDL_FadeVideo(clip c, int "start", int "end") { video = c.JDL_FadeIO(start, end) return c.HasAudio() ? AudioDub(video, c) \ : video } # JDL_Invert # # Inverts a clip. # # This function is intended primarily for use with AviSynth 2.52 and # earlier (an internal Invert function was added in AviSynth 2.53). # However, as of 2.53, the internal Invert function operates only in # RGB32. # # COLORSPACES: # [YUY2, RGB24?, RGB32] # function JDL_Invert(clip c) { return c.Levels(0, 1.0, 255, 255, 0) } # JDL_Wipe # # Combines two clips using a wipe transition. The audio tracks are # blended during the transition. # # PARAMETERS: # c1 : The first clip. # c2 : The second clip. # overlap : The desired number of transitional frames. # "direction" : The wipe direction, one of: # { "right", "left", "up", "down" } # (Default: "right") # # REQUIRES: # jdl-util.avsi (Trim2, Trim3) # SelectByString (SelectByStringEval) # function JDL_Wipe(clip c1, clip c2, int overlap, string "direction") { Assert( c1.Width() == c2.Width() \ && c1.Height() == c2.Height(), \ "JDL_Wipe: clips must have identical frame sizes") Assert(c1.FrameRate() == c2.FrameRate(), \ "JDL_Wipe: clips must have identical frame-rates") Assert(overlap > 0, "JDL_Wipe: invalid overlap length: " + String(overlap)) Assert(c1.FrameCount() > overlap && c2.FrameCount() > overlap, \ "JDL_Wipe: each clip must have more than frames") direction = Default(direction, "right") head = c1.Trim2(0, length=(c1.FrameCount() - overlap - 1)) tail = c2.Trim2(overlap + 1) trans1 = c1.Trim3(c1.FrameCount() - overlap - 1) trans2 = c2.Trim3(0, length=(overlap + 1)) Assert(trans1.FrameCount() == trans2.FrameCount(), "JDL_Wipe: bad math") trans = SelectByStringEval(direction, \ "right", """Animate(0, overlap + 1, "JDL_SpliceHorizontal", \ trans2, trans1, 0, \ trans2, trans1, c1.Width())""", \ "left", """Animate(0, overlap + 1, "JDL_SpliceHorizontal", \ trans1, trans2, c1.Width(), \ trans1, trans2, 0)""", \ "down", """Animate(0, overlap + 1, "JDL_SpliceVertical", \ trans2, trans1, 0, \ trans2, trans1, c1.Height())""", \ "up", """Animate(0, overlap + 1, "JDL_SpliceVertical", \ trans1, trans2, c1.Height(), \ trans1, trans2, 0)""", \ else="""Throw("JDL_Wipe: invalid direction")""") video = head + trans + tail audio = Dissolve(c1, c2, overlap + 1) Assert(video.FrameCount() == c1.FrameCount() + c2.FrameCount() - overlap - 1, \ "JDL_Wipe: bad math") Assert(video.FrameCount() == audio.FrameCount(), "JDL_Wipe: video/audio mismatch") return c1.HasAudio() ? AudioDub(video, audio) \ : video } # JDL_SpliceHorizontal # # Helper function to JDL_Wipe. # function JDL_SpliceHorizontal(clip c1, clip c2, int x) { # In case we're dealing with YUY2 or YV12 video, crop only on even # boundaries. x = (c1.IsRGB() || x % 2 == 0) ? x : (x - 1) return (x == 0) \ ? c2 \ : ((x == c1.Width()) \ ? c1 \ : StackHorizontal(c1.Crop(0, 0, x, 0), \ c2.Crop(x, 0, c2.Width() - x, 0))) } # JDL_SpliceVertical # # Helper function to JDL_Wipe. # function JDL_SpliceVertical(clip c1, clip c2, int y) { # In case we're dealing with YV12 video, crop only on even # boundaries. y = (!c1.IsYV12() || y % 2 == 0) ? y : (y - 1) return (y == 0) \ ? c2 \ : ((y == c1.Height()) \ ? c1 \ : StackVertical(c1.Crop(0, 0, 0, y), \ c2.Crop(0, y, 0, c2.Height() - y))) } # JDL_MaskTransition # # Combines two clips using the specified mask clip. The audio tracks are # blended during the transition. # # PARAMETERS: # c1 : The first clip. # c2 : The second clip. # transitionMask : The mask clip. # The mask should start as white and end as black. # The mask clip should use the full PC [0, 255] luma # range (call ColorYUV(levels="TV->PC") on the mask # clip if necessary). # # REQUIRES: # jdl-util.avsi (Trim2, Trim3) # function JDL_MaskTransition(clip c1, clip c2, clip transitionMask) { Assert( c1.Width() == c2.Width() \ && c1.Width() == transitionMask.Width() \ && c1.Height() == c2.Height() \ && c1.Height() == transitionMask.Height(), \ "JDL_MaskTransition: clips must have identical frame sizes") Assert( c1.FrameRate() == c2.FrameRate() \ && c1.FrameRate() == transitionMask.FrameRate(), \ "JDL_MaskTransition: clips must have identical frame-rates") overlap = transitionMask.FrameCount() Assert(overlap > 0, "JDL_MaskTransition: invalid overlap length: " + String(overlap)) Assert(c1.FrameCount() >= overlap && c2.FrameCount() >= overlap, \ "JDL_MaskTransition: clips must be longer than the transition") head = c1.Trim2(0, length=(c1.FrameCount() - overlap)) tail = c2.Trim2(overlap) trans1 = c1.Trim3(c1.FrameCount() - overlap) trans2 = c2.Trim3(0, length=overlap) Assert(trans1.FrameCount() == trans2.FrameCount(), "JDL_MaskTransition: bad math") trans = Overlay(trans2, trans1, mask=transitionMask) video = head + trans + tail audio = Dissolve(c1, c2, overlap) Assert(video.FrameCount() == audio.FrameCount(), "JDL_MaskTransition: video/audio mismatch") return c1.HasAudio ? AudioDub(video, audio) \ : video } # JDL_Fill # # Fills the specified rectangle with the specified color # # PARAMETERS: # x, y : The coordinates for the upper-left corner of the rectangle. # w, h : The rectangle width and height. # color : The RGB color for the rectangle. # # COLORSPACES: # [YUY2, RGB32] # function JDL_Fill(clip c, int x, int y, int w, int h, int color) { Assert(x >= 0 && y >= 0 && w > 0 && h > 0, "JDL_Fill: invalid region") fill = c.BlankClip(width=w, height=h, color=color) fill = fill.IsRGB32() ? fill.ResetMask() : fill return Layer(c, fill, op="add", x=x, y=y) } # JDL_Slow # # Slows a clip by the specified amount. # # PARAMETERS: # rate : The new playback rate. # e.g. 0.5 will set the playback rate to 50% of its # original speed # "blend" : Pass true to blend frames; pass false to duplicate # existing frames only. # If true, must be > 2/3. # (Default: false) # "keepPitch" : Pass true to try to preserve the audio pitch. # (Default: false) # # COLORSPACES: # [YUY2] # function JDL_Slow(clip c, float rate, bool "blend", bool "keepPitch") { Assert(rate > 0 && rate < 1, "JDL_Slow: rate must be in the range (0, 1)") blend = Default(blend, false) keepPitch = Default(keepPitch, false) Assert(!blend || rate >= (2.0 / 3.0), "JDL_Slow: rate must be > 2/3 for blending") oldFrameRate = c.FrameRate() oldSampleRate = c.AudioRate() c = c.AssumeFPS(Round(oldFrameRate * rate)) c = blend ? c.ConvertFPS(oldFrameRate) : c.ChangeFPS(oldFrameRate) c = keepPitch ? c.TimeStretch(tempo=(100.0 * rate)) \ : c.AssumeSampleRate(Round(oldSampleRate * rate)).ResampleAudio(oldSampleRate) return c } # JDL_ReplaceFirstScanline # # Replaces the first scanline in a video with a copy of the second. # Intended to throw away closed-captioning artifacts without changing # the frame size or aspect-ratio. # function JDL_ReplaceFirstScanline(clip c) { return StackVertical(c.Crop(0, 1, 0, 1), \ c.Crop(0, 1, 0, 0)) } # JDL_SplitYUV # # Separates the Y, U, V channels for easy examination, stacking them # in the following arrangement: # Y # --+-- # U | V # function JDL_SplitYUV(clip c) { Assert(c.IsYUV(), "JDL_SplitYUV: video must be YUY2 or YV12") y = c.Tweak(sat=0, coring=false) # This seems faster than YToUV(c, c).UToY() u = c.UToY() v = c.VToY() return StackVertical(y, StackHorizontal(u, v)) } # JDL_MergeYUV # # Recombines split Y, U, V channels formed by JDL_SplitYUV. # function JDL_MergeYUV(clip c) { Assert(c.IsYUV(), "JDL_MergeYUV: video must be YUY2 or YV12") Assert(c.Width() % 2 == 0, "JDL_MergeYUV: unexpected frame width") originalHeight = c.IsYV12() ? (c.Height() * 2 / 3) : (c.Height() / 2) y = c.Crop(0, 0, c.Width(), originalHeight) uv = c.Crop(0, originalHeight, c.Width(), c.Height() - originalHeight) chromaWidth = c.Width() / 2 u = uv.Crop(0, 0, chromaWidth, uv.Height()) v = uv.Crop(chromaWidth, 0, chromaWidth, uv.Height()) return YToUV(u, v, y) } # JDL_SplitRGB # # Separates the R, G, B channels for easy examination, stacking them # in the following arrangement: # R | G | B # function JDL_SplitRGB(clip c) { Assert(c.IsRGB(), "JDL_SplitRGB: video must be RGB") r = c.RGBAdjust(1, 0, 0, 0) g = c.RGBAdjust(0, 1, 0, 0) b = c.RGBAdjust(0, 0, 1, 0) return StackHorizontal(r, g, b) } # JDL_SplitScreenCompare # # Performs a split-screen comparison. Displays the left half of the # first clip beside the right half of the second clip. # # USAGE: # JDL_SplitScreenCompare(PixieDust(3), MipSmooth(preset="movieHQ")) # function JDL_SplitScreenCompare(clip leftClip, clip rightClip) { Assert( leftClip.Width() == rightClip.Width() \ && leftClip.Height() == rightClip.Height() \ && leftClip.FrameRate() == rightClip.FrameRate(), \ "JDL_SplitScreenCompare: clips must identical attributes") return JDL_SpliceHorizontal(leftClip, rightClip, leftClip.Width() / 2) }