Opposite for OpacityMask - qt

It masks the source item with another item, so the source is visible only where the mask is opaque. How to make source visible only where the mask is transparent?
Currently it's done with a shader, but I want to replace that.
Can't invert the mask, because it consists of multiple images: the sum of individually inverted images is not the same as the inversion of the sum.

There is no component made by Qt that can do what you need. OpacityMask is closest to what you need. You can view its code here on the official repository or on your machine in this path: Qt_folder/Qt_version/Qt_kit/qml/QtGraphicalEffects/OpacityMask.qml. This way you can easily browse the source of all QtGraphicalEffects components.
Using ShaderEffect is a good choice for the task.
As GrecKo pointed out there already is an invert property in OpacityMask object. It will be available with Qt 5.7 but the code is already available in the link above. You can either wait for the update or download the component and use it in your project.

If you like to use QML Items (Rectangle) you can use following codes:
import QtQuick 2.6
Item {
anchors.fill: parent
Image {
anchors.fill: parent
source: "http://i.imgur.com/R3yMj0y.jpg"
fillMode: Image.PreserveAspectCrop
focus: true
Keys.onRightPressed: _mask.maskX += 100
Keys.onLeftPressed: _mask.maskX -= 100
Keys.onUpPressed: _mask.maskY -= 100
Keys.onDownPressed: _mask.maskY += 100
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
_mask.maskX = mouseX;
_mask.maskY = mouseY;
}
}
}
Rectangle {
id: _bk
anchors.fill: parent
color: "#33000000"
visible: false
layer.enabled: true
layer.smooth: true
}
Rectangle {
id: _mask
anchors.fill: parent
color: "transparent"
visible: true
property int maskX: 0
property int maskY: 0
Rectangle {
id: circle
width: 100; height: 100
x: _mask.maskX-50; y: _mask.maskY-50
radius: 50
color: "#000"
Behavior on x { NumberAnimation { duration: 400; easing.type: Easing.OutBack; easing.overshoot: 1.4 } }
Behavior on y { NumberAnimation { duration: 400; easing.type: Easing.OutBack; easing.overshoot: 1.4 } }
}
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property variant source: _bk
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform lowp sampler2D source;
uniform lowp sampler2D maskSource;
void main(void) {
gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
}
"
}
}
Rectangle {
id: _mask2
anchors.fill: parent
color: "transparent"
visible: true
Rectangle {
id: circle2
width: 150; height: 150
x: _mask.maskX-75; y: _mask.maskY-75
radius: 75
color: "#000"
Behavior on x { NumberAnimation { duration: 550; easing.type: Easing.OutBack; easing.overshoot: 2.4 } }
Behavior on y { NumberAnimation { duration: 550; easing.type: Easing.OutBack; easing.overshoot: 2.4 } }
}
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property variant source: _bk
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform lowp sampler2D source;
uniform lowp sampler2D maskSource;
void main(void) {
gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
}
"
}
}
Rectangle {
id: _mask3
anchors.fill: parent
color: "transparent"
visible: true
Rectangle {
id: circle3
width: 220; height: 220
x: _mask.maskX-110; y: _mask.maskY-110
radius: 110
color: "#000"
Behavior on x { NumberAnimation { duration: 650; easing.type: Easing.OutBack; easing.overshoot: 3.0 } }
Behavior on y { NumberAnimation { duration: 650; easing.type: Easing.OutBack; easing.overshoot: 3.0 } }
}
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property variant source: _bk
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform lowp sampler2D source;
uniform lowp sampler2D maskSource;
void main(void) {
gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
}
"
}
}
}

Related

Sequential movement of an object by coordinates

I need to move an object by the clock and counterclockwise sequentially. But the for loop works differently, it only moves in the latter direction. When you click on the button, the object must first turn clockwise, and then counterclockwise. Maybe there is some kind of delay when performing the animation? How can I do it?
import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import QtQuick.Controls 2.14
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: "Maximized"
property int scl: 5
property int angle: 360
Node{
id: standAloneScene
DirectionalLight {
ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
}
Node {
id: sphere
Model {
id: model_sphere
source: "#Cube"
x: 200
y: 100
z: 0
materials: [
DefaultMaterial {
diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
}
]
}
}
ParallelAnimation{
id: start
running: false
NumberAnimation {
target: sphere
property: "eulerRotation.y"
to: angle
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
to: 2*angle
duration: 2000
easing.type: Easing.InOutQuad
}
}
OrthographicCamera {
id: cameraOrthographicFront
eulerRotation.y: 45
eulerRotation.x: -45
x: 600
y: 800
z: 600
}
}
Rectangle {
id: view
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
color: "#848895"
border.color: "black"
View3D {
id: topLeftView
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
Button {
id: posmoveZ
width: view.width/8
height: view.height/16
anchors.top: view.top
anchors.right: view.right
text: "start"
font.pixelSize: height
onClicked: {
for(var i=0; i<6; i++){
if(i % 2 == 0){
angle = 360
}
else{
angle = -360
}
start.restart();
}
}
}
}
}
The simplest answer to your problem is put your animations inside a SequentialAnimation, i.e.
SequentialAnimation {
loops: 3
ParallelAnimation {
id: clockwise
NumberAnimation { /* ... */ }
NumberAnimation { /* ... */ }
}
ParallelAnimation {
id: counterClockwise
NumberAnimation { /* ... */ }
NumberAnimation { /* ... */ }
}
}
Also, you should let QML use the default width and height of a Button. Here's the complete solution:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import QtQuick3D 1.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: "Maximized"
property int scl: 5
property int angle: 360
Node{
id: standAloneScene
DirectionalLight {
ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
}
Node {
id: sphere
Model {
id: model_sphere
source: "#Cube"
x: 200
y: 100
z: 0
materials: [
DefaultMaterial {
diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
}
]
}
}
SequentialAnimation {
id: sequentialAnimation
loops: 3
ParallelAnimation{
id: clockwise
running: false
property int count: 3
NumberAnimation {
target: sphere
property: "eulerRotation.y"
from: 360
to: 0
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
from: 720
to: 0
duration: 2000
easing.type: Easing.InOutQuad
}
}
ParallelAnimation{
id: counterClockwise
running: false
NumberAnimation {
target: sphere
property: "eulerRotation.y"
from: 0
to: 360
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
from: 0
to: 720
duration: 2000
easing.type: Easing.InOutQuad
}
}
}
OrthographicCamera {
id: cameraOrthographicFront
eulerRotation.y: 45
eulerRotation.x: -45
x: 600
y: 800
z: 600
}
}
Rectangle {
id: view
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
color: "#848895"
border.color: "black"
View3D {
id: topLeftView
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
Button {
id: posmoveZ
anchors.top: view.top
anchors.right: view.right
anchors.margins: 10
text: "start"
font.pixelSize: height
enabled: !clockwise.running && !counterClockwise.running
onClicked: {
sequentialAnimation.restart();
}
}
}
}

Get color of element in the point | QML

I was able to write 'Color Wheel' in QML with Rectangle elements:
And now I'm wondering how to get color of the handler's position.
I have seen that it's should be possible to achieve with canvas, but may be there is a better solution?
Here the code:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.12
Item
{
id: colorWheel
Rectangle
{
id: ring
color: "transparent"
implicitWidth: 80
implicitHeight: implicitWidth
width: parent.width < parent.height ? parent.width : parent.height
height: width
radius: width / 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
border.width: width / 15
property double wheelRadius: ring.width / 2 - border.width /2
ConicalGradient
{
source: parent
anchors.fill: parent
gradient: Gradient
{
GradientStop { position: 0; color: "#7FFF00" }
GradientStop { position: 1; color: "#7FFF00" }
}
}
MouseArea
{
id: dragArea
anchors.fill: parent
drag.target: parent
onPositionChanged: {
[handler.x, handler.y] = calculatePos(mouseX, mouseY)
}
function calculatePos(x, y)
{
var vX = x - ring.wheelRadius
var vY = y - ring.wheelRadius
var magV = Math.sqrt((Math.pow(vX, 2) + Math.pow(vY, 2)))
var aX = ring.wheelRadius + vX / magV * ring.wheelRadius
var aY = ring.wheelRadius + vY / magV * ring.wheelRadius
return [aX, aY]
}
}
Rectangle
{
id: handler
width: ring.width / 15
height: width
radius: ring.width / 30
border.width: 1
color: "transparent"
}
}
}
I was asked to add some more details to be able to post the code:
As you can see I change handler position according mouse position. I want to grab ring's color under handler's position.
Finally I have used canvas and it seems that it works well, here the shorten version of the code(didn't include handler logic):
Canvas
{
id: ring
property double wheelFullRadius: width / 2
property double borderWidth: wheelFullRadius / 6
property double wheelInnerRadius: wheelFullRadius - borderWidth / 2
function getColor()
{
return context.getImageData(handler.x, handler.y, 1, 1)
}
onPaint:
{
var context = getContext('2d')
context.reset()
context.arc(ring.wheelFullRadius, ring.wheelFullRadius, ring.wheelInnerRadius,
0, 2*Math.PI)
var grd = context.createConicalGradient(
ring.wheelFullRadius, ring.wheelFullRadius, 0
)
grd.addColorStop(0, "#7FFF00" )
grd.addColorStop(1/12, "#FFFF00" )
grd.addColorStop(2/12, "#FF7F00" )
grd.addColorStop(3/12, "#FF0000" )
grd.addColorStop(4/12, "#FF007F" )
grd.addColorStop(5/12, "#FF00FF" )
grd.addColorStop(6/12, "#7F00FF" )
grd.addColorStop(7/12, "#0000FF" )
grd.addColorStop(8/12, "#007FFF" )
grd.addColorStop(9/12, "#00FFFF" )
grd.addColorStop(10/12, "#00FF7F" )
grd.addColorStop(11/12, "#00FF00" )
grd.addColorStop(1, "#7FFF00" )
context.strokeStyle = grd
context.lineWidth = ring.borderWidth
context.stroke()
handler.update()
}
....
}
I still have concerns about performance on mobile devices, but for now it works.
You can also use Math.arc2 to get mouse angle based on ring center, then use Qt.hsva or Qt.hsla methods to get the color at the mouse position.
property color color: Qt.hsva(mousearea.angle, 1.0, 1.0, 1.0);
MouseArea {
id: mousearea
anchors.fill: parent
// angle value is between 0 and 1
property real angle: Math.atan2(width/2 - mouseX, mouseY - height/2) / 6.2831 + 0.5
}
Preview
             
Here is a circular color picker which I wrote.
I used shader effect to create a perfect color wheel
ColorPicker.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Control {
id: control
property real ringWidth: 25
property real hsvValue: 1.0
property real hsvSaturation: 1.0
readonly property color color: Qt.hsva(mousearea.angle, 1.0, 1.0, 1.0)
contentItem: Item {
implicitWidth: 175
implicitHeight: width
ShaderEffect {
id: shadereffect
width: parent.width; height: parent.height
readonly property real ringWidth: control.ringWidth / width / 2
readonly property real s: control.hsvSaturation
readonly property real v: control.hsvValue
fragmentShader: "
#version 330
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform highp float ringWidth;
uniform highp float s;
uniform highp float v;
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
highp vec2 coord = qt_TexCoord0 - vec2(0.5);
highp float ring = smoothstep(0, 0.01, -abs(length(coord) - 0.5 + ringWidth) + ringWidth);
gl_FragColor = vec4(hsv2rgb(vec3(-atan(coord.x, coord.y) / 6.2831 + 0.5, s, v)),1);
gl_FragColor *= ring;
}"
}
Rectangle {
id: indicator
x: (parent.width - width)/2
y: control.ringWidth * 0.1
width: control.ringWidth * 0.8; height: width
radius: width/2
color: 'white'
border {
width: mousearea.containsPress ? 3 : 1
color: Qt.lighter(control.color)
Behavior on width { NumberAnimation { duration: 50 } }
}
transform: Rotation {
angle: mousearea.angle * 360
origin.x: indicator.width/2
origin.y: control.availableHeight/2 - indicator.y
}
}
Rectangle {
anchors.centerIn: parent
width: control.availableWidth * 0.3
height: width
radius: width
color: control.color
border {
width: 5
color: Qt.lighter(control.color, 1.8)
}
}
MouseArea {
id: mousearea
anchors.fill: parent
property real angle: Math.atan2(width/2 - mouseX, mouseY - height/2) / 6.2831 + 0.5
}
}
}

how to clip an image along specific path in qml

I have a Background groove image
for which I have to produce progress bar effect using Progress filling Image
How to clip the Progress filling Image along the Path of the groove of the Progress Bar (Background groove image).
Currently I am trying to do clipping widthwise but it is not what I want. The clipping should happen perpendicular to the path as mentioned in path interpolator of my code.
In the code "RPM_BG.png" is background groove image which is of similar shape as "RPM_Fill.png"(Progress filling Image).
import QtQuick 2.5
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtMultimedia 5.0
import QtQuick.Controls.Styles 1.4
Window {
visible: true
color:"black"
width: 357
height: 221+200
Image
{
id:groove1
source:"qrc:/RPM_BG.png"
anchors.top:parent.top
anchors.left:parent.left
Item{
id: displayWindow1
height: parent.height
width: (225*(slider.value)/8000)+32
clip: true
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right:needle.right
anchors.rightMargin:/*8*/{switch(true)
{
case slider.value>=0 && slider.value < 111:return 10;
case slider.value>=111 && slider.value < 124:return 9.7;
case slider.value>=124 && slider.value < 132:return 8.4;
case slider.value>=132 && slider.value < 135:return 8;
case slider.value>=135 && slider.value <= 165:return 7.15;
case slider.value>=165 && slider.value <= 240:return 6;
}
}
Image
{
id:speedarcfill
anchors.top:parent.top
anchors.left:parent.left
source:"qrc:/RPM_Fill.png"
z: 1
}
}
PathInterpolator {
id: motionPath
property int value
path: Path {
startX: 27; startY: 189
PathLine { x: 98; y: 54 }
PathArc { x: 176; y: 12; radiusX: 90; radiusY: 90 }
PathLine { x: 245; y: 11 }
}
progress:slider.value/8000
}
}
Slider {
id: slider
anchors.top:groove1.bottom
anchors.topMargin:100
anchors.left:parent.left
anchors.leftMargin: 5
width: parent.width-10
height: 100
style: SliderStyle {
handle:
Rectangle {
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
implicitWidth: 10
implicitHeight: 40
}
groove: Rectangle {
width: slider.width
height: 10
color:"black"
LinearGradient {
anchors.verticalCenter: parent.verticalCenter
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
width: styleData.handlePosition
height: 10
gradient: Gradient {
GradientStop {position: 0.0; color: "#008BFF" }
GradientStop {position: 0.5; color: "#3FFFD0" }
GradientStop { position: 1.0; color: "#3FFF41" }
}
}
}
}
maximumValue: 8000
}
}
Please suggest a way for me so that I can clip the Progress filling Image perpendicular to the path of progressing.
You can use a basic fragment shader for this. Something like:
ShaderEffect {
id: displayWindow2
height: groove1.height
width: groove1.width
anchors.top: parent.top
anchors.right: parent.right
property var base: groove1
property var overlay: speedarcfill
property real pointX: motionPath.x/width
property real pointY: motionPath.y/height
property real pointAngle: (motionPath.angle + 90)%360
fragmentShader: "
uniform sampler2D base;
uniform sampler2D overlay;
varying highp vec2 qt_TexCoord0;
uniform lowp float qt_Opacity;
uniform highp float pointAngle;
uniform highp float pointX;
uniform highp float pointY;
void main() {
lowp vec4 baseTex = texture2D(base, qt_TexCoord0.st);
lowp vec4 overlayTex = texture2D(overlay, qt_TexCoord0.st);
//line equation => (y - y1)/(x - x1) = slope ; slope != infinity
highp float angle = radians(pointAngle);
highp float slope = tan(angle);
highp float deltay = qt_TexCoord0.y - pointY;
highp float deltax = qt_TexCoord0.x - pointX;
//If overlay is transparent, get the texture from base
if(overlayTex.a > 0.0)
{
//check where the current point lies, wrt the normal.
if( ( slope >= 0.0 && deltay - deltax*slope > 0.0 ) || (slope < 0.0 && deltax < 0.0))
gl_FragColor = overlayTex * qt_Opacity;
else gl_FragColor = baseTex*qt_Opacity;
}
else gl_FragColor = baseTex*qt_Opacity;
}"
}
Here's the full file,I've played around with to write this: https://bpaste.net/show/2b0c0fd1cc69

Clip QML's ShaderEffect to circular shape

I'm using ShaderEffect in QML to have a scaled visual copy of some Item.
This copy should be moveable and dynamic (live property of ShaderEffectSource set to true).
My problem is that I want it to be inside of a round Rectangle but it won't be clipped by it. ShaderEffect just overlaps its parent and is quadratic.
I've coded a quick QML example that shows the problem:
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
title: qsTr("Hello Shaders")
width: 640
height: 480
visible: true
color: "green"
Rectangle {
id: exampleRect
property bool redState: false
anchors.centerIn: parent
width: parent.width / 3
height: parent.height / 3
color: redState ? "red" : "cyan"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "white"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "green"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "yellow"
}
}
}
Timer {
interval: 2000
repeat: true
running: true
onTriggered: {
exampleRect.redState = !exampleRect.redState
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
shaderEffectContainer.x = mouse.x
shaderEffectContainer.y = mouse.y
}
}
Rectangle {
id: shaderEffectContainer
width: 100; height: width;
radius: width / 2;
border.width: 2
ShaderEffectSource {
id: source
sourceItem: exampleRect
visible: false
}
ShaderEffect {
anchors.fill: parent
property variant source: source
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0 * 1.5 * vec2(0.5, 0.5);
gl_Position = qt_Matrix * qt_Vertex;
}"
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
}
}
}
As you can see, exampleRect is declared to be round, but it's child is overlapping it.
I have already tried all possible combinations of clip property and have spent all day trying to do this with fragment/vertex shaders. With no luck, as you might guess. :)
I've solved the problem using layers as shown in this answer.
Thanks #DenimPowell for the hint.
Below is an updated example code with circular ShaderEffect.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
title: qsTr("Hello Shaders")
width: 640
height: 480
visible: true
color: "green"
Rectangle {
id: exampleRect
property bool redState: false
anchors.centerIn: parent
width: parent.width / 3
height: parent.height / 3
color: redState ? "red" : "cyan"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "white"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "green"
Rectangle {
anchors.centerIn: parent
width: parent.width / 2; height: width;
color: "yellow"
}
}
}
Timer {
interval: 2000
repeat: true
running: true
onTriggered: {
exampleRect.redState = !exampleRect.redState
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
shaderEffectContainer.x = mouse.x
shaderEffectContainer.y = mouse.y
}
}
Rectangle {
id: shaderEffectContainer
width: 100; height: width;
color: "transparent"
Rectangle {
id: rectangleSource
anchors.fill: parent
ShaderEffectSource {
id: source
sourceItem: exampleRect
visible: false
}
ShaderEffect {
anchors.fill: parent
property variant source: source
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0 * 1.5 * vec2(0.5, 0.5);
gl_Position = qt_Matrix * qt_Vertex;
}"
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
}
visible: false
layer.enabled: true
}
Rectangle {
id: maskLayer
anchors.fill: parent
radius: parent.width / 2
color: "red"
border.color: "black"
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property var colorSource: rectangleSource
fragmentShader: "
uniform lowp sampler2D colorSource;
uniform lowp sampler2D maskSource;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor =
texture2D(colorSource, qt_TexCoord0)
* texture2D(maskSource, qt_TexCoord0).a
* qt_Opacity;
}
"
}
}
// only draw border line
Rectangle {
anchors.fill: parent
radius: parent.width / 2
border.color: "black"
border.width: 1
color: "transparent"
}
}
}

Qt Use ShaderEffect for QImage rendering

I was wondering if it is possible via Qt Quick or C++ to use a shader effect on a image and then save it with the effect applied.
The reason for doing this is to achieve opengl assisted image effect manipulation (ie. emboss, pixelize, etc)
I tried this:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
title: qsTr("Hello World")
width: 640
height: 480
visible: true
Image {
id: effectImage
width: 200
height: 200
sourceSize.width: width
sourceSize.height: height
anchors.centerIn: parent
source: "qrc:/myimage.png"
layer.enabled: true
layer.effect: ShaderEffect {
fragmentShader: "
uniform lowp sampler2D source; // this item
uniform lowp float qt_Opacity; // inherited opacity of this item
varying highp vec2 qt_TexCoord0;
void main() {
lowp vec4 p = texture2D(source, qt_TexCoord0);
lowp float g = dot(p.xyz, vec3(0.344, 0.5, 0.156));
gl_FragColor = vec4(g, g, g, p.a) * qt_Opacity;
}"
}
}
Button {
id: grabIt
anchors.top: effectImage.bottom
text: qsTr("Grab")
onClicked: {
effectImage.grabToImage(function(result) {
console.debug("hallo");
result.saveToFile("test.jpg","jpg");
});
}
}
}
Applying a simple grayscale effect on the image. It works on-screen, but grabToImage() does not apply the Effect.
Is there any way to do this?
OK that was easy, I solved the problem by wrapping the image with an Item element and grabbing the image from that instead:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
title: qsTr("Hello World")
width: 640
height: 480
visible: true
Item {
id: effectImage
width: 200
height: 200
anchors.centerIn: parent
Image {
anchors.fill: parent
sourceSize.width: width
sourceSize.height: height
source: "qrc:/myimage.png"
layer.enabled: true
layer.effect: ShaderEffect {
fragmentShader: "
uniform lowp sampler2D source; // this item
uniform lowp float qt_Opacity; // inherited opacity of this item
varying highp vec2 qt_TexCoord0;
void main() {
lowp vec4 p = texture2D(source, qt_TexCoord0);
lowp float g = dot(p.xyz, vec3(0.344, 0.5, 0.156));
gl_FragColor = vec4(g, g, g, p.a) * qt_Opacity;
}"
}
}
}
Button {
id: grabIt
anchors.top: effectImage.bottom
text: qsTr("Grab")
onClicked: {
effectImage.grabToImage(function(result) {
console.debug("hallo");
result.saveToFile("test.jpg","jpg");
});
}
}
}
Hope this helps someone else :-)

Resources