First of all, I'd like to suggest that it is not a duplicate of THIS question. At least that's my opinion :)
What I want to achieve is a series of frames to "fade" animation.
I choose two PNG files (let' say they are the same size), for example:
Picture 1
Picture 2
I want to "simulate" merging them like layers in graphic editor. I put Pic1 on the top with opacity 255, Pic2 below with opacity 0, so at first I see only Pic1. Then I change their opacity, like this:
Pic1-200, Pic2-150
Pic1-150, Pic2-200
Pic1-100, Pic2-230
Is there any simple way for it?
In a winforms app this can be done pretty easily. Create a user control with a few properties:
public Image FromImage { get; set; }
public Image ToImage { get; set; }
private float opacity = 1;
Now override OnPaint
protected override void OnPaint(PaintEventArgs e)
{
if (FromImage != null && ToImage != null)
{
ColorMatrix matrix1 = new ColorMatrix();
matrix1.Matrix33 = opacity;
ImageAttributes attributes1 = new ImageAttributes();
attributes1.SetColorMatrix(matrix1, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
ColorMatrix matrix2 = new ColorMatrix();
matrix2.Matrix33 = 1 - opacity;
ImageAttributes attributes2 = new ImageAttributes();
attributes2.SetColorMatrix(matrix2, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
e.Graphics.DrawImage(FromImage, new Rectangle(0, 0, this.Width, this.Height), 0, 0, this.Width,
this.Height, GraphicsUnit.Pixel, attributes1);
e.Graphics.DrawImage(ToImage, new Rectangle(0, 0, this.Width, this.Height), 0, 0, this.Width,
this.Height, GraphicsUnit.Pixel, attributes2);
}
base.OnPaint(e);
}
Now drop a timer onto the control, set its to enabled with an elapsed time of something like 100ms. Handle the tick event:
private void timer_Tick(object sender, EventArgs e)
{
if(opacity == 0)
{
this.timer.Stop();
return;
}
this.opacity -= 0.01f;
this.Invalidate();
}
et voila. However, there's one thing to be aware of. This makes quite a flickery transition, which can be alieviated somewhat with this line in the control's constructor:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint,true);
Update based on Edit: You could turn this into a utility that takes 2 images and, using much the same code, outputs each step to a new image. Somthing like:
public class ImageUtility
{
private Image image1;
private Image image2;
public ImageUtility(Image image1, Image image2)
{
this.image1 = image1;
this.image2 = image2;
}
public void SaveTransitions(int numSteps, string outDir)
{
var opacityChange = 1.0f/(float) numSteps;
for(float opacity = 1,i=0;opacity>0;opacity-=opacityChange,i++)
{
using(var image = new Bitmap(image1.Width,image2.Width))
{
Graphics g = Graphics.FromImage(image);
ColorMatrix matrix1 = new ColorMatrix();
matrix1.Matrix33 = opacity;
ImageAttributes attributes1 = new ImageAttributes();
attributes1.SetColorMatrix(matrix1, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
ColorMatrix matrix2 = new ColorMatrix();
matrix2.Matrix33 = 1 - opacity;
ImageAttributes attributes2 = new ImageAttributes();
attributes2.SetColorMatrix(matrix2, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(image1, new Rectangle(0, 0, image1.Width, image1.Height), 0, 0, image1.Width,
image1.Height, GraphicsUnit.Pixel, attributes1);
g.DrawImage(image2, new Rectangle(0, 0, image2.Width, image2.Height), 0, 0, image2.Width,
image2.Height, GraphicsUnit.Pixel, attributes2);
image.Save(Path.Combine(outDir,"Image" + i + ".png"),ImageFormat.Png);
}
}
}
Usage:
ImageUtility util = new ImageUtility(Image.FromFile(#"C:\path\pic1.png"), Image.FromFile(#"C:\path\pic2.png"));
util.SaveTransitions(100, #"C:\path\output"); // saves 100 images
Using winforms you can use Graphics.DrawImage, using the overload that takes an ImageAttributes parameter. That class can specify manipulation to the colour (and alpha) values.
The example on the ImageAttributes page is nearly what you want. Just draw the original and transformed one in the same place, and change the colour matrix to only change the alpha level.
Related
I'm trying to make an image cropping tool.
I am following this https://www.codeproject.com/Articles/703519/Cropping-Particular-Region-In-Image-Using-Csharp
But since it's a bit old and the plugin/DLL's it uses have changed I have been trying to adapt his code from OpenCvSharp 2.0 to OpenCvSharp 2.4
When I'm converting the bitmaps to IplImages and using Cv.Mul() it gives me this error:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
I have never used OpenCvSharp other ways of creating the IplImage even reading the IplImage from a written image.
Code:
public static IplImage BitmapToIplImage(Bitmap bitmap)
{
IplImage tmp, tmp2;
System.Drawing.Rectangle bRect = new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp = Cv.CreateImage(Cv.Size(bitmap.Width, bitmap.Height), BitDepth.U8, 3);
tmp2 = Cv.CreateImage(Cv.Size(bitmap.Width, bitmap.Height), BitDepth.U8, 1);
byte[] data = new byte[Math.Abs(bmData.Stride * bmData.Height)];
tmp.SetData(bmData.Scan0, data.Length);
bitmap.UnlockBits(bmData);
// Cv.CvtColor(tmp, tmp2, ColorConversion.RgbToGray);
return tmp;
}
private void CropImage()
{
IplImage ipl = Cv.CreateImage(new CvSize(curBmp.Width, curBmp.Height), BitDepth.U8, 3);
Graphics ga = Graphics.FromImage(curBmp);
ga.FillRectangle(new SolidBrush(System.Drawing.Color.Black), new System.Drawing.Rectangle(0, 0, curBmp.Width, curBmp.Height));
SolidBrush brush = new SolidBrush(System.Drawing.Color.FromArgb(1, 1, 1));
curGraphics.FillClosedCurve(brush, imagePoints.ToArray());
Cv.Mul(BitmapToIplImage(curOgBmp), BitmapToIplImage(curBmp), ipl, 1);
ComputeCrop();
Stream s = null;
ipl.ToStream(s, ".png", null);
curBmp = new Bitmap(s);
RefreshImageViewer();
}
-----------------------------EDIT-----------------------------------------
I tried to follow what Markus posted, and I got it to work without any errors in the code.
Although the image cropped is a bit strange here are the methods I use, plus the RefreshImageViewer that is how I put the bitmap in the image control.
I have been trying to see if I missed something for hours, but I think not.
Output example: Imgur image link
Code:
public void RefreshImageViewer()
{
bmpSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
curBmp.GetHbitmap(),
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(curBmp.Width, curBmp.Height));
imageViewer.Source = bmpSource;
curGraphics = Graphics.FromImage(curBmp);
}
private void CropImage()
{
Graphics Ga = Graphics.FromImage(curBmp);
//the black image
Ga.FillRectangle(new SolidBrush(System.Drawing.Color.Black), new System.Drawing.Rectangle(0, 0, curBmp.Width, curBmp.Height));
//draw from the last point to first point
Ga.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Red, 3), imagePoints[imagePoints.Count - 1], imagePoints[0]);
//all of the rgb values are being set 1 inside the polygon
SolidBrush Brush = new SolidBrush(System.Drawing.Color.FromArgb(1, 1, 1));
//we have to prepare one mask of Multiplying operation for cropping region
curGraphics.FillPolygon(Brush, imagePoints.ToArray());
Mat accc = (BitmapToMat(curOgBmp).Mul(BitmapToMat(curBmp))).ToMat();
System.Drawing.Rectangle r = ComputeCrop();
curBmp = accc.ToBitmap().Clone(r, curOgBmp.PixelFormat);
RefreshImageViewer();
}
private System.Drawing.Rectangle ComputeCrop()
{
int smallestX = curBmp.Width, biggestX = 0, biggestY = 0, smallestY = curBmp.Height;
for (int i = 0; i < imagePoints.Count; i++)
{
biggestX = Math.Max(biggestX, imagePoints[i].X);
smallestX = Math.Min(smallestX, imagePoints[i].X);
biggestY = Math.Max(biggestY, imagePoints[i].Y);
smallestY = Math.Min(smallestY, imagePoints[i].Y);
}
System.Drawing.Rectangle rectCrop = new System.Drawing.Rectangle(smallestX, smallestY, biggestX - smallestX, biggestY - smallestY);
return rectCrop;
}
public static Mat BitmapToMat(Bitmap bitmap)
{
Mat tmp, tmp2;
System.Drawing.Rectangle bRect = new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp2 = new Mat(new OpenCvSharp.Size(bitmap.Width, bitmap.Height), MatType.CV_8U);
tmp = new Mat(bitmap.Height, bitmap.Width, MatType.CV_8UC3, bmData.Scan0);
bitmap.UnlockBits(bmData);
return tmp;
}
“IplImage” ist the old image container from OpenCv 1. As Andreas already mentioned, today you should use “Mat” instead. Have also a look here : Difference between cvMat, Mat and IpImage
Unfortunately, your code sample is not complete, hence I corrected the two methods from the original project ( https://www.codeproject.com/Articles/703519/Cropping-Particular-Region-In-Image-Using-Csharp).
The following methods are tested and work as intended in the original project in combination with the latest OpenCVSharp version (v4.x) . It should be very simple now to convert the changes to your code.
public static Mat BitmapToIplImage(Bitmap bitmap)
{
Mat tmp, tmp2;
Rectangle bRect = new Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp2 = new Mat(new Size(bitmap.Width, bitmap.Height), MatType.CV_8U);
tmp = new Mat(bitmap.Height, bitmap.Width, MatType.CV_8UC3, bmData.Scan0);
bitmap.UnlockBits(bmData);
return tmp;
}
private void crop()
{
timer1.Stop();
Graphics Ga = Graphics.FromImage(bmp);
//the black image
Ga.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, 0, bmp.Width, bmp.Height));
//draw from the last point to first point
Ga.DrawLine(new Pen(Color.Red, 3), polygonPoints[polygonPoints.Count - 1], polygonPoints[0]);
//all of the rgb values are being set 1 inside the polygon
SolidBrush Brush = new SolidBrush(Color.FromArgb(1, 1, 1));
//we have to prepare one mask of Multiplying operation for cropping region
G.FillClosedCurve(Brush, polygonPoints.ToArray());
var accc= (BitmapToIplImage(Source).Mul(BitmapToIplImage(bmp))).ToMat();
computecrop();
croplast = accc.ToBitmap().Clone(rectcrop, Source.PixelFormat);//just show cropped region part of image
pictureBox2.Image = croplast; // crop region of image
}
I need to draw over a bitmap loaded in a PictureBox. I've been looking for a way on this site but the answers don't work because the resulting image is in a wrong location.
private void pbImage_MouseClick(object sender, MouseEventArgs e)
{
currentImage = pbImage.Image;
Bitmap imageAux = new Bitmap(currentImage.Width, currentImage.Height);
using (Graphics g = Graphics.FromImage(imageAux))
{
Brush brush = new SolidBrush(Color.Red);
Pen pen = new Pen(Color.Black, 10);
g.DrawImage(currentImage, pbImage.Location);
g.Flush();
if (e.Button == MouseButtons.Left)
{
Control control = (Control)sender;
punto = ((Control)sender).PointToScreen(new Point(e.X, e.Y)); ;
g.DrawRectangle(pen, new Rectangle(punto.X, punto.Y, 50, 50));
g.FillRectangle(brush, new Rectangle(punto.X, punto.Y, 50, 50));
g.Flush();
g.Dispose();
pbImage.Invalidate();
}
}
pbImage.Refresh();
currentImage.Save("C:\\prueba3.bmp");
imageAux.Save("C:\\prueba4.bmp");
pbImage.Image = (Image)imageAux.Clone();
}
You should create additional Bitmaps to draw.
Load the original in a Bitmap.
Create a bitmap to assign to the picturebox. (backpage)
Create/Generate a grayscale version of the original bitmap.
When it needs to draw:
Clear the picturebox bitmap
Draw the grayscale onto
Draw the 50, 50 rect of the original on the selection location in the picturebox bitmap
Refresh het picturebox
Here's an example, Add a picturebox to the Form and replace the filename with your image filename.
public partial class Form1 : Form
{
private Bitmap _originalBitmap;
private Bitmap _grayScaleBitmap;
private Bitmap _pictureBoxBitmap;
//private Brush brush = new SolidBrush(Color.Red);
private Pen pen = new Pen(Color.Black, 1);
private Point? _inspectRectStart;
public Form1()
{
InitializeComponent();
_originalBitmap = (Bitmap)Bitmap.FromFile("lin-png-256x256-Rafael_256x256_png-256x256.png");
_grayScaleBitmap = MakeGrayscale3(_originalBitmap);
_pictureBoxBitmap = new Bitmap(_originalBitmap.Width, _originalBitmap.Height);
DrawImage();
pictureBox1.Image = _pictureBoxBitmap;
}
private static Bitmap MakeGrayscale3(Bitmap original)
{
//create a blank bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//get a graphics object from the new image
Graphics g = Graphics.FromImage(newBitmap);
//create the grayscale ColorMatrix
ColorMatrix colorMatrix = new ColorMatrix(
new float[][]
{
new float[] {.3f, .3f, .3f, 0, 0},
new float[] {.59f, .59f, .59f, 0, 0},
new float[] {.11f, .11f, .11f, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
//create some image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color matrix attribute
attributes.SetColorMatrix(colorMatrix);
//draw the original image on the new image
//using the grayscale color matrix
g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
//dispose the Graphics object
g.Dispose();
return newBitmap;
}
private void DrawImage()
{
using (Graphics g = Graphics.FromImage(_pictureBoxBitmap))
{
g.Clear(Color.White); // if the bitmap has transparent parts
// draw the grayscale image.
g.DrawImage(_grayScaleBitmap, new Rectangle(0, 0, _originalBitmap.Width, _originalBitmap.Height), 0, 0, _originalBitmap.Width, _originalBitmap.Height, GraphicsUnit.Pixel);
// if there is a selection, draw it.
if (_inspectRectStart.HasValue)
{
var rect = new Rectangle(_inspectRectStart.Value, new Size(50, 50));
// draw the original bitmap in a rectangle.
g.DrawImage(_originalBitmap, _inspectRectStart.Value.X, _inspectRectStart.Value.Y, rect, GraphicsUnit.Pixel);
g.DrawRectangle(pen, rect);
}
}
pictureBox1.Refresh();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
// move the rectangle position
_inspectRectStart = e.Location;
// redraw image.
DrawImage();
}
}
I think, thats because g.DrawImage(currentImage, pbImage.Location); will draw the image relative on the imageAux with the actual location of the pbImage in relation to it's parent. try new Point(); instead.
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();
}
This seems like it should be simple, but I can't seem to find any way to do it. I have a custom WinForms control that has an overridden paint method that does some custom drawing.
I have a Bitmap in memory, and all I want to do is paint over the whole thing with a HashBrush, but preserve the alpha channel, so that the transparent parts of the bitmap don't get painted.
The bitmap in memory is not a simple shape, so it will not be feasible to define it as a set of paths or anything.
EDIT: In response to showing the code, there is a lot of code in the paint routine, so I'm only including a relevant snippet, which is the method in question. This method gets called from the main paint override. It accepts a list of images which are black transparency masks and combines them into one, then it uses a ColorMatrix to change the color of the combined image it created, allowing it to be overlayed on top of the background. All I want to accomplish is being able to also paint hashmarks on top of it.
private void PaintSurface(PaintEventArgs e, Image imgParent, List<Image> surfImgs, Rectangle destRect, ToothSurfaceMaterial material)
{
using (Bitmap bmp = new Bitmap(imgParent.Width, imgParent.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
{
using (Graphics g = Graphics.FromImage(bmp))
{
foreach (Image img in surfImgs)
{
g.DrawImage(img, System.Drawing.Point.Empty);
}
}
ColorMatrix matrix = new ColorMatrix(
new float[][] {
new float[] { 0, 0, 0, 0, 0},
new float[] { 0, 0, 0, 0, 0},
new float[] { 0, 0, 0, 0, 0},
new float[] { 0, 0, 0, 0.7f, 0},
new float[] { material.R / 255.0f,
material.G / 255.0f,
material.B / 255.0f,
0, 1}
});
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorMatrix(matrix);
Rectangle r = GetSizedRect(imgParent, destRect);
e.Graphics.DrawImage(bmp,
r,
0,
0,
bmp.Width,
bmp.Height,
GraphicsUnit.Pixel, imageAttr);
}
}
The solution I ended up using was the following method. First I combine the individual masks into one, then create a new Bitmap and paint the whole thing with the HatchBrush, finally iterate through the mask and set the alpha values on the newly generated bitmap based on the mask.
private Bitmap GenerateSurface(Image imgParent, List<Image> surfImgs, ToothSurfaceMaterial material)
{
Bitmap mask = new Bitmap(imgParent.Width, imgParent.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (Graphics g = Graphics.FromImage(mask))
{
foreach (Image img in surfImgs)
{
g.DrawImage(img, System.Drawing.Point.Empty);
}
}
Bitmap output = new Bitmap(mask.Width, mask.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (Graphics g = Graphics.FromImage(output))
{
if (material.HatchStyle != null)
{
HatchBrush hb = new HatchBrush((HatchStyle)material.HatchStyle, material.FgColor, material.BgColor);
g.FillRectangle(hb, new Rectangle(0, 0, output.Width, output.Height));
}
else
{
SolidBrush sb = new SolidBrush(material.FgColor);
g.FillRectangle(sb, new Rectangle(0, 0, output.Width, output.Height));
}
}
var rect = new Rectangle(0, 0, output.Width, output.Height);
var bitsMask = mask.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var bitsOutput = output.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
int offset = 0;
for (int y = 0; y < mask.Height; y++)
{
byte* ptrMask = (byte*)bitsMask.Scan0 + y * bitsMask.Stride;
byte* ptrOutput = (byte*)bitsOutput.Scan0 + y * bitsOutput.Stride;
for (int x = 0; x < mask.Width; x++)
{
offset = 4 * x + 3;
ptrOutput[offset] = (byte)(ptrMask[offset] * 0.7);
}
}
}
mask.UnlockBits(bitsMask);
output.UnlockBits(bitsOutput);
return output;
}
I think you don't need any ColorMatrix which is overkill, you just need a ColorMap, here is the code which may not suit your requirement but should give you the idea. That's because I possibly don't understand your problem well, if you have any problem, just leave some comment and I'll try to improve the answer:
ImageAttributes imgA = new ImageAttributes();
ColorMap cm = new ColorMap();
cm.OldColor = Color.Black
cm.NewColor = Color.FromArgb((byte)(0.7*255), Color.Green);
imgA.SetRemapTable(new ColorMap[] {cm });
GraphicsUnit gu = GraphicsUnit.Pixel;
g.DrawImage(imageToDraw,new Point[]{Point.Empty,
new Point(backImage.Width/2,0),
new Point(0,backImage.Height/2)},
Rectangle.Round(imageToDraw.GetBounds(ref gu)),
GraphicsUnit.Pixel, imgA);
the new Point[] is an array of 3 Points used to locate the destination Rectangle.
The code above is used to Draw the imageToDraw on top of the backImage and convert and color of Black to the color Green with Opacity = 70%. That's what you want to fulfill your code.
UPDATE
This may be what you want, in fact your code doesn't show what you want, it just shows what you have which doesn't implement anything related to your problem now. I deduce this from your very first description in your question. The input is an image with background color (which will be made partially transparent later) being Black. Now the output you want is an image with all the non-Black region being painted with a HatchBrush. This output will then be processed to turn the Black background to a partially transparent background.
public void PaintHatchBrush(Bitmap input, HatchBrush brush){
using(Graphics g = Graphics.FromImage(input)){
g.Clip = GetForegroundRegion(input, Color.Black);
GraphicsUnit gu = GraphicsUnit.Pixel;
g.FillRectangle(brush, input.GetBounds(ref gu));
}
}
//This is implemented using `GetPixel` which is not fast, but it gives you the idea.
public Region GetForegroundRegion(Bitmap input, Color backColor){
GraphicsPath gp = new GraphicsPath();
Rectangle rect = Rectangle.Empty;
bool jumpedIn = false;
for (int i = 0; i < bm.Height; i++) {
for (int j = 0; j < bm.Width; j++) {
Color c = bm.GetPixel(j, i);
if (c != backColor&&!jumpedIn) {
rect = new Rectangle(j, i, 1, 1);
jumpedIn = true;
}
if (jumpedIn && (c == backColor || j == bm.Width - 1)) {
rect.Width = j - rect.Left;
gp.AddRectangle(rect);
jumpedIn = false;
}
}
}
return new Region(gp);
}
//Usage
HatchBrush brush = new HatchBrush(HatchStyle.Percent30, Color.Green, Color.Yellow);
PaintHatchBrush(yourImage, brush);
//then yourImage will have HatchBrush painted on the surface leaving the Black background intact.
//This image will be used in the next process to turn the Black background into 70%
//opacity background as you did using ColorMatrix (or more simply using ColorMap as I posted previously)
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.