mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 08:03:45 +08:00
[ai] Capture off-screen rows and name misframing
Task: 2026/07/28/fix-blank-row-captures-in-test-harness
This commit is contained in:
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "test/test_capture.h"
|
||||
|
||||
#include "test/test_log.h"
|
||||
#include "ui/ui_utility.h"
|
||||
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
@@ -23,6 +24,31 @@ constexpr auto kContactSheetGap = 8;
|
||||
: (name + u".png"_q);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString RectText(const QRect &rect) {
|
||||
return u"%1,%2 %3x%4"_q
|
||||
.arg(rect.x())
|
||||
.arg(rect.y())
|
||||
.arg(rect.width())
|
||||
.arg(rect.height());
|
||||
}
|
||||
|
||||
[[nodiscard]] QString MisframedDetails(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &logicalRect) {
|
||||
const auto bounds = widget->rect();
|
||||
if (!logicalRect.isEmpty() && bounds.contains(logicalRect)) {
|
||||
return QString();
|
||||
}
|
||||
const auto inside = bounds.intersected(logicalRect);
|
||||
return u"requested rect is not fully inside the grabbed widget: "
|
||||
u"requested=%1 widget=%2 inside=%3 rows=%4/%5 columns=%6/%7"_q
|
||||
.arg(RectText(logicalRect), RectText(bounds), RectText(inside))
|
||||
.arg(inside.height())
|
||||
.arg(logicalRect.height())
|
||||
.arg(inside.width())
|
||||
.arg(logicalRect.width());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QImage GrabWidget(not_null<QWidget*> widget) {
|
||||
@@ -32,13 +58,10 @@ QImage GrabWidget(not_null<QWidget*> widget) {
|
||||
QImage GrabRect(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &logicalRect) {
|
||||
const auto image = GrabWidget(widget);
|
||||
const auto ratio = image.devicePixelRatio();
|
||||
return Crop(image, QRect(
|
||||
int(std::floor(logicalRect.x() * ratio)),
|
||||
int(std::floor(logicalRect.y() * ratio)),
|
||||
int(std::ceil(logicalRect.width() * ratio)),
|
||||
int(std::ceil(logicalRect.height() * ratio))));
|
||||
const auto bounded = logicalRect.intersected(widget->rect());
|
||||
return bounded.isEmpty()
|
||||
? QImage()
|
||||
: widget->grab(bounded).toImage();
|
||||
}
|
||||
|
||||
bool LooksBlank(const QImage &image) {
|
||||
@@ -75,6 +98,7 @@ QString SaveImage(const QImage &image, const QString &name) {
|
||||
}
|
||||
|
||||
bool CaptureWidget(not_null<QWidget*> widget, const QString &name) {
|
||||
LogGeometry(name, QRect(widget->mapToGlobal(QPoint()), widget->size()));
|
||||
if (!widget->isVisible()) {
|
||||
Fail(u"capture %1"_q.arg(name), u"widget is not visible"_q);
|
||||
return false;
|
||||
@@ -84,7 +108,6 @@ bool CaptureWidget(not_null<QWidget*> widget, const QString &name) {
|
||||
Fail(u"capture %1"_q.arg(name), u"grabbed image looks blank"_q);
|
||||
return false;
|
||||
}
|
||||
LogGeometry(name, QRect(widget->mapToGlobal(QPoint()), widget->size()));
|
||||
return !SaveImage(image, name).isEmpty();
|
||||
}
|
||||
|
||||
@@ -92,13 +115,14 @@ bool CaptureRect(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &logicalRect,
|
||||
const QString &name) {
|
||||
LogGeometry(name, logicalRect);
|
||||
if (!widget->isVisible()) {
|
||||
Fail(u"capture %1"_q.arg(name), u"widget is not visible"_q);
|
||||
return false;
|
||||
} else if (!QRect(QPoint(), widget->size()).intersects(logicalRect)) {
|
||||
Fail(
|
||||
u"capture %1"_q.arg(name),
|
||||
u"rect is outside the widget bounds"_q);
|
||||
}
|
||||
const auto misframed = MisframedDetails(widget, logicalRect);
|
||||
if (!misframed.isEmpty()) {
|
||||
Fail(u"capture %1"_q.arg(name), misframed);
|
||||
return false;
|
||||
}
|
||||
const auto image = GrabRect(widget, logicalRect);
|
||||
@@ -106,10 +130,20 @@ bool CaptureRect(
|
||||
Fail(u"capture %1"_q.arg(name), u"grabbed image looks blank"_q);
|
||||
return false;
|
||||
}
|
||||
LogGeometry(name, logicalRect);
|
||||
return !SaveImage(image, name).isEmpty();
|
||||
}
|
||||
|
||||
bool CaptureMappedRect(
|
||||
not_null<QWidget*> widget,
|
||||
not_null<QWidget*> rectOrigin,
|
||||
const QRect &logicalRect,
|
||||
const QString &name) {
|
||||
return CaptureRect(
|
||||
widget,
|
||||
Ui::MapFrom(widget, rectOrigin, logicalRect),
|
||||
name);
|
||||
}
|
||||
|
||||
QImage Crop(const QImage &image, const QRect &pixelRect) {
|
||||
const auto bounded = pixelRect.intersected(image.rect());
|
||||
return bounded.isEmpty() ? QImage() : image.copy(bounded);
|
||||
|
||||
@@ -15,8 +15,10 @@ namespace Test {
|
||||
// elements, other windows, or a locked desktop session.
|
||||
[[nodiscard]] QImage GrabWidget(not_null<QWidget*> widget);
|
||||
|
||||
// Grabs the widget and crops to |logicalRect| (widget-local logical
|
||||
// coordinates), handling device pixel ratio.
|
||||
// Renders only |logicalRect| (widget-local logical coordinates) out of the
|
||||
// widget, clamped to its bounds, handling device pixel ratio. The grab
|
||||
// renders the widget itself, so a rect outside the visible area is still
|
||||
// produced.
|
||||
[[nodiscard]] QImage GrabRect(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &logicalRect);
|
||||
@@ -30,12 +32,26 @@ QString SaveImage(const QImage &image, const QString &name);
|
||||
|
||||
// Grab + blank-check + save as one evidence-grade capture: a hidden widget,
|
||||
// an empty grab, or a blank image is a logged FAIL, never a silent pass.
|
||||
// CaptureRect's rect must lie fully inside the grabbed widget; a rect that
|
||||
// leaves it is a logged FAIL naming the overlap, never a silently reframed
|
||||
// image.
|
||||
bool CaptureWidget(not_null<QWidget*> widget, const QString &name);
|
||||
bool CaptureRect(
|
||||
not_null<QWidget*> widget,
|
||||
const QRect &logicalRect,
|
||||
const QString &name);
|
||||
|
||||
// Captures |logicalRect|, expressed in |rectOrigin| coordinates, by grabbing
|
||||
// |widget| — the rect is mapped into |widget| first. A grab renders the
|
||||
// widget itself, so a row scrolled out of view is captured by passing the
|
||||
// scrolled content widget as |widget|; the window that clips it holds no
|
||||
// pixels for that row.
|
||||
bool CaptureMappedRect(
|
||||
not_null<QWidget*> widget,
|
||||
not_null<QWidget*> rectOrigin,
|
||||
const QRect &logicalRect,
|
||||
const QString &name);
|
||||
|
||||
[[nodiscard]] QImage Crop(const QImage &image, const QRect &pixelRect);
|
||||
|
||||
// Nearest-neighbor upscale for readable small-target evidence.
|
||||
|
||||
Reference in New Issue
Block a user