How to quickly batch save Bitmaps? - c#

So I have a source bitmap that is fairly large, so I shrink it down by a scale of %25 to speed things along in image processing and what not. At the end, I have a group of Rectangles (around 2000) that correspond to sections of the scaled image. I am trying to rescale the Rectangles to match the same areas on the source, then saving that area as a cropped image.
This was my initial code that saved cropped images of the scaled image:
for (int i = 0; i < cells.Count; i++)
{
for (int j = 0; j < cells[i].Count; j++)
{
Cell cell = cells[i][j];
if (cell.width < 0 || cell.height < 0)
{
return;
}
Bitmap bitmap = new Bitmap(cell.width, cell.height);
using (Graphics c = Graphics.FromImage(bitmap))
{
c.DrawImage(inputBitmap, new Rectangle(0, 0, cell.width, cell.height), new Rectangle(cell.x1, cell.y1, cell.width, cell.height), GraphicsUnit.Pixel);
}
bitmap.Save(cellDirectory + "\\cell" + i.ToString("D2") + j.ToString("D2") + ".png", ImageFormat.Png);
}
}
This is my code changed to save the cropped image of the original bitmap:
for (int i = 0; i < cells.Count; i++)
{
for (int j = 0; j < cells[i].Count; j++)
{
Cell cell = cells[i][j];
if (cell.width < 0 || cell.height < 0)
{
return;
}
int x = cell.x1 * 4;
int y = cell.y1 * 4;
int width = cell.width * 4;
int height = cell.height * 4;
Bitmap bitmap = new Bitmap(width, height);
using (Graphics c = Graphics.FromImage(bitmap))
{
c.DrawImage(input, new Rectangle(0, 0, width, height), new Rectangle(x, y, width, height), GraphicsUnit.Pixel);
}
bitmap.Save(cellDirectory + "\\cell" + i.ToString("D2") + j.ToString("D2") + ".png", ImageFormat.Png);
}
}
The program with the first code finishes in about 20 seconds on average, but for some reason the second version takes over 6 minutes. My brain math might be lying to me, but that seems to be a disproportionate time increase.
The debugging that I have done so far has revealed to me that this line:
c.DrawImage(input, new Rectangle(0, 0, width, height), new Rectangle(x, y, width, height), GraphicsUnit.Pixel);
is taking longer to complete over time. I suspect that some sort of memory leak could be causing this, but I have tried manually calling Dispose on every object I can and nothing helped. Is there some kind of under-the-hood thing that I should be aware of that is causing this?

Your original method saves a file at the original resolution, while the new method increases both width and height by a factor of 4, which is a 16x increase in the image size. The time difference (6 minutes vs 20 seconds) is roughly proportional:
(6 * 60) / 20 = 18 times slower
4 * 4 = 16 times the image size

Related

Replace colored pixels of bitmap image from another image

I have a performance problem.
For a insole model configurator, we have a piece to upload and many material images to fusion with the piece image.
I should replace every white pixel on the piece image by the corresponding pixel on the material image.
As the material image is not a mono color, I cant replace simply all white by another mono color.
Image sizes are the same. So I simply take a pixel if the color is not transparent from the piece image and with the same X and Z coordinates on the material images, I take a pixel and I set the pixel of the piece image.
But as there are many materials, it takes 5 minutes today.
Is there a mor optimised way to do this ?
Here is my method :
//For every material image, calls the fusion method below.
foreach (string material in System.IO.Directory.GetFiles(materialsPath))
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), material);
}
private static void FusionWhiteImagesWithMaterials(string whiteImageFolder, string file, string materialImageFile)
{
if (file.ToLower().EndsWith(".db") || materialImageFile.ToLower().EndsWith(".db"))
return;
List<CustomPixel> lstColoredPixels = new List<CustomPixel>();
try
{
Bitmap image = new Bitmap(file);
for (int y = 0; y < image.Height; ++y)
{
for (int x = 0; x < image.Width; ++x)
{
if (image.GetPixel(x, y).A > 0)
{
lstColoredPixels.Add(new CustomPixel(x, y));
}
}
}
Bitmap bmpTemp = new Bitmap(materialImageFile);
Bitmap target = new Bitmap(bmpTemp, new Size(image.Size.Width, image.Size.Height));
for (int y = 0; y < target.Height; y++)
{
for (int x = 0; x < target.Width; x++)
{
Color clr = image.GetPixel(x, y);
if (clr.A > 0)
{
if (clr.R > 200 && clr.G > 200 && clr.B > 200)
image.SetPixel(x, y, target.GetPixel(x, y));
else
image.SetPixel(x, y, Color.Gray);
}
}
}
...
image.Save(...);
}
catch (Exception ex)
{
}
}
//I reduced image sizes to keep on the screen. Real image sizes are 500x1240 px.
Replacing the white is one possibility, but it's not a very pretty one. Based on the images you have there, the ideal solution for this is to get the pattern with the correct alpha applied, and then paint the visible black lines over it. This is actually a process with some more steps:
Extract the alpha from the foot shape image
Extract the black lines from the foot shape image
Apply the alpha to the pattern image
Paint the black lines over the alpha-adjusted pattern image
The way I'd approach this is to extract the data of both images as ARGB byte arrays, meaning, each pixel is four bytes, in the order B, G, R, A. Then, for each pixel, we simply copy the alpha byte from the foot shape image into the alpha byte of the pattern image, so you end up with the pattern image, with the transparency of the foot shape applied to it.
Now, in a new byte array of the same size, which starts with pure 00 bytes (meaning, since A,R,G and B are all zero, transparent black), we construct the black line. Pixels can be considered "black" if they're both not white, and visible. So the ideal result, including smooth fades, is to adjust the alpha of this new image to the minimum value of the alpha and the inverse of the brightness. Since it's grayscale, any of the R, G, B will do for brightness. To get the inverse as byte value, we just take (255 - brightness).
Note, if you need to apply this to a load of images, you probably want to extract the bytes, dimensions and stride of the foot pattern image only once in advance, and keep them in variables to give to the alpha-replacing process. In fact, since the black lines image won't change either, a preprocessing step to generate that should speed things up even more.
public static void BakeImages(String whiteFilePath, String materialsFolder, String resultFolder)
{
Int32 width;
Int32 height;
Int32 stride;
// extract bytes of shape & alpha image
Byte[] shapeImageBytes;
using (Bitmap shapeImage = new Bitmap(whiteFilePath))
{
width = shapeImage.Width;
height = shapeImage.Height;
// extract bytes of shape & alpha image
shapeImageBytes = GetImageData(shapeImage, out stride, PixelFormat.Format32bppArgb);
}
using (Bitmap blackImage = ExtractBlackImage(shapeImageBytes, width, height, stride))
{
//For every material image, calls the fusion method below.
foreach (String materialImagePath in Directory.GetFiles(materialsFolder))
{
using (Bitmap patternImage = new Bitmap(materialImagePath))
using (Bitmap result = ApplyAlphaToImage(shapeImageBytes, width, height, stride, patternImage))
{
if (result == null)
continue;
// paint black lines image onto alpha-adjusted pattern image.
using (Graphics g = Graphics.FromImage(result))
g.DrawImage(blackImage, 0, 0);
result.Save(Path.Combine(resultFolder, Path.GetFileNameWithoutExtension(materialImagePath) + ".png"), ImageFormat.Png);
}
}
}
}
The black lines image:
public static Bitmap ExtractBlackImage(Byte[] shapeImageBytes, Int32 width, Int32 height, Int32 stride)
{
// Create black lines image.
Byte[] imageBytesBlack = new Byte[shapeImageBytes.Length];
// Line start offset is set to 3 to immediately get the alpha component.
Int32 lineOffsImg = 3;
for (Int32 y = 0; y < height; y++)
{
Int32 curOffs = lineOffsImg;
for (Int32 x = 0; x < width; x++)
{
// copy either alpha or inverted brightness (whichever is lowest)
// from the shape image onto black lines image as alpha, effectively
// only retaining the visible black lines from the shape image.
// I use curOffs - 1 (red) because it's the simplest operation.
Byte alpha = shapeImageBytes[curOffs];
Byte invBri = (Byte) (255 - shapeImageBytes[curOffs - 1]);
imageBytesBlack[curOffs] = Math.Min(alpha, invBri);
// Adjust offset to next pixel.
curOffs += 4;
}
// Adjust line offset to next line.
lineOffsImg += stride;
}
// Make the black lines images out of the byte array.
return BuildImage(imageBytesBlack, width, height, stride, PixelFormat.Format32bppArgb);
}
The processing to apply the foot image's transparency to the pattern image:
public static Bitmap ApplyAlphaToImage(Byte[] alphaImageBytes, Int32 width, Int32 height, Int32 stride, Bitmap texture)
{
Byte[] imageBytesPattern;
if (texture.Width != width || texture.Height != height)
return null;
// extract bytes of pattern image. Stride should be the same.
Int32 patternStride;
imageBytesPattern = ImageUtils.GetImageData(texture, out patternStride, PixelFormat.Format32bppArgb);
if (patternStride != stride)
return null;
// Line start offset is set to 3 to immediately get the alpha component.
Int32 lineOffsImg = 3;
for (Int32 y = 0; y < height; y++)
{
Int32 curOffs = lineOffsImg;
for (Int32 x = 0; x < width; x++)
{
// copy alpha from shape image onto pattern image.
imageBytesPattern[curOffs] = alphaImageBytes[curOffs];
// Adjust offset to next pixel.
curOffs += 4;
}
// Adjust line offset to next line.
lineOffsImg += stride;
}
// Make a image out of the byte array, and return it.
return BuildImage(imageBytesPattern, width, height, stride, PixelFormat.Format32bppArgb);
}
The helper function to extract the bytes from an image:
public static Byte[] GetImageData(Bitmap sourceImage, out Int32 stride, PixelFormat desiredPixelFormat)
{
Int32 width = sourceImage.Width;
Int32 height = sourceImage.Height;
BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, desiredPixelFormat);
stride = sourceData.Stride;
Byte[] data = new Byte[stride * height];
Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
sourceImage.UnlockBits(sourceData);
return data;
}
The helper function to make a new image from a byte array:
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
// Get actual data width.
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
// Copy per line, copying only data and ignoring any possible padding.
for (Int32 y = 0; y < height; ++y)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
return newImage;
}
The result in my test tool:
As you see, the black lines are preserved on top of the pattern.
GetPixel/SetPixel are notoriously slow due to locking and other overhead accessing the pixels. To improve performance you will need to use some unmanaged coding to access the data directly.
This answer should shows an example on how to improve speed when working with bitmaps.
Here is some (untested!) code adapted from that anwer:
public static unsafe Image MergeBitmaps(Bitmap mask, Bitmap background)
{
Debug.Assert(mask.PixelFormat == PixelFormat.Format32bppArgb);
BitmapData maskData = mask.LockBits(new Rectangle(0, 0, mask.Width, mask.Height),
ImageLockMode.ReadWrite, mask.PixelFormat);
BitmapData backgroundData = background.LockBits(new Rectangle(0, 0, background.Width, background.Height),
ImageLockMode.ReadWrite, background.PixelFormat);
try
{
byte bytesPerPixel = 4;
/*This time we convert the IntPtr to a ptr*/
byte* maskScan0 = (byte*)maskData.Scan0.ToPointer();
byte* backgroundScan0 = (byte*)backgroundData.Scan0.ToPointer();
for (int i = 0; i < maskData.Height; ++i)
{
for (int j = 0; j < maskData.Width; ++j)
{
byte* maskPtr = maskScan0 + i * maskData.Stride + j * bytesPerPixel;
byte* backPtr = backgroundScan0 + i * backgroundData.Stride + j * bytesPerPixel;
//maskPtr is a pointer to the first byte of the 4-byte color data
//maskPtr[0] = blueComponent;
//maskPtr[1] = greenComponent;
//maskPtr[2] = redComponent;
//maskPtr[3] = alphaComponent;
if (maskPtr[3] > 0 )
{
if (maskPtr[2] > 200 &&
maskPtr[1] > 200 &&
maskPtr[0] > 200)
{
maskPtr[3] = 255;
maskPtr[2] = backPtr[2];
maskPtr[1] = backPtr[1];
maskPtr[0] = backPtr[0];
}
else
{
maskPtr[3] = 255;
maskPtr[2] = 128;
maskPtr[1] = 128;
maskPtr[0] = 128;
}
}
}
}
return mask;
}
finally
{
mask.UnlockBits(maskData);
background.UnlockBits(backgroundData);
}
}
}
I found this solution, it is much more faster.
But it uses too much resources.
Parallel programing in C# came to my help :
//I called my method in a parallel foreach
Parallel.ForEach(System.IO.Directory.GetFiles(materialsPath), filling =>
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), filling);
});
//Instead of a classic foreach loop like this.
foreach (string material in System.IO.Directory.GetFiles(materialsPath))
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), material);
}

How to draw image on Graphics without extra pixels at outer borders?

This simple code gives me very bad result.
Image global_src_img = Image.FromFile(Application.StartupPath + "\\src.png");
Image src_img = ((Bitmap)global_src_img).Clone(new Rectangle(160, 29, 8, 14), PixelFormat.Format32bppArgb);
src_img.Save(Application.StartupPath + "\\src_clonned.png", ImageFormat.Png);
Bitmap trg_bmp = new Bitmap(100, 100);
Graphics gfx = Graphics.FromImage(trg_bmp);
gfx.FillRectangle(new Pen(Color.FromArgb(255, 0, 0, 0)).Brush, 0, 0, trg_bmp.Width, trg_bmp.Height);
gfx.DrawImageUnscaled(src_img, (trg_bmp.Width / 2) - (src_img.Width / 2), (trg_bmp.Height / 2) - (src_img.Height / 2));
gfx.Dispose();
trg_bmp.Save(Application.StartupPath + "\\trg.png", ImageFormat.Png);
It clones part of the big image with letters and writes it to another image.
Written image has extra pixels at outer borders that does not present in source image same as in cloned image.
How to avoid it?
This is cloned image (src_clonned.png) that later will be drawn on graphics.
This is saved resulting image (trg.png).
Draw directly on bitmap to avoid any pixels modifications with this:
public static void DrawImgOnImg(Image src, Bitmap trg, int x, int y)
{
for (int i = x; i < x + src.Width; i++)
for (int j = y; j < y + src.Height; j++)
{
Color src_px = ((Bitmap)src).GetPixel(i - x, j - y);
trg.SetPixel(i, j, src_px);
}
}

How to place maximum images in a blank Bitmap based on column/row input & margin variables and preserving aspect ratio of images

This might be a tricky question.
I will just cut to the chase.
First i make a blank Bitmap image:
Bitmap MasterImage = new Bitmap(PageSize.Width, PageSize.Height, PixelFormat.Format32bppArgb);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(MasterImage);
Afterwards:
I get a parameter of how much images i want to place in the Blank Bitmap (page) based on columns and rows:
int numOfColumns=2; //(could be any value)
int numOfRows=4;
So we should get something like this:
I also get a parameter if i have some margin at top image and left image:
int PagetopMargin=0; //(could be any value)
int PageLeftMargin=0;
Than i have a variable called: imagesPath which is type of List<String> it contains full path of images.
Now i loop through images:
while (imagesPath.Count > 0)
{
Image image = Image.FromFile(imagesPath[0]);
//Logic comes here.
//after finishing processing and drawing images in Bitmap i remove image from list
imagesPath.RemoveAt(0);
}
What i am trying to achieve is how to place maximum images in that Bitmap page based on column/row and margin variables and preserving aspect ratio of images. (so images wont get distorted). if some images are left behind, its okey, i will continue next blank Bitmap to place them. just need to fill the Bitmap blank image to the full.
Here is a function to fit a rectangle into another, either centered or uncentered (aligned top-left):
Rectangle FitToBox(Rectangle scr, Rectangle dest, bool centered)
{
var ratioX = (double)dest.Width / scr.Width;
var ratioY = (double)dest.Height / scr.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(scr.Width * ratio);
var newHeight = (int)(scr.Height * ratio);
if (!centered)
return new Rectangle(0, 0, newWidth, newHeight);
else
return new Rectangle((dest.Width - newWidth) / 2,
(dest.Height - newHeight) / 2, newWidth, newHeight);
}
Its basic math is taken form this post.
Here is a test bed, centered and uncentered using different random values:
Random rnd = new Random();
int cols = rnd.Next(3) + 2;
int rows = rnd.Next(4) + 3;
int w = pan_dest.Width / cols;
int h = pan_dest.Height / rows;
using (Graphics G = pan_dest.CreateGraphics())
{
G.Clear(Color.White);
for (int c = 0; c < cols; c++)
for (int r = 0; r < rows; r++)
{
Rectangle rDest = new Rectangle(c * w, r * h, w, h);
Rectangle rSrc = new Rectangle(0, 0, rnd.Next(200) + 10, rnd.Next(200) + 10);
Rectangle rResult = FitToBox(rSrc, rDest, checkBox1.Checked);
Rectangle rDestPlaced = new Rectangle(c * w + (int)rResult.X,
r * h + rResult.Y, rResult.Width, rResult.Height);
using (Pen pen2 = new Pen(Color.SlateGray, 4f))
G.DrawRectangle(pen2, Rectangle.Round(rDest));
G.DrawRectangle(Pens.Red, rDestPlaced);
G.FillEllipse(Brushes.LightPink, rDestPlaced);
}
}
You would then draw your images like this:
G.DrawImage(someImageimg, rDestPlaced, rSrc, GraphicsUnit.Pixel);
This maximizes the image sizes, not the number of images you can fit on a page, as specified in your comment. For the latter you should look into something like 2-dimensional packing..

Showing full image without Cropping part C#

I want to compare two images, where only "date of print" is different, I wanted to crop 'date' area only. But I wanted to show full image without crop area, (not the crop area only)
Code I used for cropping
static void Main(string[] args)
{
Bitmap bmp = new Bitmap(#"C:\Users\Public\Pictures\Sample Pictures\1546.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int origByteCount = rawOriginal.Stride * rawOriginal.Height;
byte[] origBytes = new Byte[origByteCount];
System.Runtime.InteropServices.Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount);
//I want to crop a 100x100 section starting at 15, 15.
int startX = 15;
int startY = 15;
int width = 100;
int height = 100;
int BPP = 4; //4 Bpp = 32 bits, 3 = 24, etc.
byte[] croppedBytes = new Byte[width * height * BPP];
//Iterate the selected area of the original image, and the full area of the new image
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width * BPP; j += BPP)
{
int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);
int croppedIndex = (i * width * BPP) + (j);
//copy data: once for each channel
for (int k = 0; k < BPP; k++)
{
croppedBytes[croppedIndex + k] = origBytes[origIndex + k];
}
}
}
//copy new data into a bitmap
Bitmap croppedBitmap = new Bitmap(width, height);
BitmapData croppedData = croppedBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
System.Runtime.InteropServices.Marshal.Copy(croppedBytes, 0, croppedData.Scan0, croppedBytes.Length);
bmp.UnlockBits(rawOriginal);
croppedBitmap.UnlockBits(croppedData);
croppedBitmap.Save(#"C:\Users\Public\Pictures\Sample Pictures\AFTERCROP_CROP.jpg");
bmp.Save(#"C:\Users\Public\Pictures\Sample Pictures\AFTERCROP-ORIG.jpg");
}
Your code is a bit overcomplicated, and you seem to be confused about what cropping is - cropping means taking a part of the original picture. What you seem to want instead is to black out some part of the original picture:
The easiest way to accomplish this is by drawing a simple filled rectangle over the original image:
var bmp = Bitmap.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg");
using (var gr = Graphics.FromImage(bmp))
{
gr.FillRectangle(Brushes.Black, 50, 50, 200, 200);
}
If you also want to preserve the original bitmap, you can just copy it over.

Moving Bitmap region

In order to make my question more understandable I give you pseudocode snippet:
// dx (i.e. offset value) can be arbitrary.
for(i = 0; i < bitmap.columnsCount - dx; i++)
{
// "=" means copy pixels from one column to another.
bitmap.column[i] = bitmap.column[i+dx];
}
How should I do this? Of course I can take raw pixels through LockBitmap and then somehow use MarshalCopy or unsafe section... But this is ugly and too complicated. Is there are better way?
I tried to find something similar to MoveBitmapRegion() method, but i can't. The idea of ​​drawing bitmap to itself did not work:
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(bmp, 0, 0, new Rectangle(dx, 0, bmp.Width - dx, bmp.Height), GraphicsUnit.Pixel);
Making a copy of the bitmap helps, but I think it's too expensive operation:
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(new Bitmap(bmp), 0, 0, new Rectangle(dx, 0, bmp.Width - dx, bmp.Height), GraphicsUnit.Pixel);
Okay, just whipped this up really quickly at the end of the day so there may be some mistakes, but in the image I tested it seemed to work fine.
private static void CopyBmpRegion(Bitmap image, Rectangle srcRect, Point destLocation)
{
//do some argument sanitising.
if (!((srcRect.X >= 0 && srcRect.Y >= 0) && ((srcRect.X + srcRect.Width) <= image.Width) && ((srcRect.Y + srcRect.Height) <= image.Height)))
throw new ArgumentException("Source rectangle isn't within the image bounds.");
if ((destLocation.X < 0 || destLocation.X > image.Width) || (destLocation.Y < 0 || destLocation.Y > image.Height))
throw new ArgumentException("Destination must be within the image.");
// Lock the bits into memory
BitmapData bmpData = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, image.PixelFormat);
int pxlSize = (bmpData.Stride / bmpData.Width); //calculate the pixel width (in bytes) of the current image.
int src = 0; int dest = 0; //source/destination pixels.
//account for the fact that not all of the source rectangle may be able to copy into the destination:
int width = (destLocation.X + srcRect.Width) <= image.Width ? srcRect.Width : (image.Width - (destLocation.X + srcRect.Width));
int height = (destLocation.Y + srcRect.Height) <= image.Height ? srcRect.Height : (image.Height - (destLocation.Y + srcRect.Height));
//managed buffer to hold the current pixel data.
byte[] buffer = new byte[pxlSize];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
//calculate the start of the current source pixel and destination pixel.
src = ((srcRect.Y + y) * bmpData.Stride) + ((srcRect.X + x) * pxlSize);
dest = ((destLocation.Y + y) * bmpData.Stride) + ((destLocation.X + x) * pxlSize);
// Can replace this with unsafe code, but that's up to you.
Marshal.Copy(new IntPtr(bmpData.Scan0.ToInt32() + src), buffer, 0, pxlSize);
Marshal.Copy(buffer, 0, new IntPtr(bmpData.Scan0.ToInt32() + dest), pxlSize);
}
}
image.UnlockBits(bmpData); //unlock the data.
}
Essentially you describe the source rectangle of the area you wish to copy (in pixels, not in bytes) i.e Rectangle(0, 0, 100, 100) would describe a block 100x100 pixels wide starting at the top left corner of the image.
Next, you describe the upper left corner of the destination rectangle. As much of the source rectangle will be copied to the destination as possible, but if the image isn't wide/tall enough to accommodate the full rectangle, it will be clipped.
And example of usage would be the following:
Image img = Image.FromFile(#"C:\Users\254288b\Downloads\mozodojo-original-image.jpg");
CopyBmpRegion((Bitmap)img, new Rectangle(5, 5, 100, 100), new Point(100, 100));
img.Save(#"C:\Users\254288b\Downloads\mozodojo-new-image.jpg", ImageFormat.Jpeg);
Which had the following results:
mozodojo-original-image.jpg
mozodojo-new-image.jpg
See how it goes.

Categories

Resources