mirror of https://github.com/Genymobile/scrcpy
Dissociate virtual display size and capture size
Allow capturing virtual displays at a lower resolution using -m/--max-size. In the original implementation in #5370, the virtual display size was necessarily the same as the capture size. The --max-size value was only allowed to determine the virtual display size when no explicit size was provided. Since the dpi was scaled down accordingly, it is often better to create a virtual display at the target capture size directly. However, not everything is rendered according to the virtual display DPI. For example, a page in Firefox is rendered too big on small virtual displays. Thus, it makes sense to be able create a virtual display at a given size, and capture it at a lower resolution with --max-size. This is now possible using OpenGL filters. Therefore, change the behavior of --max-size for virtual displays: - it does not impact --new-display without size argument anymore (the virtual display size is the main display size); - it is used to limit the capture size (whether an explicit size is provided or not). This new behavior is consistent with main display capture. Refs #5370 comment <https://github.com/Genymobile/scrcpy/pull/5370#issuecomment-2438944401> Refs <https://github.com/Genymobile/scrcpy/pull/5370>
This commit is contained in:
parent
4608a19a13
commit
daba00a819
|
@ -318,14 +318,13 @@ Disable video and audio playback on the computer (equivalent to \fB\-\-no\-video
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-new\-display\fR[=[\fIwidth\fRx\fIheight\fR][/\fIdpi\fR]]
|
\fB\-\-new\-display\fR[=[\fIwidth\fRx\fIheight\fR][/\fIdpi\fR]]
|
||||||
Create a new display with the specified resolution and density. If not provided, they default to the main display dimensions and DPI, and \fB\-\-max\-size\fR is considered.
|
Create a new display with the specified resolution and density. If not provided, they default to the main display dimensions and DPI.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
\-\-new\-display=1920x1080
|
\-\-new\-display=1920x1080
|
||||||
\-\-new\-display=1920x1080/420
|
\-\-new\-display=1920x1080/420
|
||||||
\-\-new\-display # main display size and density
|
\-\-new\-display # main display size and density
|
||||||
\-\-new\-display -m1920 # scaled to fit a max size of 1920
|
|
||||||
\-\-new\-display=/240 # main display size and 240 dpi
|
\-\-new\-display=/240 # main display size and 240 dpi
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
|
|
|
@ -590,12 +590,11 @@ static const struct sc_option options[] = {
|
||||||
.optional_arg = true,
|
.optional_arg = true,
|
||||||
.text = "Create a new display with the specified resolution and "
|
.text = "Create a new display with the specified resolution and "
|
||||||
"density. If not provided, they default to the main display "
|
"density. If not provided, they default to the main display "
|
||||||
"dimensions and DPI, and --max-size is considered.\n"
|
"dimensions and DPI.\n"
|
||||||
"Examples:\n"
|
"Examples:\n"
|
||||||
" --new-display=1920x1080\n"
|
" --new-display=1920x1080\n"
|
||||||
" --new-display=1920x1080/420 # force 420 dpi\n"
|
" --new-display=1920x1080/420 # force 420 dpi\n"
|
||||||
" --new-display # main display size and density\n"
|
" --new-display # main display size and density\n"
|
||||||
" --new-display -m1920 # scaled to fit a max size of 1920\n"
|
|
||||||
" --new-display=/240 # main display size and 240 dpi",
|
" --new-display=/240 # main display size and 240 dpi",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2891,13 +2890,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
LOGE("--new-display is incompatible with --no-video");
|
LOGE("--new-display is incompatible with --no-video");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->max_size && opts->new_display[0] != '\0'
|
|
||||||
&& opts->new_display[0] != '/') {
|
|
||||||
// An explicit size is defined (not "" nor "/<dpi>")
|
|
||||||
LOGE("Cannot specify both --new-display size and -m/--max-size");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otg) {
|
if (otg) {
|
||||||
|
|
|
@ -193,9 +193,9 @@ phone, landscape for a tablet).
|
||||||
|
|
||||||
Cropping is performed before `--capture-orientation` and `--angle`.
|
Cropping is performed before `--capture-orientation` and `--angle`.
|
||||||
|
|
||||||
For screen mirroring, `--max-size` is applied after cropping. For camera and
|
For display mirroring, `--max-size` is applied after cropping. For camera,
|
||||||
virtual display mirroring, `--max-size` is applied first (because it selects the
|
`--max-size` is applied first (because it selects the source size rather than
|
||||||
source size rather than resizing it).
|
resizing the content).
|
||||||
|
|
||||||
|
|
||||||
## Display
|
## Display
|
||||||
|
|
|
@ -8,7 +8,6 @@ To mirror a new virtual display instead of the device screen:
|
||||||
scrcpy --new-display=1920x1080
|
scrcpy --new-display=1920x1080
|
||||||
scrcpy --new-display=1920x1080/420 # force 420 dpi
|
scrcpy --new-display=1920x1080/420 # force 420 dpi
|
||||||
scrcpy --new-display # use the main display size and density
|
scrcpy --new-display # use the main display size and density
|
||||||
scrcpy --new-display -m1920 # ... scaled to fit a max size of 1920
|
|
||||||
scrcpy --new-display=/240 # use the main display size and 240 dpi
|
scrcpy --new-display=/240 # use the main display size and 240 dpi
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ public final class Size {
|
||||||
* @return The current size rounded.
|
* @return The current size rounded.
|
||||||
*/
|
*/
|
||||||
public Size round8() {
|
public Size round8() {
|
||||||
if ((width & 7) == 0 && (height & 7) == 0) {
|
if (isMultipleOf8()) {
|
||||||
// Already a multiple of 8
|
// Already a multiple of 8
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ public final class Size {
|
||||||
return new Size(w, h);
|
return new Size(w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMultipleOf8() {
|
||||||
|
return (width & 7) == 0 && (height & 7) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public Rect toRect() {
|
public Rect toRect() {
|
||||||
return new Rect(0, 0, width, height);
|
return new Rect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
|
|
||||||
private Size mainDisplaySize;
|
private Size mainDisplaySize;
|
||||||
private int mainDisplayDpi;
|
private int mainDisplayDpi;
|
||||||
private int maxSize; // only used if newDisplay.getSize() != null
|
private int maxSize;
|
||||||
private final Rect crop;
|
private final Rect crop;
|
||||||
private final boolean captureOrientationLocked;
|
private final boolean captureOrientationLocked;
|
||||||
private final Orientation captureOrientation;
|
private final Orientation captureOrientation;
|
||||||
|
@ -101,7 +101,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
int displayRotation;
|
int displayRotation;
|
||||||
if (virtualDisplay == null) {
|
if (virtualDisplay == null) {
|
||||||
if (!newDisplay.hasExplicitSize()) {
|
if (!newDisplay.hasExplicitSize()) {
|
||||||
displaySize = mainDisplaySize.limit(maxSize).round8();
|
displaySize = mainDisplaySize;
|
||||||
}
|
}
|
||||||
if (!newDisplay.hasExplicitDpi()) {
|
if (!newDisplay.hasExplicitDpi()) {
|
||||||
dpi = scaleDpi(mainDisplaySize, mainDisplayDpi, displaySize);
|
dpi = scaleDpi(mainDisplaySize, mainDisplayDpi, displaySize);
|
||||||
|
@ -128,10 +128,19 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
filter.addOrientation(displayRotation, captureOrientationLocked, captureOrientation);
|
filter.addOrientation(displayRotation, captureOrientationLocked, captureOrientation);
|
||||||
filter.addAngle(angle);
|
filter.addAngle(angle);
|
||||||
|
|
||||||
|
Size filteredSize = filter.getOutputSize();
|
||||||
|
if (!filteredSize.isMultipleOf8() || (maxSize != 0 && filteredSize.getMax() > maxSize)) {
|
||||||
|
if (maxSize != 0) {
|
||||||
|
filteredSize = filteredSize.limit(maxSize);
|
||||||
|
}
|
||||||
|
filteredSize = filteredSize.round8();
|
||||||
|
filter.addResize(filteredSize);
|
||||||
|
}
|
||||||
|
|
||||||
eventTransform = filter.getInverseTransform();
|
eventTransform = filter.getInverseTransform();
|
||||||
|
|
||||||
// DisplayInfo gives the oriented size (so videoSize includes the display rotation)
|
// DisplayInfo gives the oriented size (so videoSize includes the display rotation)
|
||||||
videoSize = filter.getOutputSize().limit(maxSize).round8();
|
videoSize = filter.getOutputSize();
|
||||||
|
|
||||||
// But the virtual display video always remains in the origin orientation (the video itself is not rotated, so it must rotated manually).
|
// But the virtual display video always remains in the origin orientation (the video itself is not rotated, so it must rotated manually).
|
||||||
// This additional display rotation must not be included in the input events transform (the expected coordinates are already in the
|
// This additional display rotation must not be included in the input events transform (the expected coordinates are already in the
|
||||||
|
@ -231,11 +240,6 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean setMaxSize(int newMaxSize) {
|
public synchronized boolean setMaxSize(int newMaxSize) {
|
||||||
if (newDisplay.hasExplicitSize()) {
|
|
||||||
// Cannot retry with a different size if the display size was explicitly provided
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
maxSize = newMaxSize;
|
maxSize = newMaxSize;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,4 +103,17 @@ public class VideoFilter {
|
||||||
double ccwAngle = -cwAngle;
|
double ccwAngle = -cwAngle;
|
||||||
transform = AffineMatrix.rotate(ccwAngle).withAspectRatio(size).fromCenter().multiply(transform);
|
transform = AffineMatrix.rotate(ccwAngle).withAspectRatio(size).fromCenter().multiply(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addResize(Size targetSize) {
|
||||||
|
if (size.equals(targetSize)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transform == null) {
|
||||||
|
// The requested scaling is performed by the viewport (by changing the output size), but the OpenGL filter must still run, even if
|
||||||
|
// resizing is not performed by the shader. So transform MUST NOT be null.
|
||||||
|
transform = AffineMatrix.IDENTITY;
|
||||||
|
}
|
||||||
|
size = targetSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue