EffectComposer: Fix effect preview zoom

Zoom must scale the component showing the preview, not the source
image. This way the effect stays consistent regardless of the zoom
level.

Fixes: QDS-11899
Change-Id: I550eb9ff693c24a853f5c25d9d79fa146448663f
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2024-02-09 16:31:30 +02:00
parent c90970afed
commit 93993c322e
2 changed files with 129 additions and 110 deletions

View File

@@ -22,11 +22,13 @@ Column {
readonly property int previewMargin: 5
property real previewScale: 1
// Create a dummy parent to host the effect qml object
function createNewComponent() {
// If we have a working effect, do not show preview image as it shows through
// transparent parts of the final image
source.visible = false;
placeHolder.visible = false;
var oldComponent = componentParent.children[0];
if (oldComponent)
@@ -48,7 +50,7 @@ Column {
errorLine = e.lineNumber;
}
effectComposerModel.setEffectError(errorString, 0, errorLine);
source.visible = true;
placeHolder.visible = true;
}
}
@@ -84,42 +86,42 @@ Column {
anchors.verticalCenter: parent.verticalCenter
HelperWidgets.AbstractButton {
enabled: sourceImage.scale < 3
enabled: root.previewScale < 3
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.zoomIn_medium
tooltip: qsTr("Zoom In")
onClicked: {
sourceImage.enableAnim(true)
sourceImage.scale += .2
sourceImage.enableAnim(false)
imageScaler.enableAnim(true)
root.previewScale += .2
imageScaler.enableAnim(false)
zoomIndicator.show()
}
}
HelperWidgets.AbstractButton {
enabled: sourceImage.scale > .4
enabled: root.previewScale > .4
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.zoomOut_medium
tooltip: qsTr("Zoom out")
onClicked: {
sourceImage.enableAnim(true)
sourceImage.scale -= .2
sourceImage.enableAnim(false)
imageScaler.enableAnim(true)
root.previewScale -= .2
imageScaler.enableAnim(false)
zoomIndicator.show()
}
}
HelperWidgets.AbstractButton {
enabled: sourceImage.scale !== 1 || sourceImage.x !== root.previewMargin
|| sourceImage.y !== root.previewMargin
enabled: root.previewScale !== 1 || imageScaler.x !== root.previewMargin
|| imageScaler.y !== root.previewMargin
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.fitAll_medium
tooltip: qsTr("Reset View")
onClicked: {
sourceImage.resetTransforms()
imageScaler.resetTransforms()
}
}
}
@@ -187,8 +189,8 @@ Column {
property bool panning: false
onPressed: {
pressX = mouseX - sourceImage.x
pressY = mouseY - sourceImage.y
pressX = mouseX - imageScaler.x
pressY = mouseY - imageScaler.y
panning = true
}
@@ -197,22 +199,21 @@ Column {
}
onWheel: (wheel) => {
let prevScale = sourceImage.scale
let oldPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y))
if (wheel.angleDelta.y > 0) {
if (sourceImage.scale < 3)
sourceImage.scale += .2
if (root.previewScale < 3)
root.previewScale += .2
} else {
if (sourceImage.scale > .4)
sourceImage.scale -= .2
if (root.previewScale > .4)
root.previewScale -= .2
}
let dScale = sourceImage.scale - prevScale
let newPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y))
imageScaler.x -= (oldPoint.x - newPoint.x) * imageScaler.scale
imageScaler.y -= (oldPoint.y - newPoint.y) * imageScaler.scale
sourceImage.x += (sourceImage.x + sourceImage.width * .5 - wheel.x) * dScale;
sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale;
sourceImage.checkBounds()
imageScaler.checkBounds()
zoomIndicator.show()
}
@@ -222,98 +223,40 @@ Column {
repeat: true
onTriggered: {
sourceImage.x = mouseArea.mouseX - mouseArea.pressX
sourceImage.y = mouseArea.mouseY - mouseArea.pressY
sourceImage.checkBounds()
imageScaler.x = mouseArea.mouseX - mouseArea.pressX
imageScaler.y = mouseArea.mouseY - mouseArea.pressY
imageScaler.checkBounds()
}
}
}
Image {
id: placeHolder
anchors.fill: parent
anchors.margins: root.previewMargin
fillMode: Image.PreserveAspectFit
source: imagesComboBox.selectedImage
smooth: true
}
Item { // Source item as a canvas (render target) for effect
id: source
anchors.fill: parent
width: sourceImage.sourceSize.width
height: sourceImage.sourceSize.height
layer.enabled: true
layer.mipmap: true
layer.smooth: true
visible: false
Image {
id: sourceImage
function checkBounds() {
let edgeMargin = 10
// correction factor to account for an observation that edgeMargin decreases
// with increased zoom
let corrFactor = 10 * sourceImage.scale
let imgW2 = sourceImage.paintedWidth * sourceImage.scale * .5
let imgH2 = sourceImage.paintedHeight * sourceImage.scale * .5
let srcW2 = source.width * .5
let srcH2 = source.height * .5
onSourceChanged: imageScaler.resetTransforms()
if (sourceImage.x < -srcW2 - imgW2 + edgeMargin + corrFactor)
sourceImage.x = -srcW2 - imgW2 + edgeMargin + corrFactor
else if (x > srcW2 + imgW2 - edgeMargin - corrFactor)
sourceImage.x = srcW2 + imgW2 - edgeMargin - corrFactor
fillMode: Image.Pad
if (sourceImage.y < -srcH2 - imgH2 + edgeMargin + corrFactor)
sourceImage.y = -srcH2 - imgH2 + edgeMargin + corrFactor
else if (y > srcH2 + imgH2 - edgeMargin - corrFactor)
sourceImage.y = srcH2 + imgH2 - edgeMargin - corrFactor
}
function resetTransforms() {
sourceImage.enableAnim(true)
sourceImage.scale = 1
sourceImage.x = root.previewMargin
sourceImage.y = root.previewMargin
sourceImage.enableAnim(false)
}
function enableAnim(flag) {
xBehavior.enabled = flag
yBehavior.enabled = flag
scaleBehavior.enabled = flag
}
onSourceChanged: sourceImage.resetTransforms()
fillMode: Image.PreserveAspectFit
x: root.previewMargin
y: root.previewMargin
width: parent.width - root.previewMargin * 2
height: parent.height - root.previewMargin * 2
source: imagesComboBox.selectedImage
smooth: true
Behavior on x {
id: xBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on y {
id: yBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on scale {
id: scaleBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
}
}
@@ -325,14 +268,90 @@ Column {
}
Item {
id: componentParent
width: source.width
height: source.height
anchors.centerIn: parent
// Cache the layer. This way heavy shaders rendering doesn't
// slow down code editing & rest of the UI.
layer.enabled: true
layer.smooth: true
id: imageScaler
x: root.previewMargin
y: root.previewMargin
width: parent.width - root.previewMargin * 2
height: parent.height - root.previewMargin * 2
scale: root.previewScale * (width > height ? height / sourceImage.sourceSize.height
: width / sourceImage.sourceSize.width)
Behavior on x {
id: xBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on y {
id: yBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on scale {
id: scaleBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
function checkBounds() {
let edgeMargin = 10
// correction factor to account for an observation that edgeMargin decreases
// with increased zoom
let corrFactor = 10 * imageScaler.scale
let imgW2 = sourceImage.paintedWidth * imageScaler.scale * .5
let imgH2 = sourceImage.paintedHeight * imageScaler.scale * .5
let srcW2 = width * .5
let srcH2 = height * .5
if (imageScaler.x < -srcW2 - imgW2 + edgeMargin + corrFactor)
imageScaler.x = -srcW2 - imgW2 + edgeMargin + corrFactor
else if (x > srcW2 + imgW2 - edgeMargin - corrFactor)
imageScaler.x = srcW2 + imgW2 - edgeMargin - corrFactor
if (imageScaler.y < -srcH2 - imgH2 + edgeMargin + corrFactor)
imageScaler.y = -srcH2 - imgH2 + edgeMargin + corrFactor
else if (y > srcH2 + imgH2 - edgeMargin - corrFactor)
imageScaler.y = srcH2 + imgH2 - edgeMargin - corrFactor
}
function resetTransforms() {
imageScaler.enableAnim(true)
root.previewScale = 1
imageScaler.x = root.previewMargin
imageScaler.y = root.previewMargin
imageScaler.enableAnim(false)
}
function enableAnim(flag) {
xBehavior.enabled = flag
yBehavior.enabled = flag
scaleBehavior.enabled = flag
}
Item {
id: componentParent
width: source.width
height: source.height
anchors.centerIn: parent
// Cache the layer. This way heavy shaders rendering doesn't
// slow down code editing & rest of the UI.
layer.enabled: true
layer.smooth: true
}
}
Rectangle {
@@ -349,7 +368,7 @@ Column {
}
Text {
text: Math.round(sourceImage.scale * 100) + "%"
text: Math.round(root.previewScale * 100) + "%"
color: StudioTheme.Values.themeTextColor
anchors.centerIn: parent
}

View File

@@ -178,7 +178,7 @@ private:
QList<CompositionNode *> m_nodes;
int m_selectedIndex = -1;
bool m_isEmpty = true;
bool m_isEmpty = false; // Init to false to force initial bake after setup
bool m_hasUnsavedChanges = false;
// True when shaders haven't changed since last baking
bool m_shadersUpToDate = true;