22use ges:: prelude:: * ;
33use gst:: { glib, subclass:: prelude:: * } ;
44use gst_video:: { prelude:: * , subclass:: prelude:: * } ;
5+ use tracing:: * ;
56
67use std:: sync:: LazyLock ;
78
@@ -57,7 +58,7 @@ pub struct State {
5758#[ derive( Debug , Copy , Clone ) ]
5859pub struct TranslationRects {
5960 // The rectangle that is used to crop the source image
60- pub src_with_cropping_applied : skia:: Rect ,
61+ pub src_with_cropping_applied : Option < skia:: Rect > ,
6162
6263 // The rectangle that is used to draw the image
6364 pub dst_rect : skia:: Rect ,
@@ -93,27 +94,28 @@ pub(crate) fn reshape_properties() -> Vec<glib::ParamSpec> {
9394 . default_value( 0 )
9495 . mutable_playing( )
9596 . build( ) ,
96- glib:: ParamSpecInt :: builder( "left" )
97+ glib:: ParamSpecInt :: builder( "crop- left" )
9798 . nick( "Crop left in pixels" )
9899 . blurb( "Crop left in pixels" )
99100 . default_value( 0 )
101+ . controllable( )
100102 . mutable_playing( )
101103 . build( ) ,
102- glib:: ParamSpecInt :: builder( "right" )
104+ glib:: ParamSpecInt :: builder( "crop- right" )
103105 . nick( "Crop right in pixels" )
104106 . blurb( "Crop right in pixels" )
105107 . default_value( 0 )
106108 . mutable_playing( )
107109 . controllable( )
108110 . build( ) ,
109- glib:: ParamSpecInt :: builder( "top" )
111+ glib:: ParamSpecInt :: builder( "crop- top" )
110112 . nick( "Crop top in pixels" )
111113 . blurb( "Crop top in pixels" )
112114 . default_value( 0 )
113115 . mutable_playing( )
114116 . controllable( )
115117 . build( ) ,
116- glib:: ParamSpecInt :: builder( "bottom" )
118+ glib:: ParamSpecInt :: builder( "crop- bottom" )
117119 . nick( "Crop bottom in pixels" )
118120 . blurb( "Crop bottom in pixels" )
119121 . default_value( 0 )
@@ -147,16 +149,16 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
147149 "padding-px" => {
148150 settings. padding_px = value. get ( ) . expect ( "type checked upstream" ) ;
149151 }
150- "left" => {
152+ "crop- left" => {
151153 settings. crop_left = value. get ( ) . expect ( "type checked upstream" ) ;
152154 }
153- "right" => {
155+ "crop- right" => {
154156 settings. crop_right = value. get ( ) . expect ( "type checked upstream" ) ;
155157 }
156- "top" => {
158+ "crop- top" => {
157159 settings. crop_top = value. get ( ) . expect ( "type checked upstream" ) ;
158160 }
159- "bottom" => {
161+ "crop- bottom" => {
160162 settings. crop_bottom = value. get ( ) . expect ( "type checked upstream" ) ;
161163 }
162164 "disable-crop-optimization" => {
@@ -172,10 +174,10 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
172174 "border-radius-px" => settings. border_radius_px . to_value ( ) ,
173175 "corner-smoothing-pct" => settings. corner_smoothing_pct . to_value ( ) ,
174176 "padding-px" => settings. padding_px . to_value ( ) ,
175- "left" => settings. crop_left . to_value ( ) ,
176- "right" => settings. crop_right . to_value ( ) ,
177- "top" => settings. crop_top . to_value ( ) ,
178- "bottom" => settings. crop_bottom . to_value ( ) ,
177+ "crop- left" => settings. crop_left . to_value ( ) ,
178+ "crop- right" => settings. crop_right . to_value ( ) ,
179+ "crop- top" => settings. crop_top . to_value ( ) ,
180+ "crop- bottom" => settings. crop_bottom . to_value ( ) ,
179181 "disable-crop-optimization" => settings. disable_crop_optimization . to_value ( ) ,
180182 _ => unimplemented ! ( ) ,
181183 }
@@ -185,6 +187,7 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
185187 & self ,
186188 canvas : & skia:: Canvas ,
187189 image : & skia:: Image ,
190+ mut direct : Option < & mut skia:: gpu:: DirectContext > ,
188191 ) -> Result < gst:: FlowSuccess , gst:: FlowError > {
189192 let crop_optimization_enabled = !self . settings ( ) . disable_crop_optimization ;
190193 gst:: trace!(
@@ -206,18 +209,54 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
206209 let mut paint = skia:: Paint :: default ( ) ;
207210 paint. set_anti_alias ( true ) ;
208211
209- let src_rect = Some ( (
210- & rects. src_with_cropping_applied ,
211- skia:: canvas:: SrcRectConstraint :: Strict ,
212- ) ) ;
212+ let cropped_image =
213+ if let Some ( ref src_with_cropping_applied) = rects. src_with_cropping_applied {
214+ let subset_result =
215+ image. make_subset ( direct. as_deref_mut ( ) , src_with_cropping_applied. round ( ) ) ;
216+
217+ match ( subset_result, direct) {
218+ ( Some ( img) , _) => img,
219+ ( None , Some ( direct_ctx) ) => {
220+ // Create a 1x1 transparent fallback image backed on GPU
221+ let data = vec ! [ 0u8 ; 4 ] ;
222+ let info = skia:: ImageInfo :: new_n32_premul ( skia:: ISize :: new ( 1 , 1 ) , None ) ;
223+
224+ let raster_image =
225+ skia:: images:: raster_from_data ( & info, skia:: Data :: new_copy ( & data) , 4 )
226+ . expect ( "Failed to create empty raster image" ) ;
227+ // Create fallback image backed on GPU
228+ skia:: gpu:: images:: texture_from_image (
229+ direct_ctx,
230+ & raster_image,
231+ skia:: gpu:: Mipmapped :: No ,
232+ skia:: gpu:: Budgeted :: No ,
233+ )
234+ . unwrap_or ( raster_image)
235+ }
236+ ( None , None ) => {
237+ let settings = self . settings ( ) ;
238+ gst:: debug!(
239+ CAT ,
240+ "Transparent fallback image input dimensions: {}x{} - settings: {:#?}" ,
241+ image. width( ) ,
242+ image. height( ) ,
243+ * settings
244+ ) ;
245+ skia:: images:: raster_from_data (
246+ & skia:: ImageInfo :: new_n32_premul ( skia:: ISize :: new ( 1 , 1 ) , None ) ,
247+ skia:: Data :: new_copy ( & vec ! [ 0u8 ; 4 ] ) ,
248+ 4 ,
249+ )
250+ . expect ( "Failed to create empty raster image" )
251+ }
252+ }
253+ } else {
254+ image. clone ( )
255+ } ;
213256
214257 canvas. draw_image_rect_with_sampling_options (
215- image,
216- if crop_optimization_enabled {
217- src_rect
218- } else {
219- None
220- } ,
258+ cropped_image,
259+ None ,
221260 rects. dst_rect ,
222261 skia:: SamplingOptions :: new ( skia:: FilterMode :: Linear , skia:: MipmapMode :: Linear ) ,
223262 & paint,
@@ -613,14 +652,25 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
613652 compositor_size
614653 } ;
615654
616- let ( padding_px, mut src_crop_left, crop_right, mut src_crop_top, crop_bottom) = {
655+ let (
656+ padding_px,
657+ mut src_crop_left,
658+ crop_right,
659+ mut src_crop_top,
660+ crop_bottom,
661+ user_cropped,
662+ ) = {
617663 let settings = self . settings ( ) ;
618664 (
619665 settings. padding_px as f32 ,
620666 settings. crop_left as f32 ,
621667 settings. crop_right as f32 ,
622668 settings. crop_top as f32 ,
623669 settings. crop_bottom as f32 ,
670+ settings. crop_left != 0
671+ || settings. crop_right != 0
672+ || settings. crop_top != 0
673+ || settings. crop_bottom != 0 ,
624674 )
625675 } ;
626676
@@ -694,7 +744,7 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
694744
695745 let crop_optimization_enabled = !self . settings ( ) . disable_crop_optimization ;
696746
697- if crop_optimization_enabled {
747+ let src_with_cropping_applied = if crop_optimization_enabled {
698748 let width_factor = ( in_info. width ( ) as f32 ) / dst_width;
699749 let height_factor = in_info. height ( ) as f32 / dst_height;
700750
@@ -755,16 +805,31 @@ pub trait ReshapeCommon: BaseTransformImpl + ObjectImpl {
755805 if compositor_rect. bottom ( ) > compositor_size. height {
756806 out_height += compositor_size. height - compositor_rect. bottom ( ) ;
757807 }
808+
809+ Some ( skia:: Rect :: from_ltrb (
810+ src_crop_left,
811+ src_crop_top,
812+ src_width,
813+ src_height,
814+ ) )
758815 } else {
759816 dst_left += padding_px;
760817 dst_top += padding_px;
761818
762819 original_dst_left += padding_px;
763820 original_dst_top += padding_px;
764- }
765821
766- let src_with_cropping_applied =
767- skia:: Rect :: from_ltrb ( src_crop_left, src_crop_top, src_width, src_height) ;
822+ if user_cropped {
823+ Some ( skia:: Rect :: from_ltrb (
824+ src_crop_left,
825+ src_crop_top,
826+ in_info. width ( ) as f32 - crop_right,
827+ in_info. height ( ) as f32 - crop_bottom,
828+ ) )
829+ } else {
830+ None
831+ }
832+ } ;
768833
769834 if dst_top < 1.0 {
770835 dst_top = 0.0 ;
0 commit comments