From 270ca4f876b26fbd9e2a736f38286c16b2f0afcc Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Fri, 18 Jul 2008 15:39:34 +0000 Subject: [PATCH] Rewrite recombining of overlapping lines for export to simple subtitle formats, and reorganisation of other functions also used in those exports Originally committed to SVN as r2267. --- aegisub/subtitle_format.cpp | 259 +++++++++++++++++++++++- aegisub/subtitle_format.h | 6 +- aegisub/subtitle_format_dvd.cpp | 4 +- aegisub/subtitle_format_encore.cpp | 4 +- aegisub/subtitle_format_microdvd.cpp | 4 +- aegisub/subtitle_format_srt.cpp | 4 +- aegisub/subtitle_format_transtation.cpp | 4 +- aegisub/subtitle_format_ttxt.cpp | 4 +- 8 files changed, 278 insertions(+), 11 deletions(-) diff --git a/aegisub/subtitle_format.cpp b/aegisub/subtitle_format.cpp index f13d074b4..c84446bb2 100644 --- a/aegisub/subtitle_format.cpp +++ b/aegisub/subtitle_format.cpp @@ -365,9 +365,172 @@ void SubtitleFormat::ConvertTags(int format,wxString lineEnd) { } +//////////////////////////// +// Remove all comment lines +void SubtitleFormat::StripComments() { + using std::list; + list::iterator next; + + for (list::iterator cur = Line->begin(); cur != Line->end(); cur = next) { + next = cur; + next++; + + AssDialogue *dlg = AssEntry::GetAsDialogue(*cur); + if (dlg && (dlg->Comment || dlg->Text.IsEmpty())) { + delete *cur; + Line->erase(cur); + } + } +} + + +///////////////////////////////// +// Remove all non-dialogue lines +void SubtitleFormat::StripNonDialogue() { + using std::list; + list::iterator next; + + for (list::iterator cur = Line->begin(); cur != Line->end(); cur = next) { + next = cur; + next++; + + if (!AssEntry::GetAsDialogue(*cur)) { + delete *cur; + Line->erase(cur); + } + } +} + + +/////////////////////////////////////////// +// Helper function for RecombineOverlaps() +static void InsertLineSortedIntoList(std::list &list, std::list::iterator next, AssDialogue *newdlg) { + std::list::iterator insertpos = next; + bool inserted = false; + while (insertpos != list.end()) { + AssDialogue *candidate = AssEntry::GetAsDialogue(*insertpos); + if (candidate && candidate->Start >= newdlg->Start) { + list.insert(insertpos, newdlg); + inserted = true; + break; + } + insertpos++; + } + if (!inserted) { + list.push_back(newdlg); + } +} + +/////////////////////////////////////////////////////////// +// Split and merge lines so there are no overlapping lines +// http://malakith.net/aegiwiki/Split-merge_algorithm_for_converting_to_simple_subtitle_formats +void SubtitleFormat::RecombineOverlaps() { + using std::list; + list::iterator next; + + for (list::iterator cur = Line->begin(); cur != Line->end(); cur = next) { + next = cur; + next++; + + if (next == Line->end()) break; + + AssDialogue *prevdlg = AssEntry::GetAsDialogue(*cur); + AssDialogue *curdlg = AssEntry::GetAsDialogue(*next); + + if (curdlg && prevdlg && prevdlg->End > curdlg->Start) { + // Use names like in the algorithm description and prepare for erasing + // old dialogues from the list + list::iterator prev = cur; + cur = next; + next++; + + // std::list::insert() inserts items before the given iterator, so + // we need 'next' for inserting. 'prev' and 'cur' can safely be erased + // from the list now. + Line->erase(prev); + Line->erase(cur); + + //Is there an A part before the overlap? + if (curdlg->Start > prevdlg->Start) { + // Produce new entry with correct values + AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone()); + newdlg->Start = prevdlg->Start; + newdlg->End = curdlg->Start; + newdlg->Text = prevdlg->Text; + + InsertLineSortedIntoList(*Line, next, newdlg); + } + + // Overlapping A+B part + { + AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone()); + newdlg->Start = curdlg->Start; + newdlg->End = (prevdlg->End < curdlg->End) ? prevdlg->End : curdlg->End; + // Put an ASS format hard linewrap between lines + newdlg->Text = curdlg->Text + _T("\\N") + prevdlg->Text; + + InsertLineSortedIntoList(*Line, next, newdlg); + } + + // Is there an A part after the overlap? + if (prevdlg->End > curdlg->End) { + // Produce new entry with correct values + AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone()); + newdlg->Start = curdlg->End; + newdlg->End = prevdlg->End; + newdlg->Text = prevdlg->Text; + + InsertLineSortedIntoList(*Line, next, newdlg); + } + + // Is there a B part after the overlap? + if (curdlg->End > prevdlg->End) { + // Produce new entry with correct values + AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone()); + newdlg->Start = prevdlg->End; + newdlg->End = curdlg->End; + newdlg->Text = curdlg->Text; + + InsertLineSortedIntoList(*Line, next, newdlg); + } + + next--; + } + } +} + + +//////////////////////////////////////////////// +// Merge identical lines that follow each other +void SubtitleFormat::MergeIdentical() { + using std::list; + list::iterator next; + + for (list::iterator cur = Line->begin(); cur != Line->end(); cur = next) { + next = cur; + next++; + + if (next == Line->end()) break; + + AssDialogue *curdlg = AssEntry::GetAsDialogue(*cur); + AssDialogue *nextdlg = AssEntry::GetAsDialogue(*next); + + if (curdlg && nextdlg && curdlg->Text == nextdlg->Text) { + // Merge timing + nextdlg->Start = (nextdlg->Start < curdlg->Start ? nextdlg->Start : curdlg->Start); + nextdlg->End = (nextdlg->End > curdlg->End ? nextdlg->End : curdlg->End); + + // Remove duplicate line + delete *cur; + Line->erase(cur); + } + } +} + + //////////////////////////////////////////// // Merge identical and/or overlapping lines -void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue) { +/*void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue) { using std::list; list::iterator next; list::iterator prev = Line->end(); @@ -389,9 +552,9 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool // Proper line else { - // Check for duplication if (previous != NULL) { - if (previous->Text == current->Text) { + // Check for duplication + if (identical && previous->Text == current->Text) { if (abs(current->Start.GetMS() - previous->End.GetMS()) < 20) { current->Start = (current->Start < previous->Start ? current->Start : previous->Start); current->End = (current->End > previous->End ? current->End : previous->End); @@ -399,6 +562,94 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool Line->erase(prev); } } + + // Check for overlap + // + if (overlaps && previous->End.GetMS() > current->Start.GetMS()) { + // Grab the data + int prev_s = previous->Start.GetMS(); + int prev_e = previous->End.GetMS(); + int cur_s = current->Start.GetMS(); + int cur_e = current->End.GetMS(); + wxString prev_t = previous->Text; + wxString cur_t = current->Text; + + // Make sure cur points to the line before those we're working with + --cur; + // Remove 'cur' and 'prev' from list + Line->erase(prev); + Line->erase(prev); + // Now make sure that 'cur' points to the item after the old 'current' + ++cur; + + // A-only part + if (prev_s < cur_s) { + // Actually lines generated from this one never move + // So we can assume it can just be inserted at cur + AssDialogue *part = AssEntry::GetAsDialogue(previous->Clone()); + part->End.SetMS(prev_s); + // Nothing to do about start-time or text, they are already correct + + // Insert into list + Line->insert(cur, part); + // 'cur' now points to one past newly inserted item + } + + // A+B part + { + // These lines never move either, so just insert + AssDialogue *part = AssEntry::GetAsDialogue(current->Clone()); + // Fix values + part->Start.SetMS(cur_s); + part->End.SetMS(prev_e); + // Inserting an ASS hard linebreak + part->Text = cur_t + _T("\\N") + prev_t; + + // Insert into list + Line->insert(cur, part); + // 'cur' now points to one past newly inserted item + } + + // B-only part + if (cur_e > prev_e) { + // We need to seek for the right position here + AssDialogue *part = AssEntry::GetAsDialogue(current->Clone()); + // Fix values + part->Start.SetMS(prev_e); + part->End.SetMS(cur_e); + part->Text = cur_t; + + list::iterator newpos = cur; + + // Insert into list + // Make sure 'cur' never moves during this + if (newpos == Line->end()) { + Line->push_back(part); + } + else { + while (newpos != Line->end()) { + AssDialogue *newline = AssEntry::GetAsDialogue(*newpos); + if (newline && part->Start.GetMS() <= newline->Start.GetMS()) { + // Suitable position + Line->insert(newpos, part); + break; + } + ++newpos; + } + } + } + + // Now 'cur' has moved around a lot so make sure everything is correct + // 'cur' currently points to one past the last inserted item + // It should really point to the last inserted item (that wasn't + // inserted at a later position) so we need to move it one back. + // In fact, 'next' should have that value. + next = cur--; + // 'prev' should already be correct + // And update the rest too + previous = AssEntry::GetAsDialogue(*prev); + current = AssEntry::GetAsDialogue(*cur); + } } // Set as previous @@ -415,4 +666,4 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool } } } -} +}*/ diff --git a/aegisub/subtitle_format.h b/aegisub/subtitle_format.h index ac7d3ade5..d0eec2801 100644 --- a/aegisub/subtitle_format.h +++ b/aegisub/subtitle_format.h @@ -71,7 +71,11 @@ protected: void ClearCopy(); void SortLines(); void ConvertTags(int format,wxString lineEnd); - void Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue); + //void Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue); + void StripComments(); + void StripNonDialogue(); + void RecombineOverlaps(); + void MergeIdentical(); void Clear(); void LoadDefault(bool defline=true); diff --git a/aegisub/subtitle_format_dvd.cpp b/aegisub/subtitle_format_dvd.cpp index e2a0e7347..bee136284 100644 --- a/aegisub/subtitle_format_dvd.cpp +++ b/aegisub/subtitle_format_dvd.cpp @@ -347,7 +347,9 @@ void DVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) { // Prepare subtitles CreateCopy(); SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); // Get subpictures std::vector pics; diff --git a/aegisub/subtitle_format_encore.cpp b/aegisub/subtitle_format_encore.cpp index f1a58e8ed..177619874 100644 --- a/aegisub/subtitle_format_encore.cpp +++ b/aegisub/subtitle_format_encore.cpp @@ -77,7 +77,9 @@ void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) { // Convert to encore CreateCopy(); SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); ConvertTags(1,_T("\r\n")); // Write lines diff --git a/aegisub/subtitle_format_microdvd.cpp b/aegisub/subtitle_format_microdvd.cpp index a138692fc..18ae56c97 100644 --- a/aegisub/subtitle_format_microdvd.cpp +++ b/aegisub/subtitle_format_microdvd.cpp @@ -178,7 +178,9 @@ void MicroDVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) { // Convert file CreateCopy(); SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); ConvertTags(1,_T("|")); // Open file diff --git a/aegisub/subtitle_format_srt.cpp b/aegisub/subtitle_format_srt.cpp index 82af786c2..e50e81d8e 100644 --- a/aegisub/subtitle_format_srt.cpp +++ b/aegisub/subtitle_format_srt.cpp @@ -182,7 +182,9 @@ void SRTSubtitleFormat::WriteFile(wxString _filename,wxString encoding) { // Convert to SRT CreateCopy(); SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); ConvertTags(2,_T("\r\n")); // Write lines diff --git a/aegisub/subtitle_format_transtation.cpp b/aegisub/subtitle_format_transtation.cpp index 5676d8da7..76e6836ba 100644 --- a/aegisub/subtitle_format_transtation.cpp +++ b/aegisub/subtitle_format_transtation.cpp @@ -79,7 +79,9 @@ void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding) // Convert to TranStation CreateCopy(); SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); // Write lines using std::list; diff --git a/aegisub/subtitle_format_ttxt.cpp b/aegisub/subtitle_format_ttxt.cpp index b073e05a7..71093f1b8 100644 --- a/aegisub/subtitle_format_ttxt.cpp +++ b/aegisub/subtitle_format_ttxt.cpp @@ -322,7 +322,9 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, AssDialogue *line) { void TTXTSubtitleFormat::ConvertToTTXT () { // Convert SortLines(); - Merge(true,true,true,false); + StripComments(); + RecombineOverlaps(); + MergeIdentical(); ConvertTags(1,_T("\r\n")); // Find last line