// Copyright (c) 2005, Rodrigo Braz Monteiro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // ----------------------------------------------------------------------------- // // AEGISUB // // Website: http://aegisub.cellosoft.com // Contact: mailto:zeratul@cellosoft.com // /////////// // Headers #include "config.h" #include "dialog_resample.h" #include "ass_file.h" #include "ass_dialogue.h" #include "ass_style.h" #include "ass_override.h" #include "subs_grid.h" #include "validators.h" #include "video_context.h" #include "utils.h" #include "subs_edit_box.h" #include "help_button.h" /////////////// // Constructor DialogResample::DialogResample(wxWindow *parent, SubtitlesGrid *_grid) : wxDialog (parent,-1,_("Resample resolution"),wxDefaultPosition) { // Set icon SetIcon(BitmapToIcon(wxBITMAP(resample_toolbutton))); // Variables AssFile *subs = AssFile::top; grid = _grid; // Margins MarginSymmetrical = NULL; // Do not remove this wxSizer *MarginBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Margin offset")); wxSizer *MarginSizer = new wxGridSizer(3,3,5,5); MarginTop = new wxTextCtrl(this,TEXT_MARGIN_T,_T("0"),wxDefaultPosition,wxSize(50,-1),0); MarginLeft = new wxTextCtrl(this,TEXT_MARGIN_L,_T("0"),wxDefaultPosition,wxSize(50,-1),0); MarginSymmetrical = new wxCheckBox(this,CHECK_SYMMETRICAL,_("Symmetrical")); MarginRight = new wxTextCtrl(this,TEXT_MARGIN_R,_T("0"),wxDefaultPosition,wxSize(50,-1),0); MarginBottom = new wxTextCtrl(this,TEXT_MARGIN_B,_T("0"),wxDefaultPosition,wxSize(50,-1),0); MarginSizer->AddSpacer(1); MarginSizer->Add(MarginTop,1,wxEXPAND); MarginSizer->AddSpacer(1); MarginSizer->Add(MarginLeft,1,wxEXPAND); MarginSizer->Add(MarginSymmetrical,1,wxEXPAND); MarginSizer->Add(MarginRight,1,wxEXPAND); MarginSizer->AddSpacer(1); MarginSizer->Add(MarginBottom,1,wxEXPAND); MarginSizer->AddSpacer(1); MarginBoxSizer->Add(MarginSizer,1,wxALIGN_CENTER|wxBOTTOM,5); MarginSymmetrical->SetValue(true); MarginRight->Enable(false); MarginBottom->Enable(false); // Resolution wxSizer *ResBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Resolution")); wxSizer *ResSizer = new wxBoxSizer(wxHORIZONTAL); int sw,sh; subs->GetResolution(sw,sh); ResXValue = wxString::Format(_T("%i"),sw); ResYValue = wxString::Format(_T("%i"),sh); ResX = new wxTextCtrl(this,-1,_T(""),wxDefaultPosition,wxSize(50,-1),0,NumValidator(&ResXValue)); ResY = new wxTextCtrl(this,-1,_T(""),wxDefaultPosition,wxSize(50,-1),0,NumValidator(&ResYValue)); wxStaticText *ResText = new wxStaticText(this,-1,_("x")); wxButton *FromVideo = new wxButton(this,BUTTON_DEST_FROM_VIDEO,_("From video")); if (!VideoContext::Get()->IsLoaded()) FromVideo->Enable(false); ResSizer->Add(ResX,1,wxRIGHT,5); ResSizer->Add(ResText,0,wxALIGN_CENTER | wxRIGHT,5); ResSizer->Add(ResY,1,wxRIGHT,5); ResSizer->Add(FromVideo,1,0,0); Anamorphic = new wxCheckBox(this,CHECK_ANAMORPHIC,_("Change aspect ratio")); ResBoxSizer->Add(ResSizer,1,wxEXPAND|wxBOTTOM,5); ResBoxSizer->Add(Anamorphic,0,0,0); // Button sizer wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer(); ButtonSizer->AddButton(new wxButton(this,wxID_OK)); ButtonSizer->AddButton(new wxButton(this,wxID_CANCEL)); ButtonSizer->AddButton(new HelpButton(this,_T("Resample"))); ButtonSizer->Realize(); // Main sizer wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL); MainSizer->Add(MarginBoxSizer,1,wxEXPAND|wxALL,5); MainSizer->Add(ResBoxSizer,0,wxEXPAND|wxALL,5); MainSizer->Add(ButtonSizer,0,wxEXPAND|wxRIGHT|wxLEFT|wxBOTTOM,5); MainSizer->SetSizeHints(this); SetSizer(MainSizer); CenterOnParent(); instance = this; } /////////////// // Event table BEGIN_EVENT_TABLE(DialogResample,wxDialog) EVT_BUTTON(wxID_OK,DialogResample::OnResample) EVT_BUTTON(BUTTON_DEST_FROM_VIDEO,DialogResample::OnGetDestRes) EVT_CHECKBOX(CHECK_SYMMETRICAL,DialogResample::OnSymmetrical) EVT_TEXT(TEXT_MARGIN_T,DialogResample::OnMarginChange) EVT_TEXT(TEXT_MARGIN_L,DialogResample::OnMarginChange) EVT_TEXT(TEXT_MARGIN_R,DialogResample::OnMarginChange) EVT_TEXT(TEXT_MARGIN_B,DialogResample::OnMarginChange) END_EVENT_TABLE() ///////////////// // Resample tags void DialogResample::ResampleTags (wxString name,int n,AssOverrideParameter *curParam,void *_curDiag) { instance->DoResampleTags(name,n,curParam,_curDiag); } void DialogResample::DoResampleTags (wxString name,int n,AssOverrideParameter *curParam,void *_curDiag) { double resizer = 1.0; bool isX = false; bool isY = false; switch (curParam->classification) { case PARCLASS_ABSOLUTE_SIZE: resizer = r; break; case PARCLASS_ABSOLUTE_POS_X: resizer = rx; isX = true; break; case PARCLASS_ABSOLUTE_POS_Y: resizer = ry; isY = true; break; case PARCLASS_RELATIVE_SIZE_X: resizer = ar; break; case PARCLASS_RELATIVE_SIZE_Y: //resizer = ry; break; case PARCLASS_DRAWING: { AssDialogueBlockDrawing block; block.text = curParam->AsText(); block.TransformCoords(m[0],m[2],rx,ry); curParam->SetText(block.GetText()); } return; default: return; } VariableDataType curType = curParam->GetType(); if (curType == VARDATA_FLOAT) { float par = curParam->AsFloat(); if (isX) par += m[0]; if (isY) par += m[2]; curParam->SetFloat(par * resizer); } if (curType == VARDATA_INT) { int par = curParam->AsInt(); if (isX) par += m[0]; if (isY) par += m[2]; curParam->SetInt(int(double(par) * resizer + 0.5)); } } //////////// // Resample void DialogResample::OnResample (wxCommandEvent &event) { // Resolutions AssFile *subs = AssFile::top; int x1,y1; subs->GetResolution(x1,y1); long x2 = 0; long y2 = 0; ResX->GetValue().ToLong(&x2); ResY->GetValue().ToLong(&y2); // Sanity check if (x1 == 0 || y1 == 0) { wxMessageBox(_T("Invalid source resolution. This should not happen. Please contact the developers."),_("Error"),wxCENTRE|wxICON_ERROR); return; } if (x2 == 0 || y2 == 0) { wxMessageBox(_("Invalid resolution: destination resolution cannot be 0 on either dimension."),_("Error"),wxCENTRE|wxICON_ERROR); return; } // Get margins MarginLeft->GetValue().ToLong(&m[0]); MarginRight->GetValue().ToLong(&m[1]); MarginTop->GetValue().ToLong(&m[2]); MarginBottom->GetValue().ToLong(&m[3]); // Add margins to original resolution x1 += m[0] + m[1]; x2 += m[2] + m[3]; // Calculate resamples rx = double(x2)/double(x1); ry = double(y2)/double(y1); r = ry; if (Anamorphic->IsChecked()) ar = rx/ry; else ar = 1.0; // Iterate through subs AssStyle *curStyle; AssDialogue *curDiag; for (entryIter cur=subs->Line.begin();cur!=subs->Line.end();cur++) { // Apply to dialogues curDiag = AssEntry::GetAsDialogue(*cur); if (curDiag && !(curDiag->Comment && (curDiag->Effect.StartsWith(_T("template")) || curDiag->Effect.StartsWith(_T("code"))))) { try { // Override tags curDiag->ParseASSTags(); curDiag->ProcessParameters(&DialogResample::ResampleTags,curDiag); // Drawing tags size_t nblocks = curDiag->Blocks.size(); AssDialogueBlockDrawing *curBlock; for (size_t i=0;iBlocks.at(i)); if (curBlock) { curBlock->TransformCoords(m[0],m[2],rx,ry); } } // Margins for (int i=0;i<2;i++) { curDiag->Margin[i] = int((curDiag->Margin[i]+m[i]) * rx + 0.5); curDiag->Margin[i+2] = int((curDiag->Margin[i+2]+m[i+2]) * ry + 0.5); } // Update curDiag->UpdateText(); curDiag->UpdateData(); curDiag->ClearBlocks(); continue; } catch (const wchar_t *err) { wxLogMessage(err); } catch (wxString err) { wxLogMessage(err); } } // Apply to styles curStyle = AssEntry::GetAsStyle(*cur); if (curStyle) { curStyle->fontsize = int(curStyle->fontsize * r + 0.5); curStyle->outline_w *= r; curStyle->shadow_w *= r; curStyle->spacing *= rx; curStyle->scalex *= ar; for (int i=0;i<2;i++) { curStyle->Margin[i] = int((curStyle->Margin[i]+m[i]) * rx + 0.5); curStyle->Margin[i+2] = int((curStyle->Margin[i+2]+m[i+2]) * ry + 0.5); } curStyle->UpdateData(); } } // Change script resolution subs->SetScriptInfo(_T("PlayResX"),wxString::Format(_T("%i"),x2)); subs->SetScriptInfo(_T("PlayResY"),wxString::Format(_T("%i"),y2)); // Flag as modified subs->FlagAsModified(_("resolution resampling")); grid->CommitChanges(); grid->editBox->Update(); EndModal(0); } ///////////////////////////////////////// // Get destination resolution from video void DialogResample::OnGetDestRes (wxCommandEvent &event) { ResX->SetValue(wxString::Format(_T("%i"),VideoContext::Get()->GetWidth())); ResY->SetValue(wxString::Format(_T("%i"),VideoContext::Get()->GetHeight())); } //////////////////////////////// // Symmetrical checkbox clicked void DialogResample::OnSymmetrical (wxCommandEvent &event) { bool state = !MarginSymmetrical->IsChecked(); MarginRight->Enable(state); MarginBottom->Enable(state); if (!state) { MarginRight->SetValue(MarginLeft->GetValue()); MarginBottom->SetValue(MarginTop->GetValue()); } } //////////////////////// // Margin value changed void DialogResample::OnMarginChange (wxCommandEvent &event) { if (!MarginSymmetrical) return; bool state = !MarginSymmetrical->IsChecked(); if (!state && (event.GetEventObject() == MarginLeft || event.GetEventObject() == MarginTop)) { MarginRight->SetValue(MarginLeft->GetValue()); MarginBottom->SetValue(MarginTop->GetValue()); } } //////////////////// // Static variables DialogResample *DialogResample::instance = NULL;