Skip to content

Commit 7443ba1

Browse files
thiblahutehappylinks
authored andcommitted
skiareshape: Rename crop properties and fix cropping logic
Rename crop properties from left/right/top/bottom to crop-left/crop-right/ crop-top/crop-bottom for clarity. Also fix the cropping implementation to properly handle both user-specified crops and optimization-based crops by using make_subset() instead of src_rect parameter, and only applying cropping when actually needed.
1 parent 68b2249 commit 7443ba1

File tree

3 files changed

+95
-30
lines changed

3 files changed

+95
-30
lines changed

video/skia/src/reshape/imp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,6 @@ impl VideoFilterImpl for SkiaReshape {
239239
skia::surface::surfaces::wrap_pixels(&out_img_info, plane_data, row_bytes, None)
240240
.ok_or(gst::FlowError::Error)?;
241241

242-
self.reshape(out_surface.canvas(), &image)
242+
self.reshape(out_surface.canvas(), &image, None)
243243
}
244244
}

video/skia/src/reshape_common.rs

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use ges::prelude::*;
33
use gst::{glib, subclass::prelude::*};
44
use gst_video::{prelude::*, subclass::prelude::*};
5+
use tracing::*;
56

67
use std::sync::LazyLock;
78

@@ -57,7 +58,7 @@ pub struct State {
5758
#[derive(Debug, Copy, Clone)]
5859
pub 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;

video/skia/src/reshapegl/imp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl SkiaReshapeGL {
116116
}
117117

118118
let canvas = out_surface.canvas();
119-
self.reshape(canvas, &image)
119+
self.reshape(canvas, &image, Some(skia_context))
120120
.map_err(|e| gst::loggable_error!(CAT, "Failed to reshape: {}", e))?;
121121

122122
/* Execute the drawing commands and submit them to the GPU */

0 commit comments

Comments
 (0)