Кадрирование: поворот после обрезки, рамка не расходится с настройкой
Поворот и отражение применялись к исходному кадру, а обрезка шла после них. Из-за этого рамка показывала одно место, а вырезалось другое. Теперь наоборот: сначала выбирается кусок, потом он поворачивается и отражается. Если после поворота кусок лёг поперёк кадра проекта, он вписывается целиком с полями. Проверено: с отражением левый и правый края вырезанного куска меняются местами, то есть отражается именно он, а не оригинал. Вторая причина расхождения — предел ползунков смещения. Рамку можно было утащить туда, куда ползунок не дотягивается: рамка рисовалась от неограниченного значения, а фильтр считался от обрезанного. Предел расширен вчетверо, а рамка теперь пересчитывается обратно из настройки — если та упёрлась, это сразу видно. Отдельно: разбор звука больше не держит главный поток. Перезапуск ждал окончания текущего файла, а это чтение гигабайтов — окно всё это время не отвечало, и меню не открывалось вовсе. Теперь проход доигрывает сам и запускается заново, а идёт он с наинизшим приоритетом.
This commit is contained in:
parent
40a0e2041f
commit
f9e50632aa
4 changed files with 121 additions and 47 deletions
|
|
@ -164,11 +164,10 @@ function TransformFilter(const AT: TMediaTransform;
|
|||
ASrcWidth, ASrcHeight, ADstWidth, ADstHeight: Integer): string;
|
||||
var
|
||||
Chain: string;
|
||||
W0, H0, Tmp: Integer;
|
||||
K, KFit, KFill: Double;
|
||||
SW, SH: Integer;
|
||||
CW, CH, CX, CY: Integer;
|
||||
PX, PY: Integer;
|
||||
PW, PH, PX, PY: Integer;
|
||||
Rot: Integer;
|
||||
|
||||
procedure Add(const AFilter: string);
|
||||
|
|
@ -185,32 +184,14 @@ begin
|
|||
Exit;
|
||||
|
||||
Chain := '';
|
||||
W0 := ASrcWidth;
|
||||
H0 := ASrcHeight;
|
||||
|
||||
{ --- Поворот и отражение --- }
|
||||
Rot := ((AT.Rotation mod 360) + 360) mod 360;
|
||||
case Rot of
|
||||
90:
|
||||
begin
|
||||
{ transpose=1 — поворот на 90 по часовой стрелке. }
|
||||
Add('transpose=1');
|
||||
Tmp := W0; W0 := H0; H0 := Tmp;
|
||||
end;
|
||||
180:
|
||||
{ Два поворота подряд дешевле и точнее, чем поворот на произвольный
|
||||
угол: последний пересчитывает каждый пиксель. }
|
||||
Add('transpose=1,transpose=1');
|
||||
270:
|
||||
begin
|
||||
Add('transpose=2');
|
||||
Tmp := W0; W0 := H0; H0 := Tmp;
|
||||
end;
|
||||
end;
|
||||
if AT.FlipH then
|
||||
Add('hflip');
|
||||
if AT.FlipV then
|
||||
Add('vflip');
|
||||
{ Порядок действий: сначала выбирается кусок кадра, и только потом он
|
||||
поворачивается и отражается.
|
||||
|
||||
Так это устроено в монтажных программах, и так понятнее: рамка
|
||||
показывает, что́ останется, а поворот применяется к оставшемуся. При
|
||||
обратном порядке обрезался бы уже перевёрнутый кадр, и в рамку
|
||||
попадало бы совсем не то, что видно. }
|
||||
|
||||
{ --- Масштаб --- }
|
||||
if AT.Fit = fmStretch then
|
||||
|
|
@ -220,16 +201,16 @@ begin
|
|||
end
|
||||
else
|
||||
begin
|
||||
KFit := Min(ADstWidth / W0, ADstHeight / H0);
|
||||
KFill := Max(ADstWidth / W0, ADstHeight / H0);
|
||||
KFit := Min(ADstWidth / ASrcWidth, ADstHeight / ASrcHeight);
|
||||
KFill := Max(ADstWidth / ASrcWidth, ADstHeight / ASrcHeight);
|
||||
if AT.Fit = fmFill then
|
||||
K := KFill
|
||||
else
|
||||
K := KFit;
|
||||
if AT.Zoom > 0 then
|
||||
K := K * AT.Zoom;
|
||||
SW := MakeEven(Round(W0 * K));
|
||||
SH := MakeEven(Round(H0 * K));
|
||||
SW := MakeEven(Round(ASrcWidth * K));
|
||||
SH := MakeEven(Round(ASrcHeight * K));
|
||||
end;
|
||||
Add(Format('scale=%d:%d', [SW, SH]));
|
||||
|
||||
|
|
@ -247,13 +228,51 @@ begin
|
|||
Add(Format('crop=%d:%d:%d:%d', [CW, CH, CX, CY]));
|
||||
end;
|
||||
|
||||
{ --- Поля, если картинка меньше кадра --- }
|
||||
if (CW < ADstWidth) or (CH < ADstHeight) then
|
||||
{ --- Поворот и отражение оставшегося куска --- }
|
||||
Rot := ((AT.Rotation mod 360) + 360) mod 360;
|
||||
case Rot of
|
||||
90:
|
||||
begin
|
||||
{ transpose=1 — поворот на 90 по часовой стрелке. }
|
||||
Add('transpose=1');
|
||||
PW := CH; PH := CW;
|
||||
end;
|
||||
180:
|
||||
begin
|
||||
{ Два поворота подряд дешевле и точнее, чем поворот на
|
||||
произвольный угол: последний пересчитывает каждый пиксель. }
|
||||
Add('transpose=1,transpose=1');
|
||||
PW := CW; PH := CH;
|
||||
end;
|
||||
270:
|
||||
begin
|
||||
Add('transpose=2');
|
||||
PW := CH; PH := CW;
|
||||
end;
|
||||
else
|
||||
PW := CW; PH := CH;
|
||||
end;
|
||||
if AT.FlipH then
|
||||
Add('hflip');
|
||||
if AT.FlipV then
|
||||
Add('vflip');
|
||||
|
||||
{ --- Приведение к кадру проекта --- }
|
||||
|
||||
{ После поворота на 90 кусок лежит поперёк и в кадр проекта уже не
|
||||
вписывается. Вписываем целиком, с полями. }
|
||||
if (PW > ADstWidth) or (PH > ADstHeight) then
|
||||
begin
|
||||
PX := Round((ADstWidth - CW) / 2 + AT.OffsetX * ADstWidth);
|
||||
PY := Round((ADstHeight - CH) / 2 + AT.OffsetY * ADstHeight);
|
||||
PX := Max(0, Min(ADstWidth - CW, PX));
|
||||
PY := Max(0, Min(ADstHeight - CH, PY));
|
||||
K := Min(ADstWidth / PW, ADstHeight / PH);
|
||||
PW := MakeEven(Round(PW * K));
|
||||
PH := MakeEven(Round(PH * K));
|
||||
Add(Format('scale=%d:%d', [PW, PH]));
|
||||
end;
|
||||
|
||||
if (PW < ADstWidth) or (PH < ADstHeight) then
|
||||
begin
|
||||
PX := Max(0, (ADstWidth - PW) div 2);
|
||||
PY := Max(0, (ADstHeight - PH) div 2);
|
||||
Add(Format('pad=%d:%d:%d:%d', [ADstWidth, ADstHeight, PX, PY]));
|
||||
end;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue