mirror of https://github.com/odrling/Aegisub
perspective tool: Support drawings
This will not work in more complex cases like lines containing both text and drawings, but it's correct in simpler ones.
This commit is contained in:
parent
9c2d6169c6
commit
20cc0b8077
|
@ -41,17 +41,22 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
Spline::Spline(const VisualToolBase &tl)
|
Spline::Spline(const VisualToolBase *tl)
|
||||||
: coord_translator(tl)
|
: coord_translator(tl)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2D Spline::ToScript(Vector2D vec) const {
|
Vector2D Spline::ToScript(Vector2D vec) const {
|
||||||
return coord_translator.ToScriptCoords(vec) * scale;
|
if (coord_translator)
|
||||||
|
vec = coord_translator->ToScriptCoords(vec);
|
||||||
|
return vec * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2D Spline::FromScript(Vector2D vec) const {
|
Vector2D Spline::FromScript(Vector2D vec) const {
|
||||||
return coord_translator.FromScriptCoords(vec / scale);
|
vec = vec / scale;
|
||||||
|
if (coord_translator)
|
||||||
|
vec = coord_translator->FromScriptCoords(vec);
|
||||||
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spline::SetScale(int new_scale) {
|
void Spline::SetScale(int new_scale) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ class VisualToolBase;
|
||||||
|
|
||||||
class Spline final : private std::vector<SplineCurve> {
|
class Spline final : private std::vector<SplineCurve> {
|
||||||
/// Visual tool to do the conversion between script and video pixels
|
/// Visual tool to do the conversion between script and video pixels
|
||||||
const VisualToolBase &coord_translator;
|
const VisualToolBase *coord_translator = nullptr;
|
||||||
/// Spline scale
|
/// Spline scale
|
||||||
int scale = 0;
|
int scale = 0;
|
||||||
int raw_scale = 0;
|
int raw_scale = 0;
|
||||||
|
@ -47,7 +47,8 @@ class Spline final : private std::vector<SplineCurve> {
|
||||||
/// Script coordinates -> Video coordinates
|
/// Script coordinates -> Video coordinates
|
||||||
Vector2D FromScript(Vector2D vec) const;
|
Vector2D FromScript(Vector2D vec) const;
|
||||||
public:
|
public:
|
||||||
Spline(const VisualToolBase &scale);
|
Spline() {};
|
||||||
|
Spline(const VisualToolBase *scale);
|
||||||
|
|
||||||
/// Encode to an ASS vector drawing
|
/// Encode to an ASS vector drawing
|
||||||
std::string EncodeToAss() const;
|
std::string EncodeToAss() const;
|
||||||
|
|
|
@ -115,6 +115,15 @@ Vector2D& SplineCurve::EndPoint() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Vector2D> SplineCurve::AnchorPoints() {
|
||||||
|
switch (type) {
|
||||||
|
case POINT: return std::vector<Vector2D>({p1});
|
||||||
|
case LINE: return std::vector<Vector2D>({p1, p2});
|
||||||
|
case BICUBIC: return std::vector<Vector2D>({p1, p2, p3, p4});
|
||||||
|
default: return std::vector<Vector2D>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const {
|
Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const {
|
||||||
return GetPoint(GetClosestParam(ref));
|
return GetPoint(GetClosestParam(ref));
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
|
|
||||||
Vector2D GetPoint(float t) const;
|
Vector2D GetPoint(float t) const;
|
||||||
Vector2D& EndPoint();
|
Vector2D& EndPoint();
|
||||||
|
std::vector<Vector2D> AnchorPoints();
|
||||||
/// Get point on the curve closest to reference
|
/// Get point on the curve closest to reference
|
||||||
Vector2D GetClosestPoint(Vector2D ref) const;
|
Vector2D GetClosestPoint(Vector2D ref) const;
|
||||||
/// Get t value for the closest point to reference
|
/// Get t value for the closest point to reference
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
#include <libaegisub/of_type_adaptor.h>
|
||||||
#include <libaegisub/split.h>
|
#include <libaegisub/split.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -536,11 +538,9 @@ int VisualToolBase::GetLineAlignment(AssDialogue *diag) {
|
||||||
return an;
|
return an;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead) {
|
std::pair<Vector2D, Vector2D> VisualToolBase::GetLineBaseExtents(AssDialogue *diag) {
|
||||||
width = 0.;
|
double width = 0.;
|
||||||
height = 0.;
|
double height = 0.;
|
||||||
descent = 0.;
|
|
||||||
extlead = 0.;
|
|
||||||
|
|
||||||
AssStyle style;
|
AssStyle style;
|
||||||
if (AssStyle *basestyle = c->ass->GetStyle(diag->Style)) {
|
if (AssStyle *basestyle = c->ass->GetStyle(diag->Style)) {
|
||||||
|
@ -550,25 +550,57 @@ void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double
|
||||||
}
|
}
|
||||||
|
|
||||||
auto blocks = diag->ParseTags();
|
auto blocks = diag->ParseTags();
|
||||||
if (param_vec tag = find_tag(blocks, "\\fs"))
|
param_vec ptag = find_tag(blocks, "\\p");
|
||||||
style.fontsize = tag->front().Get(style.fontsize);
|
|
||||||
if (param_vec tag = find_tag(blocks, "\\fn"))
|
|
||||||
style.font = tag->front().Get(style.font);
|
|
||||||
|
|
||||||
std::string text = diag->GetStrippedText();
|
if (ptag && ptag->front().Get(0)) { // A drawing
|
||||||
std::vector<std::string> textlines;
|
Spline spline;
|
||||||
boost::replace_all(text, "\\N", "\n");
|
spline.SetScale(ptag->front().Get(1));
|
||||||
agi::Split(textlines, text, '\n');
|
std::string drawing_text = join(blocks | agi::of_type<AssDialogueBlockDrawing>() | boost::adaptors::transformed([&](AssDialogueBlock *d) { return d->GetText(); }), "");
|
||||||
for (std::string line : textlines) {
|
spline.DecodeFromAss(drawing_text);
|
||||||
double linewidth = 0;
|
|
||||||
double lineheight = 0;
|
if (!spline.size())
|
||||||
if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) {
|
return std::make_pair(Vector2D(0, 0), Vector2D(0, 0));
|
||||||
// meh... let's make some ballpark estimates
|
|
||||||
linewidth = style.fontsize * line.length();
|
float left = FLT_MAX;
|
||||||
lineheight = style.fontsize;
|
float top = FLT_MAX;
|
||||||
|
float right = -FLT_MAX;
|
||||||
|
float bot = -FLT_MAX;
|
||||||
|
|
||||||
|
for (SplineCurve curve : spline) {
|
||||||
|
for (Vector2D pt : curve.AnchorPoints()) {
|
||||||
|
left = std::min(left, pt.X());
|
||||||
|
top = std::min(top, pt.Y());
|
||||||
|
right = std::max(right, pt.X());
|
||||||
|
bot = std::max(bot, pt.Y());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
width = std::max(width, linewidth);
|
|
||||||
height += lineheight;
|
return std::make_pair(Vector2D(left, top), Vector2D(right, bot));
|
||||||
|
} else {
|
||||||
|
if (param_vec tag = find_tag(blocks, "\\fs"))
|
||||||
|
style.fontsize = tag->front().Get(style.fontsize);
|
||||||
|
if (param_vec tag = find_tag(blocks, "\\fn"))
|
||||||
|
style.font = tag->front().Get(style.font);
|
||||||
|
|
||||||
|
std::string text = diag->GetStrippedText();
|
||||||
|
std::vector<std::string> textlines;
|
||||||
|
boost::replace_all(text, "\\N", "\n");
|
||||||
|
agi::Split(textlines, text, '\n');
|
||||||
|
for (std::string line : textlines) {
|
||||||
|
double linewidth = 0;
|
||||||
|
double lineheight = 0;
|
||||||
|
|
||||||
|
double descent;
|
||||||
|
double extlead;
|
||||||
|
if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) {
|
||||||
|
// meh... let's make some ballpark estimates
|
||||||
|
linewidth = style.fontsize * line.length();
|
||||||
|
lineheight = style.fontsize;
|
||||||
|
}
|
||||||
|
width = std::max(width, linewidth);
|
||||||
|
height += lineheight;
|
||||||
|
}
|
||||||
|
return std::make_pair(Vector2D(0, 0), Vector2D(width, height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,11 +132,18 @@ protected:
|
||||||
float GetLineFontSize(AssDialogue *diag);
|
float GetLineFontSize(AssDialogue *diag);
|
||||||
int GetLineAlignment(AssDialogue *diag);
|
int GetLineAlignment(AssDialogue *diag);
|
||||||
/// @brief Compute text extents of the given line without any formatting
|
/// @brief Compute text extents of the given line without any formatting
|
||||||
|
/// @param diag The dialogue line
|
||||||
|
/// @return The top left and bottom right corners of the line's bounding box respectively.
|
||||||
///
|
///
|
||||||
/// Formatting tags are stripped and \fs tags are respected, but \fscx and \fscy are kept as 100 even if
|
/// Formatting tags are stripped and \fs tags are respected, but \fscx and \fscy are kept as 100 even if
|
||||||
/// they are different in the style.
|
/// they are different in the style.
|
||||||
|
/// For text the top left corner of the bounding box will always be at the origin, but this needn't be
|
||||||
|
/// the case for drawings. The width and height of the bounding box are the shifts used for text alignment.
|
||||||
|
///
|
||||||
|
/// This function works for most common line formats, but can be inaccurate for more complex cases such as lines
|
||||||
|
/// containing both text and drawings.
|
||||||
/// Returns a rough estimate when getting the precise extents fails
|
/// Returns a rough estimate when getting the precise extents fails
|
||||||
void GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead);
|
std::pair<Vector2D, Vector2D> GetLineBaseExtents(AssDialogue *diag);
|
||||||
void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
|
void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
|
||||||
std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
|
std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
|
||||||
|
|
||||||
|
|
|
@ -334,7 +334,7 @@ void VisualToolPerspective::Draw() {
|
||||||
gl.SetRotation(angle_x, angle_y, angle_z);
|
gl.SetRotation(angle_x, angle_y, angle_z);
|
||||||
gl.SetScale(fsc);
|
gl.SetScale(fsc);
|
||||||
gl.SetShear(fax, fay);
|
gl.SetShear(fax, fay);
|
||||||
Vector2D glScale = textheight * Vector2D(1, 1) / spacing / 4;
|
Vector2D glScale = (bbox.second.Y() - bbox.first.Y()) * Vector2D(1, 1) / spacing / 4;
|
||||||
gl.SetScale(100 * glScale);
|
gl.SetScale(100 * glScale);
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
|
@ -680,17 +680,18 @@ bool VisualToolPerspective::InnerToText() {
|
||||||
|
|
||||||
float quadwidth = ab.Len();
|
float quadwidth = ab.Len();
|
||||||
float quadheight = abs(ad.Y());
|
float quadheight = abs(ad.Y());
|
||||||
float scalex = quadwidth / textwidth;
|
float scalex = quadwidth / std::max(bbox.second.X() - bbox.first.X(), 1.0f);
|
||||||
float scaley = quadheight / textheight;
|
float scaley = quadheight / std::max(bbox.second.Y() - bbox.first.Y(), 1.0f);
|
||||||
|
Vector2D scale = Vector2D(scalex, scaley);
|
||||||
|
|
||||||
float shiftv = align <= 3 ? 1 : (align <= 6 ? 0.5 : 0);
|
float shiftv = align <= 3 ? 1 : (align <= 6 ? 0.5 : 0);
|
||||||
float shifth = align % 3 == 0 ? 1 : (align % 3 == 2 ? 0.5 : 0);
|
float shifth = align % 3 == 0 ? 1 : (align % 3 == 2 ? 0.5 : 0);
|
||||||
pos = org + r[0].XY() + Vector2D(quadwidth * shifth, quadheight * shiftv);
|
pos = org + r[0].XY() - bbox.first * scale + Vector2D(quadwidth * shifth, quadheight * shiftv);
|
||||||
angle_x = rotx * rad2deg;
|
angle_x = rotx * rad2deg;
|
||||||
angle_y = -roty * rad2deg;
|
angle_y = -roty * rad2deg;
|
||||||
angle_z = -rotz * rad2deg;
|
angle_z = -rotz * rad2deg;
|
||||||
Vector2D oldfsc = fsc;
|
Vector2D oldfsc = fsc;
|
||||||
fsc = 100 * Vector2D(scalex, scaley);
|
fsc = 100 * scale;
|
||||||
fax = rawfax * scaley / scalex;
|
fax = rawfax * scaley / scalex;
|
||||||
fay = 0;
|
fay = 0;
|
||||||
|
|
||||||
|
@ -781,40 +782,39 @@ void VisualToolPerspective::TextToPersp() {
|
||||||
|
|
||||||
align = GetLineAlignment(active_line);
|
align = GetLineAlignment(active_line);
|
||||||
|
|
||||||
double descend, extlead;
|
bbox = GetLineBaseExtents(active_line);
|
||||||
GetLineBaseExtents(active_line, textwidth, textheight, descend, extlead);
|
float textwidth = std::max(bbox.second.X() - bbox.first.X(), 1.f);
|
||||||
textwidth = std::max(textwidth, 1.);
|
float textheight = std::max(bbox.second.Y() - bbox.first.Y(), 1.f);
|
||||||
textheight = std::max(textheight, 1.);
|
double shiftx = 0., shifty = 0.;
|
||||||
double textleft = 0., texttop = 0.;
|
|
||||||
|
|
||||||
switch ((align - 1) % 3) {
|
switch ((align - 1) % 3) {
|
||||||
case 1:
|
case 1:
|
||||||
textleft = -textwidth / 2;
|
shiftx = -textwidth / 2;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
textleft = -textwidth;
|
shiftx = -textwidth;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch ((align - 1) / 3) {
|
switch ((align - 1) / 3) {
|
||||||
case 0:
|
case 0:
|
||||||
texttop = -textheight;
|
shifty = -textheight;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
texttop = -textheight / 2;
|
shifty = -textheight / 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vector2D> textrect = MakeRect(Vector2D(0, 0), Vector2D(textwidth, textheight));
|
std::vector<Vector2D> textrect = MakeRect(bbox.first, bbox.second);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
Vector2D p = textrect[i];
|
Vector2D p = textrect[i];
|
||||||
// Apply \fax and \fay
|
// Apply \fax and \fay
|
||||||
p = Vector2D(p.X() + p.Y() * fax, p.X() * fay + p.Y());
|
p = Vector2D(p.X() + p.Y() * fax, p.X() * fay + p.Y());
|
||||||
// Translate to alignment point
|
// Translate to alignment point
|
||||||
p = p + Vector2D(textleft, texttop);
|
p = p + Vector2D(shiftx, shifty);
|
||||||
// Apply scaling
|
// Apply scaling
|
||||||
p = Vector2D(p.X() * fsc.X() / 100., p.Y() * fsc.Y() / 100.);
|
p = Vector2D(p.X() * fsc.X() / 100., p.Y() * fsc.Y() / 100.);
|
||||||
// Translate relative to origin
|
// Translate relative to origin
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
class VisualToolPerspective final : public VisualTool<VisualToolPerspectiveDraggableFeature> {
|
class VisualToolPerspective final : public VisualTool<VisualToolPerspectiveDraggableFeature> {
|
||||||
wxToolBar *toolBar = nullptr; /// The subtoolbar
|
wxToolBar *toolBar = nullptr; /// The subtoolbar
|
||||||
int settings = 0;
|
int settings = 0;
|
||||||
|
|
||||||
agi::OptionValue* optOuter;
|
agi::OptionValue* optOuter;
|
||||||
agi::OptionValue* optOuterLocked;
|
agi::OptionValue* optOuterLocked;
|
||||||
|
@ -69,8 +69,9 @@ class VisualToolPerspective final : public VisualTool<VisualToolPerspectiveDragg
|
||||||
|
|
||||||
int align = 0;
|
int align = 0;
|
||||||
|
|
||||||
double textwidth = 0.f;
|
// Corners of the bounding box of the event without any formatting.
|
||||||
double textheight = 0.f;
|
// The top left corner is the zero vector for text but might not be for drawings.
|
||||||
|
std::pair<Vector2D, Vector2D> bbox;
|
||||||
|
|
||||||
Vector2D fsc;
|
Vector2D fsc;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ int BUTTON_ID_BASE = 1300;
|
||||||
|
|
||||||
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context)
|
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context)
|
||||||
: VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
|
: VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
|
||||||
, spline(*this)
|
, spline(this)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue