script_name="Colourise" script_description="RGB Magic and HSL Sorcery" script_author="unanimated" script_version="5.0" script_namespace="ua.Colourise" local haveDepCtrl,DependencyControl,depRec=pcall(require,"l0.DependencyControl") if haveDepCtrl then script_version="5.0.0" depRec=DependencyControl{feed="https://raw.githubusercontent.com/unanimated/luaegisub/master/DependencyControl.json"} end info={} info.colourise=[[ "Colourise letter by letter" Alternates between 2-6 colours character by character, like 121212, or 123412341234. Works for all colour types (one at a time), based on the 'Apply to' menu. Handles inline tags and comments. "Colourise by word" Colourises by word instead of by letter. "Bounce Back" will create a sequence like 123432123432 instead of 12341234.]] info.shift=[[ "Shift" Shift can be used on an already colourised line to shift the colours by one letter. You have to set the right number of colours for it to work correctly. You'll be shitfing the colour type in the 'Apply to' menu. If "shift base" is "line", then it takes the colour for the first character from the last one. This way it can shift colours no matter how few/many tags there are and where. "Shift line by line" If you select a bunch of the same colourised lines, this shifts the colours line by line. First line is shifted by one letter, second one by two, etc.]] info.tunecolours=[[ "Tune colours" Loads all colours from a line into a GUI and lets you change them from there. Useful for changing colours in transforms or just tuning lines with multiple colours. "All selected" loads all 'unique' colours from all selected lines, rather than all from each line. This is much more useful for tuning/replacing colours in a larger selection. You can select "all/nontf/transf" to affect colours only from transforms, only those not from transforms, or all.]] info.setcolours=[[ "Set colours across whole line" This is like a preparation for a gradient by character. Select number of colours, and choose the colours to be used. For 3 colours, it will place one tag at the start, one in the middle, and one before the last character. For 2 colours, it'll be the first and last characters. Works for 2-10 colours and sets them evenly across the line.]] info.gradient=[[ "Gradient" Creates a gradient by character. (Uses Colourise button.) There are two modes: RGB and HSL. RGB is the standard, like lyger's GBC; HSL interpolates Hue, Saturation, and Lightness separately. Use the \c, \3c, \4c, \2c checkboxes on the right to choose which colours to gradient. "Short hue" makes sure that hue is interpolated in the shorter direction. Unchecking it will give you a different gradient in 50% cases. "Double HSL gradient" will make an extra round through Hue. Note that neither of these two options applies to RGB. "Use asterisks" places asterisks like lyger's GBC so that you can ungradient the line with his script. "Restart after each \N" will create the full gradient for each line if there are linebreaks. The edit box below that is for acceleration. You can still type accel in Effect, the old way, in the form: accel1.5, and this will override the GUI setting, ensuring you can have different accel for different lines. There are several differences from lyger's GBC: - RGB / HSL option - You can choose which types of colour you want to gradient - Other tags don't interfere with the colour gradients - You can use acceleration The hotkeyable macros run with \c and \3c checked, and 'short hue' for HSL.]] info.reverse=[[ "Reverse gradient" On the right, select types of colours to apply this to. For each colour type, colours from all tags in the line outside transforms are collected and returned in the opposite direction. A full gradient gets thus reversed. (This is separate from the Gradient function, so no need to check that.)]] info.match=[[ "Match Colours" This should apply to all colour tags in the line. c -> 3c: primary colour is copied to outline 3c -> c: outline colour is copied to primary c -> 4c: primary colour is copied to shadow 3c -> 4c: outline colour is copied to shadow c <-> 3c: primary and outline are switched Invert: all colours are inverted (red->green, yellow->blue, black->white)]] info.RGBHSL=[[ "Adjust RGB / HSL" Adjusts Red/Green/Blue or Hue/Saturation/Lightness. This works for lines with multiple same-type colour tags, including gradient by character. You can select from -255 to 255. Check types of colours you want it to apply to. "Apply to missing" means it will be applied to the colour set in style if there's no tag in the line. "Randomise" - if you set Lightness (or any RGB/HSL) to 20, the resulting colour will have anywhere between -20 and +20 of the original Lightness.]] info.general=[[ "Remember last" - Remembers last settings of checkboxes and dropdown menus. "Repeat last" - Repeat the function with last settings. "Save config" - Saves a config file in your Application Data folder with current settings. "Colourise" functions: if more selected, the one lowest in the GUI is run. Full manual: http://unanimated.hostfree.pw/ts/scripts-manuals.htm#colourise ]] re=require'aegisub.re' function cuts(subs) STAG="^{>?\\[^}]-}" ATAG="{[*>]?\\[^}]-}" COMM="{[^\\}]-}" ACLR="&H%x+&" ADD=aegisub.dialog.display ADP=aegisub.decode_path ak=aegisub.cancel for i=1,#subs do if subs[i].class=="dialogue" then line0=i-1 break end end end function colourise(subs,sel) cuts(subs) validateCol(subs,sel) GUI={ {x=0,y=0,class="label",label="Colours"}, {x=1,y=0,width=3,class="dropdown",name="clrs",items={"2","3","4","5","6","7"},value="2",hint="number of colours for\n'colourise letter by letter'"}, {x=0,y=1,class="label",label="Apply to: "}, {x=1,y=1,width=3,class="dropdown",name="kol",items={"\\c","\\3c","\\4c","\\2c"},value="\\c",hint="relevant for colourisng by letter, shifting, set across"}, {x=0,y=2,class="label",label="Shift base:"}, {x=1,y=2,width=3,class="dropdown",name="shit",items={"# of colours","line","1st2start","last2start","all2start"},value="# of colours",hint="shift by the number of colours the line had been colourised with,\nor shift the whole line (last colour becomes first)"}, {x=4,y=0,class="label",label="壹"}, {x=4,y=1,class="label",label="貳"}, {x=4,y=2,class="label",label="參"}, {x=4,y=3,class="label",label="肆"}, {x=4,y=4,class="label",label="伍"}, {x=4,y=5,class="label",label="陸"}, {x=4,y=6,class="label",label="漆"}, {x=5,y=0,class="color",name="c1"}, {x=5,y=1,class="color",name="c2"}, {x=5,y=2,class="color",name="c3"}, {x=5,y=3,class="color",name="c4"}, {x=5,y=4,class="color",name="c5"}, {x=5,y=5,class="color",name="c6"}, {x=5,y=6,class="color",name="c7"}, {x=0,y=3,width=3,class="checkbox",name="word",label="Colourise by word"}, {x=0,y=4,width=4,class="checkbox",name="bounce",label="Bounce back (123454321)",hint="colourise: sequence 1234321 instead of 12341234"}, {x=0,y=5,width=4,class="checkbox",name="cont",label="Shift line by line",hint="shift more with each line"}, {x=0,y=6,width=4,class="checkbox",name="across",label="Set colours across line"}, {x=6,y=0,class="label",label=" "}, {x=7,y=2,class="label",label="Red "}, {x=8,y=2,width=3,class="intedit",name="R",value=0,min=-255,max=255}, {x=7,y=3,class="label",label="Green "}, {x=8,y=3,width=3,class="intedit",name="G",value=0,min=-255,max=255}, {x=7,y=4,class="label",label="Blue "}, {x=8,y=4,width=3,class="intedit",name="B",value=0,min=-255,max=255}, {x=7,y=5,class="label",label="Hue"}, {x=8,y=5,width=3,class="intedit",name="huehue",value=0,min=-255,max=255}, {x=7,y=6,class="label",label="Saturation"}, {x=8,y=6,width=3,class="intedit",name="satur",value=0,min=-255,max=255}, {x=7,y=7,class="label",label="Lightness"}, {x=8,y=7,width=3,class="intedit",name="light",value=0,min=-255,max=255}, {x=7,y=8,class="checkbox",name="k1",label="\\c",value=true}, {x=8,y=8,class="checkbox",name="k3",label="\\3c"}, {x=9,y=8,class="checkbox",name="k4",label="\\4c"}, {x=10,y=8,class="checkbox",name="k2",label="\\2c"}, {x=7,y=9,width=2,class="checkbox",name="mktag",label="Apply to missing",hint="Apply even to colours without tags in line"}, {x=9,y=9,width=2,class="checkbox",name="randoom",label="Randomise",hint="randomise RGB/HSL within the\nspecified range in each direction"}, -- Match {x=7,y=0,height=2,class="label",label="Match\ncolours:"}, {x=8,y=0,class="checkbox",name="match13",label="c->3c",hint="copy primary to outline"}, {x=9,y=0,class="checkbox",name="match31",label="3c->c",hint="copy outline to primary"}, {x=8,y=1,class="checkbox",name="match14",label="c->4c",hint="copy primary to shadow"}, {x=9,y=1,class="checkbox",name="match34",label="3c->4c",hint="copy outline to shadow"}, {x=10,y=0,class="checkbox",name="match131",label="c<->3c",hint="switch primary and outline"}, {x=10,y=1,class="checkbox",name="invert",label="Invert",hint="invert colours (applies to the types chacked below)"}, -- Gradient {x=0,y=7,width=2,class="checkbox",name="grad",label="Gradient "}, {x=2,y=7,width=3,class="checkbox",name="hueshort",label="Shorter hue",value=true}, {x=5,y=7,class="dropdown",name="grtype",items={"RGB","HSL"},value="HSL"}, {x=0,y=8,width=4,class="checkbox",name="gradn",label="Restart after each \\N",hint="Restart gradient after each linebreak"}, {x=0,y=9,width=3,class="checkbox",name="double",label="Double HSL gradient"}, {x=0,y=10,width=3,class="checkbox",name="ast",label="Use asterisks"}, {x=0,y=11,width=3,class="floatedit",name="acc",value=1,min=0,hint="Acceleration for gradients"}, -- Tune/Reverse {x=3,y=9,width=3,class="checkbox",name="tuneall",label="Tune all selected",hint="load from / apply to all selected lines\nrather than one by one"}, {x=5,y=8,class="dropdown",name="tfmode",items={"all tags","regular","transf"},value="all tags", hint="all tags / regular tags / tags in transforms\napplies to 'Tune colours' and RGB/HSL"}, {x=3,y=10,width=3,class="checkbox",name="reverse",label="Reverse colours", hint="Reverse the direction of non-transform colours across the line \nfor the types checked on the right"}, {x=7,y=10,width=2,class="checkbox",name="rem",label="Remember last",hint="Remember last settings"}, {x=9,y=10,width=2,class="checkbox",name="rept",label="Repeat last",hint="Repeat with last settings"}, {x=7,y=11,width=2,class="checkbox",name="save",label="Save config",hint="Saves current configuration\n(for most things)"}, {x=9,y=11,width=2,class="label",label="Colourise version "..script_version..""}, {x=3,y=11,width=3,class="dropdown",name="help",value=": Help Menu :",hint="Choose a topic. Use any button that's not 'Cancel'.", items={": Help Menu :","colourise","shift","setcolours","gradient","tunecolours","reverse","match","RGBHSL","general"}} } loadconfig() if colourblind and res.rem then for key,val in ipairs(GUI) do if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" or val.class:match"edit" then val.value=res[val.name] end if val.name=="save" then val.value=false end if val.name=="help" then val.value=": Help Menu :" end end end HELP=false NOHELP=true repeat if HELP then if NOHELP then table.insert(GUI,{x=0,y=12,width=11,height=7,class="textbox",name="helpbox"}) end for key,val in ipairs(GUI) do if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" then val.value=res[val.name] end if val.name=="save" then val.value=false end if val.name=="rept" then val.value=false end if val.name=="helpbox" then val.value=info[res.help] end if val.name=="help" then val.value=": Help Menu :" end end NOHELP=false end P,res=ADD(GUI,{"Colourise","Shift","Tune/Rvrs","Match Colours","RGB/HSL","Black Out"},{ok='Colourise',close='Black Out'}) if res.help~=": Help Menu :" then HELP=true else HELP=false end until P=="Black Out" or res.help==": Help Menu :" if P=="Black Out" then ak() end if res.save then saveconfig() ak() end randomise=res.randoom if res.tfmode=="all tags" or res.tfmode=="regular" then modereg=true else modereg=false end if res.tfmode=="all tags" or res.tfmode=="transf" then modetf=true else modetf=false end if P=="Colourise" then repetition() if res.grad then gradient(subs,sel) elseif res.across then gcolours(subs,sel) else colors(subs,sel) end end if P=="Tune/Rvrs" then repetition() if res.reverse then rvrsgrad(subs,sel) else ctune(subs,sel) end end if P=="Shift" then repetition() if res.shit:match'start' then shift2(subs,sel) else shift(subs,sel) end end if P=="Match Colours" or P=="RGB/HSL" then repetition() styleget(subs) match_col(subs,sel) end lastres=res colourblind=true return sel end -- Colour Ice -- function colors(subs,sel) local c={} for k=1,7 do c[k]=res["c"..k]:gsub("#(%x%x)(%x%x)(%x%x)","&H%3%2%1&") end for z,i in ipairs(sel) do progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") line=subs[i] text=line.text tags=text:match(STAG) or "" if not tags:match("\\p1") then text=text:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) visible=nobrea(text) local kl=res.kol if kl=="\\c" then text=text:gsub("\\1?c[^l\\})]*([\\}])","%1") end if kl=="\\3c" then text=text:gsub("\\3c[^\\})]*","") end if kl=="\\4c" then text=text:gsub("\\4c[^\\})]*","") end if kl=="\\2c" then text=text:gsub("\\2c[^\\})]*","") end text=text:gsub("{}","") local col={} -- table with colours to use for t=1,res.clrs do table.insert(col,wrap(kl..c[t])) end if res.bounce and tonumber(res.clrs)>2 then for t=res.clrs-1,2,-1 do table.insert(col,wrap(kl..c[t])) end end orig=text:gsub(STAG,""):gsub("\\N","{\\N}") -- save positions for inline tags and comments inTags=inline_pos(orig) if not res.word then -- by letter local letrz=re.find(visible,".") or {} letrz=re_test(letrz,visible) nt="" local p=1 for k=2,#letrz do local ltr=letrz[k].str -- add colour tag positions to table if ltr~=" " then p=p%#col+1 -- cycle colours table.insert(inTags,{n=k-1,t=col[p]}) end end table.sort(inTags,function(a,b) return a.n0 then text=insertxt(text,BPL,"\\N") end LBP=BPL or 0 end LBP=nil visible2=nobrea(text) c=c+1 until visible==visible2 or c==666 txt_check(visible,visible2,i) line.text=text subs[i]=line end end end function insertxt(text,txtpos,thing) pos=0 tcount=0 for tg,tx in text:gmatch("("..ATAG..")([^{]*)") do sl=tx:len() tl=tg:len() if sl+posklrs then count=1 end text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) line.text=text subs[i]=line end end function shift2(subs,sel) local k=res.kol for z,i in ipairs(sel) do progress("Processing line #"..i-line0.." ["..z.."/"..#sel.."]") line=subs[i] text=line.text tags=text:match(STAG) or "" inline=text:gsub(STAG,"") local kol if res.shit:match"1st" then kol=inline:match(k.."%b&&") inline=inline:gsub(k.."%b&&","",1) else kol=inline:match(".*("..k.."%b&&)") end if res.shit:match'last' then inline=inline:gsub("(.*)"..k.."%b&&","%1") end if res.shit:match'all' then inline=inline:gsub(k.."%b&&","") end inline=inline:gsub("{}","") if kol then if tags=="" then tags=wrap(kol) else tags=addtag3(kol,tags) end text=tags..inline line.text=text subs[i]=line end end end -- Match colours -- function match_col(subs,sel) local MC if P=="Match Colours" then _=0 MC=1 for key,val in ipairs(GUI) do if val.name and val.name:match"match" and res[val.name] then _=_+1 end if val.name and val.name=="invert" and res[val.name] then _=_+1 end end if _>1 then t_error("Multiple checkboxes for matching checked.\nResults may be unpredictable.") end end local RGB, HSL lvlr=res.R lvlg=res.G lvlb=res.B hue=res.huehue sat=res.satur lite=res.light if lvlr~=0 or lvlg~=0 or lvlb~=0 then RGB=true end if hue~=0 or sat~=0 or lite~=0 then HSL=true end for z,i in ipairs(sel) do progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") line=subs[i] text=line.text if defaref and line.style=="Default" then sr=defaref elseif lastref and laststyle==line.style then sr=lastref else sr=stylechk(line.style) end lastref=sr laststyle=line.style stylecol=stylecolours() text=text:gsub("\\1c","\\c") if not text:match("^{\\") then text=text:gsub("^","{\\}") end tags=text:match(STAG) notftags=tags:gsub("\\t%b()","") -- Matching if MC then if res.match13 then text=macchi(text,"\\c","\\3c",primary) end if res.match31 then text=macchi(text,"\\3c","\\c",outline) end if res.match14 then text=macchi(text,"\\c","\\4c",primary) end if res.match34 then text=macchi(text,"\\3c","\\4c",outline) end end -- switch primary and border if MC and res.match131 then if not notftags:match("\\c&") then text=addtag3("\\c"..primary,text) end if not notftags:match("\\3c") then text=addtag3("\\3c"..outline,text) end text=text:gsub("\\c&","\\tempc&"):gsub("\\3c","\\c"):gsub("\\tempc","\\3c") end -- Invert All Colours if MC and res.invert then match="[" for n=1,4 do ctg="\\"..n.."c" ctg=ctg:gsub("1","") if not notftags:match(ctg) and n~=2 then text=addtag3(ctg..stylecol[n],text) end if n>1 and res['k'..n] then match=match..n end end match=match..']' if res.k1 then match=match..'?' end match=match:gsub("%[%]%?","") text=text:gsub("(\\"..match.."c&H)(%x%x%x%x%x%x)&",function(tg,col) invcol="" for kol in col:gmatch("(%x%x)") do dkol=tonumber(kol,16) idkol=255-dkol ikol=tohex(idkol) invcol=invcol..ikol end return tg..invcol.."&" end) end -- RGB / HSL if P=="RGB/HSL" then corols={} if res.k1 then table.insert(corols,1) end if res.k2 then table.insert(corols,2) end if res.k3 then table.insert(corols,3) end if res.k4 then table.insert(corols,4) end for i=1,#corols do kl="\\"..corols[i].."c" kl=kl:gsub("1","") if res.mktag and not notftags:match(kl) then text=addtag3(kl..stylecol[corols[i]],text) end if RGB then text=rgbhslmod(text,kl,rgbm) end if HSL then text=rgbhslmod(text,kl,hslm) end end end text=text:gsub("\\([\\}])","%1") :gsub("\\t%([^\\%)]*%)","") :gsub("{}","") line.text=text subs[i]=line end end function macchi(text,c1,c2,kv) if not notftags:match(c1.."&") then text=addtag3(c1..kv,text) end text=text:gsub(ATAG,function(ctags) ctags=ctags:gsub(c2..ACLR,""):gsub(c1.."("..ACLR..")",c1.."%1"..c2.."%1") return ctags end) return text end function rgbhslmod(text,kl,ef) local segments={} if text:match("\\t%b()") then for seg1,seg2 in text:gmatch("(.-)(\\t%b())") do table.insert(segments,seg1) table.insert(segments,seg2) end table.insert(segments,text:match("^.*\\t%b()(.-)$")) else table.insert(segments,text) end for q=1,#segments do if segments[q]:match("\\t%b()") and modetf then segments[q]=ef(segments[q],kl) end if not segments[q]:match("\\t%b()") and modereg then segments[q]=ef(segments[q],kl) end end nt="" for q=1,#segments do nt=nt..segments[q] end return nt end function rgbm(text,kl) text=text:gsub(kl.."&H(%x%x)(%x%x)(%x%x)&",function(kol1,kol2,kol3) kol1n=brightness(kol1,lvlb) kol2n=brightness(kol2,lvlg) kol3n=brightness(kol3,lvlr) return kl.."&H"..kol1n..kol2n..kol3n.."&" end) return text end function hslm(text,kl) text=text:gsub(kl.."&H(%x%x)(%x%x)(%x%x)&",function(kol1,kol2,kol3) H1,S1,L1=RGB_to_HSL(kol3,kol2,kol1) H1,S1,L1=RGB_to_HSL(kol3,kol2,kol1) H=H1+hue/255 S=S1+sat/255 L=L1+lite/255 if randomise then H2=H1-hue/255 S2=S1-sat/255 L2=L1-lite/255 H=math.random(H*1000,H2*1000)/1000 S=math.random(S*1000,S2*1000)/1000 L=math.random(L*1000,L2*1000)/1000 end H,S,L=HSLround(H,S,L) kol3n,kol2n,kol1n=HSL_to_RGB(H,S,L) kol3n=tohex(round(kol3n)) kol2n=tohex(round(kol2n)) kol1n=tohex(round(kol1n)) return kl.."&H"..kol1n..kol2n..kol3n.."&" end) return text end -- GRADIENT -- function gradient(subs,sel) styleget(subs) if res.grtype=="RGB" then GRGB=true else GRGB=false end if res.ast then ast="*" else ast="" end for z,i in ipairs(sel) do progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") line=subs[i] re_check=0 repeat text=line.text if text:match '\\p1' then break end text=text:gsub("\\N (%w)","\\N%1"):gsub(" +\\N"," \\N"):gsub("{%*\\[^}]+}",""):gsub("{}","") visible=nobrea1(text) -- comments local komments='' for cm in text:gmatch("{[^\\{}]-}") do komments=komments..cm end -- something to [try to] deal with spaces and linebreaks because dumb renderer inconsistency breaks={} for br in text:gmatch(" ?\\[Nh]") do table.insert(breaks,br) end acc=line.effect:match("accel ?(%d+%.?%d*)") or res.acc text=text:gsub("\\c&","\\1c&"):gsub("\\c([}\\])","\\1c%1") :gsub(" *(\\[Nh]) *","{%1}") :gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) text=tagmerge(text) after=text:gsub(STAG,"") nc=text:gsub(COMM,"") if text:match(ATAG.."$") then text=text.."wtfwhywouldyoudothis" end -- colours from style sr=stylechk(line.style) stylecol=stylecolours() -- which types will be used applycol={} if res.k1 and after:match("\\1c") then table.insert(applycol,1) end if res.k2 and after:match("\\2c") then table.insert(applycol,2) end if res.k3 and after:match("\\3c") then table.insert(applycol,3) end if res.k4 and after:match("\\4c") then table.insert(applycol,4) end for g=1,#applycol do ac=applycol[g] sc=stylecol[ac] tags=text:match(STAG) or "" -- linebreak adjustment if res.gradn then startc=tags:match("\\"..ac.."c&H%x+&") or "\\"..ac.."c"..sc endc=nc:match("(\\"..ac.."c&H%x+&)[^}]-}%S+$") or "" text=text:gsub("([%S])%s*({\\N.-})","{"..endc.."}%1%2{"..startc.."}"):gsub("{}","") end -- set style colour to reset c tags text=text:gsub("(\\"..ac.."c)([}\\])","%1"..sc.."%2") -- back up original orig=text text=text:gsub("{[^\\}]-}","") -- leave only releavant colour tags, nuke all other ones, add colour from style if missing at the start ctext=text:gsub("\\N","") --:gsub("\\[ibusaqk]%d?([\\}])","%1") :gsub("\\[^1234][^c][^\\}]*","") :gsub("\\[^"..ac.."]c[^\\}]*","") :gsub("{%**}","") ctext=ctext:gsub("\\[^\\}]+",function(tag) if tag:match("\\"..ac.."c%b&&") then return tag else return "" end end) :gsub("{%**}","") if not ctext:match("^{\\") then ctext="{\\kolor}"..ctext end if not ctext:match("^{[^}]-\\"..ac.."c") then ctext=ctext:gsub("^({\\[^}]-)}","%1\\"..ac.."c"..sc.."}") end -- make tables of colour tags and text after them linecol={} posi={} coltext={} pos=0 for k,t in ctext:gmatch("{[^}]-\\"..ac.."c&H(%x+)&[^}]-}([^{]+)") do table.insert(posi,pos) table.insert(linecol,k) table.insert(coltext,t) ps=re.find(t,".") pos=#ps end -- text for each colour gradtext="" -- sequence for each colour tag / text for c=1,#linecol-1 do -- get RBG [and HSL if needed] B1,G1,R1=linecol[c]:match("(%x%x)(%x%x)(%x%x)") B2,G2,R2=linecol[c+1]:match("(%x%x)(%x%x)(%x%x)") if not GRGB then H1,S1,L1=RGB_to_HSL(R1,G1,B1) H2,S2,L2=RGB_to_HSL(R2,G2,B2) if res.hueshort then if H2>H1 and H2-H1>0.5 then H1=H1+1 end if H2

0.5 then H2=H2+1 end end if res.double then if H2>H1 then H2=H2+1 else H1=H1+1 end if H1>2 or H2>2 then H2=H2-1 H1=H1-1 end end end -- letters of this sequence textseq={} ltrmatches=re.find(coltext[c],".") for l=1,#ltrmatches do table.insert(textseq,ltrmatches[l].str) end -- new text starting with original colour tag and first letter ntxt="{\\"..ac.."c&H"..linecol[c].."&}"..textseq[1] -- calculate colours for the other letters in sequence for l=2,posi[c+1] do if textseq[l]~=" " then if GRGB then -- RGB NC=acgrad(linecol[c],linecol[c+1],posi[c+1],l,acc) else -- HSL local acc_fac=(l-1)^acc/(posi[c+1])^acc H=acc_fac*(H2-H1)+H1 S=acc_fac*(S2-S1)+S1 L=acc_fac*(L2-L1)+L1 R,G,B=HSL_to_RGB(H,S,L) R=tohex(round(R)) G=tohex(round(G)) B=tohex(round(B)) NC="&H"..B..G..R.."&" end ncol="{"..ast.."\\"..ac.."c"..NC.."}" -- colour + letter ntxt=ntxt..ncol..textseq[l] else -- spaces (no tags) ntxt=ntxt..textseq[l] end end gradtext=gradtext..ntxt end -- add final tag + text gradtext=gradtext.."{\\"..ac.."c&H".. linecol[#linecol].."&}".. coltext[#coltext] text=tags..gradtext -- merge with original text=textmod(orig,text) end text=text:gsub(ATAG,function(tg) return colkill(tg) end) text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) :gsub("wtfwhywouldyoudothis","") repeat text,r=text:gsub("{([^}]-)(\\[Nh])([^}]-)}","%2{%1%3}") until r==0 text=text:gsub("{%**}",""):gsub("(%S)(\\[Nh])","%1 %2") :gsub("([^{])%*\\","%1\\") local b=0 text=text:gsub(" \\[Nh]",function() b=b+1 return breaks[b] end)..komments visible2=nobrea1(text) if visible~=visible2 then re_check=re_check+1 end until visible==visible2 or re_check==256 txt_check(visible,visible2,i) line.text=text subs[i]=line end end -- Reverse Colours -- function rvrsgrad(subs,sel) for z,i in ipairs(sel) do progress("Colourising line #"..i-line0.." ["..z.."/"..#sel.."]") line=subs[i] text=line.text text=text:gsub("\\t(%b())",function(t) return "\\tra"..t:gsub("\\","/") end) :gsub("\\c&","\\1c&") after=text:gsub(STAG,"") applycol={} if res.k1 and after:match("\\1c") then table.insert(applycol,1) end if res.k2 and after:match("\\2c") then table.insert(applycol,2) end if res.k3 and after:match("\\3c") then table.insert(applycol,3) end if res.k4 and after:match("\\4c") then table.insert(applycol,4) end for g=1,#applycol do ac=applycol[g] tagtab={} coltab={} for tt,cc in text:gmatch("(.-)(\\"..ac.."c&H%x+&)") do table.insert(tagtab,tt..cc) table.insert(coltab,cc) end END=text:match("^.*\\"..ac.."c&H%x+&(.-)$") for t=1,#tagtab do o=#tagtab-t+1 tagtab[t]=tagtab[t]:gsub("\\"..ac.."c&H%x+&",coltab[o]) end nt=END for a=#tagtab,1,-1 do nt=tagtab[a]..nt end text=nt end text=text:gsub("\\tra(%b())",function(t) return "\\t"..t:gsub("/","\\") end) :gsub("\\1c&","\\c&") line.text=text subs[i]=line end end function stylecolours() stylecol={} notf=text:gsub("\\t%b()","") table.insert(stylecol,(sr.color1:gsub("H%x%x","H"))) primary=notf:match("^{[^}]-\\c(&H%x+&)") or stylecol[1] table.insert(stylecol,(sr.color2:gsub("H%x%x","H"))) secondary=notf:match("^{[^}]-\\3c(&H%x+&)") or stylecol[2] table.insert(stylecol,(sr.color3:gsub("H%x%x","H"))) outline=notf:match("^{[^}]-\\3c(&H%x+&)") or stylecol[3] table.insert(stylecol,(sr.color4:gsub("H%x%x","H"))) shadow=notf:match("^{[^}]-\\c(&H%x+&)")or stylecol[4] return stylecol end function acgrad(C1,C2,total,l,acc) local acc_fac=(l-1)^acc/(total)^acc B1,G1,R1=C1:match("(%x%x)(%x%x)(%x%x)") B2,G2,R2=C2:match("(%x%x)(%x%x)(%x%x)") A1=C1:match("(%x%x)") R1=R1 or A1 A2=C2:match("(%x%x)") R2=R2 or A2 nR1=(tonumber(R1,16)) nR2=(tonumber(R2,16)) R=acc_fac*(nR2-nR1)+nR1 R=tohex(round(R)) CC="&H"..R.."&" if B1 then nG1=(tonumber(G1,16)) nG2=(tonumber(G2,16)) nB1=(tonumber(B1,16)) nB2=(tonumber(B2,16)) G=acc_fac*(nG2-nG1)+nG1 B=acc_fac*(nB2-nB1)+nB1 G=tohex(round(G)) B=tohex(round(B)) CC="&H"..B..G..R.."&" end return CC end -- reamnimatools --------------------------------------------- function esc(str) str=str:gsub("[%%%(%)%[%]%.%-%+%*%?%^%$]","%%%1") return str end function round(num) num=math.floor(num+0.5) return num end function tagmerge(t) repeat t,r=t:gsub("({\\[^}]-)}{(\\[^}]-})","%1%2") until r==0 return t end function wrap(str) return "{"..str.."}" end function nobra(t) return t:gsub("%b{}","") end function nobrea(t) return t:gsub("%b{}",""):gsub("\\N","") end function nobrea1(t) return t:gsub("%b{}",""):gsub(" *\\N *"," ") end function logg(m) m=tf(m) or "nil" aegisub.log("\n "..m) end function logg2(m) local lt=type(m) aegisub.log("\n >> "..lt) if lt=='table' then aegisub.log(" (#"..#m..")") if not m[1] then for k,v in pairs(m) do if type(v)=='table' then vvv='[table]' elseif type(v)=='number' then vvv=v..' (n)' else vvv=v end aegisub.log("\n "..k..': '..vvv) end elseif type(m[1])=='table' then aegisub.log("\n nested table") else aegisub.log("\n {"..table.concat(m,', ').."}") end else m=tf(m) or "nil" aegisub.log("\n "..m) end end function loggtab(m) m=tf(m) or "nil" aegisub.log("\n {"..table.concat(m,';').."}") end function progress(msg) if aegisub.progress.is_cancelled() then ak() end aegisub.progress.title(msg) end function t_error(message,cancel) ADD({{class="label",label=message}},{"OK"},{close='OK'}) if cancel then ak() end end function tf(val) if val==true then ret="true" elseif val==false then ret="false" else ret=val end return ret end function detf(txt) if txt=="true" then ret=true elseif txt=="false" then ret=false else ret=txt end return ret end function addtag3(tg,txt) no_tf=txt:gsub("\\t%b()","") tgt=tg:match("(\\%d?%a+)[%d%-&]") if not tgt then t_error("Adding tag '"..tg.."' failed.") end if no_tf:match("^({[^}]-)"..tgt.."[%d%-&]") then txt=txt:gsub("^({[^}]-)"..tgt.."[%d%-&][^\\}]*","%1"..tg) elseif not txt:match("^{\\") then txt="{"..tg.."}"..txt elseif txt:match("^{[^}]-\\t") then txt=txt:gsub("^({[^}]-)\\t","%1"..tg.."\\t") else txt=txt:gsub("^({\\[^}]-)}","%1"..tg.."}") end return txt end -- save inline tags function inline_pos(t) inTags={} tl=t:len() if tl==0 then return {} end p=0 t1='' repeat seg=t:match("^(%b{})") -- try to match tags/comments if seg then table.insert(inTags,{n=p,t=seg}) else seg=t:match("^([^{]+)") -- or match text if not seg then t_error("Error: There appears to be a problem with the brackets here...\n"..t1..t,1) end SL=re.find(seg,".") p=p+#SL -- position of next '{' [or end] end t1=t1..seg t=t:gsub("^"..esc(seg),"") tl=t:len() until tl==0 return inTags end -- rebuild inline tags function inline_ret(t,tab) tl=t:len() nt='' kill='_Z#W_' -- this is supposed to never match for k,v in ipairs(tab) do N=tonumber(v.n) if N==0 then nt=nt..v.t else m='.' -- match how many chars at the start m=m:rep(N) RS=re.find(t,m) seg=RS[1].str seg=re.sub(seg,'^'..kill,'') nt=nt..seg..v.t kill=m -- how many matched in the last round end end -- the rest seg=re.sub(t,'^'..kill,'') nt=nt..seg return nt end function retextmod(orig,text) local v1,v2,c,t2 v1=nobrea(orig) c=0 repeat t2=textmod(orig,text) v2=nobrea(text) c=c+1 until v1==v2 or c==666 if v1~=v2 then logg("Something went wrong with the text...") logg(v1) logg(v2) end return t2 end function textmod(orig,text) if text=="" then return orig end tk={} tg={} text=text:gsub("{\\\\k0}","") text=tagmerge(text) vis=nobra(text) ltrmatches=re.find(vis,".") if not ltrmatches then logg("text: "..text..'\nvisible: '..vis) logg("If you're seeing this, something really weird is happening with the re module.\nTry this again or rescan Autoload.") end for l=1,#ltrmatches do table.insert(tk,ltrmatches[l].str) end stags=text:match(STAG) or "" text=text:gsub(STAG,"") :gsub("{[^\\}]-}","") orig=orig:gsub("{([^\\}]+)}",function(c) return wrap("\\\\"..c.."|||") end) count=0 for seq in orig:gmatch("[^{]-{%*?\\[^}]-}") do chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") pos=re.find(chars,".") if pos==nil then ps=0+count else ps=#pos+count end tgl={p=ps,t=tak,a=as} table.insert(tg,tgl) count=ps end count=0 for seq in text:gmatch("[^{]-{%*?\\[^}]-}") do chars,as,tak=seq:match("([^{]-){(%*?)(\\[^}]-)}") pos=re.find(chars,".") if pos==nil then ps=0+count else ps=#pos+count end tgl={p=ps,t=tak,a=as} table.insert(tg,tgl) count=ps end newline="" for i=1,#tk do newline=newline..tk[i] newt="" for n,t in ipairs(tg) do if t.p==i then newt=newt..t.a..t.t end end if newt~="" then newline=newline.."{"..as..newt.."}" end end newtext=stags..newline:gsub("(|||)(\\\\)","%1}{%2"):gsub("({[^}]-)\\\\([^\\}]-)|||","{%2}%1") text=newtext:gsub("{}","") return text end function duplikill(tagz) local tags1={"blur","be","bord","shad","xbord","xshad","ybord","yshad","fs","fsp","fscx","fscy","frz","frx","fry","fax","fay"} local tags2={"c","2c","3c","4c","1a","2a","3a","4a","alpha"} tagz=tagz:gsub("\\t%b()",function(t) return t:gsub("\\","|") end) for i=1,#tags1 do tag=tags1[i] repeat tagz,c=tagz:gsub("|"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%1%2") until c==0 repeat tagz,c=tagz:gsub("\\"..tag.."[%d%.%-]+([^}]-)(\\"..tag.."[%d%.%-]+)","%2%1") until c==0 end tagz=tagz:gsub("\\1c&","\\c&") for i=1,#tags2 do tag=tags2[i] repeat tagz,c=tagz:gsub("|"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%1%2") until c==0 repeat tagz,c=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") until c==0 end repeat tagz,c=tagz:gsub("\\fn[^\\}]+([^}]-)(\\fn[^\\}]+)","%2%1") until c==0 repeat tagz,c=tagz:gsub("(\\[ibusq])%d(.-)(%1%d)","%2%3") until c==0 repeat tagz,c=tagz:gsub("(\\an)%d(.-)(%1%d)","%3%2") until c==0 tagz=tagz:gsub("(|i?clip%(%A-%))(.-)(\\i?clip%(%A-%))","%2%3") :gsub("(\\i?clip%b())(.-)(\\i?clip%b())",function(a,b,c) if a:match("m") and c:match("m") or not a:match("m") and not c:match("m") then return b..c else return a..b..c end end) tagz=tagz:gsub("|","\\"):gsub("\\t%([^\\%)]-%)","") return tagz end function colkill(tagz) tagz=tagz:gsub("\\1c&","\\c&") local tags2={"c","2c","3c","4c"} for i=1,#tags2 do tag=tags2[i] tagz=tagz:gsub("\\"..tag.."&H%x+&([^}]-)(\\"..tag.."&H%x+&)","%2%1") end return tagz end function RGB_to_HSL(Red,Green,Blue) R=(tonumber(Red,16)/255) G=(tonumber(Green,16)/255) B=(tonumber(Blue,16)/255) Min=math.min(R,G,B) Max=math.max(R,G,B) del_Max=Max-Min L=(Max+Min)/2 if del_Max==0 then H=0 S=0 else if L<0.5 then S=del_Max/(Max+Min) else S=del_Max/(2-Max-Min) end del_R=(((Max-R)/6)+(del_Max/2))/del_Max del_G=(((Max-G)/6)+(del_Max/2))/del_Max del_B=(((Max-B)/6)+(del_Max/2))/del_Max if R==Max then H=del_B-del_G elseif G==Max then H=(1/3)+del_R-del_B elseif B==Max then H=(2/3)+del_G-del_R end if H<0 then H=H+1 end if H>1 then H=H-1 end end return H,S,L end function HSL_to_RGB(H,S,L) if S==0 then R=L*255 G=L*255 B=L*255 else if L<0.5 then var_2=L*(1+S) else var_2=(L+S)-(S*L) end var_1=2*L-var_2 R=255*Hue_to_RGB(var_1,var_2,H+(1/3)) G=255*Hue_to_RGB(var_1,var_2,H) B=255*Hue_to_RGB(var_1,var_2,H-(1/3)) end return R,G,B end function Hue_to_RGB(v1,v2,vH) if vH<0 then vH=vH+1 end if vH>1 then vH=vH-1 end if (6*vH)<1 then return(v1+(v2-v1)*6*vH) end if (2*vH)<1 then return(v2) end if (3*vH)<2 then return(v1+(v2-v1)*((2/3)-vH)*6) end return(v1) end function HSLround(H,S,L) if H>1 then H=H-1 end if H<0 then H=H+1 end if randomise and S>1 then S=1-S end if S>1 then S=1 end if randomise then S=math.abs(S) end if S<0 then S=0 end if randomise and L>1 then L=1-L end if L>1 then L=1 end if randomise then L=math.abs(L) end if L<0 then L=0 end return H,S,L end function brightness(klr,lvl) klr=tonumber(klr,16) if randomise then rAn=math.random(klr-lvl,klr+lvl) klr=round(rAn) if klr<0 then klr=math.abs(klr) end if klr>255 then klr=255-(klr-255) end else klr=klr+lvl end if klr<0 then klr=0 end if klr<10 then klr="0"..klr else klr=tohex(klr) end return klr end function tohex(num) n1=math.floor(num/16) n2=math.floor(num%16) num=tohex1(n1)..tohex1(n2) return num end function tohex1(num) HEX={"1","2","3","4","5","6","7","8","9","A","B","C","D","E"} if num<1 then num="0" elseif num>14 then num="F" else num=HEX[num] end return num end function styleget(subs) styles={} for i=1,#subs do if subs[i].class=="style" then table.insert(styles,subs[i]) end if subs[i].class=="dialogue" then break end end end function stylechk(sn) for i=1,#styles do if sn==styles[i].name then sr=styles[i] if styles[i].name=="Default" then defaref=styles[i] end break end end if sr==nil then t_error("Style '"..sn.."' doesn't exist.",1) end return sr end function txt_check(t1,t2,i) if t1~=t2 then local bad for s=1,#badsel do if badsel[s]==i then bad=1 end end if bad then logg("Line #"..i-line0..": Operation failed, probably because of malformed tags. Undo (Ctrl+Z) and fix the line before trying again.\n") else logg("Line #"..i-line0..": It appears that characters have been lost or added. \n If the problem isn't obvious from the two lines below, it's probably a failure of the re module.\n Undo (Ctrl+Z) and try again (Repeat Last might work). If the problem persists, rescan Autoload Dir.\n If you keep getting the SAME strange results, then it may be a bug in Colourise.\n>> "..t1.."\n--> "..t2.."\n") end end end function re_test(letrz,visible) local count=0 repeat local nt='' for l=1,#letrz do local ltr=letrz[l].str nt=nt..ltr end count=count+1 if nt~=visible then letrz=re.find(visible,".") or {} end until nt==visible or count==100 return letrz end function validateCol(subs,sel) local err badsel={} for z,i in ipairs(sel) do local line=subs[i] tx=line.text for c in tx:gmatch("\\[1234]?c[^l\\})]+") do if not c:match("c&H%x%x%x%x%x%x&") then logg("Line #"..i-line0..": "..c) err=1 table.insert(badsel,i) end end end if err==1 then t_error("Some malformed colour tags have been found in selected lines.\nThis means the values don't match the standard format of\n&&HFFFFFF&&. Some things are likely to break.\nSee the log for a list of errors found.") end end function repetition() if lastres and res.rept then res=lastres end end function saveconfig() colconf="Colourise Configutation\n\n" for key,val in ipairs(GUI) do if val.class=="checkbox" and val.name~="save" then colconf=colconf..val.name..":"..tf(res[val.name]).."\n" end if val.class=="dropdown" and val.name~=": Help Menu :" or val.class=="color" then colconf=colconf..val.name..":"..res[val.name].."\n" end end colourconfig=ADP("?user").."\\colourise.conf" file=io.open(colourconfig,"w") file:write(colconf) file:close() ADD({{class="label",label="Config Saved to:\n"..colourconfig}},{"OK"},{close='OK'}) end function loadconfig() colourconfig=ADP("?user").."\\colourise.conf" file=io.open(colourconfig) if file~=nil then konf=file:read("*all") io.close(file) for key,val in ipairs(GUI) do if val.class=="checkbox" or val.class=="dropdown" or val.class=="color" then if konf:match(val.name) and val.name~="help" then val.value=detf(konf:match(val.name..":(.-)\n")) end end end end end function grad_rgb(subs,sel) cuts(subs) validateCol(subs,sel) res={grtype="RGB",acc=1,k1=true,k3=true} gradient(subs,sel) return sel end function grad_hsl(subs,sel) cuts(subs) validateCol(subs,sel) res={grtype="HSL",acc=1,k1=true,k3=true,hueshort=true} gradient(subs,sel) return sel end if haveDepCtrl then depRec:registerMacros({ {script_name,script_description,colourise}, {": Non-GUI macros :/Colourise: Gradient by Character, RGB","GBC RGB",grad_rgb}, {": Non-GUI macros :/Colourise: Gradient by Character, HSL","GBC HSL",grad_hsl}, },false) else aegisub.register_macro(script_name,script_description,colourise) aegisub.register_macro(": Non-GUI macros :/Colourise: Gradient by Character, RGB","GBC RGB",grad_rgb) aegisub.register_macro(": Non-GUI macros :/Colourise: Gradient by Character, HSL","GBC HSL",grad_hsl) end