I have created the code below to help me print the admin form as a "report" like document showing the date and graph needed for documentation.
try
{
Graphics g = this.CreateGraphics();
AdminPage = new Bitmap(Size.Width, Size.Height, g);
Graphics Printed = Graphics.FromImage(AdminPage);
Printed.CopyFromScreen(519, 340, 0,0,this.Size);//519,340 this.Location.Y,this.Location.X
printPreviewAdminDialogue.ShowDialog();
}
catch(Exception )
{
MessageBox.Show("Please check printer connection!");
}
I have used the coordinate data of the screen as:
Printed.CopyFromScreen(519, 340, 0,0,this.Size);
Will this still work on any size screen or will this result in some formatting problems on other devices rather than just my laptop?
So far it looks fine Print Preview of what i want with the current code
This method takes a Control (SourceControl) reference and returns a Bitmap resulting from a screen capture of the Control's Window.
Parameters:
SourceControl: The control to be printed. It can be a TopLevel Window (a Form) or a Child Control.
Dpi: The DPI resolution of the resulting Bitmap.
ScaleToDpi: if set to true, the size of the Bitmap will be scaled to match the Dpi parameter, defining a scale factor relative to current screen resolution. E.g.: If parameter Dpi = 300 and the current screen resolution is 96 Dpi, the resulting Bitmap size will be scaled with a factor of 3.125
ClientAreaOnly: If true, captures the SourceControl client area, otherwise the full window bounds.
InterpolationMode: Defines the resulting quality of a scaled bitmap. For enlargements, Bicubic or HighQualityBicubic gives better results. Bicubic may render a shaper image. The usefulness of this parameter depends on how the image is used. The perceived visual quality may not be the same when the image is printed on paper. When printing, sharper images give a better result.
The results can be tested using a PrintPreviewControl to see the difference between a scaled and a non-scaled Bitmap.
Print the a Form including its borders with a resolution of 300 DPI but not scaled to the new resolution (modifies the resulting Bitmap resolution only):
Bitmap FullSize300Dpi = PrintControlFromScreen(this, false, 300, false, InterpolationMode.Default);
Print the a Form ClientArea with a resolution of 300 DPI and scale its dimensions to the new resolution using a HighQualityBicubic Interpolation:
Bitmap ClientArea300DpiScaled = PrintControlFromScreen(this, true, 300, true, InterpolationMode.HighQualityBicubic);
The same, but it prints the client area of a button1 control with a resolution of 96 DPI with a Bilinear Interpolation:
Bitmap Child96DpiUnscaled = PrintControlFromScreen(this.button1, true, 96, false, InterpolationMode.Bilinear);
public Bitmap PrintControlFromScreen(Control SourceControl, bool ClientAreaOnly, float Dpi, bool ScaleToDpi, InterpolationMode Interpolation)
{
using (Graphics graphics = SourceControl.CreateGraphics())
{
SizeF ScaleFactor = new SizeF((Dpi / graphics.DpiX), (Dpi / graphics.DpiY));
SizeF BitmapSize;
if (ScaleToDpi)
{
BitmapSize = ClientAreaOnly ? new SizeF((SourceControl.ClientRectangle.Size.Width * ScaleFactor.Width),
(SourceControl.ClientRectangle.Size.Height * ScaleFactor.Height))
: new SizeF((SourceControl.Bounds.Size.Width * ScaleFactor.Width),
(SourceControl.Bounds.Size.Height * ScaleFactor.Height));
}
else
{
BitmapSize = ClientAreaOnly ? SourceControl.ClientRectangle.Size : SourceControl.Bounds.Size;
}
using (Bitmap bitmap = new Bitmap((int)BitmapSize.Width, (int)BitmapSize.Height))
{
bitmap.SetResolution(ScaleFactor.Width * graphics.DpiX, ScaleFactor.Height * graphics.DpiY);
using (Graphics ImageGraph = Graphics.FromImage(bitmap))
{
ImageGraph.CompositingQuality = CompositingQuality.HighQuality;
ImageGraph.CompositingMode = CompositingMode.SourceCopy;
ImageGraph.SmoothingMode = SmoothingMode.HighQuality;
ImageGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
ImageGraph.InterpolationMode = Interpolation;
if (ClientAreaOnly)
{
ImageGraph.CopyFromScreen(SourceControl.PointToScreen(SourceControl.ClientRectangle.Location),
new Point(0, 0), SourceControl.ClientRectangle.Size);
}
else
{
if (SourceControl.TopLevelControl == SourceControl)
{
ImageGraph.CopyFromScreen(SourceControl.Bounds.Location,
new Point(0, 0), SourceControl.Bounds.Size);
}
else
{
ImageGraph.CopyFromScreen(SourceControl.PointToScreen(SourceControl.ClientRectangle.Location),
new Point(0, 0), SourceControl.Size);
}
}
if (ScaleToDpi) ImageGraph.ScaleTransform(ScaleFactor.Width, ScaleFactor.Height);
ImageGraph.DrawImage(bitmap, new Point(0, 0));
return (Bitmap)bitmap.Clone();
};
};
};
}
UPDATE1 (Example of Print Preview):
This is one possible way to show a PrintPreview of the Bitmap. It should of course be adapted to an actual Printer. This is for general use.
A Bitmap is created in a Button event handler and a PrintPreview Dialog is shown to seee the result.
This creates a ScreenShot of a PictureBox control in the current Form
(this), takes the ClientArea only, sets the resuluton to 300Dpi, does
not scale the image (keeps the screen original size), using a Bicubic
Interpolation for rendering.
Bitmap screenCapture = PrintControlFromScreen(this.pictureBox1, true, 300, false, InterpolationMode.Bicubic);
This is the Button click handler from where you can call the PrintControlFromScreen() method:
private void button1_Click(object sender, EventArgs e)
{
Bitmap screenCapture = PrintControlFromScreen(this.pictureBox1, true, 300, false, InterpolationMode.Bicubic);
PrintDocument PrintDoc = new PrintDocument();
PrintDoc.DocumentName = "ScreenShot";
PrintDoc.DefaultPageSettings.PrinterResolution = new PrinterResolution() { X = 300, Y = 300 };
PrintDoc.DefaultPageSettings.Landscape = PrintDoc.DefaultPageSettings.PaperSize.Width < screenCapture.Width;
PrintDoc.OriginAtMargins = true;
PrintDoc.PrintPage += (s, ppe) =>
{
Rectangle BitmapSize = new Rectangle(new Point(0, 0),
new Size(screenCapture.Width, screenCapture.Height));
Graphics _imagegraph = Graphics.FromImage(screenCapture);
_imagegraph.CompositingMode = CompositingMode.SourceCopy;
_imagegraph.CompositingQuality = CompositingQuality.HighQuality;
_imagegraph.SmoothingMode = SmoothingMode.HighQuality;
_imagegraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
_imagegraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
ImageAttributes ImageAttr = new ImageAttributes();
ImageAttr.ClearThreshold(ColorAdjustType.Bitmap);
ppe.Graphics.DrawImage(screenCapture, BitmapSize, 0F, 0F,
BitmapSize.Width, BitmapSize.Height, GraphicsUnit.Pixel, ImageAttr);
};
PrintPreviewDialog pPreviewDiag = new PrintPreviewDialog();
pPreviewDiag.Document = PrintDoc;
pPreviewDiag.AutoScaleDimensions = new SizeF(Screen.PrimaryScreen.BitsPerPixel * 1.5F,
Screen.PrimaryScreen.BitsPerPixel * 1.5F);
pPreviewDiag.AutoScaleMode = AutoScaleMode.Dpi;
pPreviewDiag.StartPosition = FormStartPosition.CenterScreen;
pPreviewDiag.ShowDialog();
}
Related
I am creating EMF images in C# with scaling mode of 125% (Please refer the link updated in the bottom of the post to change the scaling mode in windows machine). The image size and resolution change, according to the machine's scaling settings regardless of the DPI settings in use in the code.
//Set the height and width of the EMF image
int imageWidth = 1280;
int imageHeight = 720;
//Adjust the witdh for the screen resoultion
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
imageWidth = (int)(imageWidth / 96.0 * graphics.DpiX);
imageHeight = (int)(imageHeight / 96.0 * graphics.DpiY);
}
Image image = null;
//Stream to create a EMF image
MemoryStream stream = new MemoryStream();
//Create graphics with bitmap and render the graphics in stream for EMF image
using (Bitmap bitmap = new Bitmap(imageWidth, imageHeight))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
IntPtr hdc = g.GetHdc();
Rectangle rect = new Rectangle(0, 0, imageWidth, imageHeight);
image = new Metafile(stream, hdc, rect, MetafileFrameUnit.Pixel, EmfType.EmfPlusDual);
g.ReleaseHdc();
}
}
using (Graphics graphics = Graphics.FromImage(image))
{
SetGraphicsProperties(graphics);
graphics.DrawRectangle(Pens.Black, new Rectangle(100, 100, 100, 100));
}
//This gives the expected resolution and file size regardless of scaling settigs of the windows machine
image.Save("RasterImage.emf");
image.Dispose();
stream.Position = 0;
//This gives the unexpected resolution and file size with different scaling settings in windows machine. Only works as expected when the scaling settings are set with 100%. Usually, I will dump this stream into a file-stream to get the vector graphics image.
Image streamImg = Image.FromStream(stream);// Just for example, I used this Image.FromStream() method to repoduce the issue.
streamImg.Save("StreamImage.emf");
streamImg.Dispose();
// Just to set the graphics properties
internal static void SetGraphicsProperties(Graphics graphics)
{
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.GammaCorrected;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PageUnit = GraphicsUnit.Pixel;
}
In the above code, I am saving two images.
RasterImage.emf
StreamImage.emf
Both images are created with different resolutions and file size. This makes some scaling issues in my drawings.
To change the display settings in windows please do the below,
RClick desktop-> Display Settings -> Scale and Layout (Choose 125%)
Link for changing the scaling - https://winaero.com/blog/set-display-custom-scaling-windows-10/
I am using windows 10. How to avoid these variations in image resolution and size?
Thanks,
Meikandan
I am developing an application for image processing. To zoom the image, I enlarge PictureBox. But after enlarging I get below image as result.
But I want result like below image
Here is my Code :
picturebox1.Size = new Size((int)(height * zoomfactor), (int)
(width* zoomfactor));
this.picturebox1.Refresh();
The PictureBox by itself will always create a nice and smooth version.
To create the effect you want you need to draw zoomed versions yourself. In doing this you need to set the
Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
Then no blurring will happen..
Example:
private void trackBar1_Scroll(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)pictureBox1.Image;
Size sz = bmp.Size;
Bitmap zoomed = (Bitmap)pictureBox2.Image;
if (zoomed != null) zoomed.Dispose();
float zoom = (float)(trackBar1.Value / 4f + 1);
zoomed = new Bitmap((int)(sz.Width * zoom), (int)(sz.Height * zoom));
using (Graphics g = Graphics.FromImage(zoomed))
{
if (cbx_interpol.Checked) g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.DrawImage(bmp, new Rectangle( Point.Empty, zoomed.Size) );
}
pictureBox2.Image = zoomed;
}
Of course you need to avoid setting the PBox to Sizemode Zoom or Stretch!
Going through the steps mentioned here
and using IDAutomationCode39, I am getting the barcode image, however they are very blurr and only scans bigger size images. My barcode id will be upto 30 characters long, which is causing a very wide barcode image. Where could the problem lie? Is it the IDAutomationCode39 or my setting in my button click event below?
private void button1_Click(object sender, EventArgs e)
{
string abbre = GenerateProdCodeFromProductName();
txt2.Text = abbre;
string barcode = txt1.Text;
Bitmap bitm = new Bitmap(barcode.Length * 45, 160);
bitm.SetResolution(240, 240);
using (Graphics graphic = Graphics.FromImage(bitm))
{
Font newfont = new Font("IDAutomationHC39M", 6);
PointF point = new PointF(5f, 5f);
SolidBrush black = new SolidBrush(Color.Black);
SolidBrush white = new SolidBrush(Color.White);
graphic.FillRectangle(white, 0, 0, bitm.Width, bitm.Height);
graphic.DrawString("*" + barcode + "*", newfont, black, point);
}
using (MemoryStream Mmst = new MemoryStream())
{
bitm.Save("ms", ImageFormat.Jpeg);
pictureBox1.Image = bitm;
pictureBox1.Width = bitm.Width;
pictureBox1.Height = bitm.Height;
}
}
Thank you.
I don't see any PlugIn reference in your code, you are just using a Code39 ASCII font to print a Barcode on a Bitmap.
The problem I see is that the size of the resulting Bitmap is unscaled.
What I mean is, you let the size of the Barcode determine the size of the final graphic image.
It is usually the opposite. You have some defined dimensions for a Bitmap, because of layout constraints: you have to print the barcode to a label, for example.
If the generated Barcode is wider than its container, you have to normalize it (scale it to fit).
Here is what I propose. The size of the Bitmap if fixed (300, 150). When the Barcode is generated, its size is scaled to fit the Bitmap size.
The quality of the image is preserved, using an Bicubic Interpolation in case of down scaling. The resulting graphics is also Anti-Aliased.
(Anti-Alias has a good visual render. May not be the best choice for printing. It also depends on the printer.)
The generated Bitmap is then passed to a PictureBox for visualization and saved to disk as a PNG image (this format because of its loss-less compression).
Here is the result:
string Barcode = "*8457QK3P9*";
using (Bitmap bitmap = new Bitmap(300, 150))
{
bitmap.SetResolution(240, 240);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
Font font = new Font("IDAutomationSHC39M", 10, FontStyle.Regular, GraphicsUnit.Point);
graphics.Clear(Color.White);
StringFormat stringformat = new StringFormat(StringFormatFlags.NoWrap);
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
graphics.TextContrast = 10;
PointF TextPosition = new PointF(10F, 10F);
SizeF TextSize = graphics.MeasureString(Barcode, font, TextPosition, stringformat);
if (TextSize.Width > bitmap.Width)
{
float ScaleFactor = (bitmap.Width - (TextPosition.X / 2)) / TextSize.Width;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.ScaleTransform(ScaleFactor, ScaleFactor);
}
graphics.DrawString(Barcode, font, new SolidBrush(Color.Black), TextPosition, StringFormat.GenericTypographic);
bitmap.Save(#"[SomePath]\[SomeName].png", ImageFormat.Png);
this.pictureBox1.Image = (Bitmap)bitmap.Clone();
font.Dispose();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
counter--;
DrawLetter();
if (counter == 0)
{
t.Stop();
TakeScreenShot();
}
}
private void DrawLetter()
{
var letter = counter.ToString();
Graphics g = Graphics.FromHdc(GetDC(IntPtr.Zero));
float width = ((float)this.ClientRectangle.Width);
float height = ((float)this.ClientRectangle.Width);
float emSize = height;
Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
SizeF size = g.MeasureString(letter.ToString(), font);
g.DrawString(letter, font, new SolidBrush(Color.White), (width - size.Width) / 2, 0);
}
private Font FindBestFitFont(Graphics g, String text, Font font, Size proposedSize)
{
// Compute actual size, shrink if needed
while (true)
{
SizeF size = g.MeasureString(text, font);
// It fits, back out
if (size.Height <= proposedSize.Height &&
size.Width <= proposedSize.Width) { return font; }
// Try a smaller font (90% of old size)
Font oldFont = font;
font = new Font(font.Name, (float)(font.Size * .9), font.Style);
oldFont.Dispose();
}
}
void TakeScreenShot()
{
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + #"\ScreenCaptures\newfile.png", ImageFormat.Png);
}
I am able to draw the string but it is writing on top of itself.
How can I clear it? Basically I want the countdown to appear on the screen then take a screenshot.
Right now the number is overwritten by another.
You can do the following: create an additional transparent form, and it will display timer values. This will allow you to erase the previous value. In addition, this will allow to get rid of the function call GetDC via PInvoke.
Form timerForm; // main form field
// Create and show additional transparent form before starting the timer
timerForm = new Form
{
FormBorderStyle = FormBorderStyle.None,
WindowState = FormWindowState.Maximized,
TransparencyKey = SystemColors.Control,
ShowInTaskbar = false
};
timerForm.Show();
timer.Start();
Change the method DrawLetter as follows
private void DrawLetter()
{
var letter = counter.ToString();
Graphics g = timerForm.CreateGraphics();
float width = ClientRectangle.Width;
float height = ClientRectangle.Width;
float emSize = height;
using (Font font1 = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular))
using (Font font2 = FindBestFitFont(g, letter, font1, ClientRectangle.Size))
using (var brush = new SolidBrush(Color.White))
{
SizeF size = g.MeasureString(letter, font2);
g.Clear(SystemColors.Control);
g.DrawString(letter, font2, brush, (width - size.Width) / 2, 0);
}
}
We must release all used resources like fonts and brushes. For this I applied using.
Change the timer tick event handler as follows
private void timer1_Tick(object sender, EventArgs e)
{
counter--;
DrawLetter();
if (counter == 0)
{
timer.Stop();
TakeScreenShot();
timerForm.Dispose(); // must release
}
}
FindBestFitFont and TakeScreenShot methods remain unchanged.
Draw your font to a different bitmap. Transparent background (or whatever doesn't invert, see below - perhaps black).
(now you could also draw it with a different colored shadow to mitigate drawing on similar colored background - but the natures of SRCINVERT/XOR, below, will mitigate this as well)
Use BitBlt to copy it to the screen
Use the SRCINVERT raster op.
(note: the colors may be different as it is XORing it with pixels underneath)
Now when is is time to erase, just make the same bitblt with the same contents as previous, the double XOR effect caused by SRCINVERT will have the effect of erasing it.
Then draw the next font.
Note: if desktop is updated between calls, all bets are off.
better...
Rather than attempting a transparent background, draw it on a white background. This will eliminate contrast issues with the font, eliminate concern with dynamic updates, and eliminate problems with erasing. Sometimes you have to admit - the method & code isn't the problem, the requirements are the problem. This all depends of course on the source of the requirements, etc.
If it needs to look professional, don't put the content on the screen, draw it after you take the screen capture.
If you end up using the transparent window approach, the screen shot may miss the transparent window. To get it, see this question:
Capture screenshot Including Semitransparent windows in .NET. (could be fixed by newer .net / newer windows versions)
You need to invalidate all the windows on the desktop by using the InvalidateRect function to erase the previously drawn letter.
See additional codes below for the DrawLetter method.
[DllImport("user32")]
private static extern bool InvalidateRect(IntPtr hwnd, IntPtr rect, bool bErase);
private void DrawLetter()
{
var letter = counter.ToString();
Graphics g = Graphics.FromHdc(GetDC(IntPtr.Zero));
float width = ((float)this.ClientRectangle.Width);
float height = ((float)this.ClientRectangle.Width);
float emSize = height;
Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
SizeF size = g.MeasureString(letter.ToString(), font);
// Invalidate all the windows.
InvalidateRect(IntPtr.Zero, IntPtr.Zero, true);
// Sometimes, the letter is drawn before the windows are invalidated.
// To fix that, add a small delay before drawing the letter.
System.Threading.Thread.Sleep(100);
// Finally, draw the letter.
g.DrawString(letter, font, new SolidBrush(Color.White), (width - size.Width) / 2, 0);
}
A solution is:
You must take a snapshot of that area you want to show counter before all things. Then call DrawImage function to draw snapshot image before call DrawString function every time.
I have worked on image capture window application. When I have captured image by application in window tablet then image quality low and show darkness in captured image background. When I have captured image by tablet then image is good quality.
What is missing/problem in my code?
I have used code share by you...
private void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap bitmap = (Bitmap)eventArgs.Frame.Clone();
ImgContainer.Image = b;
}
private void btnKeep_Click(object sender, EventArgs e)
{
int width = 457;
int height = 350;
Image tmpimg = ImgContainer.Image;
System.Drawing.Bitmap b = new System.Drawing.Bitmap(ImgContainer.Image, width, height);
System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(b);
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, width, height);
System.Drawing.Imaging.ImageCodecInfo codec = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[1];
System.Drawing.Imaging.EncoderParameters eParams = new System.Drawing.Imaging.EncoderParameters(1);
eParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
string ImagePath = Guid.NewGuid().ToString();
string imagefullpath = System.AppDomain.CurrentDomain.BaseDirectory + "imageFolder\\" + ImagePath + ".jpg";
b.Save(imagefullpath);
}
Show you captured by application image ............
Show you captured by tablet ............
Please give me any idea and solution remove darkness captured by application (above image).
You can use a DrawImage with an ImageAttributes instance to change the gamma. I found 0.5f to work:
Here is a function that applies a gamma value to a bitmap and returns a modified bitmap. It is up to you to ..:
make sure you don't leak resources
make sure to apply the gamma always to the original and not repeatedly to the same bitmap when giving the user a trackbar to find a good value..
The function:
public static Bitmap ApplyGamma(Bitmap bmp0, float gamma)
{
Bitmap bmp1 = new Bitmap(bmp0.Width, bmp0.Height);
using (Graphics g = Graphics.FromImage(bmp1))
{
ImageAttributes attributes = new ImageAttributes();
attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
g.DrawImage(bmp0, new Rectangle(0, 0, bmp0.Width, bmp0.Height),
0, 0, bmp0.Width, bmp0.Height, GraphicsUnit.Pixel, attributes);
}
return bmp1;
}
The calling code I used:
Image img = Image.FromFile(yourImage); // some image to use
float gamma = (float)(trackBar1.Value / 10f); // a trackbar to test
Text = "Gamma = " + gamma; // a control display
pictureBox1.Image = ApplyGamma((Bitmap)img, gamma);
If you also want to change contrast and/or brightness you can use a ColorMatrix. See here for an example!