mirror of https://github.com/odrling/Aegisub
Another, much more complex and interesting, sample.
Originally committed to SVN as r1545.
This commit is contained in:
parent
dad1d4d02f
commit
25842be356
|
@ -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
|
Loading…
Reference in New Issue