Aegisub/automation/include/utils.auto3

234 lines
6.2 KiB
Plaintext

--[[
Copyright (c) 2005, Niels Martin Hansen, Rodrigo Braz Monteiro
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the Aegisub Group nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
]]
-- Variables with tables only hold references to the actual tables
-- Since a line is a table, a line needs to be copied, otherwise things break in bad ways
function copy_line(input)
local output = {}
output.kind = input.kind
if input.kind == "scomment" then
output.text = input.text
elseif input.kind == "comment" or input.kind == "dialogue" then
output.layer = input.layer
output.start_time = input.start_time
output.end_time = input.end_time
output.style = input.style
output.name = input.name
output.margin_l = input.margin_l
output.margin_r = input.margin_r
output.margin_v = input.margin_v
output.effect = input.effect
output.text = input.text
output.text_stripped = input.text_stripped
output.karaoke = input.karaoke -- Don't bother copying the karaoke table, it shouldn't be changed anyway
end
return output
end
-- Generates ASS hexadecimal string from R,G,B integer components, in &HBBGGRR& format
function ass_color(r,g,b)
return string.format("&H%02X%02X%02X&",b,g,r)
end
-- Format an alpha-string for \Xa style overrides
function ass_alpha(a)
return string.format("&H%02X&", a)
end
-- Format an ABGR string for use in style definitions (these don't end with & either)
function ass_style_color(r,g,b,a)
return string.format("&H%02X%02X%02X%02X",a,b,g,r)
end
-- Extract colour components of an ASS colour
function extract_color(s)
local a, b, g, r, d1, d2
-- Try a style first
d1, d2, a, b, g, r = string.find(s, "&H(%x%x)(%x%x)(%x%x)(%x%x)")
if a then
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), tonumber(a, 16)
end
-- Then a colour override
d1, d2, b, g, r = string.find(s, "&H(%x%x)(%x%x)(%x%x)&")
if b then
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), 0
end
-- Then an alpha override
d1, d2, a = string.find(s, "&H(%x%x)&")
if a then
return 0, 0, 0, tonumber(a, 16)
end
-- Ok how about HTML format then?
d1, d2, r, g, b, a = string.find(s, "#(%x%x)(%x%x)?(%x%x)?(%x%x)?")
if r then
return tonumber(r or 0, 16), tonumber(g or 0, 16), tonumber(b or 0, 16), tonumber(a or 0, 16)
end
-- Failed...
return nil
end
aegisub.colorstring_to_rgb = extract_color
-- Create an alpha override code from a style definition colour code
function alpha_from_style(scolor)
local r, g, b, a = extract_color(scolor)
return ass_alpha(a)
end
-- Create an colour override code from a style definition colour code
function color_from_style(scolor)
local r, g, b = extract_color(scolor)
return ass_color(r, g, b)
end
-- Converts HSV (Hue, Saturation, Value) to RGB
function HSV_to_RGB(H,S,V)
local r,g,b;
-- Saturation is zero, make grey
if S == 0 then
r = V*255
if r < 0 then
r = 0
end
if r > 255 then
r = 255
end
g = r
b = r
-- Else, calculate color
else
-- Calculate subvalues
local Hi = math.floor(H/60)
local f = H/60.0 - Hi
local p = V*(1-S)
local q = V*(1-f*S)
local t = V*(1-(1-f)*S)
-- Do math based on hue index
if Hi == 0 then
r = V*255.0
g = t*255.0
b = p*255.0
elseif Hi == 1 then
r = q*255.0
g = V*255.0
b = p*255.0
elseif Hi == 2 then
r = p*255.0
g = V*255.0
b = t*255.0
elseif Hi == 3 then
r = p*255.0
g = q*255.0
b = V*255.0
elseif Hi == 4 then
r = t*255.0
g = p*255.0
b = V*255.0
elseif Hi == 5 then
r = V*255.0
g = p*255.0
b = q*255.0
end
end
r = math.floor(r)
g = math.floor(g)
b = math.floor(b)
return r,g,b
end
-- Removes spaces at the start and end of string
function trim (s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end
-- UTF-8 string handling functions
-- Contributed by roxfan
-- Get the offset for the next character in the string, given the current offset
function next_utf_char(str, off)
local leadb = string.byte(str, off)
if leadb < 128 then
return off+1
elseif leadb < 224 then
return off+2
elseif leadb < 240 then
return off+3
elseif leadb < 248 then
return off+4
end
aegisub.output_debug(string.format("bad utf-8 in %q at %d",str,off))
return -1
end
-- Get the number of characters in the UTF-8 string (not the number of bytes)
function utf_len(str)
local i = 1
local len = 0
while i<=string.len(str) do
i = next_utf_char(str, i)
len = len + 1
end
-- aegisub.output_debug(string.format("utf_len(%q)=%d",str,len))
return len
end
-- Get the "head" and "tail" of a string, treating it as a sequence of words separated by one or more space-characters
function string.headtail(s)
local a, b, head, tail = string.find(s, "(.-)%s+(.*)")
if a then
return head, tail
else
return s, ""
end
end
-- Exclusive or of two boolean values
function xor(a, b)
if a and not b then
return a
elseif b and not a then
return b
else
return false
end
end