Skip to content

Commit 746d472

Browse files
committed
fix(screenshot): proper rounding of logical dimensions
1 parent d75f811 commit 746d472

1 file changed

Lines changed: 34 additions & 17 deletions

File tree

src/capture/screenshot_service.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,14 @@ namespace {
424424
return targets;
425425
}
426426

427+
[[nodiscard]] int scaleLogicalFloor(int logical, double scale) {
428+
return static_cast<int>(std::floor(static_cast<double>(logical) * scale));
429+
}
430+
431+
[[nodiscard]] int scaleLogicalCeil(int logical, double scale) {
432+
return static_cast<int>(std::ceil(static_cast<double>(logical) * scale));
433+
}
434+
427435
[[nodiscard]] std::optional<ScreencopyImage>
428436
composeGlobalRegion(LogicalRect globalRegion, std::vector<GlobalRegionPiece> pieces) {
429437
if (globalRegion.width <= 0 || globalRegion.height <= 0 || pieces.empty()) {
@@ -446,10 +454,8 @@ namespace {
446454
});
447455
}
448456

449-
const auto scaled = [canvasScale](int logical) { return static_cast<int>(std::lround(logical * canvasScale)); };
450-
451-
const int canvasWidth = scaled(globalRegion.width);
452-
const int canvasHeight = scaled(globalRegion.height);
457+
const int canvasWidth = scaleLogicalCeil(globalRegion.width, canvasScale);
458+
const int canvasHeight = scaleLogicalCeil(globalRegion.height, canvasScale);
453459
if (canvasWidth <= 0 || canvasHeight <= 0) {
454460
return std::nullopt;
455461
}
@@ -462,10 +468,12 @@ namespace {
462468
for (auto& piece : pieces) {
463469
const int globalPieceX = piece.output->logicalX + piece.localRegion.x;
464470
const int globalPieceY = piece.output->logicalY + piece.localRegion.y;
465-
const int destX = scaled(globalPieceX - globalRegion.x);
466-
const int destY = scaled(globalPieceY - globalRegion.y);
467-
const int targetWidth = scaled(piece.localRegion.width);
468-
const int targetHeight = scaled(piece.localRegion.height);
471+
const int offsetX = globalPieceX - globalRegion.x;
472+
const int offsetY = globalPieceY - globalRegion.y;
473+
const int destX = scaleLogicalFloor(offsetX, canvasScale);
474+
const int destY = scaleLogicalFloor(offsetY, canvasScale);
475+
const int targetWidth = scaleLogicalCeil(offsetX + piece.localRegion.width, canvasScale) - destX;
476+
const int targetHeight = scaleLogicalCeil(offsetY + piece.localRegion.height, canvasScale) - destY;
469477
if (!resampleRgbaImage(piece.image, targetWidth, targetHeight)) {
470478
return std::nullopt;
471479
}
@@ -507,19 +515,28 @@ namespace {
507515
minLogicalY = std::min(minLogicalY, frame.output->logicalY);
508516
}
509517

510-
const auto scaled = [canvasScale](int logical) { return static_cast<int>(std::lround(logical * canvasScale)); };
518+
const auto outputPixelRect = [canvasScale, minLogicalX, minLogicalY](const WaylandOutput& output) {
519+
const int offsetX = output.logicalX - minLogicalX;
520+
const int offsetY = output.logicalY - minLogicalY;
521+
const int x = scaleLogicalFloor(offsetX, canvasScale);
522+
const int y = scaleLogicalFloor(offsetY, canvasScale);
523+
return LogicalRect{
524+
.x = x,
525+
.y = y,
526+
.width = scaleLogicalCeil(offsetX + output.logicalWidth, canvasScale) - x,
527+
.height = scaleLogicalCeil(offsetY + output.logicalHeight, canvasScale) - y,
528+
};
529+
};
511530

512531
int canvasWidth = 0;
513532
int canvasHeight = 0;
514533
for (auto& frame : frames) {
515-
const auto* out = frame.output;
516-
const int targetWidth = scaled(out->logicalWidth);
517-
const int targetHeight = scaled(out->logicalHeight);
518-
if (!resampleRgbaImage(frame.image, targetWidth, targetHeight)) {
534+
const LogicalRect pixelRect = outputPixelRect(*frame.output);
535+
if (!resampleRgbaImage(frame.image, pixelRect.width, pixelRect.height)) {
519536
return std::nullopt;
520537
}
521-
canvasWidth = std::max(canvasWidth, scaled(out->logicalX - minLogicalX) + targetWidth);
522-
canvasHeight = std::max(canvasHeight, scaled(out->logicalY - minLogicalY) + targetHeight);
538+
canvasWidth = std::max(canvasWidth, pixelRect.x + pixelRect.width);
539+
canvasHeight = std::max(canvasHeight, pixelRect.y + pixelRect.height);
523540
}
524541

525542
if (canvasWidth <= 0 || canvasHeight <= 0) {
@@ -532,8 +549,8 @@ namespace {
532549
canvas.rgba.assign(static_cast<std::size_t>(canvasWidth) * static_cast<std::size_t>(canvasHeight) * 4U, 0);
533550

534551
for (const auto& frame : frames) {
535-
const auto* out = frame.output;
536-
blitOpaqueRgba(canvas, scaled(out->logicalX - minLogicalX), scaled(out->logicalY - minLogicalY), frame.image);
552+
const LogicalRect pixelRect = outputPixelRect(*frame.output);
553+
blitOpaqueRgba(canvas, pixelRect.x, pixelRect.y, frame.image);
537554
}
538555

539556
return canvas;

0 commit comments

Comments
 (0)