This commit is contained in:
2026-05-14 17:30:20 +08:00
parent e43171521d
commit 5e6d8046e1
10 changed files with 224 additions and 77 deletions
+41 -17
View File
@@ -34,6 +34,7 @@
#include <QSet>
#include <QPainter>
#include <QPainterPath>
#include <QTransform>
#include <QPolygonF>
#include <QRect>
@@ -2275,41 +2276,64 @@ bool ProjectWorkspace::resolveBlackholeByCopyBackground(const QString& id, const
}
const QImage srcSnapshot = bg;
QPainterPath holePath;
holePath.addPolygon(QPolygonF(ent.cutoutPolygonWorld));
holePath.closeSubpath();
const QRect targetRect = holePath.boundingRect().toAlignedRect().intersected(QRect(QPoint(0, 0), bg.size()));
if (!targetRect.isValid() || targetRect.width() <= 0 || targetRect.height() <= 0) {
QSize logicalSz;
if (!image_file::probeImagePixelSize(bgAbs, &logicalSz) || !logicalSz.isValid() || logicalSz.width() < 1 ||
logicalSz.height() < 1) {
return false;
}
QRect srcRect(targetRect.topLeft() + sourceOffsetPx, targetRect.size());
if (srcRect.left() < 0) srcRect.moveLeft(0);
if (srcRect.top() < 0) srcRect.moveTop(0);
if (srcRect.right() >= bg.width()) srcRect.moveRight(bg.width() - 1);
if (srcRect.bottom() >= bg.height()) srcRect.moveBottom(bg.height() - 1);
srcRect = srcRect.intersected(QRect(QPoint(0, 0), bg.size()));
if (srcRect.width() != targetRect.width() || srcRect.height() != targetRect.height()) {
QPainterPath holePathWorld;
holePathWorld.addPolygon(QPolygonF(ent.cutoutPolygonWorld));
holePathWorld.closeSubpath();
const QRect targetWorld =
holePathWorld.boundingRect().toAlignedRect().intersected(QRect(0, 0, logicalSz.width(), logicalSz.height()));
if (!targetWorld.isValid() || targetWorld.width() <= 0 || targetWorld.height() <= 0) {
return false;
}
QRect srcWorld(targetWorld.topLeft() + sourceOffsetPx, targetWorld.size());
if (srcWorld.left() < 0) srcWorld.moveLeft(0);
if (srcWorld.top() < 0) srcWorld.moveTop(0);
if (srcWorld.right() >= logicalSz.width()) srcWorld.moveRight(logicalSz.width() - 1);
if (srcWorld.bottom() >= logicalSz.height()) srcWorld.moveBottom(logicalSz.height() - 1);
srcWorld = srcWorld.intersected(QRect(0, 0, logicalSz.width(), logicalSz.height()));
if (srcWorld.width() != targetWorld.width() || srcWorld.height() != targetWorld.height()) {
return false;
}
const qreal sx = bg.width() / qreal(logicalSz.width());
const qreal sy = bg.height() / qreal(logicalSz.height());
QTransform worldToBg;
worldToBg.scale(sx, sy);
const QRect targetImg =
worldToBg.mapRect(QRectF(targetWorld)).toAlignedRect().intersected(QRect(0, 0, bg.width(), bg.height()));
const QRect srcImg =
worldToBg.mapRect(QRectF(srcWorld)).toAlignedRect().intersected(QRect(0, 0, bg.width(), bg.height()));
if (!targetImg.isValid() || !srcImg.isValid() || targetImg.width() != srcImg.width() ||
targetImg.height() != srcImg.height()) {
return false;
}
const QPainterPath holePathImg = worldToBg.map(holePathWorld);
{
QPainter p(&bg);
p.setRenderHint(QPainter::Antialiasing, true);
p.setClipPath(holePath);
p.drawImage(targetRect.topLeft(), srcSnapshot, srcRect);
p.setClipPath(holePathImg);
p.drawImage(targetImg.topLeft(), srcSnapshot, srcImg);
p.end();
}
const QImage patch = bg.copy(targetRect);
const QImage patch = bg.copy(targetImg);
const auto before = m_project.entities();
QString overlayRel;
if (!writeBlackholeOverlayPng(m_projectDir, id, ents[hit].blackholeOverlayPath, patch, targetRect,
if (!writeBlackholeOverlayPng(m_projectDir, id, ents[hit].blackholeOverlayPath, patch, targetWorld,
&overlayRel)) {
return false;
}
ents[hit].blackholeOverlayPath = overlayRel;
ents[hit].blackholeOverlayRect = targetRect;
ents[hit].blackholeOverlayRect = targetWorld;
ents[hit].blackholeVisible = hideBlackholeAfterFill ? false : ents[hit].blackholeVisible;
if (ents[hit].blackholeId.isEmpty()) {
ents[hit].blackholeId = QStringLiteral("blackhole-%1").arg(ents[hit].id);