/// <summary> /// Copies a region of the source bitmap into this fast bitmap /// </summary> /// <param name="source">The source image to copy</param> /// <param name="srcRect">The region on the source bitmap that will be copied over</param> /// <param name="destRect">The region on this fast bitmap that will be changed</param> /// <exception cref="ArgumentException">The provided source bitmap is the same bitmap locked in this FastBitmap</exception> public void CopyRegion(Bitmap source, Rectangle srcRect, Rectangle destRect) { // Throw exception when trying to copy same bitmap over if (source == _bitmap) { throw new ArgumentException(@"Copying regions across the same bitmap is not supported", nameof(source)); } var srcBitmapRect = new Rectangle(0, 0, source.Width, source.Height); var destBitmapRect = new Rectangle(0, 0, Width, Height); // Check if the rectangle configuration doesn't generate invalid states or does not affect the target image if (srcRect.Width <= 0 || srcRect.Height <= 0 || destRect.Width <= 0 || destRect.Height <= 0 || !srcBitmapRect.IntersectsWith(srcRect) || !destRect.IntersectsWith(destBitmapRect)) { return; } // Find the areas of the first and second bitmaps that are going to be affected srcBitmapRect = Rectangle.Intersect(srcRect, srcBitmapRect); // Clip the source rectangle on top of the destination rectangle in a way that clips out the regions of the original bitmap // that will not be drawn on the destination bitmap for being out of bounds srcBitmapRect = Rectangle.Intersect(srcBitmapRect , new Rectangle(srcRect.X, srcRect.Y, destRect.Width, destRect.Height)); destBitmapRect = Rectangle.Intersect(destRect, destBitmapRect); // Clip the source bitmap region yet again here srcBitmapRect = Rectangle.Intersect(srcBitmapRect , new Rectangle(-destRect.X + srcRect.X, -destRect.Y + srcRect.Y, Width, Height)); // Calculate the rectangle containing the maximum possible area that is supposed to be affected by the copy region operation int copyWidth = Math.Min(srcBitmapRect.Width, destBitmapRect.Width); int copyHeight = Math.Min(srcBitmapRect.Height, destBitmapRect.Height); if (copyWidth == 0 || copyHeight == 0) { return; } int srcStartX = srcBitmapRect.Left; int srcStartY = srcBitmapRect.Top; int destStartX = destBitmapRect.Left; int destStartY = destBitmapRect.Top; using (var fastSource = source.FastLock()) { ulong strideWidth = (ulong)copyWidth * BytesPerPixel; // Perform copies of whole pixel rows for (int y = 0; y < copyHeight; y++) { int destX = destStartX; int destY = destStartY + y; int srcX = srcStartX; int srcY = srcStartY + y; long offsetSrc = srcX + srcY * fastSource.Stride; long offsetDest = destX + destY * Stride; NativeMethod.Memcpy(_scan0 + offsetDest, fastSource._scan0 + offsetSrc, strideWidth); } } }
/// <summary> /// Clears a square region of this image w/ a given color /// </summary> /// <param name="region"></param> /// <param name="color"></param> public void ClearRegion(Rectangle region, int color) { var thisReg = new Rectangle(0, 0, Width, Height); if (!region.IntersectsWith(thisReg)) { return; } // If the region covers the entire image, use faster Clear(). if (region == thisReg) { Clear(color); return; } var minX = region.X; var maxX = region.X + region.Width; var minY = region.Y; var maxY = region.Y + region.Height; // Bail out of optimization if there's too few rows to make this worth it if (maxY - minY < 16) { for (int y = minY; y < maxY; y++) { for (int x = minX; x < maxX; x++) { *(_scan0 + x + y * Stride) = color; } } return; } ulong strideWidth = (ulong)region.Width * BytesPerPixel; // Uniform color pixel values can be mem-set straight away int component = color & 0xFF; if (component == ((color >> 8) & 0xFF) && component == ((color >> 16) & 0xFF) && component == ((color >> 24) & 0xFF)) { for (int y = minY; y < maxY; y++) { NativeMethod.Memset(_scan0 + minX + y * Stride, component, strideWidth); } } else { // Prepare a horizontal slice of pixels that will be copied over each horizontal row down. int[] row = new int[region.Width]; fixed(int *pRow = row) { int count = region.Width; int rem = count % 8; count /= 8; int *pSrc = pRow; while (count-- > 0) { *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; } while (rem-- > 0) { *pSrc++ = color; } int *sx = _scan0 + minX; for (int y = minY; y < maxY; y++) { NativeMethod.Memcpy(sx + y * Stride, pRow, strideWidth); } } } }