mirror of
https://github.com/odrling/Aegisub
synced 2025-04-11 22:56:02 +02:00
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>
|
||||
|
||||
Spline::Spline(const VisualToolBase &tl)
|
||||
Spline::Spline(const VisualToolBase *tl)
|
||||
: coord_translator(tl)
|
||||
{
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
|
@ -36,7 +36,7 @@ class VisualToolBase;
|
||||
|
||||
class Spline final : private std::vector<SplineCurve> {
|
||||
/// Visual tool to do the conversion between script and video pixels
|
||||
const VisualToolBase &coord_translator;
|
||||
const VisualToolBase *coord_translator = nullptr;
|
||||
/// Spline scale
|
||||
int scale = 0;
|
||||
int raw_scale = 0;
|
||||
@ -47,7 +47,8 @@ class Spline final : private std::vector<SplineCurve> {
|
||||
/// Script coordinates -> Video coordinates
|
||||
Vector2D FromScript(Vector2D vec) const;
|
||||
public:
|
||||
Spline(const VisualToolBase &scale);
|
||||
Spline() {};
|
||||
Spline(const VisualToolBase *scale);
|
||||
|
||||
/// Encode to an ASS vector drawing
|
||||
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 {
|
||||
return GetPoint(GetClosestParam(ref));
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
|
||||
Vector2D GetPoint(float t) const;
|
||||
Vector2D& EndPoint();
|
||||
std::vector<Vector2D> AnchorPoints();
|
||||
/// Get point on the curve closest to reference
|
||||
Vector2D GetClosestPoint(Vector2D ref) const;
|
||||
/// Get t value for the closest point to reference
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include <libaegisub/of_type_adaptor.h>
|
||||
#include <libaegisub/split.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
@ -536,11 +538,9 @@ int VisualToolBase::GetLineAlignment(AssDialogue *diag) {
|
||||
return an;
|
||||
}
|
||||
|
||||
void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead) {
|
||||
width = 0.;
|
||||
height = 0.;
|
||||
descent = 0.;
|
||||
extlead = 0.;
|
||||
std::pair<Vector2D, Vector2D> VisualToolBase::GetLineBaseExtents(AssDialogue *diag) {
|
||||
double width = 0.;
|
||||
double height = 0.;
|
||||
|
||||
AssStyle 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();
|
||||
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);
|
||||
param_vec ptag = find_tag(blocks, "\\p");
|
||||
|
||||
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;
|
||||
if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) {
|
||||
// meh... let's make some ballpark estimates
|
||||
linewidth = style.fontsize * line.length();
|
||||
lineheight = style.fontsize;
|
||||
if (ptag && ptag->front().Get(0)) { // A drawing
|
||||
Spline spline;
|
||||
spline.SetScale(ptag->front().Get(1));
|
||||
std::string drawing_text = join(blocks | agi::of_type<AssDialogueBlockDrawing>() | boost::adaptors::transformed([&](AssDialogueBlock *d) { return d->GetText(); }), "");
|
||||
spline.DecodeFromAss(drawing_text);
|
||||
|
||||
if (!spline.size())
|
||||
return std::make_pair(Vector2D(0, 0), Vector2D(0, 0));
|
||||
|
||||
float left = FLT_MAX;
|
||||
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);
|
||||
int GetLineAlignment(AssDialogue *diag);
|
||||
/// @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
|
||||
/// 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
|
||||
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);
|
||||
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.SetScale(fsc);
|
||||
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);
|
||||
|
||||
// Draw grid
|
||||
@ -680,17 +680,18 @@ bool VisualToolPerspective::InnerToText() {
|
||||
|
||||
float quadwidth = ab.Len();
|
||||
float quadheight = abs(ad.Y());
|
||||
float scalex = quadwidth / textwidth;
|
||||
float scaley = quadheight / textheight;
|
||||
float scalex = quadwidth / std::max(bbox.second.X() - bbox.first.X(), 1.0f);
|
||||
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 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_y = -roty * rad2deg;
|
||||
angle_z = -rotz * rad2deg;
|
||||
Vector2D oldfsc = fsc;
|
||||
fsc = 100 * Vector2D(scalex, scaley);
|
||||
fsc = 100 * scale;
|
||||
fax = rawfax * scaley / scalex;
|
||||
fay = 0;
|
||||
|
||||
@ -781,40 +782,39 @@ void VisualToolPerspective::TextToPersp() {
|
||||
|
||||
align = GetLineAlignment(active_line);
|
||||
|
||||
double descend, extlead;
|
||||
GetLineBaseExtents(active_line, textwidth, textheight, descend, extlead);
|
||||
textwidth = std::max(textwidth, 1.);
|
||||
textheight = std::max(textheight, 1.);
|
||||
double textleft = 0., texttop = 0.;
|
||||
bbox = GetLineBaseExtents(active_line);
|
||||
float textwidth = std::max(bbox.second.X() - bbox.first.X(), 1.f);
|
||||
float textheight = std::max(bbox.second.Y() - bbox.first.Y(), 1.f);
|
||||
double shiftx = 0., shifty = 0.;
|
||||
|
||||
switch ((align - 1) % 3) {
|
||||
case 1:
|
||||
textleft = -textwidth / 2;
|
||||
shiftx = -textwidth / 2;
|
||||
break;
|
||||
case 2:
|
||||
textleft = -textwidth;
|
||||
shiftx = -textwidth;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch ((align - 1) / 3) {
|
||||
case 0:
|
||||
texttop = -textheight;
|
||||
shifty = -textheight;
|
||||
break;
|
||||
case 1:
|
||||
texttop = -textheight / 2;
|
||||
shifty = -textheight / 2;
|
||||
break;
|
||||
default:
|
||||
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++) {
|
||||
Vector2D p = textrect[i];
|
||||
// Apply \fax and \fay
|
||||
p = Vector2D(p.X() + p.Y() * fax, p.X() * fay + p.Y());
|
||||
// Translate to alignment point
|
||||
p = p + Vector2D(textleft, texttop);
|
||||
p = p + Vector2D(shiftx, shifty);
|
||||
// Apply scaling
|
||||
p = Vector2D(p.X() * fsc.X() / 100., p.Y() * fsc.Y() / 100.);
|
||||
// Translate relative to origin
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
|
||||
class VisualToolPerspective final : public VisualTool<VisualToolPerspectiveDraggableFeature> {
|
||||
wxToolBar *toolBar = nullptr; /// The subtoolbar
|
||||
int settings = 0;
|
||||
int settings = 0;
|
||||
|
||||
agi::OptionValue* optOuter;
|
||||
agi::OptionValue* optOuterLocked;
|
||||
@ -69,8 +69,9 @@ class VisualToolPerspective final : public VisualTool<VisualToolPerspectiveDragg
|
||||
|
||||
int align = 0;
|
||||
|
||||
double textwidth = 0.f;
|
||||
double textheight = 0.f;
|
||||
// Corners of the bounding box of the event without any formatting.
|
||||
// The top left corner is the zero vector for text but might not be for drawings.
|
||||
std::pair<Vector2D, Vector2D> bbox;
|
||||
|
||||
Vector2D fsc;
|
||||
|
||||
|
@ -34,7 +34,7 @@ int BUTTON_ID_BASE = 1300;
|
||||
|
||||
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context)
|
||||
: VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
|
||||
, spline(*this)
|
||||
, spline(this)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user