If i change the colour of the image and then try to resize, it only resizes the original image. Why is this happening and how do i fix it?
Here is my code:
private PrintDocument printDoc = new PrintDocument();
private PageSettings pgSettings = new PageSettings();
private PrinterSettings prtSettings = new PrinterSettings();
Bitmap myBitmapImage; // image (bitmap) for some background mountains
Boolean isInvert = false;
Boolean isLOaded = false;
Boolean isGrayscale = false;
Boolean isThreshold = false;
Boolean isResize = false;
OpenFileDialog ofd;
Bitmap bmBack;
public EditImage()
{
printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
}
private void EditImage_Load(object sender, EventArgs e)
{
}
private void EditImage_Paint(object sender, PaintEventArgs e)
{
if (isLOaded == true)
{
Graphics gWindow; // reference to the graphic surface of this window
Graphics gBack; // reference to in-memory surface
bmBack = new Bitmap(Width, Height); // bitmap for window surface copy
gWindow = e.Graphics; // get our current window's surface
gBack = Graphics.FromImage(bmBack); // create surfaces from the bitmaps
gBack.DrawImage(myBitmapImage, 0, 0, Width, Height);
if (isInvert == true)
{
InvertBitmap(bmBack);
}
else if (isGrayscale == true)
{
GrayscaleBitmap(bmBack);
}
else if (isThreshold == true)
{
ThresholdBitmap(bmBack);
}
else if (isResize == true)
{
bmBack = resizeImage(bmBack, 10, 100);
}
gWindow.DrawImage(bmBack, 0, 0);
}
}
private void toolStripMenuItemLoadImage_Click(object sender, EventArgs e)
{
using (ofd = new OpenFileDialog())
{
ofd.Title = "Load Image";
if (ofd.ShowDialog() == DialogResult.OK)
{
myBitmapImage = new Bitmap(ofd.FileName);
this.Invalidate();
}
}
isLOaded = true;
}
private void GrayscaleBitmap(Bitmap bmp)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
IntPtr ptr = bmpData.Scan0;
int numPixels = bmpData.Width * bmp.Height;
int numBytes = numPixels * 4;
byte[] rgbValues = new byte[numBytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, numBytes);
for (int i = 0; i < rgbValues.Length; i += 4)
{
byte gray = (byte)(.3 * rgbValues[i + 0]); //blue
gray += (byte)(.59 * rgbValues[i + 1]); //green
gray += (byte)(.11 * rgbValues[i + 2]); //red
rgbValues[i + 0] = gray; //blue
rgbValues[i + 1] = gray; //green
rgbValues[i + 2] = gray; //red
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, numBytes);
bmp.UnlockBits(bmpData);
}
private Bitmap resizeImage(Bitmap sourceBMP, int width, int height)
{
Bitmap result = new Bitmap(width, height);
using(Graphics g = Graphics.FromImage(result))
g.DrawImage(sourceBMP, 0, 0, width, height);
return result;
}
i also have methods which deal with when the user clicks on a button and sets the bools to appropriate values so that it calls the correct method. The images DO change colour - as intended.. but when i click on resize, i want it to resize the version of the image that has changed colour - not the original image...
There are lots of things wrong here:
resizeImage()
You should dispose of the Graphics object that you create here (wrap it in a using() statement)
Don't call invalidate from here, this function just resizes your image, you invalidate after you've changed your image and your Paint method paints the image which has now changed.
You should also think about disposing of the sourceBMP right before you return the function if you no longer have any use for it.
GrayscaleBitmap()
This looks right, but again there's no reason to invalidate here. You should invalidate after you call this method in the calling function. It makes more sense.
EditImage_Paint
You should not be calling these functions from within your Paint event. And you should not be creating a new Bitmap and a new Graphics class on each Paint. This is way more work than necessary. These functions should only execute when the data needs to be changed based on user input (user clicks a button to apply a grayscale effect).
For what you want to do, you should only need 2 bitmap variables at most. One to store the original unmodified bitmap in case you want to let the user "Reset" it or to undo any effects (most effects cause permanent data loss, you can't make a grayscale image color again). And the other to store the bitmap that gets painted. Each time the user applies an effect, it modifies the 2nd bitmap, and then calls invalidate.
All your Paint function should do is paint the 2nd bitmap:
Bitmap originalBitmap; // load from file, do not modify
Bitmap currentBitmap; // when user clicks an effect, modify this bitmap and then Invalidate afterward
private void EditImage_Paint(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the currentBitmap centered on the window
SizeF clientSize = this.ClientSize;
double zoomRatio = Math.Min(
clientSize.Width / currentBitmap.Width,
clientSize.Height / currentBitmap.Height
);
SizeF zoomedSize = new SizeF(
(float)(currentBitmap.Width * zoomRatio),
(float)(currentBitmap.Height * zoomRatio)
);
PointF imageOffset = new PointF(
(clientSize.Width - zoomedSize.Width) / 2,
(clientSize.Height - zoomedSize.Height) / 2
);
e.Graphics.Clear(Color.White);
e.Graphics.DrawImage(currentBitmap, imageOffset.X, imageOffset.Y, zoomedSize.Width, zoomedSize.Height);
}
This emulates a simple zoom effect that centers the image on the control and draws it to fit the window.
Related
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.Image);
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
//for (int counter = 2; counter < rgbValues.Length; counter +=64)
// rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 0);
}
i mean to see in pictureBox2 the image get fill slowly like a paint get painted each time a bit. and not at once. with the original colors of the image in pictureBox1 to copy the image in pictureBox1 to pictureBox2 buti nstead in once to make it slowly and each time copy some pixels or one by one until the whole image paint is completed in pictureBox2.
I tried this.
in time tick event :
int cc = 0;
private void timer1_Tick(object sender, EventArgs e)
{
cc++;
pictureBox2.Invalidate();
}
in pictureBox2 paint event
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.Image);
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * cc;//bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 0);
}
but the code in the pictureBox2 paint event delete the image in the pictureBox2 delete slowly from the top to the bottom.
but i want the opposite that it will start that the pictureBox2 is clear and then the image will be painted slowly.
i tried to change the line :
Bitmap bmp = new Bitmap(pictureBox1.Image);
to
Bitmap bmp = new Bitmap(512, 512);
but then it does nothing in the pictureBox2.
Here's an example that will copy the image line by line:
private async void button1_Click(object sender, EventArgs e)
{
if (pictureBox1.Image != null)
{
button1.Enabled = false;
Bitmap bmp1 = new Bitmap(pictureBox1.Image);
Bitmap bmp2 = new Bitmap(bmp1.Width, bmp1.Height);
pictureBox2.Image = bmp2;
using (Graphics G = Graphics.FromImage(bmp2))
{
for (int y = 0; y < bmp1.Height; y++)
{
Rectangle rc = new Rectangle(new Point(0, y), new Size(bmp1.Width, 1));
G.DrawImage(bmp1, rc, rc, GraphicsUnit.Pixel);
pictureBox2.Invalidate();
await Task.Delay(1);
}
}
button1.Enabled = true;
}
}
Sample run:
To make it go faster, you can increase the height of the rectangle being copied, and then make the for loop jump by that much:
int height = 12; // added
for (int y = 0; y < bmp1.Height; y = y + height) // change
{
Rectangle rc = new Rectangle(new Point(0, y), new Size(bmp1.Width, height)); // change
G.DrawImage(bmp1, rc, rc, GraphicsUnit.Pixel);
pictureBox2.Invalidate();
await Task.Delay(1);
}
Here's another approach showing a radial expansion using a GraphicsPath, Region, and a Clip:
private async void button2_Click(object sender, EventArgs e)
{
if (pictureBox1.Image != null)
{
button2.Enabled = false;
Bitmap bmp1 = new Bitmap(pictureBox1.Image);
Bitmap bmp2 = new Bitmap(bmp1.Width, bmp1.Height);
pictureBox2.Image = bmp2;
int radius = Math.Max(bmp1.Width, bmp1.Height);
Point center = new Point(bmp1.Width / 2, bmp2.Height / 2);
using (Graphics G = Graphics.FromImage(bmp2))
{
int step = 10;
for (int r=0; r <=radius; r=r+step)
{
Rectangle rc = new Rectangle(center, new Size(1, 1));
rc.Inflate(r, r);
using (System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath())
{
gp.AddEllipse(rc);
using (Region rgn = new Region(gp))
{
G.Clip = rgn;
G.DrawImage(bmp1, 0, 0);
}
}
pictureBox2.Invalidate();
await Task.Delay(1);
}
}
button2.Enabled = true;
}
}
Play with the step value to make it go faster or slower:
I'm trying to achieve an effect similar to this site. I am basically trying to implement a way to "compare" two similar images (with different colors, etc). I managed to do this in a not so brilliant way using two PictureBox controls (Winforms) one next to the other, and changing their Size and Location attributes on a MouseMove event.
The result works, but it flickers a lot and it's not really the best way to do it.
Is there a better way to do this, maybe with a WPF or by changing the code in any way? Here it is:
private void pbImg1_MouseMove(object sender, MouseEventArgs e)
{
pbImg2.Image = CropImage(array[1], new Rectangle(pbImg1.Size.Width, 0, totalSize.Width - pbImg1.Size.Width, 240));
pbImg1.Size = new Size(e.X, pbImg1.Height);
pbImg2.Location = new Point(pbImg1.Size.Width + pbImg1.Location.X, pbImg2.Location.Y);
pbImg2.Size = new Size(totalSize.Width - pbImg1.Size.Width, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
private void pbImg2_MouseMove(object sender, MouseEventArgs e)
{
pbImg1.Image = CropImage(array[0], new Rectangle(0, 0, totalSize.Width - pbImg2.Size.Width, 240));
pbImg1.Size = new Size(pbImg1.Width + e.X, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
public Bitmap CropImage(Bitmap source, Rectangle section)
{
// An empty bitmap which will hold the cropped image
//TRY CATCH
Bitmap bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
// Draw the given area (section) of the source image
// at location 0,0 on the empty bitmap (bmp)
g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
return bmp;
}
And here you can see the behavior of the program:
https://gfycat.com/VillainousReadyAmazonparrot
You need two images imageLeft, imageRight with the same size as the picturebox:
private int pos = 0; //x coordinate of mouse in picturebox
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if(pos == 0)
{
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height));
}
else
{
e.Graphics.DrawImage(Properties.Resources.imageLeft, new Rectangle(0, 0, pos, pictureBox1.Height),
new Rectangle(0, 0, pos, pictureBox1.Height), GraphicsUnit.Pixel);
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height),
new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height), GraphicsUnit.Pixel);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pos = e.X;
pictureBox1.Invalidate();
}
if (ImagesComparion1.ImageComparison(File1, file2, image_scan_text_rect) == true)
{
/* Logger.Write("File1 is >>>> " + combinedTemp);
// Logger.Write("File2 is >>>> " + fi.FullName);
Logger.Write("Last File is >>>> " + last_file);
Logger.Write("image_scan_text_rect values are >>>>> " + image_scan_text_rect.ToString());
Logger.Write("ImagesComparion1.ImageComparison(File1, file2, image_scan_text_rect) is now true");*/
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null;
}
pictureBox1.Load(last_file);
File1.Dispose();
Properties.Resources.RadarImageClose.Dispose();
label18.Text = "The Radar Is Not Active Now";
label18.Visible = true;
if (paintDemoMode == true)
{
Bitmap bmp = new Bitmap(combinedTemp);
Bitmap bb = new Bitmap(bmp);
bmp.Dispose();
bmp = null;
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null;
}
pictureBox1.Image = bb;
image_scan_text_rect = new Rectangle(25, 240, 341, 39);
float x = ((float)image_scan_text_rect.X / bb.Width) * (float)this.pictureBox1.Width;
//float y = ((float)Rect.Y / this.pictureBox1.ClientSize.Height) * (float)FirstImage.Height;
float y = ((float)image_scan_text_rect.Y / bb.Height) * (float)this.pictureBox1.Height;
//float newRight = ((float)Rect.Right / (float)pictureBox1.ClientSize.Width) * ((float)FirstImage.Width);
float newRight = ((float)image_scan_text_rect.Right / bb.Width) * (float)pictureBox1.Width;
//float newBottom = ((float)Rect.Bottom / (float)pictureBox1.ClientSize.Height) * ((float)FirstImage.Height);
float newBottom = ((float)image_scan_text_rect.Bottom / bb.Height) * (float)pictureBox1.Height;
rectToDrawOut = new RectangleF(x, y, newRight - x, newBottom - y);
pictureBox1.Image.Save(#"d:\testit.png", System.Drawing.Imaging.ImageFormat.Png);
}
return;
}
When i click a button and paintDemoMode is true then its showing this rectToDrawOut as rectangle on the pictureBox1
Now i want to save to my hard disk the image show now in the pictureBox1 including the rectangle.
But its aving just the image from the pictureBox1 without the rectangle. I tried also instead pictureBox1.Image.Save i tried bb.Save but again it didnt save the rectangle. whats wrong here ?
In the pictureBox1_Paint event im drawing the rectangle if its needed this is the code:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 2))
{
if (paintDemoButtonSwitch == true)
{
e.Graphics.DrawRectangle(pen, rectToDrawOut.X, rectToDrawOut.Y, rectToDrawOut.Width, rectToDrawOut.Height);
}
}
}
Thanks for helping.
You are drawing the rectangle on the screen where the bitmap just have been drawn by the regular Paint event of the control. That doesn't affect the bitmap.
You can use the DrawToBitmap method to render the control to a bitmap. That will include the rectangle, and it will also include any scaling/cropping that the control does when it renders the image:
using (Bitmap b = new Bitmap(pictureBox1.Width, pictureBox1.Height)) {
pictureBox1.DrawToBitmap(b, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height));
b.Save(#"d:\testit.png", System.Drawing.Imaging.ImageFormat.Png);
}
It's because you don't change the Image, you change the output to the screen. You need to create a graphics object from the image with
Graphics g = Graphics.FromImage(pictureBox1.Image);
If you add a rectangle with that graphics object it should work.
Edit:
Replace the line where you save the image with this:
Graphics g = Graphics.FromImage(pictureBox1.Image);
using (Pen pen = new Pen(Color.Red, 2))
{
if (paintDemoButtonSwitch == true)
{
g.DrawRectangle(pen, rectToDrawOut.X, rectToDrawOut.Y, rectToDrawOut.Width, rectToDrawOut.Height);
}
}
pictureBox1.Image.Save(#"d:\testit.png", System.Drawing.Imaging.ImageFormat.Png);
I have two Bitmaps, named largeBmp and smallBmp. I want to draw smallBmp onto largeBmp, then draw the result onto the screen. SmallBmp's white pixels should be transparent. Here is the code I'm using:
public Bitmap Superimpose(Bitmap largeBmp, Bitmap smallBmp) {
Graphics g = Graphics.FromImage(largeBmp);
g.CompositingMode = CompositingMode.SourceCopy;
smallBmp.MakeTransparent();
int margin = 5;
int x = largeBmp.Width - smallBmp.Width - margin;
int y = largeBmp.Height - smallBmp.Height - margin;
g.DrawImage(smallBmp, new Point(x, y));
return largeBmp;
}
The problem is that the result winds up transparent wherever smallBmp was transparent! I just want to see through to largeBmp, not to what's behind it.
CompositingMode.SourceCopy is the problem here. You want CompositingMode.SourceOver to get alpha blending.
Specify the transparency color of your small bitmap. e.g.
Bitmap largeImage = new Bitmap();
Bitmap smallImage = new Bitmap();
--> smallImage.MakeTransparent(Color.White);
Graphics g = Graphics.FromImage(largeImage);
g.DrawImage(smallImage, new Point(10,10);
Winform copy image on top of another
private void timerFFTp_Tick(object sender, EventArgs e)
{
if (drawBitmap)
{
Bitmap bitmap = new Bitmap(_fftControl.Width, _fftControl.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
_fftControl.DrawToBitmap(bitmap, new Rectangle(0, 0, _fftControl.Width, _fftControl.Height));
if (!fDraw)
{
bitmap.MakeTransparent();
Bitmap fftFormBitmap = new Bitmap(_fftForm.BackgroundImage);
Graphics g = Graphics.FromImage(fftFormBitmap);
g.DrawImage(bitmap, 0, 0);
_fftForm.BackgroundImage = fftFormBitmap;
}
else
{
fDraw = false;
_fftForm.Width = bitmap.Width + 16;
_fftForm.Height = bitmap.Height + 48;
_fftForm.BackgroundImage = bitmap;
}
}
}
What is the best (least resource heavy) way to fade an image in and out every 20 seconds with a duration of 1 second, against a black background (screensaver), in C# ?
(an image about 350x130px).
I need this for a simple screensaver that's going to run on some low level computers (xp).
Right now I'm using this method against a pictureBox, but it is too slow:
private Image Lighter(Image imgLight, int level, int nRed, int nGreen, int nBlue)
{
Graphics graphics = Graphics.FromImage(imgLight);
int conversion = (5 * (level - 50));
Pen pLight = new Pen(Color.FromArgb(conversion, nRed,
nGreen, nBlue), imgLight.Width * 2);
graphics.DrawLine(pLight, -1, -1, imgLight.Width, imgLight.Height);
graphics.Save();
graphics.Dispose();
return imgLight;
}
You could probably use a Color Matrix like in this example on msdn
http://msdn.microsoft.com/en-us/library/w177ax15%28VS.71%29.aspx
Instead of using a Pen and the DrawLine() method, you can use Bitmap.LockBits to access the memory of your image directly. Here's a good explanation of how it works.
Put a Timer on your form, and in the constructor, or the Form_Load,
write
timr.Interval = //whatever interval you want it to fire at;
timr.Tick += FadeInAndOut;
timr.Start();
Add a private method
private void FadeInAndOut(object sender, EventArgs e)
{
Opacity -= .01;
timr.Enabled = true;
if (Opacity < .05) Opacity = 1.00;
}
Here's my take on this
private void animateImageOpacity(PictureBox control)
{
for(float i = 0F; i< 1F; i+=.10F)
{
control.Image = ChangeOpacity(itemIcon[selected], i);
Thread.Sleep(40);
}
}
public static Bitmap ChangeOpacity(Image img, float opacityvalue)
{
Bitmap bmp = new Bitmap(img.Width, img.Height); // Determining Width and Height of Source Image
Graphics graphics = Graphics.FromImage(bmp);
ColorMatrix colormatrix = new ColorMatrix {Matrix33 = opacityvalue};
ImageAttributes imgAttribute = new ImageAttributes();
imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
graphics.DrawImage(img, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute);
graphics.Dispose(); // Releasing all resource used by graphics
return bmp;
}
It's also recommended to create another thread because this will freeze your main one.