update
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -153,7 +153,8 @@ signals:
|
||||
void requestMoveCamera(const QString& id, const QPointF& delta);
|
||||
void requestCameraViewScaleAdjust(const QString& id, double factor);
|
||||
void selectedCameraChanged(bool hasSelection, const QString& id, const QPointF& centerWorld, double viewScale);
|
||||
void requestResolveBlackholeCopy(const QString& entityId, const QPoint& sourceOffsetPx);
|
||||
void requestResolveBlackholeCopy(const QString& entityId, const QRect& targetWorldInBackground,
|
||||
const QRect& srcWorldInBackground);
|
||||
void entityDragActiveChanged(bool on);
|
||||
void selectedEntityPreviewChanged(const QString& id, int depth, const QPointF& originWorld);
|
||||
/// 预览模式下点击实体:anchorView 为实体质心在视图中的位置,用于摆放介绍浮层
|
||||
@@ -269,6 +270,8 @@ private:
|
||||
bool m_initialBgLoadFinishedEmitted = false;
|
||||
QImage m_bhCopyPatch; // 黑洞「复制背景」预览:原图子区域
|
||||
QPoint m_bhCopyPatchOrigin;
|
||||
/// 与 m_bhCopyPatch 对应的逻辑像素范围(vips 路径可能将 patch 缩小到 maxOutput,与逻辑尺寸不同)
|
||||
QSize m_bhCopyPatchLogicalSize;
|
||||
bool m_bhCopyPatchValid = false;
|
||||
mutable QPixmap m_bgPixmap;
|
||||
mutable bool m_pixmapDirty = true;
|
||||
@@ -337,6 +340,9 @@ private:
|
||||
DragMode m_dragMode = DragMode::None;
|
||||
QPointF m_dragStartMouseWorld;
|
||||
|
||||
/// 视口 LOD 下:黑洞「复制背景」预览纹理须覆盖 hole∪source;拖动时按需从磁盘重载。
|
||||
void reloadBlackholeCopyVipsPatchUnionIfNeeded();
|
||||
|
||||
QVector<Entity> m_entities;
|
||||
QVector<ToolView> m_tools;
|
||||
QVector<core::Project::Camera> m_cameraOverlays;
|
||||
|
||||
Reference in New Issue
Block a user