Blend.qml 17 KB

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