Skip to content

Commit af360f3

Browse files
committed
[crop] Compute corners more sensibly in 'soft' mode
The previous behavior was pretty annoying, it shows that I did not use it much.
1 parent f0c326c commit af360f3

File tree

2 files changed

+57
-54
lines changed

2 files changed

+57
-54
lines changed

input.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ d vf del -1
1111
# or use the ready-made "toggle" binding
1212
C script-message-to crop toggle-crop hard
1313

14+
# remove the soft zoom
15+
0 set video-pan-x 0; set video-pan-y 0; set video-zoom 0
16+
1417
# encode.lua
1518
# ============
1619
# use default profile (makes vp8 webms)

scripts/crop.lua

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ end
4545
local assdraw = require 'mp.assdraw'
4646
local active = false
4747
local active_mode = "" -- same possible values as opts.mode
48+
local rect_centered = false
49+
local rect_keepaspect = false
4850
local needs_drawing = false
4951
local crop_first_corner = nil -- in normalized video space
5052
local crop_cursor = {
@@ -56,11 +58,28 @@ function redraw()
5658
needs_drawing = true
5759
end
5860

59-
function sort_corners(c1, c2)
60-
local r1, r2 = {}, {}
61-
if c1.x < c2.x then r1.x, r2.x = c1.x, c2.x else r1.x, r2.x = c2.x, c1.x end
62-
if c1.y < c2.y then r1.y, r2.y = c1.y, c2.y else r1.y, r2.y = c2.y, c1.y end
63-
return r1, r2
61+
function rect_from_two_points(p1, p2, centered, ratio)
62+
local c1 = {p1.x, p1.y}
63+
local c2 = {p2.x, p2.y}
64+
if ratio then
65+
-- adjust position of p2, such
66+
if math.abs(c2[1] - c1[1]) < ratio * math.abs(c2[2] - c1[2]) then
67+
local is_left = c2[1] < c1[1] and -1 or 1
68+
c2[1] = c1[1] + is_left * math.abs(c2[2] - c1[2]) * ratio
69+
else
70+
local is_up = c2[2] < c1[2] and -1 or 1
71+
c2[2] = c1[2] + is_up * math.abs(c2[1] - c1[1]) / ratio
72+
end
73+
end
74+
if centered then
75+
-- p1 is center => convert it into corner
76+
c1[1] = c1[1] - (c2[1] - c1[1])
77+
c1[2] = c1[2] - (c2[2] - c1[2])
78+
end
79+
-- sort corners
80+
if c1[1] > c2[1] then c1[1], c2[1] = c2[1], c1[1] end
81+
if c1[2] > c2[2] then c1[2], c2[2] = c2[2], c1[2] end
82+
return { x = c1[1], y = c1[2] }, { x = c2[1], y = c2[2] }
6483
end
6584

6685
function clamp(low, value, high)
@@ -94,23 +113,6 @@ function video_norm_to_screen(point, dim)
94113
}
95114
end
96115

97-
function position_to_ensure_ratio(moving, fixed, ratio)
98-
-- corners are in screen coordinates
99-
local x = moving.x
100-
local y = moving.y
101-
if math.abs(x - fixed.x) < ratio * math.abs(y - fixed.y) then
102-
local is_left = x < fixed.x and -1 or 1
103-
x = fixed.x + is_left * math.abs(y - fixed.y) * ratio
104-
else
105-
local is_up = y < fixed.y and -1 or 1
106-
y = fixed.y + is_up * math.abs(x - fixed.x) / ratio
107-
end
108-
return {
109-
x = x,
110-
y = y,
111-
}
112-
end
113-
114116
function draw_shade(ass, unshaded, window)
115117
ass:new_event()
116118
ass:pos(0, 0)
@@ -212,19 +214,15 @@ function draw_crop_zone()
212214
x = crop_cursor.x,
213215
y = crop_cursor.y,
214216
}
215-
if active_mode == "soft" then
216-
if crop_first_corner then
217-
cursor = position_to_ensure_ratio(cursor, video_norm_to_screen(crop_first_corner, dim), dim.w / dim.h)
218-
end
219-
elseif active_mode == "hard" or active_mode == "delogo" then
220-
cursor = clamp_point({ x = dim.ml, y = dim.mt }, cursor, { x = dim.w - dim.mr, y = dim.h - dim.mb })
221-
end
222217
local ass = assdraw.ass_new()
223218

224219
if crop_first_corner and (opts.draw_shade or opts.draw_frame) then
225-
local first_corner = video_norm_to_screen(crop_first_corner, dim)
226220
local frame = {}
227-
frame.top_left, frame.bottom_right = sort_corners(first_corner, cursor)
221+
frame.top_left, frame.bottom_right = rect_from_two_points(
222+
video_norm_to_screen(crop_first_corner, dim),
223+
cursor,
224+
rect_centered,
225+
rect_keepaspect and dim.w/dim.h)
228226
-- don't draw shade over non-visible video parts
229227
if opts.draw_shade then
230228
local window = {
@@ -263,24 +261,30 @@ function draw_crop_zone()
263261
end
264262
end
265263

266-
function crop_video(x, y, w, h, dim)
264+
function crop_video(x1, y1, x2, y2)
267265
if active_mode == "soft" then
266+
local w = x2 - x1
267+
local h = y2 - y1
268268
local dim = mp.get_property_native("osd-dimensions")
269269
if not dim then return end
270270

271271
local zoom = mp.get_property_number("video-zoom")
272-
local newZoom2 = math.log(dim.w * (2 ^ zoom) / (dim.w - dim.ml - dim.mr) / w) / math.log(2)
273272
local newZoom1 = math.log(dim.h * (2 ^ zoom) / (dim.h - dim.mt - dim.mb) / h) / math.log(2)
274-
mp.set_property("video-zoom", (newZoom1 + newZoom2) / 2) -- they should be ~ the same, but let's not play favorites
275-
mp.set_property("video-pan-x", 0.5 - (x + w / 2))
276-
mp.set_property("video-pan-y", 0.5 - (y + h / 2))
273+
local newZoom2 = math.log(dim.w * (2 ^ zoom) / (dim.w - dim.ml - dim.mr) / w) / math.log(2)
274+
mp.set_property("video-zoom", math.min(newZoom1, newZoom2))
275+
mp.set_property("video-pan-x", 0.5 - (x1 + w / 2))
276+
mp.set_property("video-pan-y", 0.5 - (y1 + h / 2))
277277
elseif active_mode == "hard" or active_mode == "delogo" then
278+
x1 = clamp(0, x1, 1)
279+
y1 = clamp(0, y1, 1)
280+
x2 = clamp(0, x2, 1)
281+
y2 = clamp(0, y2, 1)
278282
local vop = mp.get_property_native("video-out-params")
279283
local vf_table = mp.get_property_native("vf")
280-
local x = math.floor(x * vop.w)
281-
local y = math.floor(y * vop.h)
282-
local w = math.floor(w * vop.w)
283-
local h = math.floor(h * vop.h)
284+
local x = math.floor(x1 * vop.w + 0.5)
285+
local y = math.floor(y1 * vop.h + 0.5)
286+
local w = math.floor((x2 - x1) * vop.w + 0.5)
287+
local h = math.floor((y2 - y1) * vop.h + 0.5)
284288
if active_mode == "delogo" then
285289
-- delogo is a little special and needs some padding to function
286290
w = math.min(vop.w - 1, w)
@@ -304,23 +308,19 @@ function update_crop_zone_state()
304308
cancel_crop()
305309
return
306310
end
307-
local corner
308-
if active_mode == "soft" then
309-
if crop_first_corner then
310-
corner = position_to_ensure_ratio(crop_cursor, video_norm_to_screen(crop_first_corner, dim), dim.w / dim.h)
311-
else
312-
corner = crop_cursor
313-
end
314-
elseif active_mode == "hard" or active_mode == "delogo" then
315-
corner = clamp_point({ x = dim.ml, y = dim.mt }, crop_cursor, { x = dim.w - dim.mr, y = dim.h - dim.mb })
316-
end
317-
local corner_video = screen_to_video_norm(corner, dim)
311+
local corner = crop_cursor
318312
if crop_first_corner == nil then
319-
crop_first_corner = corner_video
313+
crop_first_corner = screen_to_video_norm(crop_cursor, dim)
320314
redraw()
321315
else
322-
local c1, c2 = sort_corners(crop_first_corner, corner_video)
323-
crop_video(c1.x, c1.y, c2.x - c1.x, c2.y - c1.y)
316+
local c1, c2 = rect_from_two_points(
317+
video_norm_to_screen(crop_first_corner, dim),
318+
crop_cursor,
319+
rect_centered,
320+
rect_keepaspect and dim.w/dim.h)
321+
local c1norm = screen_to_video_norm(c1, dim)
322+
local c2norm = screen_to_video_norm(c2, dim)
323+
crop_video(c1norm.x, c1norm.y, c2norm.x, c2norm.y)
324324
cancel_crop()
325325
end
326326
end

0 commit comments

Comments
 (0)