11local opts = {
2- mode = " hard" , -- can be "hard" or "soft". If hard, use video- crop, if soft use zoom + pan. Or a bonus "delogo" mode
2+ mode = " hard" , -- can be "hard" or "soft". If hard, apply a crop filter , if soft zoom + pan. Or a bonus "delogo" mode
33 draw_shade = true ,
44 shade_opacity = " 77" ,
55 draw_frame = false ,
@@ -8,7 +8,6 @@ local opts = {
88 draw_crosshair = true ,
99 draw_text = true ,
1010 mouse_support = true ,
11- fix_borders = true ,
1211 coarse_movement = 30 ,
1312 left_coarse = " LEFT" ,
1413 right_coarse = " RIGHT" ,
@@ -50,7 +49,7 @@ local rect_centered = false
5049local rect_keepaspect = false
5150local needs_drawing = false
5251local crop_first_corner = nil -- in normalized video space
53- local cursor = {
52+ local crop_cursor = {
5453 x = 0 ,
5554 y = 0
5655}
@@ -83,11 +82,6 @@ function rect_from_two_points(p1, p2, centered, ratio)
8382 return { x = c1 [1 ], y = c1 [2 ] }, { x = c2 [1 ], y = c2 [2 ] }
8483end
8584
86- function round (num , num_decimal_places )
87- local mult = 10 ^ (num_decimal_places or 0 )
88- return math.floor (num * mult + 0.5 ) / mult
89- end
90-
9185function clamp (low , value , high )
9286 if value <= low then
9387 return low
@@ -98,10 +92,10 @@ function clamp(low, value, high)
9892 end
9993end
10094
101- function clamp_point (point , dim )
95+ function clamp_point (top_left , point , bottom_right )
10296 return {
103- x = clamp (dim . ml , point .x , dim . w - dim . mr ),
104- y = clamp (dim . mt , point .y , dim . h - dim . mb )
97+ x = clamp (top_left . x , point .x , bottom_right . x ),
98+ y = clamp (top_left . y , point .y , bottom_right . y )
10599 }
106100end
107101
@@ -216,7 +210,10 @@ function draw_crop_zone()
216210 return
217211 end
218212
219- cursor = clamp_point (cursor , dim )
213+ local cursor = {
214+ x = crop_cursor .x ,
215+ y = crop_cursor .y ,
216+ }
220217 local ass = assdraw .ass_new ()
221218
222219 if crop_first_corner and (opts .draw_shade or opts .draw_frame ) then
@@ -250,13 +247,9 @@ function draw_crop_zone()
250247 local cursor_norm = screen_to_video_norm (cursor , dim )
251248 local text = string.format (" %d, %d" , cursor_norm .x * vop .w , cursor_norm .y * vop .h )
252249 if crop_first_corner then
253- local crop_zone_w = math.abs ((cursor_norm .x - crop_first_corner .x ) * vop .w )
254- local crop_zone_h = math.abs ((cursor_norm .y - crop_first_corner .y ) * vop .h )
255- local crop_zone_aspect = round (crop_zone_w / crop_zone_h , 3 )
256- text = string.format (" %s (%dx%d/%s)" , text ,
257- crop_zone_w ,
258- crop_zone_h ,
259- crop_zone_aspect
250+ text = string.format (" %s (%dx%d)" , text ,
251+ math.abs ((cursor_norm .x - crop_first_corner .x ) * vop .w ),
252+ math.abs ((cursor_norm .y - crop_first_corner .y ) * vop .h )
260253 )
261254 end
262255 draw_position_text (ass , text , cursor , { w = dim .w , h = dim .h }, 6 )
@@ -287,33 +280,25 @@ function crop_video(x1, y1, x2, y2)
287280 x2 = clamp (0 , x2 , 1 )
288281 y2 = clamp (0 , y2 , 1 )
289282 local vop = mp .get_property_native (" video-out-params" )
283+ local vf_table = mp .get_property_native (" vf" )
290284 local x = math.floor (x1 * vop .w + 0.5 )
291285 local y = math.floor (y1 * vop .h + 0.5 )
292286 local w = math.floor ((x2 - x1 ) * vop .w + 0.5 )
293287 local h = math.floor ((y2 - y1 ) * vop .h + 0.5 )
294- if active_mode == " hard" then
295- local video_crop = tostring (w ) .. " x" .. tostring (h ) .. " +" .. tostring (x ) .. " +" .. tostring (y )
296- mp .set_property_native (" video-crop" , video_crop )
297- elseif active_mode == " delogo" then
298- local vf_table = mp .get_property_native (" vf" )
288+ if active_mode == " delogo" then
299289 -- delogo is a little special and needs some padding to function
300290 w = math.min (vop .w - 1 , w )
301291 h = math.min (vop .h - 1 , h )
302292 x = math.max (1 , x )
303293 y = math.max (1 , y )
304294 if x + w == vop .w then w = w - 1 end
305295 if y + h == vop .h then h = h - 1 end
306- vf_table [# vf_table + 1 ] = {
307- name = " delogo" ,
308- params = { x = tostring (x ), y = tostring (y ), w = tostring (w ), h = tostring (h ) }
309- }
310- mp .set_property_native (" vf" , vf_table )
311- end
312- local subdata = mp .get_property_native (" sub-ass-extradata" )
313- if subdata ~= nil and opts .fix_borders then
314- local playresy = subdata :match (" PlayResY:%s*(%d+)" )
315- mp .set_property_native (" sub-ass-force-style" , " PlayResX=" .. tostring (tonumber (playresy ) * (w / h )))
316296 end
297+ vf_table [# vf_table + 1 ] = {
298+ name = (active_mode == " hard" ) and " crop" or " delogo" ,
299+ params = { x = tostring (x ), y = tostring (y ), w = tostring (w ), h = tostring (h ) }
300+ }
301+ mp .set_property_native (" vf" , vf_table )
317302 end
318303end
319304
@@ -323,14 +308,14 @@ function update_crop_zone_state()
323308 cancel_crop ()
324309 return
325310 end
326- cursor = clamp_point ( cursor , dim )
311+ local corner = crop_cursor
327312 if crop_first_corner == nil then
328- crop_first_corner = screen_to_video_norm (cursor , dim )
313+ crop_first_corner = screen_to_video_norm (crop_cursor , dim )
329314 redraw ()
330315 else
331316 local c1 , c2 = rect_from_two_points (
332317 video_norm_to_screen (crop_first_corner , dim ),
333- cursor ,
318+ crop_cursor ,
334319 rect_centered ,
335320 rect_keepaspect and dim .w / dim .h )
336321 local c1norm = screen_to_video_norm (c1 , dim )
@@ -357,42 +342,6 @@ function cancel_crop()
357342 active = false
358343end
359344
360- function remove_crop (mode )
361- -- decide whether to remove video-crop or delogo first
362- -- soft crop is not supported and remove_first will be set to "delogo"
363- local remove_first = (mode == " hard" or mode == " delogo" ) and mode or " delogo"
364- local remove_delogo = function ()
365- local vf_table = mp .get_property_native (" vf" )
366- if # vf_table > 0 then
367- for i = # vf_table , 1 , - 1 do
368- if vf_table [i ].name == " delogo" then
369- table.remove (vf_table , i )
370- mp .set_property_native (" vf" , vf_table )
371- mp .osd_message (" Removed delogo filter." )
372- local subdata = mp .get_property_native (" sub-ass-extradata" )
373- if subdata ~= nil and opts .fix_borders then
374- local playresx = subdata :match (" PlayResX:%s*(%d+)" )
375- mp .set_property_native (" sub-ass-force-style" , " PlayResX=" .. playresx )
376- end
377- return true
378- end
379- end
380- end
381- return false
382- end
383- local remove_hard = function ()
384- video_crop = mp .get_property_native (" video-crop" )
385- if video_crop ~= " " then
386- mp .set_property_native (" video-crop" , " " )
387- mp .osd_message (" Reset video-crop to empty." )
388- return true
389- end
390- return false
391- end
392- return (remove_first == " delogo" and remove_delogo () or remove_hard ())
393- or (remove_first == " hard" and remove_hard () or remove_delogo ())
394- end
395-
396345function start_crop (mode )
397346 if active then return end
398347 if not mp .get_property_native (" osd-dimensions" ) then return end
@@ -401,7 +350,7 @@ function start_crop(mode)
401350 return
402351 end
403352 local mode_maybe = mode or opts .mode
404- if mode_maybe == " delogo " then
353+ if mode_maybe ~= ' soft ' then
405354 local hwdec = mp .get_property (" hwdec-current" )
406355 if hwdec and hwdec ~= " no" and not string.find (hwdec , " -copy$" ) then
407356 msg .error (" Cannot crop with hardware decoding active (see manual)" )
@@ -412,7 +361,7 @@ function start_crop(mode)
412361 active_mode = mode_maybe
413362
414363 if opts .mouse_support then
415- cursor .x , cursor .y = mp .get_mouse_pos ()
364+ crop_cursor .x , crop_cursor .y = mp .get_mouse_pos ()
416365 end
417366 redraw ()
418367 for key , func in pairs (bindings ) do
@@ -431,14 +380,32 @@ function toggle_crop(mode)
431380 end
432381 local toggle_mode = mode or opts .mode
433382 if toggle_mode == " soft" then return end -- can't toggle soft mode
434- if not remove_crop () then -- only start_crop if no crops are removed
383+
384+ local remove_filter = function ()
385+ local to_remove = (toggle_mode == " hard" ) and " crop" or " delogo"
386+ local vf_table = mp .get_property_native (" vf" )
387+ if # vf_table > 0 then
388+ for i = # vf_table , 1 , - 1 do
389+ if vf_table [i ].name == to_remove then
390+ for j = i , # vf_table - 1 do
391+ vf_table [j ] = vf_table [j + 1 ]
392+ end
393+ vf_table [# vf_table ] = nil
394+ mp .set_property_native (" vf" , vf_table )
395+ return true
396+ end
397+ end
398+ end
399+ return false
400+ end
401+ if not remove_filter () then
435402 start_crop (mode )
436403 end
437404end
438405
439406-- bindings
440407if opts .mouse_support then
441- bindings [" MOUSE_MOVE" ] = function () cursor .x , cursor .y = mp .get_mouse_pos (); redraw () end
408+ bindings [" MOUSE_MOVE" ] = function () crop_cursor .x , crop_cursor .y = mp .get_mouse_pos (); redraw () end
442409end
443410for _ , key in ipairs (opts .accept ) do
444411 bindings [key ] = update_crop_zone_state
@@ -448,8 +415,8 @@ for _, key in ipairs(opts.cancel) do
448415end
449416function movement_func (move_x , move_y )
450417 return function ()
451- cursor .x = cursor .x + move_x
452- cursor .y = cursor .y + move_y
418+ crop_cursor .x = crop_cursor .x + move_x
419+ crop_cursor .y = crop_cursor .y + move_y
453420 redraw ()
454421 end
455422end
@@ -462,6 +429,6 @@ bindings_repeat[opts.right_fine] = movement_func(opts.fine_movement, 0)
462429bindings_repeat [opts .up_fine ] = movement_func (0 , - opts .fine_movement )
463430bindings_repeat [opts .down_fine ] = movement_func (0 , opts .fine_movement )
464431
465- mp . add_key_binding ( nil , " remove-crop " , remove_crop )
432+
466433mp .add_key_binding (nil , " start-crop" , start_crop )
467434mp .add_key_binding (nil , " toggle-crop" , toggle_crop )
0 commit comments