I'm trying to apply a simple alpha mask on a QML item using ShaderEffectItem.
Here is a minimal (non-)working example: I have a red-to-white gradient as the background and want to draw a green 200x200 square on top of it. The alpha mask for this square should be 0.0 at the left and 1.0 at the right border, so it should be transparent at the left border.
import QtQuick 1.1
import Qt.labs.shaders 1.0
Rectangle {
width: 300
height: 300
id: wrapper
gradient: Gradient {
GradientStop { position: 0.0; color: "red" }
GradientStop { position: 1.0; color: "white" }
}
Rectangle {
id: square
anchors.centerIn: parent
width: 200
height: 200
color: "green"
}
ShaderEffectItem {
anchors.centerIn: parent
width: 200
height: 200
property variant source: ShaderEffectSource {
sourceItem: square
hideSource: true
}
fragmentShader: "
varying vec2 qt_TexCoord0;
uniform sampler2D source;
void main(void)
{
vec4 sourceColor = texture2D(source, qt_TexCoord0);
float alpha = qt_TexCoord0.x; // = 0.0 at left, 1.0 at right border
sourceColor.a *= alpha; // apply alpha mask
gl_FragColor = sourceColor;
}
"
}
}
I expected the following output (drawn with GIMP):
But this is actually shown:
What am I doing wrong?
I'm using qmlviewer (Qt 4.8.2) to display the QML file with the -opengl option in order to enable the shader effect.
Maybe this is related to this strange behavior with alpha blending on QGLFramebufferObjects I found some weeks ago...
Try modifying the main function of the fragment shader to :
void main(void)
{
gl_FragColor = texture2D(source, qt_TexCoord0).rgba*qt_TexCoord0.x;
}
Related
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
}
}
}
I'm trying to visualise the intersection of two images using a shader. Here are the images:
I have the foundation set up, using the default fragment shader code as shown here:
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
width: 640
height: 480
title: "Finding image intersection with a shader"
visible: true
Column {
Row {
Column {
Image {
id: shadow1
source: "https://i.stack.imgur.com/VzpV8.png"
}
Label {
text: "shadow1"
}
}
Column {
Image {
id: shadow2
source: "https://i.stack.imgur.com/z9bxx.png"
}
Label {
text: "shadow2"
}
}
Column {
Item {
width: 100
height: 100
Image {
source: shadow1.source
}
Image {
source: shadow2.source
}
}
Label {
text: "intersection"
}
}
}
Row {
Column {
ShaderEffect {
width: shadow1.width
height: shadow1.height
property var source: shadow1
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
}
Label {
text: "shader"
}
}
}
}
}
The result so far:
How can I take these two images as input and render the resulting intersection?
Here is one way to do it:
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
width: 640
height: 480
title: "Finding image intersection with a shader"
visible: true
Column {
Row {
Column {
Image {
id: shadow1
source: "https://i.stack.imgur.com/VzpV8.png"
}
Label {
text: "shadow1"
}
}
Column {
Image {
id: shadow2
source: "https://i.stack.imgur.com/z9bxx.png"
}
Label {
text: "shadow2"
}
}
Column {
Item {
width: 100
height: 100
Image {
source: shadow1.source
}
Image {
source: shadow2.source
}
}
Label {
text: "intersection"
}
}
}
Row {
Column {
ShaderEffect {
width: shadow1.width
height: shadow1.height
property var source1: shadow1
property var source2: shadow2
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source1;
uniform sampler2D source2;
uniform lowp float qt_Opacity;
void main() {
vec4 colour1 = texture2D(source1, qt_TexCoord0);
vec4 colour2 = texture2D(source2, qt_TexCoord0);
if (colour1.a > 0.0 && colour2.a > 0.0) {
// There is an intersection here; mark it.
gl_FragColor = vec4(0.4, 0.0, 0.0, 0.5);
} else {
// There's no intersection here; make this pixel transparent.
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
}"
}
Label {
text: "shader"
}
}
}
}
}
I'll briefly explain the additions I made:
uniform sampler2D source1;
uniform sampler2D source2;
These two lines declare the input images. sampler2D refers to Qt Quick's Image type.
vec4 colour1 = texture2D(source1, qt_TexCoord0);
vec4 colour2 = texture2D(source2, qt_TexCoord0);
These two lines declare two vec4 variables to store the colour for each pixel at this coordinate (qt_TextCoord0).
if (colour1.a > 0.0 && colour2.a > 0.0) {
// There is an intersection here; mark it.
gl_FragColor = vec4(0.4, 0.0, 0.0, 0.5);
} else {
// There's no intersection here; make this pixel transparent.
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
The if statements check the alpha values of each pixel. If they're both non-zero, a semi-transparent red pixel will be drawn.
The result:
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
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 :-)
QML gradient allows only from top to bottom in a Rectangle. The documentation says that
it has to be done through combination of rotation and clipping.
I have just started learning QML (and little experience with HTML/CSS). Here is my code which I think can be improved for a lot better:
import QtQuick 1.0
Rectangle {
width: 400; height: 400;
Rectangle {
width: 567; height: 567;
gradient: Gradient {
GradientStop {
position: 0.0;
color: "white";
}
GradientStop {
position: 1.0;
color: "blue";
}
}
x: 116.5;
transformOrigin: Item.Top;
rotation: 45.0;
}
}
Can you please suggest what are the better ways to do this?
I've solve this probling with the following code: https://code.google.com/p/ytd-meego/source/browse/trunk/playground/FeedBook/qmltube/HorizontalGradient.qml?r=144
Here is what I've done using the example of NielsMayer:
Rectangle {
width: parent.height
height: parent.width
anchors.centerIn: parent
rotation: 90
gradient: Gradient {
GradientStop { position: 0.0; color: "black" }
GradientStop { position: 1.0; color: "white" }
}
}
And this is working well. Have fun !
The Qt Graphical Effects module introduced in Qt 5.1 provides three gradient types.
With the LinearGradient item (effect) it is no longer necessary to apply rotation in order to achieve e.g. a horizontal color gradient.
In particular by means of the start and end point (attributes) of LinearGradient any gradient direction is possible.
The following code implements a 45° angle (as proposed in the original post) by starting top right with white and ending bottom left with black:
import QtQuick 2.0
import QtGraphicalEffects 1.0
Item {
id:myItem
width: 300
height: 300
LinearGradient {
anchors.fill: parent
start: Qt.point(myItem.width, 0)
end: Qt.point(0, myItem.height)
gradient: Gradient {
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 1.0; color: "black" }
}
}
}
I'm afraid the documentation is correct.
The only other way I can think of is to write a custom QML component in C++ which does what you want.
If you have Qt/C++ knowledge you could start here:
http://doc.qt.nokia.com/4.7-snapshot/qml-extending.html
http://doc.qt.nokia.com/4.7-snapshot/declarative-tutorials-extending-chapter1-basics.html
The Rectangle could be a good template or base class:
http://qt.gitorious.org/qt/qt/blobs/4.8/src/declarative/graphicsitems/qdeclarativerectangle_p.h
You can use the orientation property from Qt 5.12