Custom Blend Mode


#1

I need to implement custom BlendMode such as Color Burn. So I need get destination FrameBuffer or DrawContext of Element.Parent but how? Or use another approach with same context like this?:

In OnRender:

  1. draw this.Element with opacity = 0;
  2. Capture framebuffer of this.Element (this actually our destination color target);
  3. draw Element with opacity = 1;
  4. Capture framebuffer of this.Element again (this actually our source color target);
  5. finally draw with PixelColor: BlendEquation(sample(source.ColorBuffer, ...), sample(destination.ColorBuffer, ...))

PS How about GL_EXT_shader_framebuffer_fetch which use by Apple?


#2

Hey @maxgraey, did you ever get blend modes working? :slight_smile:


#3

I started some work in this direction. But I think this pretty inefficient. Anyway I’m not working with fuse framework anymore. So if this helps you I share my code for you:

using Uno;
using Uno.IO;
using Uno.UX;
using Uno.Graphics;
using Uno.Collections;
using Fuse.Elements;
using Fuse.Drawing;
using Fuse.Resources;
using Fuse.Drawing.Primitives;

namespace Fuse.Effects.MG {
	public enum BlendingMode {
        // Basic
		Normal = 0,
        Multiply,
        Screen,

        // Extended
        Overlay,
        Darken,
        Lighten,
        ColorDodge,
        ColorBurn,
        SoftLight,
        HardLight,
        Difference,
        Exclusion,
        Hue,
        Saturation,
        Color,
        Luminosity,

        // Porter/Duff Compositing
        Clear,
        Copy,   // Same as Normal
        Source, // Same as Normal
        SourceIn,
        SourceOut,
        SourceAtop,
        Destination, // Do nothing
        DestinationOver,
        DestinationIn,
        DestinationOut,
        DestinationAtop,
        Xor,

        // Misc
        PlusDarker,
        PlusLighter
	}

	public class BlendEffect : BasicEffect {
        BlendingMode mBlendingMode;
		public BlendingMode Mode {
			get { return mBlendingMode; }
			set {
				if (mBlendingMode != value) {
					mBlendingMode = value;
                    OnRenderingChanged();
				}
			}
		}

		public BlendEffect() : base(EffectType.Composition) {
			mBlendingMode = BlendingMode.Normal;
		}

        public static float4 NormalBlend(float4 source, float4 dest) {
            return (source + dest) * 0.5f;
        }

        public static float4 MultiplyBlend(float4 source, float4 dest) {
            return source * dest;
        }

        protected override void OnRooted() {
            base.OnRooted();

            debug_log Element.Parent;
        }

        protected override void OnRender(DrawContext context, Rect rect) {

            // Clip our source Element's rectangle by viewport rectangle for better performance
            // Affect on Scroller's content and elements outside of viewport bound
			var viewportRect = new Rect(float2(0f), context.Viewport.Size);
			var actualSourceRect = Rect.Intersect(rect, viewportRect);
            //var actualDestRect   = Rect.Intersect(rect, viewportRect);

            Element.Opacity = 0f;
            Element.InvalidateVisual();
            //context.Flush();
            var destFramebuffer = Element.CaptureRegion(context, actualSourceRect, int2(0));

            Element.Opacity = 1f;
			var sourceFramebuffer = Element.CaptureRegion(context, actualSourceRect, int2(0));

            debug_log rect;


            if (sourceFramebuffer == null || destFramebuffer == null)
				return;

            var DestTexture = destFramebuffer.ColorBuffer;

            draw Fuse.Drawing.Planar.Image {
                DrawContext: context;
                Node: Element;
                Position: actualSourceRect.Position;
                Size: actualSourceRect.Size;
                DepthTestEnabled: false;
                Invert: true;
                Texture: sourceFramebuffer.ColorBuffer;
                TextureColor: prev TextureColor;

                PixelColor: NormalBlend(TextureColor, sample(DestTexture, TexCoord.XY, SamplerState.LinearClamp));
            };

            FramebufferPool.Release(destFramebuffer);
            FramebufferPool.Release(sourceFramebuffer);
        }
    }
}

#4

Sorry to hear that man, thanks for sharing