// Copyright 2013 - Felix Obermaier (www.ivv-aachen.de)
//
// This file is part of SharpMap.Layers.HeatLayer.
// SharpMap.Layers.HeatLayer is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// SharpMap.Layers.HeatLayer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ***********************************************************************************************
//
// Original idea by Konstantin Vasserman, published on
// http://www.codeproject.com/Articles/5527/Blending-of-images-raster-operations-and-basic-col
//
// ***********************************************************************************************
//
// Modifications
// - ReSharper Renaming
// - removed unsafe constructs
// - removed cloning of bitmaps in
// - PerChannelProcess
// - RgbProcess
// - Changed argument type from Image to Bitmap
//
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace SharpMap.Layers
{
///
/// Class for image blending operations.
///
///
///
/// ToDo This class is neat, but overkill:
/// Only is used along with the
///
/// function
internal static class ImageBlender
{
///NTSC defined color weights
private const float NtscRedWeight = 0.299f;
private const float NtscGreenWeight = 0.587f;
private const float NtscBlueWeight = 0.144f;
private const ushort HlsMax = 360;
private const byte RgbMax = 255;
private const byte HueUndefined = 0;
private delegate byte PerChannelProcessDelegate(ref byte nSrc, ref byte nDst);
private delegate void RgbProcessDelegate(byte sR, byte sG, byte sB, ref byte dR, ref byte dG, ref byte dB);
///
/// Method to invert an image
///
/// The image to invert
public static void Invert(Image img)
{
if (img == null)
throw new Exception("Image must be provided");
var cMatrix = new ColorMatrix(new[]
{
new[] {-1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
new[] { 0.0f,-1.0f, 0.0f, 0.0f, 0.0f },
new[] { 0.0f, 0.0f,-1.0f, 0.0f, 0.0f },
new[] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f },
new[] { 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }
});
ApplyColorMatrix(img, cMatrix);
}
///
/// Method to adjust an image's brightness
///
/// The image
/// Adjustment value for the red channel [-1f, 1f]
/// Adjustment value for the green channel [-1f, 1f]
/// Adjustment value for the blue channel [-1f, 1f]
public static void AdjustBrightness(Image img, float adjValueR, float adjValueG, float adjValueB)
{
if (img == null)
throw new Exception("Image must be provided");
var cMatrix = new ColorMatrix(new[]
{
new[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
new[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f },
new[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
new[] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f },
new[] { adjValueR, adjValueG, adjValueB, 0.0f, 1.0f }
});
ApplyColorMatrix(img, cMatrix);
}
///
/// Method to adjust an image's brightness
///
/// The image
/// Adjustment value for all channels [-1f, 1f]
public static void AdjustBrightness(Image img, float adjValue)
{
AdjustBrightness(img, adjValue, adjValue, adjValue);
}
// Saturation. 0.0 = desaturate, 1.0 = identity, -1.0 = complementary colors
public static void AdjustSaturation(Image img, float sat, float rweight, float gweight, float bweight)
{
if (img == null)
throw new Exception("Image must be provided");
var cMatrix = new ColorMatrix(new[] {
new [] { (1.0f-sat)*rweight+sat, (1.0f-sat)*rweight, (1.0f-sat)*rweight, 0.0f, 0.0f },
new [] { (1.0f-sat)*gweight, (1.0f-sat)*gweight+sat, (1.0f-sat)*gweight, 0.0f, 0.0f },
new [] { (1.0f-sat)*bweight, (1.0f-sat)*bweight, (1.0f-sat)*bweight+sat, 0.0f, 0.0f },
new [] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f },
new [] { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }
});
ApplyColorMatrix(img, cMatrix);
}
// Saturation. 0.0 = desaturate, 1.0 = identity, -1.0 = complementary colors
public static void AdjustSaturation(Image img, float sat)
{
AdjustSaturation(img, sat, NtscRedWeight, NtscGreenWeight, NtscBlueWeight);
}
// Weights between 0.0 and 1.0
public static void Desaturate(Image img, float redWeight, float greenWeight, float blueWeight)
{
AdjustSaturation(img, 0.0f, redWeight, greenWeight, blueWeight);
}
// Desaturate using "default" NTSC defined color weights
public static void Desaturate(Image img)
{
AdjustSaturation(img, 0.0f, NtscRedWeight, NtscGreenWeight, NtscBlueWeight);
}
public static void ApplyColorMatrix(Image img, ColorMatrix colMatrix)
{
using (var gr = Graphics.FromImage(img))
{
var attrs = new ImageAttributes();
attrs.SetColorMatrix(colMatrix);
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs);
}
}
#region BlendImages functions ...
/*
destImage - image that will be used as background
destX, destY - define position on destination image where to start applying blend operation
destWidth, destHeight - width and height of the area to apply blending
srcImage - image to use as foreground (source of blending)
srcX, srcY - starting position of the source image
*/
public static void BlendImages(Bitmap destImage, int destX, int destY, int destWidth, int destHeight,
Bitmap srcImage, int srcX, int srcY, BlendOperation blendOp)
{
if (destImage == null)
throw new Exception("Destination image must be provided");
if (destImage.Width < destX + destWidth || destImage.Height < destY + destHeight)
throw new Exception("Destination image is smaller than requested dimensions");
if (srcImage == null)
throw new Exception("Source image must be provided");
if (srcImage.Width < srcX + destWidth || srcImage.Height < srcY + destHeight)
throw new Exception("Source image is smaller than requested dimentions");
Bitmap tempBmp = null;
using (var gr = Graphics.FromImage(destImage))
{
gr.CompositingMode = CompositingMode.SourceCopy;
switch (blendOp)
{
case BlendOperation.SourceCopy:
gr.DrawImage(srcImage, new Rectangle(destX, destY, destWidth, destHeight),
srcX, srcY, destWidth, destHeight, GraphicsUnit.Pixel);
break;
case BlendOperation.RopMergePaint:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, MergePaint);
break;
case BlendOperation.RopNotSourceErase:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, NotSourceErase);
break;
case BlendOperation.RopSourceAnd:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, SourceAnd);
break;
case BlendOperation.RopSourceErase:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, SourceErase);
break;
case BlendOperation.RopSourceInvert:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, SourceInvert);
break;
case BlendOperation.RopSourcePaint:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, SourcePaint);
break;
case BlendOperation.BlendDarken:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendDarken);
break;
case BlendOperation.BlendMultiply:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendMultiply);
break;
case BlendOperation.BlendScreen:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendScreen);
break;
case BlendOperation.BlendLighten:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendLighten);
break;
case BlendOperation.BlendHardLight:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendHardLight);
break;
case BlendOperation.BlendDifference:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendDifference);
break;
case BlendOperation.BlendPinLight:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendPinLight);
break;
case BlendOperation.BlendOverlay:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendOverlay);
break;
case BlendOperation.BlendExclusion:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendExclusion);
break;
case BlendOperation.BlendSoftLight:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendSoftLight);
break;
case BlendOperation.BlendColorBurn:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendColorBurn);
break;
case BlendOperation.BlendColorDodge:
tempBmp = PerChannelProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendColorDodge);
break;
case BlendOperation.BlendHue:
tempBmp = RgbProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendHue);
break;
case BlendOperation.BlendSaturation:
tempBmp = RgbProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendSaturation);
break;
case BlendOperation.BlendColor:
tempBmp = RgbProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendColor);
break;
case BlendOperation.BlendLuminosity:
tempBmp = RgbProcess(destImage, destX, destY, destWidth, destHeight,
srcImage, srcX, srcY, BlendLuminosity);
break;
}
if (tempBmp != null && tempBmp != destImage)
{
gr.DrawImage(tempBmp, 0, 0, tempBmp.Width, tempBmp.Height);
tempBmp.Dispose();
}
}
}
public static void BlendImages(Bitmap destImage, Bitmap srcImage, BlendOperation blendOp)
{
BlendImages(destImage, 0, 0, destImage.Width, destImage.Height, srcImage, 0, 0, blendOp);
}
public static void BlendImages(Bitmap destImage, BlendOperation blendOp)
{
BlendImages(destImage, 0, 0, destImage.Width, destImage.Height, null, 0, 0, blendOp);
}
public static void BlendImages(Bitmap destImage, int destX, int destY, BlendOperation blendOp)
{
BlendImages(destImage, destX, destY, destImage.Width - destX, destImage.Height - destY, null, 0, 0, blendOp);
}
public static void BlendImages(Bitmap destImage, int destX, int destY, int destWidth, int destHeight, BlendOperation blendOp)
{
BlendImages(destImage, destX, destY, destWidth, destHeight, null, 0, 0, blendOp);
}
#endregion BlendImages functions ...
#region Private Blending Functions ...
private static Bitmap PerChannelProcess(Bitmap destImg, int destX, int destY, int destWidth, int destHeight,
Bitmap srcImg, int srcX, int srcY,
PerChannelProcessDelegate channelProcessFunction)
{
var dst = destImg; // new Bitmap(destImg);
var dstData = dst.LockBits(new Rectangle(destX, destY, destWidth, destHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var dstStride = Math.Abs(dstData.Stride);
var dstScan0 = dstData.Scan0;
var dstBuffer = new byte[dstStride];
var src = srcImg; //new Bitmap(srcImg);
var srcData = src.LockBits(new Rectangle(srcX, srcY, destWidth, destHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var srcStride = Math.Abs(srcData.Stride);
var srcScan0 = srcData.Scan0;
var srcBuffer = new byte[srcStride];
for (var y = 0; y < destHeight; y++)
{
System.Runtime.InteropServices.Marshal.Copy(srcScan0 + y * srcStride, srcBuffer, 0, srcStride);
System.Runtime.InteropServices.Marshal.Copy(dstScan0 + y * dstStride, dstBuffer, 0, dstStride);
for (var x = 0; x < 3* destWidth; x++)
{
dstBuffer[x] = channelProcessFunction(ref srcBuffer[x], ref dstBuffer[x]);
}
System.Runtime.InteropServices.Marshal.Copy(dstBuffer, 0, dstScan0 + y * dstStride, dstStride);
}
//unsafe
//{
// byte* pDst = (byte*)(void*)dstScan0;
// byte* pSrc = (byte*)(void*)srcScan0;
// for (int y = 0; y < destHeight; y++)
// {
// for (int x = 0; x < destWidth * 3; x++)
// {
// pDst[x + y * dstStride] = channelProcessFunction(ref pSrc[x + y * srcStride], ref pDst[x + y * dstStride]);
// }
// }
//}
src.UnlockBits(srcData);
dst.UnlockBits(dstData);
return dst;
}
private static Bitmap RgbProcess(Bitmap destImg, int destX, int destY, int destWidth, int destHeight,
Bitmap srcImg, int srcX, int srcY,
RgbProcessDelegate rgbProcessFunction)
{
var dst = destImg; //new Bitmap(destImg);
var dstData = dst.LockBits(new Rectangle(destX, destY, destWidth, destHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var dstStride = Math.Abs(dstData.Stride);
var dstScan0 = dstData.Scan0;
var dstBuffer = new byte[dstStride];
var src = srcImg; //new Bitmap(srcImg);
var srcData = src.LockBits(new Rectangle(srcX, srcY, destWidth, destHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var srcStride = Math.Abs(srcData.Stride);
var srcScan0 = srcData.Scan0;
var srcBuffer = new byte[srcStride];
for (var y = 0; y < destHeight; y++)
{
System.Runtime.InteropServices.Marshal.Copy(srcScan0 + y * srcStride, srcBuffer, 0, srcStride);
System.Runtime.InteropServices.Marshal.Copy(dstScan0 + y * dstStride, dstBuffer, 0, dstStride);
for (var x = 0; x < destWidth; x += 3)
{
rgbProcessFunction(srcBuffer[x + 2], srcBuffer[x + 1], srcBuffer[x],
ref dstBuffer[x + 2], ref dstBuffer[x + 1], ref dstBuffer[x]);
}
System.Runtime.InteropServices.Marshal.Copy(dstBuffer, 0, dstScan0 + y * dstStride, dstStride);
}
//unsafe
//{
// byte* pDst = (byte*)(void*)dstScan0;
// byte* pSrc = (byte*)(void*)srcScan0;
// for (int y = 0; y < destHeight; y++)
// {
// for (int x = 0; x < destWidth; x++)
// {
// rgbProcessFunction(
// pSrc[x * 3 + 2 + y * srcStride], pSrc[x * 3 + 1 + y * srcStride], pSrc[x * 3 + y * srcStride],
// ref pDst[x * 3 + 2 + y * dstStride], ref pDst[x * 3 + 1 + y * dstStride], ref pDst[x * 3 + y * dstStride]
// );
// }
// }
//}
src.UnlockBits(srcData);
dst.UnlockBits(dstData);
return dst;
}
#endregion Private Blending Functions ...
#region HLS Conversion Functions ...
private static void RgbToHls(byte red, byte green, byte blue, out ushort hue, out ushort lightness, out ushort saturation)
{
/* calculate lightness */
var cMax = Math.Max(Math.Max(red, green), blue);
var cMin = Math.Min(Math.Min(red, green), blue);
lightness = (ushort)((((cMax + cMin) * HlsMax) + RgbMax) / (2 * RgbMax));
if (cMax == cMin)
{ /* r=g=b --> achromatic case */
saturation = 0; /* saturation */
hue = HueUndefined; /* hue */
}
else
{ /* chromatic case */
/* saturation */
if (lightness <= (HlsMax / 2))
saturation = (ushort)((((cMax - cMin) * HlsMax) + ((cMax + cMin) / 2)) / (cMax + cMin));
else
saturation = (ushort)((((cMax - cMin) * HlsMax) + ((2 * RgbMax - cMax - cMin) / 2)) / (2 * RgbMax - cMax - cMin));
/* hue */
var redDelta = (((cMax - red) * (HlsMax / 6f)) + ((cMax - cMin) / 2f)) / (cMax - cMin);
var greendelta = (((cMax - green) * (HlsMax / 6f)) + ((cMax - cMin) / 2f)) / (cMax - cMin);
var blueDelta = (((cMax - blue) * (HlsMax / 6f)) + ((cMax - cMin) / 2f)) / (cMax - cMin);
if (red == cMax)
hue = (ushort)(blueDelta - greendelta);
else if (green == cMax)
hue = (ushort)((HlsMax / 3) + redDelta - blueDelta);
else /* B == cMax */
hue = (ushort)(((2 * HlsMax) / 3) + greendelta - redDelta);
//if (hue < 0)
// hue += HlsMax;
if (hue > HlsMax)
hue -= HlsMax;
}
}
private static void HlsToRgb(ushort hue, ushort lightness, ushort saturation, out byte red, out byte green, out byte blue)
{
if (saturation == 0)
{/* achromatic case */
red = green = blue = (byte)((lightness * RgbMax) / HlsMax);
}
else
{/* chromatic case */
/* set up magic numbers */
float magic2; /* calculated magic numbers (really!) */
if (lightness <= (HlsMax / 2))
magic2 = (lightness * (HlsMax + saturation) + (HlsMax / 2f)) / HlsMax;
else
magic2 = lightness + saturation - ((lightness * saturation) + (HlsMax / 2f)) / HlsMax;
var magic1 = 2 * lightness - magic2; /* calculated magic number 1 (really!) */
/* get RGB, change units from HLSMAX to RGBMAX */
red = (byte)((HueToRgb(magic1, magic2, hue + (HlsMax / 3)) * RgbMax + (HlsMax / 2)) / HlsMax);
green = (byte)((HueToRgb(magic1, magic2, hue) * RgbMax + (HlsMax / 2)) / HlsMax);
blue = (byte)((HueToRgb(magic1, magic2, hue - (HlsMax / 3)) * RgbMax + (HlsMax / 2)) / HlsMax);
}
}
/* utility routine for HLStoRGB */
private static float HueToRgb(float n1, float n2, float hue)
{
/* range check: note values passed add/subtract thirds of range */
if (hue < 0)
hue += HlsMax;
if (hue > HlsMax)
hue -= HlsMax;
/* return r,g, or b value from this tridrant */
if (hue < (HlsMax / 6))
return n1 + (((n2 - n1) * hue + (HlsMax / 12)) / (HlsMax / 6));
if (hue < (HlsMax / 2))
return n2;
if (hue < ((HlsMax * 2) / 3))
return n1 + (((n2 - n1) * (((HlsMax * 2) / 3) - hue) + (HlsMax / 12)) / (HlsMax / 6));
return n1;
}
#endregion HLS Conversion Functions ...
#region Raster Operation Functions ...
// (NOT Source) OR Destination
private static byte MergePaint(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min((255 - src) | dst, 255), 0);
}
// NOT (Source OR Destination)
private static byte NotSourceErase(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(255 - (src | dst), 255), 0);
}
// Source AND Destination
private static byte SourceAnd(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(src & dst, 255), 0);
}
// Source AND (NOT Destination)
private static byte SourceErase(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(src & (255 - dst), 255), 0);
}
// Source XOR Destination
private static byte SourceInvert(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(src ^ dst, 255), 0);
}
// Source OR Destination
private static byte SourcePaint(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(src | dst, 255), 0);
}
#endregion Raster Operation Functions ...
#region Blend Pixels Functions ...
// Choose darkest color
private static byte BlendDarken(ref byte src, ref byte dst)
{
return ((src < dst) ? src : dst);
}
// Multiply
private static byte BlendMultiply(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min((src / 255.0f * dst / 255.0f) * 255.0f, 255), 0);
}
// Screen
private static byte BlendScreen(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min(255 - ((255 - src) / 255.0f * (255 - dst) / 255.0f) * 255.0f, 255), 0);
}
// Choose lightest color
private static byte BlendLighten(ref byte src, ref byte dst)
{
return ((src > dst) ? src : dst);
}
// hard light
private static byte BlendHardLight(ref byte src, ref byte dst)
{
return ((src < 128)
? (byte)Math.Max(Math.Min((src / 255.0f * dst / 255.0f) * 255.0f * 2, 255), 0)
: (byte)Math.Max(Math.Min(255 - ((255 - src) / 255.0f * (255 - dst) / 255.0f) * 255.0f * 2, 255), 0));
}
// difference
private static byte BlendDifference(ref byte src, ref byte dst)
{
return (byte)((src > dst) ? src - dst : dst - src);
}
// pin light
private static byte BlendPinLight(ref byte src, ref byte dst)
{
return (src < 128) ? ((dst > src) ? src : dst) : ((dst < src) ? src : dst);
}
// overlay
private static byte BlendOverlay(ref byte src, ref byte dst)
{
return ((dst < 128)
? (byte)Math.Max(Math.Min((src / 255.0f * dst / 255.0f) * 255.0f * 2, 255), 0)
: (byte)Math.Max(Math.Min(255 - ((255 - src) / 255.0f * (255 - dst) / 255.0f) * 255.0f * 2, 255), 0));
}
// exclusion
private static byte BlendExclusion(ref byte src, ref byte dst)
{
return (byte)(src + dst - 2 * (dst * src) / 255f);
}
// Soft Light (XFader formula)
private static byte BlendSoftLight(ref byte src, ref byte dst)
{
return (byte)Math.Max(Math.Min((dst * src / 255f) + dst * (255 - ((255 - dst) * (255 - src) / 255f) - (dst * src / 255f)) / 255f, 255), 0);
}
// Color Burn
private static byte BlendColorBurn(ref byte src, ref byte dst)
{
return (src == 0) ? (byte)0 : (byte)Math.Max(Math.Min(255 - (((255 - dst) * 255) / src), 255), 0);
}
// Color Dodge
private static byte BlendColorDodge(ref byte src, ref byte dst)
{
return (src == 255) ? (byte)255 : (byte)Math.Max(Math.Min((dst * 255) / (255 - src), 255), 0);
}
// use source Hue
private static void BlendHue(byte sR, byte sG, byte sB, ref byte dR, ref byte dG, ref byte dB)
{
ushort sH, sL, sS, dH, dL, dS;
RgbToHls(sR, sG, sB, out sH, out sL, out sS);
RgbToHls(dR, dG, dB, out dH, out dL, out dS);
HlsToRgb(sH, dL, dS, out dR, out dG, out dB);
}
// use source Saturation
private static void BlendSaturation(byte sR, byte sG, byte sB, ref byte dR, ref byte dG, ref byte dB)
{
ushort sH, sL, sS, dH, dL, dS;
RgbToHls(sR, sG, sB, out sH, out sL, out sS);
RgbToHls(dR, dG, dB, out dH, out dL, out dS);
HlsToRgb(dH, dL, sS, out dR, out dG, out dB);
}
// use source Color
private static void BlendColor(byte sR, byte sG, byte sB, ref byte dR, ref byte dG, ref byte dB)
{
ushort sH, sL, sS, dH, dL, dS;
RgbToHls(sR, sG, sB, out sH, out sL, out sS);
RgbToHls(dR, dG, dB, out dH, out dL, out dS);
HlsToRgb(sH, dL, sS, out dR, out dG, out dB);
}
// use source Luminosity
private static void BlendLuminosity(byte sR, byte sG, byte sB, ref byte dR, ref byte dG, ref byte dB)
{
ushort sH, sL, sS, dH, dL, dS;
RgbToHls(sR, sG, sB, out sH, out sL, out sS);
RgbToHls(dR, dG, dB, out dH, out dL, out dS);
HlsToRgb(dH, sL, dS, out dR, out dG, out dB);
}
#endregion Blend Pixels Functions ...
}
}