I will like to get a screen capture and save it in the format of png of the entire screen. How can I do that?
Could I use the Snipping Tool library to accomplish this? There are some tutorials on the internet that show you how to do this with windows forms and the image is in the format of bitmap.
Here's a little method to capture the contents of any screen.
private static void CaptureScreen(Screen window, string file)
{
try
{
Rectangle s_rect = window.Bounds;
using (Bitmap bmp = new Bitmap(s_rect.Width, s_rect.Height))
{
using (Graphics gScreen = Graphics.FromImage(bmp))
gScreen.CopyFromScreen(s_rect.Location, Point.Empty, s_rect.Size);
bmp.Save(file, System.Drawing.Imaging.ImageFormat.Png);
}
}
catch (Exception) { /*TODO: Any exception handling.*/ }
}
Example of usage:
CaptureScreen(Screen.PrimaryScreen, #"B:\exampleScreenshot.png");
EDIT: Coming back to this later I realized it's probably more useful to return an Image object from the function so you can choose how to use the captured bitmap.
I've also made the function a bit more robust now so that it can capture multiple screens (i.e. in a multi-monitor setup). It should accommodate screens of varying heights, but I can't test this myself.
public static Image CaptureScreens(params Screen[] screens) {
if (screens == null || screens.Length == 0)
throw new ArgumentNullException("screens");
// Order them in logical left-to-right fashion.
var orderedScreens = screens.OrderBy(s => s.Bounds.Left).ToList();
// Calculate the total width needed to fit all the screen into a single image
var totalWidth = orderedScreens.Sum(s => s.Bounds.Width);
// In order to handle screens of different sizes, make sure to make the Bitmap large enough to fit the tallest screen
var maxHeight = orderedScreens.Max(s => s.Bounds.Top + s.Bounds.Height);
var bmp = new Bitmap(totalWidth, maxHeight);
int offset = 0;
// Copy each screen to the bitmap
using (var g = Graphics.FromImage(bmp)) {
foreach (var screen in orderedScreens) {
g.CopyFromScreen(screen.Bounds.Left, screen.Bounds.Top, offset, screen.Bounds.Top, screen.Bounds.Size);
offset += screen.Bounds.Width;
}
}
return bmp;
}
New example:
// Capture all monitors and save them to file
CaptureScreens(Screen.AllScreens).Save(#"C:\Users\FooBar\screens.png");
Related
Yes yes... I've seen other posts related to this issue, and yes... I've googled about it.
But so far, I was not able to get to the result I need.
I'm loading a large image taken in 300 dpi, and I need to resize it.
I know... I know... dpi is relative and doesn't really matter... what matters are the dimensions in pixels:
DPI is essentially the number of pixels that correspond to an inch when the image is printed not when it is viewed on a screen. Therefore by increasing the DPI of the image, you do not increase the size of the image on the screen. You only increase the quality of print.
Even though the DPI information stored in the EXIF of an image is somewhat useless, it is causing me problems.
The image I'm resizing is losing the original exif information, including the horizontal and vertical resolution (dpi), and thus it is saving with a default of 96 dpi. Possible reason to this is that only JPEG and another format can hold metadata information.
The end image result is should look like this: 275x375 at 300dpi
Instead is looking like this: 275x375 at 96dpi
You can argue that they are they same, and I agree, but we have a corel draw script that used to load these images, and since this dpi information is different, it places it in different sizes on the document.
Here's what I'm using for resizing:
public System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
Bitmap result = new Bitmap(width, height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
That does the work very well, but loses the EXIF information.
Setting the SetResolution to SetResolution(300, 300) does not work!
I looked at reading and changing the EXIF information of an image, and I've tried:
public void setImageDpi(string Filename, string NewRes)
{
Image Pic;
PropertyItem[] PropertyItems;
byte[] bDescription = new Byte[NewRes.Length];
int i;
string FilenameTemp;
System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
EncoderParameters EncParms = new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");
// copy description into byte array
for (i = 0; i < NewRes.Length; i++) bDescription[i] = (byte)NewRes[i];
// load the image to change
Pic = Image.FromFile(Filename);
foreach (PropertyItem item in Pic.PropertyItems)
{
if (item.Id == 282 || item.Id == 283)
{
PropertyItem myProperty = item;
myProperty.Value = bDescription;
myProperty.Type = 2;
myProperty.Len = NewRes.Length;
Pic.SetPropertyItem(item);
Console.WriteLine(item.Type);
}
}
// we cannot store in the same image, so use a temporary image instead
FilenameTemp = Filename + ".temp";
// for lossless rewriting must rotate the image by 90 degrees!
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
EncParms.Param[0] = EncParm;
// now write the rotated image with new description
Pic.Save(FilenameTemp, CodecInfo, EncParms);
// for computers with low memory and large pictures: release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the original file, will be replaced later
System.IO.File.Delete(Filename);
// now must rotate back the written picture
Pic = Image.FromFile(FilenameTemp);
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate270);
EncParms.Param[0] = EncParm;
Pic.Save(Filename, CodecInfo, EncParms);
// release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the temporary picture
System.IO.File.Delete(FilenameTemp);
}
That didn't work either.
I tried looking and changing the EXIF information for DPI (282 and 283) later in the process as such:
Encoding _Encoding = Encoding.UTF8;
Image theImage = Image.FromFile("somepath");
PropertyItem propItem282 = theImage.GetPropertyItem(282);
propItem282.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem282);
PropertyItem propItem283 = theImage.GetPropertyItem(283);
propItem283.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem283);
theImage.Save("somepath");
But the program crashes saying that Property Cannot be Found.
If the property doesn't exist, apparently I can't add it:
A PropertyItem is not intended to be used as a stand-alone object. A PropertyItem object is intended to be used by classes that are derived from Image. A PropertyItem object is used to retrieve and to change the metadata of existing image files, not to create the metadata. Therefore, the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.
I'm stuck... all I need is a resized image with a dpi set to 300, it shouldn't be so hard.
Any help much appreciated. Thanks
The following code worked for me:
const string InputFileName = "test_input.jpg";
const string OutputFileName = "test_output.jpg";
var newSize = new Size(640, 480);
using (var bmpInput = Image.FromFile(InputFileName))
{
using (var bmpOutput = new Bitmap(bmpInput, newSize))
{
foreach (var id in bmpInput.PropertyIdList)
bmpOutput.SetPropertyItem(bmpInput.GetPropertyItem(id));
bmpOutput.SetResolution(300.0f, 300.0f);
bmpOutput.Save(OutputFileName, ImageFormat.Jpeg);
}
}
When I inspect the output file I can see EXIF data and the DPI has been changed to 300.
The error with the following code is, it only find, partially, the first image itself.
Let me be more clear, I took a screenshot of an image, cropped it and putted it on the desktop. Then I saved the actual original image in full size (not cropped) on desktop as well.
When I run the code, it correctly detects all the coordinates of the cropped image on the desktop, however, when I open the full size image and hover it, it won't detect anything and return null, while it should be able to detect the pixels location inside the full size image as well.
I'm gonna post a video of what I mean, to be more clear:
https://www.youtube.com/watch?v=Ha3eGxWcAF8
As you see, it does not detect the pixels on the main image.
What's wrong on there?
Why it detects only the loaded cropped image, instead of a position on the screenshot based on the actual full fize image?
Code below:
public static Point? Find(Bitmap first, Bitmap second)
{
if (null == first || null == second)
{
return null;
}
if (first.Width < second.Width || first.Height < second.Height)
{
return null;
}
var firstArray = GetPixelArray(first);
var secondArray = GetPixelArray(second);
foreach (var firstLineMatchPoint in FindMatch(firstArray.Take(first.Height - second.Height), secondArray[0]))
{
if (IssecondPresentAtLocation(firstArray, secondArray, firstLineMatchPoint, 1))
{
return firstLineMatchPoint;
}
}
return null;
}
private static int[][] GetPixelArray(Bitmap bitmap)
{
var result = new int[bitmap.Height][];
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
for (int y = 0; y < bitmap.Height; ++y)
{
result[y] = new int[bitmap.Width];
Marshal.Copy(bitmapData.Scan0 + y * bitmapData.Stride, result[y], 0, result[y].Length);
}
bitmap.UnlockBits(bitmapData);
return result;
}
private static IEnumerable<Point> FindMatch(IEnumerable<int[]> firstLines, int[] secondLine)
{
var y = 0;
foreach (var firstLine in firstLines)
{
for (int x = 0, n = firstLine.Length - secondLine.Length; x < n; ++x)
{
if (ContainSameElements(firstLine, x, secondLine, 0, secondLine.Length))
{
yield return new Point(x, y);
}
}
y += 1;
}
}
private static bool ContainSameElements(int[] first, int firstStart, int[] second, int secondStart, int length)
{
for (int i = 0; i < length; ++i)
{
if (first[i + firstStart] != second[i + secondStart])
{
return false;
}
}
return true;
}
private static bool IssecondPresentAtLocation(int[][] first, int[][] second, Point point, int alreadyVerified)
{
//we already know that "alreadyVerified" lines already match, so skip them
for (int y = alreadyVerified; y < second.Length; ++y)
{
if (!ContainSameElements(first[y + point.Y], point.X, second[y], 0, second.Length))
{
return false;
}
}
return true;
}
static void Main(string[] args)
{
int counter = 1;
while (true)
{
Console.WriteLine("Executing pixel search");
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
Bitmap bitmap2 = (Bitmap)Image.FromFile(#"path_to_desktop\cropped.jpg", true);
if (Find(bitmap, bitmap2) == null)
{
Console.WriteLine("Not found.");
}
else {
Console.WriteLine($"{Find(bitmap, bitmap2)}.");
}
System.Threading.Thread.Sleep(500); // loop to detect new position
counter++;
}
}
Edit 7.11.2020: I published a Sample Project and Video as a result of this question:
Sample:
https://github.com/DataJuggler/SubImageCreator
Could be called SubImageSearcher also.
Video:
How To Search Images Using C# And DataJuggler.PixelDatabase
https://youtu.be/JKc7QtdaxWY
Search for a sub image in a larger image using C#.
Nuget Updates
DataJuggler.PixelDatabase .Net Core
DataJuggler.PixelDatabase.Net .Net Framework
// Return a sub image after a PixelDatabase is loaded
Bitmap subImage = CreateSubImage(Point topLeft, Rectangle size);
// Search for a sub image inside a larger image
SearchResult result = PixelDatabase.SearchForSubImage(bitmap, searchDepth);
Search Result contains a Point of the top left and a score. A score of zero is a perfect match.
The sample project draws a little yellow rectangle on the Top Left hand corner if the sub image is found inside the larger image.
--
End Edit
Are the images something you can share? If not a sample image might give something to work with. If yes, tell me what you are trying to find and maybe I can find "where the pixels are you are searching for".
Another thing to watch out for is if either of the images were stretched, any difference like that could distort exact pixel colors and locations.
Are you using .Net Framework or .Net Core?
I have a Nuget package that might help you with this:
Nuget:
DataJuggler.PixelDatabase .Net Core
DataJuggler.PixelDatabase.Net .Net Framework
Both packages were updated yesterday.
Showing a .Net Core example
using DataJuggler.PixelDatabase;
// local
DataJuggler.PixelDatabase.PixelDatabase pixelDatabase = null;
// Load the pixelDatabase (you can also pass in a Bitmap or an Image also)
pixelDatabase = PixelDatabaseLoader.LoadPixelDatabase(FullImagePath, null);
// get information about a pixel at the coordinates given
PixelInformation pixel = PixelDatabase.GetPixel(x, y);
The pixel information object contains the Red Green and Blue colors, plus the X, Y and a lot of read only properties such as Total, BlueRed, MinMaxDifference, etc., that make querying and updating Bitmaps pretty simple.
Another thing you could do is if you know of a certain color you are looking for:
// color
Color color = Color.FromArgb(128, 55, 92);
// get the pixels that match a certain color
List<PixelInformation> pixels = PixelDatabase.GetPixels(color);
That might help you locate certain groups of pixels in the image.
The same code that powers the Nuget packages is also used on my site:
https://PixelDatabase.Net
The site and the Nuget packages use a language called Bitmap Query Language, or BQL for short. This is very similar to SQL for databases if you have learned that yet.
There are lots of help and videos, but if you do any Pixel querying, I think you will find it worth the price of free.
The full source code is on Git Hub:
https://github.com/DataJuggler/PixelDatabase.Net .Net Framework
https://github.com/DataJuggler/PixelDatabase .Net Core
And Help is located here:
https://pixeldatabase.net/Help
And lots of videos here:
https://www.youtube.com/playlist?list=PLKrW5tXCPiX2PxrLPszDzlcEZwQG-Qb8r
Yes yes... I've seen other posts related to this issue, and yes... I've googled about it.
But so far, I was not able to get to the result I need.
I'm loading a large image taken in 300 dpi, and I need to resize it.
I know... I know... dpi is relative and doesn't really matter... what matters are the dimensions in pixels:
DPI is essentially the number of pixels that correspond to an inch when the image is printed not when it is viewed on a screen. Therefore by increasing the DPI of the image, you do not increase the size of the image on the screen. You only increase the quality of print.
Even though the DPI information stored in the EXIF of an image is somewhat useless, it is causing me problems.
The image I'm resizing is losing the original exif information, including the horizontal and vertical resolution (dpi), and thus it is saving with a default of 96 dpi. Possible reason to this is that only JPEG and another format can hold metadata information.
The end image result is should look like this: 275x375 at 300dpi
Instead is looking like this: 275x375 at 96dpi
You can argue that they are they same, and I agree, but we have a corel draw script that used to load these images, and since this dpi information is different, it places it in different sizes on the document.
Here's what I'm using for resizing:
public System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
Bitmap result = new Bitmap(width, height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
That does the work very well, but loses the EXIF information.
Setting the SetResolution to SetResolution(300, 300) does not work!
I looked at reading and changing the EXIF information of an image, and I've tried:
public void setImageDpi(string Filename, string NewRes)
{
Image Pic;
PropertyItem[] PropertyItems;
byte[] bDescription = new Byte[NewRes.Length];
int i;
string FilenameTemp;
System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
EncoderParameters EncParms = new EncoderParameters(1);
EncoderParameter EncParm;
ImageCodecInfo CodecInfo = GetEncoderInfo("image/jpeg");
// copy description into byte array
for (i = 0; i < NewRes.Length; i++) bDescription[i] = (byte)NewRes[i];
// load the image to change
Pic = Image.FromFile(Filename);
foreach (PropertyItem item in Pic.PropertyItems)
{
if (item.Id == 282 || item.Id == 283)
{
PropertyItem myProperty = item;
myProperty.Value = bDescription;
myProperty.Type = 2;
myProperty.Len = NewRes.Length;
Pic.SetPropertyItem(item);
Console.WriteLine(item.Type);
}
}
// we cannot store in the same image, so use a temporary image instead
FilenameTemp = Filename + ".temp";
// for lossless rewriting must rotate the image by 90 degrees!
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
EncParms.Param[0] = EncParm;
// now write the rotated image with new description
Pic.Save(FilenameTemp, CodecInfo, EncParms);
// for computers with low memory and large pictures: release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the original file, will be replaced later
System.IO.File.Delete(Filename);
// now must rotate back the written picture
Pic = Image.FromFile(FilenameTemp);
EncParm = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate270);
EncParms.Param[0] = EncParm;
Pic.Save(Filename, CodecInfo, EncParms);
// release memory now
Pic.Dispose();
Pic = null;
GC.Collect();
// delete the temporary picture
System.IO.File.Delete(FilenameTemp);
}
That didn't work either.
I tried looking and changing the EXIF information for DPI (282 and 283) later in the process as such:
Encoding _Encoding = Encoding.UTF8;
Image theImage = Image.FromFile("somepath");
PropertyItem propItem282 = theImage.GetPropertyItem(282);
propItem282.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem282);
PropertyItem propItem283 = theImage.GetPropertyItem(283);
propItem283.Value = _Encoding.GetBytes("300" + '\0');
theImage.SetPropertyItem(propItem283);
theImage.Save("somepath");
But the program crashes saying that Property Cannot be Found.
If the property doesn't exist, apparently I can't add it:
A PropertyItem is not intended to be used as a stand-alone object. A PropertyItem object is intended to be used by classes that are derived from Image. A PropertyItem object is used to retrieve and to change the metadata of existing image files, not to create the metadata. Therefore, the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.
I'm stuck... all I need is a resized image with a dpi set to 300, it shouldn't be so hard.
Any help much appreciated. Thanks
The following code worked for me:
const string InputFileName = "test_input.jpg";
const string OutputFileName = "test_output.jpg";
var newSize = new Size(640, 480);
using (var bmpInput = Image.FromFile(InputFileName))
{
using (var bmpOutput = new Bitmap(bmpInput, newSize))
{
foreach (var id in bmpInput.PropertyIdList)
bmpOutput.SetPropertyItem(bmpInput.GetPropertyItem(id));
bmpOutput.SetResolution(300.0f, 300.0f);
bmpOutput.Save(OutputFileName, ImageFormat.Jpeg);
}
}
When I inspect the output file I can see EXIF data and the DPI has been changed to 300.
I have a .png image i wish to overlay on a base image.
My overlay image contains just a red slant line. I need to get the red line overlayed on the base image at the same co-ordinate as it is in overlay image.
The problem is I do not have the co-ordinates location.
I need to find it programmatically with C#. The overlay image will always be transparent or of white background. What code to find the line co-ordinates from overlay image?
You can create new image, render background image first and then render overlay image over it. Since overlay has alpha channel and line is placed where it should be (i mean there is opaque space on top and left side of line) you do not need coordinates. Illustration code:
Image imageBackground = Image.FromFile("bitmap1.png");
Image imageOverlay = Image.FromFile("bitmap2.png");
Image img = new Bitmap(imageBackground.Width, imageBackground.Height);
using (Graphics gr = Graphics.FromImage(img))
{
gr.DrawImage(imageBackground, new Point(0, 0));
gr.DrawImage(imageOverlay, new Point(0, 0));
}
img.Save("output.png", ImageFormat.Png);
If you are using WPF, why not use a path for your overlay instead of an image if it is a simple line? This would allow it to scale to any size, and has methods for manipulating its dimensions.
If you are using Winforms, there are some similar graphics drawing capabilities you might leverage. Getting the dimensions of the image should be easy, assuming you're using a PictureBox, the following properties should suffice:
myPictureBox.Top
myPictureBox.Bottom
myPictureBox.Left
myPictureBox.Right
Similarly, for a WPF Image:
myImage.Margin
I already needed to create a blank space around an image and I used the ImageFactory library to do that.
Here is the code. I guess you are capable to figure out how to adjust to your needs.
public static Image ResizedImage(Image image, int desiredWidth, int desiredHeight)
{
Image res = (Bitmap)image.Clone();
Image resizedImage;
ImageLayer imageLayer = new ImageLayer();
try
{
if (res != null)
{
//white background
res = new Bitmap(desiredWidth, desiredHeight, res.PixelFormat);
//image to handle
using (var imgf = new ImageFactory(true))
{
imgf
.Load(image)
.Resize(new ResizeLayer(new Size(desiredWidth, desiredHeight),
ResizeMode.Max,
AnchorPosition.Center,
false));
resizedImage = (Bitmap)imgf.Image.Clone();
}
//final image
if (resizedImage != null)
{
imageLayer.Image = resizedImage;
imageLayer.Size = new Size(resizedImage.Width, resizedImage.Height);
imageLayer.Opacity = 100;
using (var imgf = new ImageFactory(true))
{
imgf
.Load(res)
.BackgroundColor(Color.White)
.Overlay(imageLayer);
res = (Bitmap)imgf.Image.Clone();
}
}
}
}
catch (Exception ex)
{
ex.Message;
}
return res;
}
I have the following task. Take a base image and overlay on it another one. The base image is 8b png as well as overlay.
Here are the base (left) and overlay (right) images.
Here is a result and how it must look.
The picture in the left is a screenshot when one picture is on top of another (html and positioning) and the second is the result of programmatic merging.
As you can see in the screenshot the borders of the text is darker. Also here are the sizes of the images
Base image 14.9 KB
Overlay image 6.87 KB
Result image 34.8 KB
The size of the resulting image is also huge
Here is my code that I use to merge those pictures
/*...*/
public Stream Concatinate(Stream baseStream, params Stream[] overlayStreams) {
var #base = Image.FromStream(baseStream);
var canvas = new Bitmap(#base.Width, #base.Height);
using (var g = canvas.ToGraphics()) {
g.DrawImage(#base, 0, 0);
foreach (var item in overlayStreams) {
using (var overlayImage = Image.FromStream(item)) {
try {
Overlay(#base as Bitmap, overlayImage as Bitmap, g);
} catch {
}
}
}
}
var ms = new MemoryStream();
canvas.Save(ms, ImageFormat.Png);
canvas.Dispose();
#base.Dispose();
return ms;
}
/*...*/
/*Tograpics extension*/
public static Graphics ToGraphics(this Image image,
CompositingQuality compositingQuality = CompositingQuality.HighQuality,
SmoothingMode smoothingMode = SmoothingMode.HighQuality,
InterpolationMode interpolationMode = InterpolationMode.HighQualityBicubic) {
var g = Graphics.FromImage(image);
g.CompositingQuality = compositingQuality;
g.SmoothingMode = smoothingMode;
g.InterpolationMode = interpolationMode;
return g;
}
private void Overlay(Bitmap source, Bitmap overlay, Graphics g) {
if (source.Width != overlay.Width || source.Height != overlay.Height)
throw new Exception("Source and overlay dimensions do not match");
var area = new Rectangle(0, 0, source.Width, source.Height);
g.DrawImage(overlay, area, area, GraphicsUnit.Pixel);
}
My questions are
What should I do in order to merge images to achieve the result as in the screenshot?
How can I lower the size of the result image?
Is the System.Drawing a suitable tool for this or is there any better tool for working with png for .NET?
The answers to your questions are:
1) Just call method ToGraphics with a argument CompositingQuality.Default instead of using default argument values like in the example:
using (var g = canvas.ToGraphics(compositingQuality: CompositingQuality.Default))
The problem is with CompositingQuality.HighQuality is that it makes a composition of both images into one, but you want to make an overlay, not to make a composition of two images.
2) The size will be similar to the one you specified and that can not be changed, it is due to a image format.
3) If you are programming in c# for desktop, than System.Drawing is the best choice as far as I know.