diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj b/aegisub/build/Aegisub/Aegisub.vcxproj
index 10152cc75..a698646fb 100644
--- a/aegisub/build/Aegisub/Aegisub.vcxproj
+++ b/aegisub/build/Aegisub/Aegisub.vcxproj
@@ -209,6 +209,7 @@
+
@@ -405,6 +406,7 @@
+
diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj.filters b/aegisub/build/Aegisub/Aegisub.vcxproj.filters
index 8d778257d..5a7274c26 100644
--- a/aegisub/build/Aegisub/Aegisub.vcxproj.filters
+++ b/aegisub/build/Aegisub/Aegisub.vcxproj.filters
@@ -687,6 +687,9 @@
Automation\Lua
+
+ Features\Resolution resampler
+
@@ -1238,6 +1241,9 @@
Automation\Lua
+
+ Features\Resolution resampler
+
diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile
index c7acc3f58..6782a83c4 100644
--- a/aegisub/src/Makefile
+++ b/aegisub/src/Makefile
@@ -204,6 +204,7 @@ SRC += \
plugin_manager.cpp \
preferences.cpp \
preferences_base.cpp \
+ resolution_resampler.cpp \
scintilla_text_ctrl.cpp \
scintilla_text_selection_controller.cpp \
search_replace_engine.cpp \
diff --git a/aegisub/src/ass_dialogue.cpp b/aegisub/src/ass_dialogue.cpp
index 03a2721d8..8411b9ac9 100644
--- a/aegisub/src/ass_dialogue.cpp
+++ b/aegisub/src/ass_dialogue.cpp
@@ -285,34 +285,3 @@ AssEntry *AssDialogue::Clone() const {
*const_cast(&clone->Id) = Id;
return clone;
}
-
-void AssDialogueBlockDrawing::TransformCoords(int mx, int my, double x, double y) {
- // HACK: Implement a proper parser ffs!!
- // Could use Spline but it'd be slower and this seems to work fine
- bool is_x = true;
- std::string final;
-
- for (auto const& cur : agi::Split(text, ' ')) {
- if (std::all_of(begin(cur), end(cur), isdigit)) {
- int val = boost::lexical_cast(agi::str(cur));
- if (is_x)
- val = (int)((val + mx) * x + .5);
- else
- val = (int)((val + my) * y + .5);
- final += std::to_string(val);
- final += ' ';
- }
- else if (cur.size() == 1) {
- char c = tolower(cur[0]);
- if (c == 'm' || c == 'n' || c == 'l' || c == 'b' || c == 's' || c == 'p' || c == 'c') {
- is_x = true;
- final += c;
- final += ' ';
- }
- }
- }
-
- // Write back final
- final.pop_back();
- text = final;
-}
diff --git a/aegisub/src/ass_dialogue.h b/aegisub/src/ass_dialogue.h
index 0fb287a7f..e3b085640 100644
--- a/aegisub/src/ass_dialogue.h
+++ b/aegisub/src/ass_dialogue.h
@@ -97,11 +97,11 @@ public:
class AssDialogueBlockDrawing : public AssDialogueBlock {
public:
+ using AssDialogueBlock::text;
int Scale;
AssBlockType GetType() const override { return AssBlockType::DRAWING; }
AssDialogueBlockDrawing(std::string const& text, int scale) : AssDialogueBlock(text), Scale(scale) { }
- void TransformCoords(int trans_x,int trans_y,double mult_x,double mult_y);
};
class AssDialogueBlockOverride : public AssDialogueBlock {
diff --git a/aegisub/src/command/tool.cpp b/aegisub/src/command/tool.cpp
index b142f0a33..9a14e56d8 100644
--- a/aegisub/src/command/tool.cpp
+++ b/aegisub/src/command/tool.cpp
@@ -51,6 +51,7 @@
#include "../dialog_translation.h"
#include "../include/aegisub/context.h"
#include "../options.h"
+#include "../resolution_resampler.h"
#include "../video_context.h"
#include
diff --git a/aegisub/src/dialog_resample.cpp b/aegisub/src/dialog_resample.cpp
index 785436cf4..44e7abbca 100644
--- a/aegisub/src/dialog_resample.cpp
+++ b/aegisub/src/dialog_resample.cpp
@@ -21,20 +21,13 @@
#include "dialog_resample.h"
-#include "ass_dialogue.h"
#include "ass_file.h"
-#include "ass_style.h"
#include "include/aegisub/context.h"
#include "help_button.h"
#include "libresrc/libresrc.h"
+#include "resolution_resampler.h"
#include "video_context.h"
-#include
-
-#include
-#include
-#include
-
#include
#include
#include
@@ -144,113 +137,3 @@ void DialogResample::OnMarginChange(wxSpinCtrl *src, wxSpinCtrl *dst) {
if (symmetrical->IsChecked())
dst->SetValue(src->GetValue());
}
-
-namespace {
- struct resample_state {
- const int *margin;
- double rx;
- double ry;
- double ar;
- };
-
- void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) {
- resample_state *state = static_cast(ud);
-
- double resizer = 1.0;
- int shift = 0;
-
- switch (cur->classification) {
- case AssParameterClass::ABSOLUTE_SIZE:
- resizer = state->ry;
- break;
-
- case AssParameterClass::ABSOLUTE_POS_X:
- resizer = state->rx;
- shift = state->margin[LEFT];
- break;
-
- case AssParameterClass::ABSOLUTE_POS_Y:
- resizer = state->ry;
- shift = state->margin[TOP];
- break;
-
- case AssParameterClass::RELATIVE_SIZE_X:
- resizer = state->ar;
- break;
-
- case AssParameterClass::RELATIVE_SIZE_Y:
- //resizer = ry;
- break;
-
- case AssParameterClass::DRAWING: {
- AssDialogueBlockDrawing block(cur->Get(), 1);
- block.TransformCoords(state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
- cur->Set(block.GetText());
- return;
- }
-
- default:
- return;
- }
-
- VariableDataType curType = cur->GetType();
- if (curType == VariableDataType::FLOAT)
- cur->Set((cur->Get() + shift) * resizer);
- else if (curType == VariableDataType::INT)
- cur->Set((cur->Get() + shift) * resizer + 0.5);
- }
-
- void resample_line(resample_state *state, AssEntry &line) {
- AssDialogue *diag = dynamic_cast(&line);
- if (diag && !(diag->Comment && (boost::starts_with(diag->Effect.get(), "template") || boost::starts_with(diag->Effect.get(), "code")))) {
- boost::ptr_vector blocks(diag->ParseTags());
-
- for (auto block : blocks | agi::of_type())
- block->ProcessParameters(resample_tags, state);
-
- for (auto drawing : blocks | agi::of_type())
- drawing->TransformCoords(state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
-
- for (size_t i = 0; i < 3; ++i)
- diag->Margin[i] = int((diag->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
-
- diag->UpdateText(blocks);
- }
- else if (AssStyle *style = dynamic_cast(&line)) {
- style->fontsize = int(style->fontsize * state->ry + 0.5);
- style->outline_w *= state->ry;
- style->shadow_w *= state->ry;
- style->spacing *= state->rx;
- style->scalex *= state->ar;
- for (int i = 0; i < 3; i++)
- style->Margin[i] = int((style->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
- style->UpdateData();
- }
- }
-}
-
-void ResampleResolution(AssFile *ass, ResampleSettings const& settings) {
- int src_x, src_y;
- ass->GetResolution(src_x, src_y);
-
- // Add margins to original resolution
- src_x += settings.margin[LEFT] + settings.margin[RIGHT];
- src_y += settings.margin[TOP] + settings.margin[BOTTOM];
-
- resample_state state = {
- settings.margin,
- double(settings.script_x) / double(src_x),
- double(settings.script_y) / double(src_y),
- 1.0
- };
-
- if (settings.change_ar)
- state.ar = state.rx / state.ry;
-
- for_each(ass->Line.begin(), ass->Line.end(), std::bind(resample_line, &state, std::placeholders::_1));
-
- ass->SetScriptInfo("PlayResX", std::to_string(settings.script_x));
- ass->SetScriptInfo("PlayResY", std::to_string(settings.script_y));
-
- ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
-}
diff --git a/aegisub/src/dialog_resample.h b/aegisub/src/dialog_resample.h
index e1f01ecb9..3b55ef227 100644
--- a/aegisub/src/dialog_resample.h
+++ b/aegisub/src/dialog_resample.h
@@ -23,23 +23,7 @@ namespace agi { struct Context; }
class AssFile;
class wxCheckBox;
class wxSpinCtrl;
-
-/// Configuration parameters for a resample
-struct ResampleSettings {
- /// Amount to add to each margin
- int margin[4];
- /// New X resolution
- int script_x;
- /// New Y resolution
- int script_y;
- /// Should the aspect ratio of the subs be changed?
- bool change_ar;
-};
-
-/// Resample the subtitles in the project
-/// @param file Subtitles to resample
-/// @param settings Resample configuration settings
-void ResampleResolution(AssFile *file, ResampleSettings const& settings);
+struct ResampleSettings;
/// @class DialogResample
/// @brief Configuration dialog for resolution resampling
diff --git a/aegisub/src/resolution_resampler.cpp b/aegisub/src/resolution_resampler.cpp
new file mode 100644
index 000000000..a3509dee4
--- /dev/null
+++ b/aegisub/src/resolution_resampler.cpp
@@ -0,0 +1,177 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include "config.h"
+
+#include "resolution_resampler.h"
+
+#include "ass_dialogue.h"
+#include "ass_file.h"
+#include "ass_style.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+enum {
+ LEFT = 0,
+ RIGHT = 1,
+ TOP = 2,
+ BOTTOM = 3
+};
+
+namespace {
+ std::string transform_drawing(std::string const& drawing, int shift_x, int shift_y, double scale_x, double scale_y) {
+ bool is_x = true;
+ std::string final;
+ final.reserve(drawing.size());
+
+ for (auto const& cur : agi::Split(drawing, ' ')) {
+ if (std::all_of(begin(cur), end(cur), isdigit)) {
+ int val = boost::lexical_cast(agi::str(cur));
+ if (is_x)
+ val = (int)((val + shift_x) * scale_x + .5);
+ else
+ val = (int)((val + shift_y) * scale_y + .5);
+ final += std::to_string(val);
+ final += ' ';
+ }
+ else if (cur.size() == 1) {
+ char c = tolower(cur[0]);
+ if (c == 'm' || c == 'n' || c == 'l' || c == 'b' || c == 's' || c == 'p' || c == 'c') {
+ is_x = true;
+ final += c;
+ final += ' ';
+ }
+ }
+ }
+
+ final.pop_back();
+ return final;
+ }
+
+ struct resample_state {
+ const int *margin;
+ double rx;
+ double ry;
+ double ar;
+ };
+
+ void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) {
+ resample_state *state = static_cast(ud);
+
+ double resizer = 1.0;
+ int shift = 0;
+
+ switch (cur->classification) {
+ case AssParameterClass::ABSOLUTE_SIZE:
+ resizer = state->ry;
+ break;
+
+ case AssParameterClass::ABSOLUTE_POS_X:
+ resizer = state->rx;
+ shift = state->margin[LEFT];
+ break;
+
+ case AssParameterClass::ABSOLUTE_POS_Y:
+ resizer = state->ry;
+ shift = state->margin[TOP];
+ break;
+
+ case AssParameterClass::RELATIVE_SIZE_X:
+ resizer = state->ar;
+ break;
+
+ case AssParameterClass::RELATIVE_SIZE_Y:
+ break;
+
+ case AssParameterClass::DRAWING: {
+ cur->Set(transform_drawing(
+ cur->Get(),
+ state->margin[LEFT], state->margin[TOP], state->rx, state->ry));
+ return;
+ }
+
+ default:
+ return;
+ }
+
+ VariableDataType curType = cur->GetType();
+ if (curType == VariableDataType::FLOAT)
+ cur->Set((cur->Get() + shift) * resizer);
+ else if (curType == VariableDataType::INT)
+ cur->Set((cur->Get() + shift) * resizer + 0.5);
+ }
+
+ void resample_line(resample_state *state, AssEntry &line) {
+ AssDialogue *diag = dynamic_cast(&line);
+ if (diag && !(diag->Comment && (boost::starts_with(diag->Effect.get(), "template") || boost::starts_with(diag->Effect.get(), "code")))) {
+ boost::ptr_vector blocks(diag->ParseTags());
+
+ for (auto block : blocks | agi::of_type())
+ block->ProcessParameters(resample_tags, state);
+
+ for (auto drawing : blocks | agi::of_type())
+ drawing->text = transform_drawing(drawing->text, state->margin[LEFT], state->margin[TOP], state->rx, state->ry);
+
+ for (size_t i = 0; i < 3; ++i)
+ diag->Margin[i] = int((diag->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
+
+ diag->UpdateText(blocks);
+ }
+ else if (AssStyle *style = dynamic_cast(&line)) {
+ style->fontsize = int(style->fontsize * state->ry + 0.5);
+ style->outline_w *= state->ry;
+ style->shadow_w *= state->ry;
+ style->spacing *= state->rx;
+ style->scalex *= state->ar;
+ for (int i = 0; i < 3; i++)
+ style->Margin[i] = int((style->Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
+ style->UpdateData();
+ }
+ }
+}
+
+void ResampleResolution(AssFile *ass, ResampleSettings const& settings) {
+ int src_x, src_y;
+ ass->GetResolution(src_x, src_y);
+
+ // Add margins to original resolution
+ src_x += settings.margin[LEFT] + settings.margin[RIGHT];
+ src_y += settings.margin[TOP] + settings.margin[BOTTOM];
+
+ resample_state state = {
+ settings.margin,
+ double(settings.script_x) / double(src_x),
+ double(settings.script_y) / double(src_y),
+ 1.0
+ };
+
+ if (settings.change_ar)
+ state.ar = state.rx / state.ry;
+
+ for (auto& line : ass->Line)
+ resample_line(&state, line);
+
+ ass->SetScriptInfo("PlayResX", std::to_string(settings.script_x));
+ ass->SetScriptInfo("PlayResY", std::to_string(settings.script_y));
+
+ ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
+}
diff --git a/aegisub/src/resolution_resampler.h b/aegisub/src/resolution_resampler.h
new file mode 100644
index 000000000..0e19572c7
--- /dev/null
+++ b/aegisub/src/resolution_resampler.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+class AssFile;
+
+/// Configuration parameters for a resample
+struct ResampleSettings {
+ /// Amount to add to each margin
+ int margin[4];
+ /// New X resolution
+ int script_x;
+ /// New Y resolution
+ int script_y;
+ /// Should the aspect ratio of the subs be changed?
+ bool change_ar;
+};
+
+/// Resample the subtitles in the project
+/// @param file Subtitles to resample
+/// @param settings Resample configuration settings
+void ResampleResolution(AssFile *file, ResampleSettings const& settings);