skip to Main Content

I have been working on a project where I need to apply image masking that applies an effect like this:

Pic1: https://i.stack.imgur.com/6zI2x.jpg

Pic2: https://i.stack.imgur.com/z7IVX.jpg

Mask frame: https://i.stack.imgur.com/3syEm.jpg

Desired effect: https://i.stack.imgur.com/t2kO5.jpg

I got it to work by using OpacityMask however to do that I had to use some photoshop and edit my mask frame image. I need to apply this affect to multiple mask frames with different shapes therefore using photoshop to edit all of them seem troublesome. Moreover, the inside of the mask frame images arent all transparent either.

Is there any ideas you can give me to solve this issue without using any pre photoshoping each mask frame images. I tried to look into ShaderEffect but I could not really understand how I should use it for my purpose. Moreover I searched for a OpacityMask like effect but working only on part of the mask image which has a specific color/specific shaped area. However, I could not find any.

2

Answers


  1. If you know that the geometry of your picture frame is just a rectangle, we can just create a rectangular mask aligned within your picture frame. I worked out that your picture frame was 410×500 pixels and decided to shrink it to 50% i.e. 205×250 pixels. At that scale, I worked out that your picture frame had a border size of about 18 pixels. So I created an inner rectangle based on those dimensions and used that rectangle for the OpacityMask maskSource:

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtGraphicalEffects 1.0
    
    Page {
        Image {
            id: pic1
            anchors.fill: parent
            source: "https://i.stack.imgur.com/6zI2x.jpg"
        }
    
        Image {
            id: pic2
            anchors.fill: parent
            visible: false
            source: "https://i.stack.imgur.com/z7IVX.jpg"
        }
    
        Image {
            id: rawMask
            anchors.centerIn: parent
            width: 205
            height: 250
            source: "https://i.stack.imgur.com/3syEm.jpg"
        }
    
        Item {
            id: mask
            anchors.fill: parent
            Rectangle {
                id: borderMask
                x: rawMask.x + 18
                y: rawMask.y + 18
                width: rawMask.width - 36
                height: rawMask.height - 36
                color: "white"
            }
        }
    
        OpacityMask {
            anchors.fill: parent
            source: pic2
            maskSource: mask
        }
    }
    

    enter image description here

    Login or Signup to reply.
  2. ShaderEffect appears to be the only option, considering what you said in the comments that the frame shape could be anything.
    The code examples below show how to solve your issue with ShaderEffect.

    QML code

    The only property on the QML side is the rect, which defines the x, y, width, and height of the frame, which are scaled down to between 0 and 1.

    Image { id: img;  visible: false; source: "2.jpg" }
    Image { id: frme; visible: false; source: "3.jpg" }
    Image { id: back; visible: false; source: "1.jpg" }
    
    ShaderEffect {
        width: img.sourceSize.width / 3.5
        height: img.sourceSize.height / 3.5
        property var back: back
        property var image: img
        property var frame: frme
    
        property vector4d rect: Qt.vector4d(width/2-50, height/2-60, 100, 120);
        readonly property vector4d frect: Qt.vector4d(rect.x/width,rect.y/height,
                                                      rect.z/width,rect.w/height);
        fragmentShader: "qrc:/shader.glsl"
    }
    

    shader.glsl

    I discovered that the saturation inside the image is very different from other areas after using a color picker in different points of the frame image.

    So, in order to decide where to mask in the image, I used saturation.

    uniform highp sampler2D back;
    uniform highp sampler2D image;
    uniform highp sampler2D frame;
    varying highp vec2 qt_TexCoord0;
    uniform highp vec4 frect;
    uniform highp float qt_Opacity;
    
    // From https://gist.github.com/983/e170a24ae8eba2cd174f
    vec3 rgb2hsv(vec3 c) {
        vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
        vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
        vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
        float d = q.x - min(q.w, q.y);
        float e = 1.0e-10;
        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
    }
    
    void main() {
        vec2 u = qt_TexCoord0;
        vec2 frameCoord = (u - frect.xy) / frect.zw ;
    
        gl_FragColor = texture2D(back, u);
    
        if(frameCoord.x>0. && frameCoord.y>0. && frameCoord.x<1. && frameCoord.y<1.) {
            vec4 mask = texture2D(frame, frameCoord);
            vec3 hsv = rgb2hsv(mask.xyz);
    
            gl_FragColor = mask;
            // Check that the saturation is between 0 and 0.2.
            if(abs(hsv.y - 0.1) < 0.1) {
                gl_FragColor = texture2D(image, u);
            }
        }
    }
    

    Note

    You can also change the last line of code if you want the frame’s shadow to cover your image.

    gl_FragColor = mix(texture2D(image, u), mask, 1. - hsv.z);
    

    Result

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search