1/**************************************************************************** 2** 3** Copyright (C) 2017 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the Qt Graphical Effects module. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40import QtQuick 2.12 41import QtGraphicalEffects.private 1.12 42 43/*! 44 \qmltype Blend 45 \inqmlmodule QtGraphicalEffects 46 \since QtGraphicalEffects 1.0 47 \inherits QtQuick2::Item 48 \ingroup qtgraphicaleffects-blend 49 \brief Merges two source items by using a blend mode. 50 51 Blend mode can be selected with the \l{Blend::mode}{mode} property. 52 53 \table 54 \header 55 \li source 56 \li foregroundSource 57 \li Effect applied 58 \row 59 \li \image Original_bug.png 60 \li \image Original_butterfly.png 61 \li \image Blend_bug_and_butterfly.png 62 \endtable 63 64 \note This effect is available when running with OpenGL. 65 66 \section1 Example 67 68 The following example shows how to apply the effect. 69 \snippet Blend-example.qml example 70 71*/ 72 73Item { 74 id: rootItem 75 76 /*! 77 This property defines the source item that is going to be the base when 78 \l{Blend::foregroundSource}{foregroundSource} is blended over it. 79 80 \note It is not supported to let the effect include itself, for 81 instance by setting source to the effect's parent. 82 */ 83 property variant source 84 85 /*! 86 This property defines the item that is going to be blended over the 87 \l{Blend::source}{source}. 88 89 \note It is not supported to let the effect include itself, for 90 instance by setting foregroundSource to the effect's parent. 91 */ 92 property variant foregroundSource 93 94 /*! 95 This property defines the mode which is used when foregroundSource is 96 blended over source. Values are case insensitive. 97 98 \table 99 \header 100 \li mode 101 \li description 102 \row 103 \li normal 104 \li The pixel component values from foregroundSource are written 105 over source by using alpha blending. 106 \row 107 \li addition 108 \li The pixel component values from source and foregroundSource are 109 added together and written. 110 \row 111 \li average 112 \li The pixel component values from source and foregroundSource are 113 averaged and written. 114 \row 115 \li color 116 \li The lightness value from source is combined with hue and 117 saturation from foregroundSource and written. 118 \row 119 \li colorBurn 120 \li The darker pixels from source are darkened more, if both source 121 and foregroundSource pixels are light the result is light. 122 \row 123 \li colorDodge 124 \li The lighter pixels from source are lightened more, if both 125 source and foregroundSource pixels are dark the result is dark. 126 \row 127 \li darken 128 \li The darker pixel component value from source and 129 foregroundSource is written. 130 \row 131 \li darkerColor 132 \li The lower luminance pixel rgb-value from source and 133 foregroundSource is written. 134 \row 135 \li difference 136 \li The absolute pixel component value difference between source and 137 foregroundSource is written. 138 \row 139 \li divide 140 \li The pixel component values from source is divided by the value 141 from foregroundSource and written. 142 \row 143 \li exclusion 144 \li The pixel component value difference with reduced contrast 145 between source and foregroundSource is written. 146 \row 147 \li hardLight 148 \li The pixel component values from source are lightened or darkened 149 according to foregroundSource values and written. 150 \row 151 \li hue 152 \li The hue value from foregroundSource is combined with saturation 153 and lightness from source and written. 154 \row 155 \li lighten 156 \li The lightest pixel component value from source and 157 foregroundSource is written. 158 \row 159 \li lighterColor 160 \li The higher luminance pixel rgb-value from source and 161 foregroundSource is written. 162 \row 163 \li lightness 164 \li The lightness value from foregroundSource is combined with hue 165 and saturation from source and written. 166 \row 167 \li multiply 168 \li The pixel component values from source and foregroundSource are 169 multiplied together and written. 170 \row 171 \li negation 172 \li The inverted absolute pixel component value difference between 173 source and foregroundSource is written. 174 \row 175 \li saturation 176 \li The saturation value from foregroundSource is combined with hue 177 and lightness from source and written. 178 \row 179 \li screen 180 \li The pixel values from source and foregroundSource are negated, 181 then multiplied, negated again, and written. 182 \row 183 \li subtract 184 \li Pixel value from foregroundSource is subracted from source and 185 written. 186 \row 187 \li softLight 188 \li The pixel component values from source are lightened or darkened 189 slightly according to foregroundSource values and written. 190 191 \endtable 192 193 \table 194 \header 195 \li Example source 196 \li Example foregroundSource 197 \row 198 \li \image Original_bug.png 199 \li \image Original_butterfly.png 200 \endtable 201 202 \table 203 \header 204 \li Output examples with different mode values 205 \li 206 \li 207 \row 208 \li \image Blend_mode1.png 209 \li \image Blend_mode2.png 210 \li \image Blend_mode3.png 211 \row 212 \li \b { mode: normal } 213 \li \b { mode: addition } 214 \li \b { mode: average } 215 \row 216 \li \image Blend_mode4.png 217 \li \image Blend_mode5.png 218 \li \image Blend_mode6.png 219 \row 220 \li \b { mode: color } 221 \li \b { mode: colorBurn } 222 \li \b { mode: colorDodge } 223 \row 224 \li \image Blend_mode7.png 225 \li \image Blend_mode8.png 226 \li \image Blend_mode9.png 227 \row 228 \li \b { mode: darken } 229 \li \b { mode: darkerColor } 230 \li \b { mode: difference } 231 \row 232 \li \image Blend_mode10.png 233 \li \image Blend_mode11.png 234 \li \image Blend_mode12.png 235 \row 236 \li \b { mode: divide } 237 \li \b { mode: exclusion } 238 \li \b { mode: hardlight } 239 \row 240 \li \image Blend_mode13.png 241 \li \image Blend_mode14.png 242 \li \image Blend_mode15.png 243 \row 244 \li \b { mode: hue } 245 \li \b { mode: lighten } 246 \li \b { mode: lighterColor } 247 \row 248 \li \image Blend_mode16.png 249 \li \image Blend_mode17.png 250 \li \image Blend_mode18.png 251 \row 252 \li \b { mode: lightness } 253 \li \b { mode: negation } 254 \li \b { mode: multiply } 255 \row 256 \li \image Blend_mode19.png 257 \li \image Blend_mode20.png 258 \li \image Blend_mode21.png 259 \row 260 \li \b { mode: saturation } 261 \li \b { mode: screen } 262 \li \b { mode: subtract } 263 \row 264 \li \image Blend_mode22.png 265 \row 266 \li \b { mode: softLight } 267 \endtable 268 */ 269 property string mode: "normal" 270 271 /*! 272 This property allows the effect output pixels to be cached in order to 273 improve the rendering performance. 274 275 Every time the source or effect properties are changed, the pixels in the 276 cache must be updated. Memory consumption is increased, because an extra 277 buffer of memory is required for storing the effect output. 278 279 It is recommended to disable the cache when the source or the effect 280 properties are animated. 281 282 By default, the property is set to false. 283 284 */ 285 property bool cached: false 286 287 SourceProxy { 288 id: backgroundSourceProxy 289 input: rootItem.source 290 } 291 292 SourceProxy { 293 id: foregroundSourceProxy 294 input: rootItem.foregroundSource 295 } 296 297 ShaderEffectSource { 298 id: cacheItem 299 anchors.fill: parent 300 visible: rootItem.cached 301 smooth: true 302 sourceItem: shaderItem 303 live: true 304 hideSource: visible 305 } 306 307 ShaderEffect { 308 id: shaderItem 309 property variant backgroundSource: backgroundSourceProxy.output 310 property variant foregroundSource: foregroundSourceProxy.output 311 property string mode: rootItem.mode 312 anchors.fill: parent 313 314 fragmentShader: fragmentShaderBegin + blendModeNormal + fragmentShaderEnd 315 316 function buildFragmentShader() { 317 var shader = fragmentShaderBegin 318 319 switch (mode.toLowerCase()) { 320 case "addition" : shader += blendModeAddition; break; 321 case "average" : shader += blendModeAverage; break; 322 case "color" : shader += blendModeColor; break; 323 case "colorburn" : shader += blendModeColorBurn; break; 324 case "colordodge" : shader += blendModeColorDodge; break; 325 case "darken" : shader += blendModeDarken; break; 326 case "darkercolor" : shader += blendModeDarkerColor; break; 327 case "difference" : shader += blendModeDifference; break; 328 case "divide" : shader += blendModeDivide; break; 329 case "exclusion" : shader += blendModeExclusion; break; 330 case "hardlight" : shader += blendModeHardLight; break; 331 case "hue" : shader += blendModeHue; break; 332 case "lighten" : shader += blendModeLighten; break; 333 case "lightercolor" : shader += blendModeLighterColor; break; 334 case "lightness" : shader += blendModeLightness; break; 335 case "negation" : shader += blendModeNegation; break; 336 case "normal" : shader += blendModeNormal; break; 337 case "multiply" : shader += blendModeMultiply; break; 338 case "saturation" : shader += blendModeSaturation; break; 339 case "screen" : shader += blendModeScreen; break; 340 case "subtract" : shader += blendModeSubtract; break; 341 case "softlight" : shader += blendModeSoftLight; break; 342 default: shader += "gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);"; break; 343 } 344 345 shader += fragmentShaderEnd 346 fragmentShader = shader 347 348 // Workaraound for a bug just to make sure display gets updated when the mode changes. 349 backgroundSourceChanged() 350 } 351 352 Component.onCompleted: { 353 buildFragmentShader() 354 } 355 356 onModeChanged: { 357 buildFragmentShader() 358 } 359 360 property string blendModeAddition: "result.rgb = min(rgb1 + rgb2, 1.0);" 361 property string blendModeAverage: "result.rgb = 0.5 * (rgb1 + rgb2);" 362 property string blendModeColor: "result.rgb = HSLtoRGB(vec3(RGBtoHSL(rgb2).xy, RGBtoL(rgb1)));" 363 property string blendModeColorBurn: "result.rgb = clamp(1.0 - ((1.0 - rgb1) / max(vec3(1.0 / 256.0), rgb2)), vec3(0.0), vec3(1.0));" 364 property string blendModeColorDodge: "result.rgb = clamp(rgb1 / max(vec3(1.0 / 256.0), (1.0 - rgb2)), vec3(0.0), vec3(1.0));" 365 property string blendModeDarken: "result.rgb = min(rgb1, rgb2);" 366 property string blendModeDarkerColor: "result.rgb = 0.3 * rgb1.r + 0.59 * rgb1.g + 0.11 * rgb1.b > 0.3 * rgb2.r + 0.59 * rgb2.g + 0.11 * rgb2.b ? rgb2 : rgb1;" 367 property string blendModeDifference: "result.rgb = abs(rgb1 - rgb2);" 368 property string blendModeDivide: "result.rgb = clamp(rgb1 / rgb2, 0.0, 1.0);" 369 property string blendModeExclusion: "result.rgb = rgb1 + rgb2 - 2.0 * rgb1 * rgb2;" 370 property string blendModeHardLight: "result.rgb = vec3(channelBlendHardLight(rgb1.r, rgb2.r), channelBlendHardLight(rgb1.g, rgb2.g), channelBlendHardLight(rgb1.b, rgb2.b));" 371 property string blendModeHue: "result.rgb = HSLtoRGB(vec3(RGBtoHSL(rgb2).x, RGBtoHSL(rgb1).yz));" 372 property string blendModeLighten: "result.rgb = max(rgb1, rgb2);" 373 property string blendModeLighterColor: "result.rgb = 0.3 * rgb1.r + 0.59 * rgb1.g + 0.11 * rgb1.b > 0.3 * rgb2.r + 0.59 * rgb2.g + 0.11 * rgb2.b ? rgb1 : rgb2;" 374 property string blendModeLightness: "result.rgb = HSLtoRGB(vec3(RGBtoHSL(rgb1).xy, RGBtoL(rgb2)));" 375 property string blendModeMultiply: "result.rgb = rgb1 * rgb2;" 376 property string blendModeNegation: "result.rgb = 1.0 - abs(1.0 - rgb1 - rgb2);" 377 property string blendModeNormal: "result.rgb = rgb2; a = max(color1.a, color2.a);" 378 property string blendModeSaturation: "lowp vec3 hsl1 = RGBtoHSL(rgb1); result.rgb = HSLtoRGB(vec3(hsl1.x, RGBtoHSL(rgb2).y, hsl1.z));" 379 property string blendModeScreen: "result.rgb = 1.0 - (vec3(1.0) - rgb1) * (vec3(1.0) - rgb2);" 380 property string blendModeSubtract: "result.rgb = max(rgb1 - rgb2, vec3(0.0));" 381 property string blendModeSoftLight: "result.rgb = rgb1 * ((1.0 - rgb1) * rgb2 + (1.0 - (1.0 - rgb1) * (1.0 - rgb2)));" 382 383 property string fragmentCoreShaderWorkaround: (GraphicsInfo.profile === GraphicsInfo.OpenGLCoreProfile ? "#version 150 core 384 #define varying in 385 #define texture2D texture 386 out vec4 fragColor; 387 #define gl_FragColor fragColor 388 " : "") 389 390 property string fragmentShaderBegin: fragmentCoreShaderWorkaround + " 391 varying mediump vec2 qt_TexCoord0; 392 uniform highp float qt_Opacity; 393 uniform lowp sampler2D backgroundSource; 394 uniform lowp sampler2D foregroundSource; 395 396 highp float RGBtoL(highp vec3 color) { 397 highp float cmin = min(color.r, min(color.g, color.b)); 398 highp float cmax = max(color.r, max(color.g, color.b)); 399 highp float l = (cmin + cmax) / 2.0; 400 return l; 401 } 402 403 highp vec3 RGBtoHSL(highp vec3 color) { 404 highp float cmin = min(color.r, min(color.g, color.b)); 405 highp float cmax = max(color.r, max(color.g, color.b)); 406 highp float h = 0.0; 407 highp float s = 0.0; 408 highp float l = (cmin + cmax) / 2.0; 409 highp float diff = cmax - cmin; 410 411 if (diff > 1.0 / 256.0) { 412 if (l < 0.5) 413 s = diff / (cmin + cmax); 414 else 415 s = diff / (2.0 - (cmin + cmax)); 416 417 if (color.r == cmax) 418 h = (color.g - color.b) / diff; 419 else if (color.g == cmax) 420 h = 2.0 + (color.b - color.r) / diff; 421 else 422 h = 4.0 + (color.r - color.g) / diff; 423 424 h /= 6.0; 425 } 426 return vec3(h, s, l); 427 } 428 429 highp float hueToIntensity(highp float v1, highp float v2, highp float h) { 430 h = fract(h); 431 if (h < 1.0 / 6.0) 432 return v1 + (v2 - v1) * 6.0 * h; 433 else if (h < 1.0 / 2.0) 434 return v2; 435 else if (h < 2.0 / 3.0) 436 return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h); 437 438 return v1; 439 } 440 441 highp vec3 HSLtoRGB(highp vec3 color) { 442 highp float h = color.x; 443 highp float l = color.z; 444 highp float s = color.y; 445 446 if (s < 1.0 / 256.0) 447 return vec3(l, l, l); 448 449 highp float v1; 450 highp float v2; 451 if (l < 0.5) 452 v2 = l * (1.0 + s); 453 else 454 v2 = (l + s) - (s * l); 455 456 v1 = 2.0 * l - v2; 457 458 highp float d = 1.0 / 3.0; 459 highp float r = hueToIntensity(v1, v2, h + d); 460 highp float g = hueToIntensity(v1, v2, h); 461 highp float b = hueToIntensity(v1, v2, h - d); 462 return vec3(r, g, b); 463 } 464 465 lowp float channelBlendHardLight(lowp float c1, lowp float c2) { 466 return c2 > 0.5 ? (1.0 - (1.0 - 2.0 * (c2 - 0.5)) * (1.0 - c1)) : (2.0 * c1 * c2); 467 } 468 469 void main() { 470 lowp vec4 result = vec4(0.0); 471 lowp vec4 color1 = texture2D(backgroundSource, qt_TexCoord0); 472 lowp vec4 color2 = texture2D(foregroundSource, qt_TexCoord0); 473 lowp vec3 rgb1 = color1.rgb / max(1.0/256.0, color1.a); 474 lowp vec3 rgb2 = color2.rgb / max(1.0/256.0, color2.a); 475 highp float a = max(color1.a, color1.a * color2.a); 476 " 477 478 property string fragmentShaderEnd: " 479 gl_FragColor.rgb = mix(rgb1, result.rgb, color2.a); 480 gl_FragColor.rbg *= a; 481 gl_FragColor.a = a; 482 gl_FragColor *= qt_Opacity; 483 } 484 " 485 } 486} 487