Skip to content

Commit

Permalink
Merge pull request #12 from YourMJK/add-move-mode
Browse files Browse the repository at this point in the history
Add move functionality
  • Loading branch information
MonoS authored Jan 30, 2024
2 parents bef7dfc + 32b4df5 commit 923b3f9
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 60 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
SupMover - Shift timings and Screen Area of PGS/Sup subtitle

# Usage
`SupMover (<input.sup> <output.sup>) [delay (ms)] [crop (<left> <top> <right> <bottom>)] [resync (<num>/<den> | multFactor)] [add_zero] [tonemap <perc>] [cut_merge <Cut&Merge option>]`
`SupMover (<input.sup> <output.sup>) [delay (ms)] [move (<delta x> <delta y>)] [crop (<left> <top> <right> <bottom>)] [resync (<num>/<den> | multFactor)] [add_zero] [tonemap <perc>] [cut_merge <Cut&Merge option>]`

`SupMover (<input.sup> <output.sup> <ms>)` old syntax, kept for backward compatibility

Expand All @@ -11,10 +11,14 @@ SupMover - Shift timings and Screen Area of PGS/Sup subtitle
* Apply a milliseconds delay, positive or negative, to all the subpic of the subtitle, it can be fractional as the SUP speficication have a precision of 1/90ms
* resync
* Multiply all the timestamp by this factor, this can also be supplied as a fraction
* move
* Shift the windows position of all subpic by the inputed parameters (the image data is left untouched).
* Position is clamped to the screen edges so that windows are always fully contained within the screen area.
* crop
* Crop the windows area of all subpic by the inputed parameters.
* This is done losslessly by only shifting the windows position (the image data is left untouched).
* Crop functionality is not exstensivelly tested when multiple Composition Object or Windows are present or when the windows are is outside the new screen area, a warning is issued if that's the case and i strongly advise to check the resulting subtitle with a video player, also handling of the Object Cropped flag and windows area bigger than the new screen area is not implemented, a warning is issued if needed
* If both move and crop are selected, the crop is performed after the move.
* delay + resync
* If both modes are selected the delay will be adjusted if it comes before the resync parameter, for example if the program is launched with `delay 1000 resync 1.001` it will be internally adjusted to 1001ms, instead if it's launched with `resync 1.001 delay 1000` it will not
* add_zero
Expand Down
165 changes: 106 additions & 59 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ struct t_timestamp {
unsigned long ms;
};

struct t_move {
int16_t deltaX;
int16_t deltaY;
};

struct t_crop {
uint16_t left;
uint16_t top;
Expand Down Expand Up @@ -138,6 +143,7 @@ struct t_PDS {

struct t_cmd {
int32_t delay = 0;
t_move move = {};
t_crop crop = {};
double resync = 1;
bool addZero = false;
Expand Down Expand Up @@ -504,6 +510,7 @@ bool parseCutMerge(t_cutMerge* cutMerge) {

bool ParseCMD(int32_t argc, char** argv, t_cmd& cmd) {
cmd.delay = 0;
cmd.move = {};
cmd.crop = {};
cmd.resync = 1;
cmd.addZero = false;
Expand Down Expand Up @@ -539,6 +546,11 @@ bool ParseCMD(int32_t argc, char** argv, t_cmd& cmd) {
*/
}
}
else if (command == "move") {
cmd.move.deltaX = atoi(argv[i + 1]);
cmd.move.deltaY = atoi(argv[i + 2]);
i += 3;
}
else if (command == "crop") {
cmd.crop.left = atoi(argv[i + 1]);
cmd.crop.top = atoi(argv[i + 2]);
Expand Down Expand Up @@ -687,7 +699,7 @@ int main(int32_t argc, char** argv)


if (argc < 4) {
std::printf("Usage: SupMover (<input.sup> <output.sup>) [delay (ms)] [crop (<left> <top> <right> <bottom>)] [resync (<num>/<den> | multFactor)] [add_zero] [tonemap <perc>]\r\n");
std::printf("Usage: SupMover (<input.sup> <output.sup>) [delay (ms)] [move (<delta x> <delta y>)] [crop (<left> <top> <right> <bottom>)] [resync (<num>/<den> | multFactor)] [add_zero] [tonemap <perc>]\r\n");
std::printf("delay and resync command are executed in the order supplied\r\n");
return 0;
}
Expand All @@ -700,11 +712,12 @@ int main(int32_t argc, char** argv)


bool doDelay = cmd.delay != 0;
bool doMove = cmd.move.deltaX != 0 || cmd.move.deltaY != 0;
bool doCrop = (cmd.crop.left + cmd.crop.top + cmd.crop.right + cmd.crop.bottom) > 0;
bool doResync = cmd.resync != 1;
bool doTonemap = cmd.tonemap != 1;

bool doSomething = doDelay || doCrop || doResync || cmd.addZero || doTonemap || cmd.cutMerge.doCutMerge;
bool doSomething = doDelay || doMove || doCrop || doResync || cmd.addZero || doTonemap || cmd.cutMerge.doCutMerge;

FILE* input = std::fopen(argv[1], "rb");
if (input == nullptr) {
Expand Down Expand Up @@ -801,7 +814,7 @@ int main(int32_t argc, char** argv)
break;
case 0x16:
//std::printf("PCS\r\n");
if (doCrop || cmd.addZero || cmd.cutMerge.doCutMerge) {
if (doMove | doCrop || cmd.addZero || cmd.cutMerge.doCutMerge) {
pcs = ReadPCS(&buffer[start + HEADER_SIZE]);
offsetCurrPCS = start;

Expand Down Expand Up @@ -904,79 +917,113 @@ int main(int32_t argc, char** argv)
case 0x17:
//std::printf("WDS\r\n");
fixPCS = false;
if (doCrop) {
if (doMove || doCrop) {
wds = ReadWDS(&buffer[start + HEADER_SIZE]);

if (wds.numberOfWindows > 1) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Multiple windows at timestamp %lu:%02lu:%02lu.%03lu! Please Check!\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);
}

for (int i = 0; i < wds.numberOfWindows; i++) {
t_rect wndRect;
uint16_t corrHor = 0;
uint16_t corrVer = 0;

wndRect.x = wds.windows[i].WindowsHorPos;
wndRect.y = wds.windows[i].WindowsVerPos;
wndRect.width = wds.windows[i].WindowsWidth;
wndRect.height = wds.windows[i].WindowsHeight;
if (doMove) {
for (int i = 0; i < wds.numberOfWindows; i++) {
t_window *window = &wds.windows[i];
int16_t minDeltaX = -(int16_t)window->WindowsHorPos;
int16_t minDeltaY = -(int16_t)window->WindowsVerPos;
int16_t maxDeltaX = pcs.width - (window->WindowsHorPos + window->WindowsWidth);
int16_t maxDeltaY = pcs.height - (window->WindowsVerPos + window->WindowsHeight);
int16_t clampedDeltaX = std::min(std::max(cmd.move.deltaX, minDeltaX), maxDeltaX);
int16_t clampedDeltaY = std::min(std::max(cmd.move.deltaY, minDeltaY), maxDeltaY);

if (wndRect.width > screenRect.width
|| wndRect.height > screenRect.height) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Window is bigger then new screen area at timestamp %lu:%02lu:%02lu.%03lu\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);
std::printf("Implement it!\r\n");
/*
pcs.width = wndRect.width;
pcs.height = wndRect.height;
fixPCS = true;
*/
}
else {
if (!rectIsContained(screenRect, wndRect)) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Window is outside new screen area at timestamp %lu:%02lu:%02lu.%03lu\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);
window->WindowsHorPos += clampedDeltaX;
window->WindowsVerPos += clampedDeltaY;

uint16_t wndRightPoint = wndRect.x + wndRect.width;
uint16_t screenRightPoint = screenRect.x + screenRect.width;
if (wndRightPoint > screenRightPoint) {
corrHor = wndRightPoint - screenRightPoint;
}

uint16_t wndBottomPoint = wndRect.y + wndRect.height;
uint16_t screenBottomPoint = screenRect.y + screenRect.height;
if (wndBottomPoint > screenBottomPoint) {
corrVer = wndBottomPoint - screenBottomPoint;
for (int j = 0; j < pcs.numCompositionObject; j++) {
t_compositionObject *object = &pcs.compositionObject[j];
if (object->windowID != window->windowID) continue;

if (object->objectCroppedFlag == 0x40) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Object Cropped Flag set at timestamp %lu:%02lu:%02lu.%03lu! Crop fields are not supported yet.\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);
/*
object->objCropHorPos += clampedDeltaX;
object->objCropVerPos += clampedDeltaY;
*/
}
object->objectHorPos += clampedDeltaX;
object->objectVerPos += clampedDeltaY;
fixPCS = true;
}
}
}

if (corrHor + corrVer != 0) {
std::printf("Please check\r\n");
if (doCrop) {
for (int i = 0; i < wds.numberOfWindows; i++) {
t_rect wndRect;
uint16_t corrHor = 0;
uint16_t corrVer = 0;

wndRect.x = wds.windows[i].WindowsHorPos;
wndRect.y = wds.windows[i].WindowsVerPos;
wndRect.width = wds.windows[i].WindowsWidth;
wndRect.height = wds.windows[i].WindowsHeight;

if (wndRect.width > screenRect.width
|| wndRect.height > screenRect.height) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Window is bigger then new screen area at timestamp %lu:%02lu:%02lu.%03lu\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);
std::printf("Implement it!\r\n");
/*
pcs.width = wndRect.width;
pcs.height = wndRect.height;
fixPCS = true;
*/
}
else {
if (!rectIsContained(screenRect, wndRect)) {
t_timestamp timestamp = PTStoTimestamp(header.pts1);
std::printf("Window is outside new screen area at timestamp %lu:%02lu:%02lu.%03lu\r\n", timestamp.hh, timestamp.mm, timestamp.ss, timestamp.ms);

uint16_t wndRightPoint = wndRect.x + wndRect.width;
uint16_t screenRightPoint = screenRect.x + screenRect.width;
if (wndRightPoint > screenRightPoint) {
corrHor = wndRightPoint - screenRightPoint;
}

uint16_t wndBottomPoint = wndRect.y + wndRect.height;
uint16_t screenBottomPoint = screenRect.y + screenRect.height;
if (wndBottomPoint > screenBottomPoint) {
corrVer = wndBottomPoint - screenBottomPoint;
}

if (corrHor + corrVer != 0) {
std::printf("Please check\r\n");
}
}
}
}

if (cmd.crop.left > wds.windows[i].WindowsHorPos) {
wds.windows[i].WindowsHorPos = 0;
}
else {
wds.windows[i].WindowsHorPos -= (cmd.crop.left + corrHor);
}
if (cmd.crop.left > wds.windows[i].WindowsHorPos) {
wds.windows[i].WindowsHorPos = 0;
}
else {
wds.windows[i].WindowsHorPos -= (cmd.crop.left + corrHor);
}

if (cmd.crop.top > wds.windows[i].WindowsVerPos) {
wds.windows[i].WindowsVerPos = 0;
}
else {
wds.windows[i].WindowsVerPos -= (cmd.crop.top + corrVer);
}
if (cmd.crop.top > wds.windows[i].WindowsVerPos) {
wds.windows[i].WindowsVerPos = 0;
}
else {
wds.windows[i].WindowsVerPos -= (cmd.crop.top + corrVer);
}

if (corrVer != 0 || corrHor != 0) {
for (int j = 0; j < pcs.numCompositionObject; j++) {
if (pcs.compositionObject[j].windowID != wds.windows[i].windowID) continue;
pcs.compositionObject[j].objectVerPos -= corrVer;
pcs.compositionObject[j].objectHorPos -= corrHor;
if (corrVer != 0 || corrHor != 0) {
for (int j = 0; j < pcs.numCompositionObject; j++) {
if (pcs.compositionObject[j].windowID != wds.windows[i].windowID) continue;
pcs.compositionObject[j].objectVerPos -= corrVer;
pcs.compositionObject[j].objectHorPos -= corrHor;
}
fixPCS = true;
}
fixPCS = true;
}
}

Expand Down

0 comments on commit 923b3f9

Please sign in to comment.