Blend.qml 19 KB

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