Merge branches 'info', 'video_panning_option', 'fixes', 'misc', 'vapoursynth' and 'wangqr_gui' into feature

This commit is contained in:
arch1t3cht 2022-11-03 00:03:23 +01:00
17 changed files with 258 additions and 45 deletions

View File

@ -9,9 +9,9 @@ We absolutely do, and I'm aware that adding another one [doesn't sound like](htt
- [AegisubDC](https://github.com/Ristellise/AegisubDC) has the most modern features (in particular video-panning), but is Windows-only and not actively maintained anymore.
- [The TypesettingTools fork](https://github.com/TypesettingTools/Aegisub) is the one that will one day become the upstream version and builds relatively effortlessly on all operating systems, but at the moment it's not moving much. It's the base for this fork, and I hope to one day merge most of these additions into it.
- Only PR'ing the changes in here to various forks would cause even more chaos
- I try to convince myself that this isn't really a "fork" in the traditional sense - one which aims to provide extended support and stability fixes. It's a collection of new feature additions which I built myself, together with some of the most important new features floating around other forks.
- ~~I try to convince myself that this isn't really a "fork" in the traditional sense - one which aims to provide extended support and stability fixes. It's a collection of new feature additions which I built myself, together with some of the most important new features floating around other forks.~~ At this point it's probably too late to still be saying this. Still, the general mission hasn't changed. This fork collects new features and critical bugfixes, but won't be putting extra time into maintenance aspects like cleanup and refactors. Partly, this is also because any big refactors would make it harder to pull these changes into upstream repositories or future forks.
While this is usually also the version of Aegisub I'm currently using, I make absolutely no promises on stability. **Don't** use this version if you're just looking for any version of Aegisub - this is mostly intended for typesetting and other advanced usage.
While this is usually also the version of Aegisub I'm currently using, I make no promises on stability. **Don't** use this version if you're just looking for any version of Aegisub - this is mostly intended for typesetting and other advanced usage.
### Organization
Being a collection of different feature additions, this repository consists of a set of branches for different features, so that they can easily be merged into other repositories. The [`feature`](https://github.com/arch1t3cht/Aegisub/tree/feature) branch merges together all the features I deem as currently usable. Due to the structure of the repository, I will be force-pushing to this branch and some of the individual branches very frequently, so they're not ideal for basing further branches on.
@ -31,10 +31,11 @@ This list is for navigating the repository. Go to the [release page](https://git
- [`workarounds`](https://github.com/arch1t3cht/Aegisub/tree/workarounds): Same as `bugfixes`, but these are hacky fixes that probably shouldn't be pulled without more work.
- [`fixes`](https://github.com/arch1t3cht/Aegisub/tree/fixes): Miscellaneous bugfixes
- [`misc`](https://github.com/arch1t3cht/Aegisub/tree/misc): Other miscellaneous additions
- [`wangqr_gui`](https://github.com/arch1t3cht/Aegisub/tree/wangqr_gui): Merge wangqr's changes regarding the GUI. In particular, add high-DPI compatibility.
- [`misc_dc`](https://github.com/arch1t3cht/Aegisub/tree/misc_dc): Miscellaneous changes taken from AegisubDC
- [`xa2-ds`](https://github.com/arch1t3cht/Aegisub/tree/xa2-ds): Add XAudio2 backend and allow stereo playback for some other backends, by wangqr and Shinon.
- [`stereo`](https://github.com/arch1t3cht/Aegisub/tree/stereo): Add multi-channel support for the other audio backends where possible.
- [`video_panning_feature`](https://github.com/arch1t3cht/Aegisub/tree/video_panning_feature): Merge [moex3's video zoom and panning](https://github.com/TypesettingTools/Aegisub/pull/150), with an OSX fix and more options to control zoom behavior
- [`video_panning_option`](https://github.com/arch1t3cht/Aegisub/tree/video_panning_option): Merge [moex3's video zoom and panning](https://github.com/TypesettingTools/Aegisub/pull/150), with several bugfixes and more options to control zoom behavior
- [`spectrum-frequency-mapping`](https://github.com/arch1t3cht/Aegisub/tree/spectrum-frequency-mapping): Merge EleonoreMizo's [spectrum display improvements](https://github.com/TypesettingTools/Aegisub/pull/94), and also make Shift+Scroll vertically zoom the audio display
- [`wangqr_time_video`](https://github.com/arch1t3cht/Aegisub/tree/wangqr_time_video): Merge wangqr's feature adding a tool for timing subtitles to changes in the video
@ -64,11 +65,13 @@ If you're compiling yourself, try adding `--force-fallback-for=zlib` to the meso
### Compilation
For compilation on Windows, see the TSTools documentation below. Also check the [GitHub workflow](https://github.com/arch1t3cht/Aegisub/blob/cibuilds/.github/workflows/ci.yml) for the project arguments.
On Linux, you can use the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a base, in particular for installing the necessary dependencies if you don't want to compile them yourself.
To compile manually,
- Install Meson (at the moment, you'll need to downgrade Meson below 0.63.0: `pip install meson==0.62.2`)
On Arch Linux, there is an AUR package called [aegisub-arch1t3cht-git](https://aur.archlinux.org/packages/aegisub-arch1t3cht-git). It's not maintained by me but seems to work.
On other distributions or for manual compilation you can use this package or the [TSTools PKGBUILD](https://aur.archlinux.org/packages/aegisub-ttools-meson-git) as a reference, in particular for installing the necessary dependencies if you don't want to compile them yourself.
If all dependencies are installed:
- Install Meson
- Clone the repository
- In the repository, run `meson setup build` for the default configuration. See below for further options.
- In the repository, run `meson setup build --buildtype=release` for the default configuration. See below for further options.
- `cd` to the `build` directory and run `ninja`
- You'll get an `aegisub` binary in the `build` folder. To install it to a system-wide location, run `ninja install`. To install to `/usr` instead of `/usr/local`, pass `--prefix=/usr` when configuring or reconfiguring meson.
- When recompiling after pulling new commits, skip the `meson setup` setup and just immediately run `ninja` from the build directory - even when the build configuration changed.

View File

@ -60,7 +60,11 @@ public:
case dt::ERROR: SetStyling(tok.length, ss::ERROR); break;
case dt::ARG: SetStyling(tok.length, ss::PARAMETER); break;
case dt::COMMENT: SetStyling(tok.length, ss::COMMENT); break;
case dt::DRAWING: SetStyling(tok.length, ss::DRAWING); break;
case dt::DRAWING_CMD:SetStyling(tok.length, ss::DRAWING_CMD);break;
case dt::DRAWING_X: SetStyling(tok.length, ss::DRAWING_X); break;
case dt::DRAWING_Y: SetStyling(tok.length, ss::DRAWING_Y); break;
case dt::DRAWING_ENDPOINT_X: SetStyling(tok.length, ss::DRAWING_ENDPOINT_X); break;
case dt::DRAWING_ENDPOINT_Y: SetStyling(tok.length, ss::DRAWING_ENDPOINT_Y); break;
case dt::TEXT: SetStyling(tok.length, ss::NORMAL); break;
case dt::TAG_NAME: SetStyling(tok.length, ss::TAG); break;
case dt::OPEN_PAREN: case dt::CLOSE_PAREN: case dt::ARG_SEP: case dt::TAG_START:
@ -72,6 +76,8 @@ public:
case dt::WHITESPACE:
if (ranges.size() && ranges.back().type == ss::PARAMETER)
SetStyling(tok.length, ss::PARAMETER);
else if (ranges.size() && ranges.back().type == ss::DRAWING_ENDPOINT_X)
SetStyling(tok.length, ss::DRAWING_ENDPOINT_X); // connect the underline between x and y of endpoints
else
SetStyling(tok.length, ss::NORMAL);
break;
@ -118,6 +124,64 @@ class WordSplitter {
}
}
void SplitDrawing(size_t &i) {
size_t starti = i;
// First, split into words
size_t dpos = pos;
size_t tlen = 0;
bool tokentype = text[pos] == ' ' || text[pos] == '\t';
while (tlen < tokens[i].length) {
bool newtype = text[dpos] == ' ' || text[dpos] == '\t';
if (newtype != tokentype) {
tokentype = newtype;
SwitchTo(i, tokentype ? dt::DRAWING_FULL : dt::WHITESPACE, tlen);
tokens[i].type = tokentype ? dt::WHITESPACE : dt::DRAWING_FULL;
tlen = 0;
}
++tlen;
++dpos;
}
// Then, label all the tokens
dpos = pos;
int num_coord = 0;
char lastcmd = ' ';
for (size_t j = starti; j <= i; j++) {
char c = text[dpos];
if (tokens[j].type == dt::WHITESPACE) {
} else if (c == 'm' || c == 'n' || c == 'l' || c == 's' || c == 'b' || c == 'p' || c == 'c') {
tokens[j].type = dt::DRAWING_CMD;
if (tokens[j].length != 1)
tokens[j].type = dt::ERROR;
if (num_coord % 2 != 0)
tokens[j].type = dt::ERROR;
lastcmd = c;
num_coord = 0;
} else {
bool valid = true;
for (size_t k = 0; k < tokens[j].length; k++) {
char c = text[dpos + k];
if (!((c >= '0' && c <= '9') || c == '.' || c == '-' || c == 'e')) {
valid = false;
}
}
if (!valid)
tokens[j].type = dt::ERROR;
else if (lastcmd == 'b' && num_coord % 6 >= 4)
tokens[j].type = num_coord % 2 == 0 ? dt::DRAWING_ENDPOINT_X : dt::DRAWING_ENDPOINT_Y;
else
tokens[j].type = num_coord % 2 == 0 ? dt::DRAWING_X : dt::DRAWING_Y;
++num_coord;
}
dpos += tokens[j].length;
}
}
public:
WordSplitter(std::string const& text, std::vector<DialogueToken> &tokens)
: text(text)
@ -131,6 +195,9 @@ public:
size_t len = tokens[i].length;
if (tokens[i].type == dt::TEXT)
SplitText(i);
else if (tokens[i].type == dt::DRAWING_FULL) {
SplitDrawing(i);
}
pos += len;
}
}
@ -163,9 +230,51 @@ void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
switch (tokens[i].type) {
case dt::TEXT:
if (in_drawing)
tokens[i].type = dt::DRAWING;
tokens[i].type = dt::DRAWING_FULL;
break;
case dt::TAG_NAME:
if (i + 3 < tokens.size() && (len == 4 || len == 5) && !strncmp(str.c_str() + pos + len - 4, "clip", 4)) {
if (tokens[i + 1].type != dt::OPEN_PAREN)
goto tag_p;
size_t drawing_start = 0;
size_t drawing_end = 0;
// Try to find a vector clip
for (size_t j = i + 2; j < tokens.size(); j++) {
if (tokens[j].type == dt::ARG_SEP) {
if (drawing_start) {
break; // More than two arguents - this is a rectangular clip
}
drawing_start = j + 1;
} else if (tokens[j].type == dt::CLOSE_PAREN) {
drawing_end = j;
break;
} else if (tokens[j].type != dt::WHITESPACE && tokens[j].type != dt::ARG) {
break;
}
}
if (!drawing_end)
goto tag_p;
if (!drawing_start)
drawing_start = i + 2;
if (drawing_end == drawing_start + 1)
goto tag_p;
// We found a clip between drawing_start and drawing_end. Now, join
// all the tokens into one and label it as a drawing.
size_t tokenlen = 0;
for (size_t j = drawing_start; j < drawing_end; j++) {
tokenlen += tokens[j].length;
}
tokens[drawing_start].length = tokenlen;
tokens[drawing_start].type = dt::DRAWING_FULL;
tokens.erase(tokens.begin() + drawing_start + 1, tokens.begin() + drawing_end);
last_ovr_end -= drawing_end - drawing_start - 1;
}
tag_p:
if (len != 1 || i + 1 >= tokens.size() || str[pos] != 'p')
break;
@ -199,7 +308,7 @@ void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
case dt::KARAOKE_VARIABLE: break;
case dt::LINE_BREAK: break;
default:
tokens[i].type = in_drawing ? dt::DRAWING : dt::TEXT;
tokens[i].type = in_drawing ? dt::DRAWING_FULL : dt::TEXT;
if (i > 0 && tokens[i - 1].type == tokens[i].type) {
tokens[i - 1].length += tokens[i].length;
tokens.erase(tokens.begin() + i);

View File

@ -225,7 +225,7 @@ int Framerate::FrameAtTime(int ms, Time type) const {
return int((ms * numerator / denominator - 999) / 1000);
if (ms > timecodes.back())
return int((ms * numerator - last + denominator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
return int((ms * numerator - numerator / 2 - last + numerator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
return (int)distance(lower_bound(timecodes.rbegin(), timecodes.rend(), ms, std::greater<int>()), timecodes.rend()) - 1;
}

View File

@ -39,7 +39,12 @@ namespace agi {
ERROR,
COMMENT,
WHITESPACE,
DRAWING,
DRAWING_FULL,
DRAWING_CMD,
DRAWING_X,
DRAWING_Y,
DRAWING_ENDPOINT_X,
DRAWING_ENDPOINT_Y,
KARAOKE_TEMPLATE,
KARAOKE_VARIABLE
};
@ -49,7 +54,11 @@ namespace agi {
enum {
NORMAL = 0,
COMMENT,
DRAWING,
DRAWING_CMD,
DRAWING_X,
DRAWING_Y,
DRAWING_ENDPOINT_X,
DRAWING_ENDPOINT_Y,
OVERRIDE,
PUNCTUATION,
TAG,

View File

@ -84,7 +84,9 @@ VapoursynthAudioProvider::VapoursynthAudioProvider(agi::fs::path const& filename
num_samples = vi->numSamples;
}
catch (VapoursynthError const& err) {
throw agi::AudioProviderError(agi::format("Vapoursynth error: %s", err.GetMessage()));
// Unlike the video provider manager, the audio provider factory catches AudioProviderErrors and picks whichever source doesn't throw one.
// So just rethrow the Error here with an extra label so the user will see the error message and know the audio wasn't loaded with VS
throw VapoursynthError(agi::format("Vapoursynth error: %s", err.GetMessage()));
}
template<typename T>
@ -115,7 +117,7 @@ void VapoursynthAudioProvider::FillBufferWithFrame(void *buf, int n, int64_t sta
std::vector<const uint8_t *> planes(channels);
for (int c = 0; c < channels; c++) {
planes[c] = vs.GetAPI()->getReadPtr(frame, c);
planes[c] = vs.GetAPI()->getReadPtr(frame, c) + bytes_per_sample * start;
if (planes[c] == nullptr) {
vs.GetAPI()->freeFrame(frame);
throw VapoursynthError("Failed to read audio channel");

View File

@ -231,7 +231,9 @@
"Background" : {
"Brackets" : "",
"Comment" : "",
"Drawing" : "",
"Drawing Command" : "",
"Drawing X" : "",
"Drawing Y" : "",
"Error" : "rgb(255, 200, 200)",
"Karaoke Template" : "",
"Karaoke Variable" : "",
@ -244,7 +246,9 @@
"Bold" : {
"Brackets" : false,
"Comment" : true,
"Drawing" : true,
"Drawing Command" : true,
"Drawing X" : false,
"Drawing Y" : false,
"Error" : false,
"Karaoke Template" : true,
"Karaoke Variable" : true,
@ -254,9 +258,14 @@
"Slashes" : false,
"Tags" : true
},
"Underline": {
"Drawing Endpoint": true
},
"Brackets" : "rgb(20, 50, 255)",
"Comment" : "rgb(0,0,0)",
"Drawing" : "rgb(0,0,0)",
"Drawing Command" : "rgb(0,0,0)",
"Drawing X" : "rgb(90,40,40)",
"Drawing Y" : "rgb(40,90,40)",
"Error" : "rgb(200, 0, 0)",
"Karaoke Template" : "rgb(128, 0, 192)",
"Karaoke Variable" : "rgb(128, 0, 192)",

View File

@ -231,7 +231,9 @@
"Background" : {
"Brackets" : "",
"Comment" : "",
"Drawing" : "",
"Drawing Command" : "",
"Drawing X" : "",
"Drawing Y" : "",
"Error" : "rgb(255, 200, 200)",
"Karaoke Template" : "",
"Karaoke Variable" : "",
@ -244,7 +246,9 @@
"Bold" : {
"Brackets" : false,
"Comment" : true,
"Drawing" : true,
"Drawing Command" : true,
"Drawing X" : false,
"Drawing Y" : false,
"Error" : false,
"Karaoke Template" : true,
"Karaoke Variable" : true,
@ -254,9 +258,14 @@
"Slashes" : false,
"Tags" : true
},
"Underline": {
"Drawing Endpoint": true
},
"Brackets" : "rgb(20, 50, 255)",
"Comment" : "rgb(0,0,0)",
"Drawing" : "rgb(0,0,0)",
"Drawing Command" : "rgb(0,0,0)",
"Drawing X" : "rgb(90,40,40)",
"Drawing Y" : "rgb(40,90,40)",
"Error" : "rgb(200, 0, 0)",
"Karaoke Template" : "rgb(128, 0, 192)",
"Karaoke Variable" : "rgb(128, 0, 192)",

View File

@ -259,7 +259,11 @@ void Interface_Colours(wxTreebook *book, Preferences *parent) {
p->OptionAdd(syntax, _("Background"), "Colour/Subtitle/Background");
p->OptionAdd(syntax, _("Normal"), "Colour/Subtitle/Syntax/Normal");
p->OptionAdd(syntax, _("Comments"), "Colour/Subtitle/Syntax/Comment");
p->OptionAdd(syntax, _("Drawings"), "Colour/Subtitle/Syntax/Drawing");
p->OptionAdd(syntax, _("Drawing Commands"), "Colour/Subtitle/Syntax/Drawing Command");
p->OptionAdd(syntax, _("Drawing X Coords"), "Colour/Subtitle/Syntax/Drawing X");
p->OptionAdd(syntax, _("Drawing Y Coords"), "Colour/Subtitle/Syntax/Drawing Y");
p->OptionAdd(syntax, _("Underline Spline Endpoints"), "Colour/Subtitle/Syntax/Underline/Drawing Endpoint");
p->CellSkip(syntax);
p->OptionAdd(syntax, _("Brackets"), "Colour/Subtitle/Syntax/Brackets");
p->OptionAdd(syntax, _("Slashes and Parentheses"), "Colour/Subtitle/Syntax/Slashes");
p->OptionAdd(syntax, _("Tags"), "Colour/Subtitle/Syntax/Tags");

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<description>Aegisub subtitle editor</description>
<description>Aegisub Subtitle Editor</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->

View File

@ -138,7 +138,10 @@ SubsTextEditCtrl::SubsTextEditCtrl(wxWindow* parent, wxSize wsize, long style, a
OPT_SUB("Subtitle/Edit Box/Font Size", &SubsTextEditCtrl::SetStyles, this);
Subscribe("Normal");
Subscribe("Comment");
Subscribe("Drawing");
Subscribe("Drawing Command");
Subscribe("Drawing X");
Subscribe("Drawing Y");
OPT_SUB("Colour/Subtitle/Syntax/Underline/Drawing Endpoint", &SubsTextEditCtrl::SetStyles, this);
Subscribe("Brackets");
Subscribe("Slashes");
Subscribe("Tags");
@ -230,7 +233,13 @@ void SubsTextEditCtrl::SetStyles() {
namespace ss = agi::ass::SyntaxStyle;
SetSyntaxStyle(ss::NORMAL, font, "Normal", default_background);
SetSyntaxStyle(ss::COMMENT, font, "Comment", default_background);
SetSyntaxStyle(ss::DRAWING, font, "Drawing", default_background);
SetSyntaxStyle(ss::DRAWING_CMD, font, "Drawing Command", default_background);
SetSyntaxStyle(ss::DRAWING_X, font, "Drawing X", default_background);
SetSyntaxStyle(ss::DRAWING_Y, font, "Drawing Y", default_background);
SetSyntaxStyle(ss::DRAWING_ENDPOINT_X, font, "Drawing X", default_background);
SetSyntaxStyle(ss::DRAWING_ENDPOINT_Y, font, "Drawing Y", default_background);
StyleSetUnderline(ss::DRAWING_ENDPOINT_X, OPT_GET("Colour/Subtitle/Syntax/Underline/Drawing Endpoint")->GetBool());
StyleSetUnderline(ss::DRAWING_ENDPOINT_Y, OPT_GET("Colour/Subtitle/Syntax/Underline/Drawing Endpoint")->GetBool());
SetSyntaxStyle(ss::OVERRIDE, font, "Brackets", default_background);
SetSyntaxStyle(ss::PUNCTUATION, font, "Slashes", default_background);
SetSyntaxStyle(ss::TAG, font, "Tags", default_background);

View File

@ -23,15 +23,17 @@
#include <boost/algorithm/string/replace.hpp>
int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script) {
int result;
if (agi::fs::HasExtension(filename, "py") || agi::fs::HasExtension(filename, "vpy")) {
return api->evaluateFile(script, filename.string().c_str());
result = api->evaluateFile(script, filename.string().c_str());
} else {
std::string fname = filename.string();
boost::replace_all(fname, "\\", "\\\\");
boost::replace_all(fname, "'", "\\'");
std::string vscript = "filename = '" + fname + "'\n" + default_script;
return api->evaluateBuffer(script, vscript.c_str(), "aegisub");
result = api->evaluateBuffer(script, vscript.c_str(), "aegisub");
}
return result;
}
#endif // WITH_VAPOURSYNTH

View File

@ -78,7 +78,12 @@ VapourSynthWrapper::VapourSynthWrapper() {
if (!getVSScriptAPI)
throw VapoursynthError("Failed to get address of getVSScriptAPI from " VSSCRIPT_SO);
// Python will set the program's locale to the user's default locale, which will break
// half of wxwidgets on some operating systems due to locale mismatches. There's not really anything
// we can do to fix it except for saving it and setting it back to its original value afterwards.
std::string oldlocale(setlocale(LC_ALL, NULL));
scriptapi = getVSScriptAPI(VSSCRIPT_API_VERSION);
setlocale(LC_ALL, oldlocale.c_str());
if (!scriptapi)
throw VapoursynthError("Failed to get Vapoursynth ScriptAPI");

View File

@ -201,7 +201,7 @@ void VideoDisplay::Render() try {
E(glMatrixMode(GL_PROJECTION));
E(glLoadIdentity());
E(glOrtho(0.0f, client_w, client_h, 0.0f, -1000.0f, 1000.0f));
E(glOrtho(0.0f, std::max(client_w, 1), std::max(client_h, 1), 0.0f, -1000.0f, 1000.0f));
if (OPT_GET("Video/Overscan Mask")->GetBool()) {
double ar = con->videoController->GetAspectRatioValue();
@ -233,19 +233,19 @@ catch (const agi::Exception &err) {
}
void VideoDisplay::DrawOverscanMask(float horizontal_percent, float vertical_percent) const {
Vector2D v(viewport_width, viewport_height);
Vector2D size = Vector2D(horizontal_percent, vertical_percent) / 2 * v;
Vector2D v = Vector2D(viewport_width, viewport_height) / scale_factor;
Vector2D size = Vector2D(horizontal_percent, vertical_percent) * v;
// Clockwise from top-left
Vector2D corners[] = {
size,
Vector2D(viewport_width - size.X(), size),
Vector2D(viewport_width / scale_factor - size.X(), size),
v - size,
Vector2D(size, viewport_height - size.Y())
Vector2D(size, viewport_height / scale_factor - size.Y())
};
// Shift to compensate for black bars
Vector2D pos(viewport_left, viewport_top);
Vector2D pos = Vector2D(viewport_left, viewport_top) / scale_factor;
for (auto& corner : corners)
corner = corner + pos;
@ -267,7 +267,7 @@ void VideoDisplay::DrawOverscanMask(float horizontal_percent, float vertical_per
std::vector<int> vstart(1, 0);
std::vector<int> vcount(1, count);
gl.DrawMultiPolygon(points, vstart, vcount, Vector2D(viewport_left, viewport_top), Vector2D(viewport_width, viewport_height), true);
gl.DrawMultiPolygon(points, vstart, vcount, pos, v, true);
}
void VideoDisplay::PositionVideo() {

View File

@ -63,11 +63,12 @@ public:
int GetHeight() const override { return vi->height; }
double GetDAR() const override { return dar; }
std::vector<int> GetKeyFrames() const override { return keyframes; }
std::string GetColorSpace() const override { return colorspace; }
std::string GetRealColorSpace() const override { return colorspace; }
std::string GetColorSpace() const override { return GetRealColorSpace(); }
std::string GetRealColorSpace() const override { return colorspace == "Unknown" ? "None" : colorspace; }
bool HasAudio() const override { return false; }
virtual bool WantsCaching() const override { return true; }
virtual std::string GetDecoderName() const override { return "VapourSynth"; }
bool WantsCaching() const override { return true; }
std::string GetDecoderName() const override { return "VapourSynth"; }
bool ShouldSetVideoProperties() const override { return colorspace != "Unknown"; }
};
std::string colormatrix_description(int colorFamily, int colorRange, int matrix) {
@ -90,7 +91,7 @@ std::string colormatrix_description(int colorFamily, int colorRange, int matrix)
case VSC_MATRIX_ST240_M:
return str + ".240M";
default:
return "None";
return "Unknown"; // Will return "None" in GetColorSpace
}
}
@ -137,7 +138,7 @@ VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename
// Assume constant frame rate, since handling VFR would require going through all frames when loading.
// Users can load custom timecodes files to deal with VFR.
// Alternatively (TODO) the provider could read timecodes and keyframes from a second output node.
fps = (double) vi->fpsNum / vi->fpsDen;
fps = agi::vfr::Framerate(vi->fpsNum, vi->fpsDen);
// Find the first frame to get some info
const VSFrame *frame;

View File

@ -74,14 +74,47 @@ TEST(lagi_syntax, spellcheck) {
}
TEST(lagi_syntax, drawing) {
tok_str("incorrect{\\p1}m 10 10{\\p}correct", false,
tok_str("incorrect{\\clip(m 10 10 l 20 20 c)\\p1}m 10 10 b 0 0 0 100 100 0{\\p}correct", false,
expect_style(ss::SPELLING, 9u);
expect_style(ss::OVERRIDE, 1u);
expect_style(ss::PUNCTUATION, 1u);
expect_style(ss::TAG, 4u);
expect_style(ss::PUNCTUATION, 1u);
expect_style(ss::DRAWING_CMD, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_X, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_Y, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_CMD, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_X, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_Y, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_CMD, 1u);
expect_style(ss::PUNCTUATION, 2u);
expect_style(ss::TAG, 1u);
expect_style(ss::PARAMETER, 1u);
expect_style(ss::OVERRIDE, 1u);
expect_style(ss::DRAWING, 7u);
expect_style(ss::DRAWING_CMD, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_X, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_Y, 2u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_CMD, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_X, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_Y, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_X, 1u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_Y, 3u);
expect_style(ss::NORMAL, 1u);
expect_style(ss::DRAWING_ENDPOINT_X, 4u);
expect_style(ss::DRAWING_ENDPOINT_Y, 1u);
expect_style(ss::OVERRIDE, 1u);
expect_style(ss::PUNCTUATION, 1u);
expect_style(ss::TAG, 1u);

View File

@ -149,6 +149,12 @@ TEST(lagi_vfr, cfr_round_trip_exact) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i)));
}
}
TEST(lagi_vfr, cfr_round_trip_start) {
@ -157,6 +163,12 @@ TEST(lagi_vfr, cfr_round_trip_start) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, START), START));
}
}
TEST(lagi_vfr, cfr_round_trip_end) {
@ -165,6 +177,12 @@ TEST(lagi_vfr, cfr_round_trip_end) {
for (int i = -10; i < 11; i++) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
}
ASSERT_NO_THROW(fps = Framerate(24000, 1001));
int frames[] = {-100, -10, -1, 0, 1, 10, 100, 6820};
for (int i : frames) {
EXPECT_EQ(i, fps.FrameAtTime(fps.TimeAtFrame(i, END), END));
}
}
TEST(lagi_vfr, vfr_round_trip_exact) {

View File

@ -108,12 +108,12 @@ TEST(lagi_word_split, drawing) {
SplitWords(text, tokens);
ASSERT_EQ(15u, tokens.size());
ASSERT_EQ(17u, tokens.size());
EXPECT_EQ(dt::WORD, tokens[0].type);
EXPECT_EQ(dt::WORD, tokens[2].type);
EXPECT_EQ(dt::WORD, tokens[14].type);
EXPECT_EQ(dt::WORD, tokens[16].type);
EXPECT_EQ(dt::DRAWING, tokens[8].type);
EXPECT_EQ(dt::DRAWING_CMD, tokens[8].type);
}
TEST(lagi_word_split, unclosed_ovr) {