/// <summary> /// Superimposes one bitmap onto another. /// </summary> /// <param name="backBmpData">BitmapData from the bitmap to be drawn onto.</param> /// <param name="frontBmp">Bitmap to draw from.</param> /// <param name="transparent">Whether or not transparency is desired.</param> /// <param name="point">Point on the back bitmap to copy the front bitmap.</param> public static void Superimpose(BitmapData backBmpData, Bitmap frontBmp, bool transparent, Point point) { //Currently only supports Format24bppRgb and Format32bppArgb. if ((backBmpData.PixelFormat != PixelFormat.Format24bppRgb && backBmpData.PixelFormat != PixelFormat.Format32bppArgb) || (frontBmp.PixelFormat != PixelFormat.Format24bppRgb && frontBmp.PixelFormat != PixelFormat.Format32bppArgb)) { throw new Exception("Currently only supports Format24bppRgb and Format32bppArgb."); } //If the point is not contained in the back bitmap then we return doing nothing as that is implicitly performing the out of bounds superimposition. if (backBmpData.Width <= point.X || backBmpData.Height <= point.Y) { return; } Size size = SuperimposerHelper.GetFrontBmpSize(frontBmp, point, backBmpData.Width, backBmpData.Height); BitmapData frontBmpData = frontBmp.LockBits(new Rectangle(0, 0, frontBmp.Width, frontBmp.Height), ImageLockMode.ReadOnly, frontBmp.PixelFormat); if (transparent && frontBmp.PixelFormat == PixelFormat.Format32bppArgb) { Blend(backBmpData, frontBmpData, point, size); } else { Copy(backBmpData, frontBmpData, point, size); } frontBmp.UnlockBits(frontBmpData); }
/// <summary> /// Blends one bitmap into another using alpha blending. /// </summary> /// <param name="backBmpData">BitmapData from the bitmap to be drawn onto.</param> /// <param name="frontBmpData">BitmapData from the bitmap to draw from.</param> /// <param name="point">Point on the back bitmap to copy the front bitmap.</param> /// <param name="size">Size of the subset of pixels to use from the front bitmap.</param> private static void Blend(BitmapData backBmpData, BitmapData frontBmpData, Point point, Size size) { unsafe { //Bpp of each bitmap may be different. int backBpp = Bitmap.GetPixelFormatSize(backBmpData.PixelFormat) / 8; int frontBpp = Bitmap.GetPixelFormatSize(frontBmpData.PixelFormat) / 8; int pointX = point.X; int pointY = point.Y; int sizeHeight = size.Height; int frontStride = frontBmpData.Stride; int frontSizeStride = size.Width * frontBpp; byte *frontBmpDataScan0 = (byte *)frontBmpData.Scan0; int backStride = backBmpData.Stride; byte *backBmpDataScan0 = (byte *)backBmpData.Scan0; //Need to handle two cases where the back bitmap is Format24bppRgb or Format32bppArgb. //Lots of duplicate code because handling this nested in loops will cause degraded performance. if (backBmpData.PixelFormat == PixelFormat.Format24bppRgb) { //Iterate through each row of pixels in parallel. Parallel.For(0, sizeHeight, y => { byte *backRow = backBmpDataScan0 + ((y + pointY) * backStride); byte *frontRow = frontBmpDataScan0 + (y * frontStride); int backRowByte = pointX * backBpp; int frontRowByte = 0; //Iterate through the pixels in the rows. Should only need to check one condition as the same amount of pixels should be traversed in both bitmaps. for (; frontRowByte < frontSizeStride; backRowByte += backBpp, frontRowByte += frontBpp) { int alpha = frontRow[frontRowByte + 3] + 1; int invAlpha = 256 - frontRow[frontRowByte + 3]; backRow[backRowByte] = (byte)((alpha * frontRow[frontRowByte] + invAlpha * backRow[backRowByte]) >> 8); backRow[backRowByte + 1] = (byte)((alpha * frontRow[frontRowByte + 1] + invAlpha * backRow[backRowByte + 1]) >> 8); backRow[backRowByte + 2] = (byte)((alpha * frontRow[frontRowByte + 2] + invAlpha * backRow[backRowByte + 2]) >> 8); } }); } if (backBmpData.PixelFormat == PixelFormat.Format32bppArgb) { //Iterate through each row of pixels in parallel. Parallel.For(0, sizeHeight, y => { byte *backRow = backBmpDataScan0 + ((y + pointY) * backStride); byte *frontRow = frontBmpDataScan0 + (y * frontStride); int backRowByte = pointX * backBpp; int frontRowByte = 0; //Iterate through the pixels in the rows. Should only need to check one condition as the same amount of pixels should be traversed in both bitmaps. for (; frontRowByte < frontSizeStride; backRowByte += backBpp, frontRowByte += frontBpp) { int frontAlphaByte = frontRow[frontRowByte + 3]; int inverseFrontAlphaByte = frontRow[frontRowByte + 3]; int backAlphaByte = (backRow[backRowByte + 3]); float backAlpha = backAlphaByte / 255f; float frontAlpha = frontAlphaByte / 255f; float inverseFrontAlpha = 1 - frontAlpha; float blendedAlpha = (backAlpha + (1 - backAlpha) * frontAlpha); backRow[backRowByte] = SuperimposerHelper.Clamp((frontRow[frontRowByte] * frontAlpha + backRow[backRowByte] * backAlpha * inverseFrontAlpha) / blendedAlpha); backRow[backRowByte + 1] = SuperimposerHelper.Clamp((frontRow[frontRowByte + 1] * frontAlpha + backRow[backRowByte + 1] * backAlpha * inverseFrontAlpha) / blendedAlpha); backRow[backRowByte + 2] = SuperimposerHelper.Clamp((frontRow[frontRowByte + 2] * frontAlpha + backRow[backRowByte + 2] * backAlpha * inverseFrontAlpha) / blendedAlpha); backRow[backRowByte + 3] = SuperimposerHelper.Clamp(255 * blendedAlpha); } }); } } }