Jump to content

Additional blending modes


ReMake

Recommended Posts

It is very difficult to find the information about algorithms of blend modes in the Internet.  pegtop.net and the blog by Andrey Zhuravlev in LiveJournal - two sites where I found the most detailed and the helpful information. Below is the fragments of the code for additional blend modes for paint.net written on the basis of the analysis of information from the websites shown above. All modes have been tested on similarity with blend modes of the Photoshop. In most cases results had the full coincidence, but sometimes results had a small difference.

 

Spoiler

case LinearBurn:
    byte LBR = Int32Util.ClampToByte(src[x, y].R + CurrentPixel.R - 255);
    byte LBG = Int32Util.ClampToByte(src[x, y].G + CurrentPixel.G - 255);
    byte LBB = Int32Util.ClampToByte(src[x, y].B + CurrentPixel.B - 255);
    CurrentPixel = ColorBgra.FromBgra(LBB, LBG, LBR, CurrentPixel.A);
    break;

case SoftLight:
    byte SLR = Int32Util.ClampToByte((src[x, y].R * CurrentPixel.R >> 8) + src[x, y].R * (255 - ((255 - src[x, y].R) * (255 - CurrentPixel.R) >> 8) - (src[x, y].R * CurrentPixel.R >> 8)) / 255);
    byte SLG = Int32Util.ClampToByte((src[x, y].G * CurrentPixel.G >> 8) + src[x, y].G * (255 - ((255 - src[x, y].G) * (255 - CurrentPixel.G) >> 8) - (src[x, y].G * CurrentPixel.G >> 8)) / 255);
    byte SLB = Int32Util.ClampToByte((src[x, y].B * CurrentPixel.B >> 8) + src[x, y].B * (255 - ((255 - src[x, y].B) * (255 - CurrentPixel.B) >> 8) - (src[x, y].B * CurrentPixel.B >> 8)) / 255);
    CurrentPixel = ColorBgra.FromBgra(SLB, SLG, SLR, CurrentPixel.A);
    break;

case HardLight:
    byte HLR = (CurrentPixel.R <= 128) ? Int32Util.ClampToByte(src[x, y].R * CurrentPixel.R >> 7) : Int32Util.ClampToByte(255 - ((255 - src[x, y].R) * (255 - CurrentPixel.R) >> 7));
    byte HLG = (CurrentPixel.G <= 128) ? Int32Util.ClampToByte(src[x, y].G * CurrentPixel.G >> 7) : Int32Util.ClampToByte(255 - ((255 - src[x, y].G) * (255 - CurrentPixel.G) >> 7));
    byte HLB = (CurrentPixel.B <= 128) ? Int32Util.ClampToByte(src[x, y].B * CurrentPixel.B >> 7) : Int32Util.ClampToByte(255 - ((255 - src[x, y].B) * (255 - CurrentPixel.B) >> 7));
    CurrentPixel = ColorBgra.FromBgra(HLB, HLG, HLR, CurrentPixel.A);
    break;

case VividLight:
    byte VLR, VLG, VLB;
    if (CurrentPixel.R <= 128)
    {
        VLR = ((src[x, y].R + 2 * CurrentPixel.R) >= 255) ? Int32Util.ClampToByte(((src[x, y].R - (255 - 2 * CurrentPixel.R)) * 255) / (2 * CurrentPixel.R + 1)) : (byte)(0);
    }
    else
    {
        VLR = ((src[x, y].R + 2 * (CurrentPixel.R - 128)) <= 255) ? Int32Util.ClampToByte((255 * src[x, y].R) / (255 - 2 * (CurrentPixel.R - 128))) : (byte)(255);
    }

    if (CurrentPixel.G <= 128)
    {
        VLG = ((src[x, y].G + 2 * CurrentPixel.G) >= 255) ? Int32Util.ClampToByte(((src[x, y].G - (255 - 2 * CurrentPixel.G)) * 255) / (2 * CurrentPixel.G + 1)) : (byte)(0);
    }
    else
    {
        VLG = ((src[x, y].G + 2 * (CurrentPixel.G - 128)) <= 255) ? Int32Util.ClampToByte((255 * src[x, y].G) / (255 - 2 * (CurrentPixel.G - 128))) : (byte)(255);
    }

    if (CurrentPixel.B <= 128)
    {
        VLB = ((src[x, y].B + 2 * CurrentPixel.B) >= 255) ? Int32Util.ClampToByte(((src[x, y].B - (255 - 2 * CurrentPixel.B)) * 255) / (2 * CurrentPixel.B + 1)) : (byte)(0);
    }
    else
    {
        VLB = ((src[x, y].B + 2 * (CurrentPixel.B - 128)) <= 255) ? Int32Util.ClampToByte((255 * src[x, y].B) / (255 - 2 * (CurrentPixel.B - 128))) : (byte)(255);
    }
    CurrentPixel = ColorBgra.FromBgra(VLB, VLG, VLR, CurrentPixel.A);
    break;

case LinearLight:
    byte LLR = Int32Util.ClampToByte(src[x, y].R + 2 * CurrentPixel.R - 255);
    byte LLG = Int32Util.ClampToByte(src[x, y].G + 2 * CurrentPixel.G - 255);
    byte LLB = Int32Util.ClampToByte(src[x, y].B + 2 * CurrentPixel.B - 255);
    CurrentPixel = ColorBgra.FromBgra(LLB, LLG, LLR, CurrentPixel.A);
    break;

case PinLight:
    byte PLR, PLG, PLB;
    if (CurrentPixel.R < 128)
    {
        PLR = (src[x, y].R <= (2 * CurrentPixel.R)) ? src[x, y].R : Int32Util.ClampToByte(2 * CurrentPixel.R);
    }
    else
    {
        PLR = (src[x, y].R >= (2 * (CurrentPixel.R - 128))) ? src[x, y].R : Int32Util.ClampToByte(2 * (CurrentPixel.R - 128));
    }
    if (CurrentPixel.G < 128)
    {
        PLG = (src[x, y].G <= (2 * CurrentPixel.G)) ? src[x, y].G : Int32Util.ClampToByte(2 * CurrentPixel.G);
    }
    else
    {
        PLG = (src[x, y].G >= (2 * (CurrentPixel.G - 128))) ? src[x, y].G : Int32Util.ClampToByte(2 * (CurrentPixel.G - 128));
    }
    if (CurrentPixel.B < 128)
    {
        PLB = (src[x, y].B <= (2 * CurrentPixel.B)) ? src[x, y].B : Int32Util.ClampToByte(2 * CurrentPixel.B);
    }
    else
    {
        PLB = (src[x, y].B >= (2 * (CurrentPixel.B - 128))) ? src[x, y].B : Int32Util.ClampToByte(2 * (CurrentPixel.B - 128));
    }
    CurrentPixel = ColorBgra.FromBgra(PLB, PLG, PLR, CurrentPixel.A);
    break;

case HardMix:
    byte HMR = Int32Util.ClampToByte((CurrentPixel.R < 255 - src[x, y].R) ? 0 : 255);
    byte HMG = Int32Util.ClampToByte((CurrentPixel.G < 255 - src[x, y].G) ? 0 : 255);
    byte HMB = Int32Util.ClampToByte((CurrentPixel.B < 255 - src[x, y].B) ? 0 : 255);
    CurrentPixel = ColorBgra.FromBgra(HMB, HMG, HMR, CurrentPixel.A);
    break;

case Exclusion:
    byte ER = Int32Util.ClampToByte(src[x, y].R + CurrentPixel.R - (src[x, y].R * CurrentPixel.R >> 7));
    byte EG = Int32Util.ClampToByte(src[x, y].G + CurrentPixel.G - (src[x, y].G * CurrentPixel.G >> 7));
    byte EB = Int32Util.ClampToByte(src[x, y].B + CurrentPixel.B - (src[x, y].B * CurrentPixel.B >> 7));
    CurrentPixel = ColorBgra.FromBgra(EB, EG, ER, CurrentPixel.A);
    break;

case Subtract:
    byte SR = Int32Util.ClampToByte(src[x, y].R - CurrentPixel.R);
    byte SG = Int32Util.ClampToByte(src[x, y].G - CurrentPixel.G);
    byte SB = Int32Util.ClampToByte(src[x, y].B - CurrentPixel.B);
    CurrentPixel = ColorBgra.FromBgra(SB, SG, SR, CurrentPixel.A);
    break;

case Divide: // not full compliance to the PS's mode
    byte DR = (CurrentPixel.R != 0) ? Int32Util.ClampToByte(src[x, y].R * 255 / CurrentPixel.R) : (byte)(0);
    byte DG = (CurrentPixel.G != 0) ? Int32Util.ClampToByte(src[x, y].G * 255 / CurrentPixel.G) : (byte)(0);
    byte DB = (CurrentPixel.B != 0) ? Int32Util.ClampToByte(src[x, y].B * 255 / CurrentPixel.B) : (byte)(0);
    CurrentPixel = ColorBgra.FromBgra(DB, DG, DR, CurrentPixel.A);
    break;

 

 

Perhaps these codes are not perfect, and someone will be able to improve them. It would be very nice and helpful.

 

P.S. Perhaps when the algorithms will be brought to perfection, Rick will include these additional modes into paint.net (although this is a great piece of work).

Edited by toe_head2001
Fixed syntax highlighting
  • Like 3
  • Upvote 5
Link to comment
Share on other sites

21 hours ago, TrevorOutlaw said:

Thank you for this.  I was able to create my own effect where I used Gaussian Blur, the default PDN blending options, and your algorithms through CodeLab.

 

BlurBlend.zip

 

Trevor, thank you for sharing! I gave this a test run last night and I'm quite impressed.

  • Upvote 1

1496930299_DraconicSig.png.8e49d04e5ce393bdcdf9f0dfb54bf1c8.png

Link to comment
Share on other sites

@ReMake

 

I remember I was digging online and looking for the math behind the layer blending modes and found that Microsoft have their own versions as well.  Is it worth taking a look?

 

https://msdn.microsoft.com/en-us/library/windows/desktop/hh706313(v=vs.85).aspx

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...