/// <summary> /// Copies a rectangular block of pixel data from a source image to a this image (Blt = BlockTransfer). /// </summary> /// <param name="xDst">The x destination coordinate (where to place the block within dst).</param> /// <param name="yDst">The y destination coordinate (where to place the block within dst).</param> /// <param name="src">The source image.</param> /// <param name="xSrc">The x source coordinate (where to start copying from within src).</param> /// <param name="ySrc">The y source coordinate (where to start copying from within src).</param> /// <param name="width">The width of the block to copy. (default is src.Width).</param> /// <param name="height">The height of the block to copy (default is src.Height).</param> /// <remarks> /// All specified parameters are clipped to avoid out-of-bounds indices. No warnings or exceptions are issued /// in case clipping results in a smaller or an empty block. /// </remarks> public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, int width = 0, int height = 0) { if (width == 0) { width = src.Width; } if (height == 0) { height = src.Height; } ClipBlt(ref xDst, Width, ref xSrc, src.Width, ref width); ClipBlt(ref yDst, Height, ref ySrc, src.Height, ref height); if (width <= 0 || height <= 0) { return; } CopyFunc copyLine; if (PixelFormat.ColorFormat.Equals(src.PixelFormat.ColorFormat)) { // Case: same color space, just loop over scanlines from src and copy line-wise into this ImageData copyLine = delegate(byte[] srcScanLineBytes, int destinationIndex) { Array.Copy(srcScanLineBytes, 0, PixelData, destinationIndex, srcScanLineBytes.Length); }; } else { // Wee need to perform pixel-conversion while copying. -> still GetLineBytes and then perform pixel conversion switch (PixelFormat.ColorFormat) { case ColorFormat.RGBA: switch (src.PixelFormat.ColorFormat) { case ColorFormat.RGB: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst RGBA { PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; PixelData[destinationIndex + i + 3] = byte.MaxValue; } }; break; case ColorFormat.Intensity: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i++) // jump 1 unit per loop because we want to copy src Intensity to dst RGBA { PixelData[destinationIndex + i + 0] = srcLineBytes[i]; PixelData[destinationIndex + i + 1] = srcLineBytes[i]; PixelData[destinationIndex + i + 2] = srcLineBytes[i]; PixelData[destinationIndex + i + 3] = byte.MaxValue; } }; break; default: throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGBA"); } break; case ColorFormat.RGB: switch (src.PixelFormat.ColorFormat) { case ColorFormat.RGBA: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i += 4) // jump 4 units per loop because we want to copy src RGBA to dst RGB { PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; // skip source alpha } }; break; case ColorFormat.Intensity: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i++) // jump 1 unit per loop because we want to copy src Intensity to dst RGB { PixelData[destinationIndex + i + 0] = srcLineBytes[i]; PixelData[destinationIndex + i + 1] = srcLineBytes[i]; PixelData[destinationIndex + i + 2] = srcLineBytes[i]; } }; break; default: throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGB"); } break; case ColorFormat.Intensity: switch (src.PixelFormat.ColorFormat) { case ColorFormat.RGB: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst Intensity { // Quick integer Luma conversion (not accurate) // See http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color int r = srcLineBytes[destinationIndex + i + 0]; int g = srcLineBytes[destinationIndex + i + 1]; int b = srcLineBytes[destinationIndex + i + 2]; PixelData[destinationIndex + i] = (byte)((r + r + b + g + g + g) / 6); } }; break; case ColorFormat.RGBA: copyLine = delegate(byte[] srcLineBytes, int destinationIndex) { for (int i = 0; i < srcLineBytes.Length; i += 4) // jump 4 units per loop because we want to copy src RGBA to dst Intensity { // Quick integer Luma conversion (not accurate) // See http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color int r = srcLineBytes[destinationIndex + i + 0]; int g = srcLineBytes[destinationIndex + i + 1]; int b = srcLineBytes[destinationIndex + i + 2]; PixelData[destinationIndex + i] = (byte)((r + r + b + g + g + g) / 6); } }; break; default: throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGB"); } break; default: throw new ArgumentOutOfRangeException(ToString(), "Unknown destination pixel format"); } // end switch } // end else block // loop over the ScanLineEnumerator and call CopyLine delegate var srcEnumerator = src.ScanLines(xSrc, ySrc, width, height); while (srcEnumerator.MoveNext()) { var srcScanLine = srcEnumerator.Current; if (srcScanLine != null) { byte[] srcScanLineBytes = srcScanLine.GetScanLineBytes(); int destinationIndex = yDst * PixelFormat.BytesPerPixel * Width + xDst * PixelFormat.BytesPerPixel; // move down by yDst and add (move right) xDst copyLine(srcScanLineBytes, destinationIndex); yDst++; // increment yDst == move to the next line } } }