This commit is contained in:
2026-05-15 00:46:37 +08:00
parent b4c7697e8a
commit f64e624a39
8 changed files with 190 additions and 100 deletions
+104 -28
View File
@@ -1214,21 +1214,6 @@ bool EditorCanvas::startBlackholeCopyResolve(const QString& entityId) {
srcRect.translate(shift, 0.0);
srcRect = clampRectTopLeftToBounds(srcRect, bg);
m_bhCopyPatchValid = false;
m_bhCopyPatch = QImage();
if (m_useViewportLod && !m_bgAbsPath.isEmpty()) {
QRect uni =
holeRect.united(srcRect).toAlignedRect().intersected(bg.toAlignedRect());
if (uni.isValid() && uni.width() > 0 && uni.height() > 0 &&
core::image_file::loadRegionToQImage(m_bgAbsPath, uni, 8192, 8192, &m_bhCopyPatch) &&
!m_bhCopyPatch.isNull()) {
if (m_bhCopyPatch.format() != QImage::Format_ARGB32_Premultiplied) {
m_bhCopyPatch = m_bhCopyPatch.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
m_bhCopyPatchOrigin = uni.topLeft();
m_bhCopyPatchValid = true;
}
}
if (!m_bhCopyPatchValid && m_bgImageDirty) {
m_bgImageDirty = false;
m_bgImage = readImageTolerant(m_bgAbsPath);
@@ -1244,17 +1229,59 @@ bool EditorCanvas::startBlackholeCopyResolve(const QString& entityId) {
m_blackholeCopySourceRect = srcRect;
m_blackholeCopyDragging = false;
m_blackholeCopyDragOffset = QPointF();
reloadBlackholeCopyVipsPatchUnionIfNeeded();
updateCursor();
update();
return true;
}
void EditorCanvas::reloadBlackholeCopyVipsPatchUnionIfNeeded() {
if (!m_blackholeCopyResolveActive || !m_useViewportLod || m_bgAbsPath.isEmpty() || !m_bgLogicalSize.isValid() ||
m_bgLogicalSize.width() < 1 || m_bgLogicalSize.height() < 1) {
m_bhCopyPatchValid = false;
m_bhCopyPatch = QImage();
m_bhCopyPatchLogicalSize = QSize();
return;
}
const QRect bgBounds(0, 0, m_bgLogicalSize.width(), m_bgLogicalSize.height());
const QRect uni = m_blackholeCopyHoleRect.united(m_blackholeCopySourceRect)
.toAlignedRect()
.intersected(bgBounds);
if (!uni.isValid() || uni.width() < 1 || uni.height() < 1) {
m_bhCopyPatchValid = false;
m_bhCopyPatch = QImage();
m_bhCopyPatchLogicalSize = QSize();
return;
}
if (m_bhCopyPatchValid && m_bhCopyPatchLogicalSize.isValid() && !m_bhCopyPatch.isNull()) {
const QRect cached(m_bhCopyPatchOrigin, m_bhCopyPatchLogicalSize);
if (cached.contains(uni)) {
return;
}
}
QImage patch;
if (!core::image_file::loadRegionToQImageExact(m_bgAbsPath, uni, &patch) || patch.isNull()) {
m_bhCopyPatchValid = false;
m_bhCopyPatch = QImage();
m_bhCopyPatchLogicalSize = QSize();
return;
}
if (patch.format() != QImage::Format_ARGB32_Premultiplied) {
patch = patch.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
m_bhCopyPatch = std::move(patch);
m_bhCopyPatchOrigin = uni.topLeft();
m_bhCopyPatchLogicalSize = uni.size();
m_bhCopyPatchValid = true;
}
void EditorCanvas::cancelBlackholeCopyResolve() {
if (!m_blackholeCopyResolveActive) {
return;
}
m_bhCopyPatchValid = false;
m_bhCopyPatch = QImage();
m_bhCopyPatchLogicalSize = QSize();
m_blackholeCopyResolveActive = false;
m_blackholeCopyEntityId.clear();
m_blackholeCopyHoleRect = QRectF();
@@ -1567,7 +1594,7 @@ bool EditorCanvas::loadRgbCropForEntitySegmentStroke(const QRectF& polyBrWorld,
}
}
if (outCropRgb.isNull()) {
(void)core::image_file::loadRegionToQImage(m_bgAbsPath, cropWorld, 4096, 4096, &outCropRgb);
(void)core::image_file::loadRegionToQImageExact(m_bgAbsPath, cropWorld, &outCropRgb);
if (!outCropRgb.isNull() && outCropRgb.format() != QImage::Format_ARGB32_Premultiplied) {
outCropRgb = outCropRgb.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
@@ -2027,11 +2054,11 @@ void EditorCanvas::paintEvent(QPaintEvent* e) {
if (!m_presentationPreviewMode && m_blackholeCopyResolveActive &&
!m_blackholeCopyHoleRect.isNull() && !m_blackholeCopySourceRect.isNull()) {
const QRect srcRect = m_blackholeCopySourceRect.toAlignedRect();
const QRect dstRect = m_blackholeCopyHoleRect.toAlignedRect();
const QRectF holeF = m_blackholeCopyHoleRect;
const QRectF srcF(m_blackholeCopySourceRect.topLeft(), QSizeF(holeF.width(), holeF.height()));
const bool havePreview =
(m_bhCopyPatchValid && !m_bhCopyPatch.isNull()) || !m_bgImage.isNull();
if (havePreview && srcRect.isValid() && dstRect.isValid()) {
if (havePreview && holeF.width() >= 1.0 && holeF.height() >= 1.0) {
QPainterPath holePath;
for (const auto& ent : m_entities) {
if (ent.id == m_blackholeCopyEntityId && !ent.cutoutPolygonWorld.isEmpty()) {
@@ -2044,11 +2071,31 @@ void EditorCanvas::paintEvent(QPaintEvent* e) {
p.setClipPath(holePath);
}
p.setOpacity(0.75);
if (m_bhCopyPatchValid && !m_bhCopyPatch.isNull()) {
const QRect srcLocal = srcRect.translated(-m_bhCopyPatchOrigin);
p.drawImage(dstRect.topLeft(), m_bhCopyPatch, srcLocal);
} else {
p.drawImage(dstRect.topLeft(), m_bgImage, srcRect);
if (m_bhCopyPatchValid && !m_bhCopyPatch.isNull() && m_bhCopyPatchLogicalSize.isValid() &&
m_bhCopyPatchLogicalSize.width() > 0 && m_bhCopyPatchLogicalSize.height() > 0) {
const QRectF srcWorldInPatch = srcF.translated(-m_bhCopyPatchOrigin);
const QRectF bound(0, 0, m_bhCopyPatchLogicalSize.width(), m_bhCopyPatchLogicalSize.height());
const QRectF srcVis = srcWorldInPatch.intersected(bound);
if (!srcVis.isEmpty()) {
const double sx = double(m_bhCopyPatch.width()) / double(m_bhCopyPatchLogicalSize.width());
const double sy = double(m_bhCopyPatch.height()) / double(m_bhCopyPatchLogicalSize.height());
const QRectF srcPixels(srcVis.x() * sx, srcVis.y() * sy, srcVis.width() * sx, srcVis.height() * sy);
const QPointF dVis = srcVis.topLeft() - srcWorldInPatch.topLeft();
const QRectF dstFrag(holeF.topLeft() + dVis, srcVis.size());
p.drawImage(dstFrag, m_bhCopyPatch, srcPixels);
}
} else if (!m_bgImage.isNull() && m_bgLogicalSize.isValid() && m_bgLogicalSize.width() > 0 &&
m_bgLogicalSize.height() > 0) {
const QRectF bgB(0, 0, m_bgLogicalSize.width(), m_bgLogicalSize.height());
const double bx = double(m_bgImage.width()) / double(m_bgLogicalSize.width());
const double by = double(m_bgImage.height()) / double(m_bgLogicalSize.height());
const QRectF srcFW = srcF.intersected(bgB);
if (!srcFW.isEmpty()) {
const QPointF dVis = srcFW.topLeft() - srcF.topLeft();
const QRectF dstFrag(holeF.topLeft() + dVis, srcFW.size());
const QRectF srcPixels(srcFW.x() * bx, srcFW.y() * by, srcFW.width() * bx, srcFW.height() * by);
p.drawImage(dstFrag, m_bgImage, srcPixels);
}
}
p.setOpacity(1.0);
p.restore();
@@ -3002,6 +3049,7 @@ void EditorCanvas::mouseMoveEvent(QMouseEvent* e) {
src.moveTopLeft(wp - m_blackholeCopyDragOffset);
src = clampRectTopLeftToBounds(src, worldRectOfBackground());
m_blackholeCopySourceRect = src;
reloadBlackholeCopyVipsPatchUnionIfNeeded();
update();
}
e->accept();
@@ -3204,10 +3252,38 @@ void EditorCanvas::mouseReleaseEvent(QMouseEvent* e) {
if (m_blackholeCopyResolveActive && e->button() == Qt::LeftButton) {
if (m_blackholeCopyDragging) {
m_blackholeCopyDragging = false;
const QPointF delta = m_blackholeCopySourceRect.topLeft() - m_blackholeCopyHoleRect.topLeft();
emit requestResolveBlackholeCopy(
m_blackholeCopyEntityId,
QPoint(static_cast<int>(std::round(delta.x())), static_cast<int>(std::round(delta.y()))));
QRect targetW;
QRect srcW;
if (m_bgLogicalSize.isValid() && m_bgLogicalSize.width() > 0 && m_bgLogicalSize.height() > 0) {
const QRect bounds(0, 0, m_bgLogicalSize.width(), m_bgLogicalSize.height());
// 洞与源不得各自 toAlignedRect:浮点外接框相同宽高时取整常会差 1px,导致 emit 空矩形。
// 以洞的对齐矩形为准,源矩形与之同尺寸,仅平移量取 hole/source 左上角差值的四舍五入。
targetW = m_blackholeCopyHoleRect.toAlignedRect().intersected(bounds);
if (targetW.isValid() && targetW.width() > 0 && targetW.height() > 0) {
const QPointF deltaF =
m_blackholeCopySourceRect.topLeft() - m_blackholeCopyHoleRect.topLeft();
const QPoint d(int(std::lround(deltaF.x())), int(std::lround(deltaF.y())));
srcW = QRect(targetW.topLeft() + d, targetW.size());
if (srcW.left() < bounds.left()) {
srcW.moveLeft(bounds.left());
}
if (srcW.top() < bounds.top()) {
srcW.moveTop(bounds.top());
}
if (srcW.right() > bounds.right()) {
srcW.moveRight(bounds.right());
}
if (srcW.bottom() > bounds.bottom()) {
srcW.moveBottom(bounds.bottom());
}
}
}
if (targetW.isValid() && srcW.isValid() && targetW.width() > 0 && targetW.height() > 0 &&
targetW.width() == srcW.width() && targetW.height() == srcW.height()) {
emit requestResolveBlackholeCopy(m_blackholeCopyEntityId, targetW, srcW);
} else {
emit requestResolveBlackholeCopy(m_blackholeCopyEntityId, QRect(), QRect());
}
}
cancelBlackholeCopyResolve();
e->accept();