diff --git a/OverLua/docs/sample2.lua b/OverLua/docs/sample2.lua new file mode 100644 index 000000000..a4247936e --- /dev/null +++ b/OverLua/docs/sample2.lua @@ -0,0 +1,235 @@ +--[[ + +Sample script for OverLua + - demonstrate basic reading in an ASS subtitle file and rendering its lines on the video + +Given into the public domain. +(You can do anything you want with this file, with no restrictions whatsoever. + You don't get any warranties of any kind either, though.) + +Originally authored by Niels Martin Hansen. + +]] + +-- Set up some parameters +timing_input_file = overlua_datastring +-- Just the font name to use. +font_name = "Arial" +-- This is height in pixels, I suppose ;) +font_size = 40 +-- This is the position of the _baseline_ of the text, neither top, bottom nor center! +ypos = 50 +-- Duration of fadein/out in seconds +fadetime = 1 + +-- Error out if no file name was given (data= in Avisynth invocation) +assert(timing_input_file, "Missing timing input file for sample effect.") + + +-- ASS file reading stuff +function parsenum(str) + return tonumber(str) or 0 +end +function parse_ass_time(ass) + local h, m, s, cs = ass:match("(%d+):(%d+):(%d+)%.(%d+)") + return parsenum(cs)/100 + parsenum(s) + parsenum(m)*60 + parsenum(h)*3600 +end + +function parse_k_timing(text) + local syls = {} + local cleantext = "" + local i = 1 + for timing, syltext in text:gmatch("{\\k(%d+)}([^{]*)") do + local syl = {dur = parsenum(timing)/100, text = syltext, i = i} + table.insert(syls, syl) + cleantext = cleantext .. syltext + i = i + 1 + end + return syls, cleantext +end + +function read_input_file(name) + for line in io.lines(name) do + local start_time, end_time, fx, text = line:match("Dialogue: 0,(.-),(.-),Default,,0000,0000,0000,(.-),(.*)") + if text then + local ls = {} + ls.start_time = parse_ass_time(start_time) + ls.end_time = parse_ass_time(end_time) + ls.fx = fx + ls.rawtext = text + ls.kara, ls.cleantext = parse_k_timing(text) + table.insert(lines, ls) + end + end +end + +function init() + if inited then return end + inited = true + + lines = {} + read_input_file(timing_input_file) +end + + +-- Get/create a "sparks" texture, singleton-style +function get_sparks_texture(width, height) + -- Check if it already exists, just return it then + if sparks_texture then return sparks_texture end + -- We'll make a 128x128 black image with some blurred whitish spots on + local surf = cairo.image_surface_create(128, 128, "rgb24") + local c = surf.create_context() + -- Paint it all black + c.set_source_rgb(0,0,0) + c.paint() + -- Then create a very light yellow + c.set_source_rgb(1,1,0.9) + -- And create 50 small, random circles + for i = 1, 50 do + local x, y = math.random(120)+4, math.random(120)+4 + c.arc(x, y, 3, 0, 2*math.pi) + c.fill() + end + -- And blur the result + raster.gaussian_blur(surf, 2.5) + + -- Then create a texture of it. + -- sparks_texture becomes a global variable + sparks_texture = cairo.pattern_create_for_surface(surf) + sparks_texture.set_extend("repeat") + + return sparks_texture +end + + +function render_frame(f, t) + init() + + -- Create a blurred copy of the video frame + local fsurf = f.create_cairo_surface() + raster.gaussian_blur(fsurf, 5) + + -- Function to create "wobble" effect on the text + local function blubble_mapper(x, y) + local nx = x + math.sin(x/30 + y/10 + t*2.0)*3 + local ny = y + math.cos(y/20 + x/20 + t*2.3)*3 + return nx, ny + end + + -- Find lines to be drawn + for i, line in pairs(lines) do + -- Check if the line is within time range. + -- "In time range" means starts fadetime seconds later than current time or ends "fadetime" seconds earlier. + if line.start_time <= t+fadetime and line.end_time > t-fadetime then + local x = 0 + local y = ypos + + -- Prepare a surface to draw on + local surf = cairo.image_surface_create(f.width, 200, "argb32") + local c = surf.create_context() + -- Select the font + c.select_font_face(font_name) + c.set_font_size(font_size) + -- Get the text extents for the line text if we don't have them already + if not line.te then line.te = c.text_extents(line.cleantext); line.fe = c.font_extents() end + -- And calculate the start X to have the line centered + x = (f.width - line.te.width) / 2 - line.te.x_bearing + -- Then make a path for the text + c.move_to(x, y) + c.text_path(line.cleantext) + + -- Create the "wobble" effect on the text + -- First get a Path object for the text + local path = c.copy_path() + -- Run the path through the mapping function + path.map_coords(blubble_mapper) + -- Clear the path in the context + c.new_path() + -- And add the modified path back + c.append_path(path) + + -- Prepare drawing the text outline + c.set_line_width(8) + -- Red outline + c.set_source_rgba(1, 0, 0, 1) + -- Stroke it but keep the path in the canvas + c.stroke_preserve() + -- Blur this outline + raster.gaussian_blur(surf, 1.5) + -- Prepare another, smaller outline on top + c.set_line_width(3) + -- This one is white + c.set_source_rgba(1, 1, 1, 1) + -- Stroke that one too, but clear the path afterwards + c.stroke() + + -- Now loop over the syllables to draw them one by one + local sumdur = line.start_time + for j, syl in pairs(line.kara) do + -- Get the text extents for this syllable + if not syl.te then syl.te = c.text_extents(syl.text) end + -- Prepare the path + c.move_to(x, y) + c.text_path(syl.text) + -- Wobble the path; this will work because the mapper is deterministic on X, Y and timestamp + local path = c.copy_path() + path.map_coords(blubble_mapper) + c.new_path() + c.append_path(path) + -- And advance X position + x = x + syl.te.x_advance + -- Now figure out whether this syllable is the active one or not + -- Use a more complicated test, this makes the first syllable be highlighted + -- also while the line is fading in, and the last while the line is fading out. + if (syl.i == 1 and t < sumdur+syl.dur) or + (syl.i == #line.kara and t > sumdur) or + (t >= sumdur and t < sumdur+syl.dur) then + -- Get the "sparks" texture + local sparks = get_sparks_texture() + -- Prepare a transformation matrix for it + local texmat = cairo.matrix_create() + texmat.init_rotate(t/10) + texmat.scale(3, 3) + sparks.set_matrix(texmat) + -- Use the texture + c.set_source(sparks) + -- And fill the path + c.fill() + else + -- Not the active syllable, fill it with a blurred video frame + -- Remember fsurf is the blurred video frame + c.set_source_surface(fsurf, 0, 0) + c.fill_preserve() + -- Also add a slight darkening to the fill + c.set_source_rgba(0, 0, 0, 0.2) + c.fill() + end + -- Advance the sum of syllable durations + sumdur = sumdur + syl.dur + end + + -- Figure out whether we're past the actual start/end time of the line and do some fading then + local final = surf + if t < line.start_time or t > line.end_time then + -- Make invisibility the amount the line is invisible + local invisibility + if t < line.start_time then + invisibility = (line.start_time - t) / fadetime + else + invisibility = (t - line.end_time) / fadetime + end + -- We'll need a new surface object here + final = cairo.image_surface_create(surf.get_width(), surf.get_height(), "argb32") + local c = final.create_context() + -- So we can alpha-blend the original drawn text image onto it using invisibility as alpha + c.set_source_surface(surf, 0, 0) + c.paint_with_alpha(1-invisibility) + -- And then do some heavy blur-out + raster.gaussian_blur(final, invisibility*15) + end + + -- Finally just overlay the text image on the video + f.overlay_cairo_surface(final, 0, 0) + end + end +end