Кадрирование: поворот после обрезки, рамка не расходится с настройкой

Поворот и отражение применялись к исходному кадру, а обрезка шла после
них. Из-за этого рамка показывала одно место, а вырезалось другое.
Теперь наоборот: сначала выбирается кусок, потом он поворачивается и
отражается. Если после поворота кусок лёг поперёк кадра проекта, он
вписывается целиком с полями.

Проверено: с отражением левый и правый края вырезанного куска меняются
местами, то есть отражается именно он, а не оригинал.

Вторая причина расхождения — предел ползунков смещения. Рамку можно было
утащить туда, куда ползунок не дотягивается: рамка рисовалась от
неограниченного значения, а фильтр считался от обрезанного. Предел
расширен вчетверо, а рамка теперь пересчитывается обратно из настройки —
если та упёрлась, это сразу видно.

Отдельно: разбор звука больше не держит главный поток. Перезапуск ждал
окончания текущего файла, а это чтение гигабайтов — окно всё это время не
отвечало, и меню не открывалось вовсе. Теперь проход доигрывает сам и
запускается заново, а идёт он с наинизшим приоритетом.
This commit is contained in:
chaiman 2026-08-20 19:23:54 +03:00
commit f9e50632aa
4 changed files with 121 additions and 47 deletions

View file

@ -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;