How to re-size QRCode image without losing resolution? - c#

I'm generating a QR code using the QR code library "MessagingToolkit.QRCode.Codec" but it's generating a large size QR code and I'm trying to resize it to 70x70px, however the generated image's resolution is not good.
protected void BtnPrint_Click(object sender, EventArgs e)
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += PrintPage;
pd.Print();
}
private void PrintPage(object o, PrintPageEventArgs e)
{
QRCodeEncoder encoder = new QRCodeEncoder();
String EmployeeId= this.Request.QueryString.Get("EmployeeId").ToString();
Bitmap img = new Bitmap (encoder.Encode(EmployeeId), new Size(70,70));
Point loc = new Point(100, 100);
e.Graphics.DrawImage(img, loc);
}
How can I resize this QRCode without losing the resolution ?
EDIT:
Is it possible to create the QRCode from the beginning with the required size ?

You can
encoder.QRCodeScale = (number).
It controls the "scale" used to create the image.
The "scale" controls directly the size of the dots (in pixels). A scale of 1 means each dot is 1 pixel wide/high.
Note that there is a bug in the size calculation, so the image could be cropped of 1 pixel both in width and in height (if you look, the last row/column are blank)
QRCodeEncoder encoder = new QRCodeEncoder();
encoder.QRCodeScale = 1;
using (var bmp = encoder.Encode(EmployeeId))
{
// There is a bug in QRCodeEncoder, so that it will add an
// additional "blank" row/column
var bmp2 = bmp.Clone(new Rectangle(0, 0, bmp.Width - 1, bmp.Height - 1), bmp.PixelFormat))
// use bmp2
}

If you compare these QR Codes we have created in a graphics program you will see the difference in resolution. The first (2488) has a scale of 35 and the second (2490) has a scale of 40. What this means is that each module is either 35 or 40 pixels square. If you were making a QR Code for a billboard (as opposed to say a business card) you would need to ensure that the resolution of each module was high enough that it doesn't 'pixelate' (blur) when printed really large. The down side is that you are creating a larger image which will take up more memory if (like Codacast) you are creating (and saving) lots of them - hope this helps :-)
http://www.codacast.com/qrimages/2488.png
http://www.codacast.com/qrimages/2490.png

Related

Is there a way to place a vector drawing in the PrintPage-event of printDocument?

I am writing an application that creates "print" files for a laser engraver.
It is using specific RGB values for setting the laser speed and power (e.g. RGB(0,0,0) for setting 1, RGB(255,0,0) for setting 2). It will ignore any element where the RGB value is just a little bit off.
As we are printing nameplates with this device, I need as result a PDF file with many nameplates on it. I have coded a "nameplate designer" where you can design the nameplate (e.g. line in RGB(0,0,0) at x=10mm, y=10mm ....).
My initial thought was to code an interpreter which does return a Bitmap and place these bitmaps on the print file. It works, it looks good, BUT due to the pixel graphics, the colors are not 100% accurate at the edges.
should be all RGB(0,255,0)
It is 100% accurate when I put the interpreter in the PrintPage-event of printDocument, but I would like to maintain the possibility to export the image as Bitmap when needed (e.g. a customer asks for a preview). Now could I just copy the interpreter (1 for Bitmap, 1 for PrintPage), but for maintainability it would be better to have this code just once for both output ways.
I tried with Metafile, but don't know how to place it in the PrintPage-event properly (page is just blank currently) // or how to create the Metafile properly
private Metafile printMF()
{
Graphics grfx = CreateGraphics();
MemoryStream ms = new MemoryStream();
IntPtr ipHdc = grfx.GetHdc();
Metafile mf = new Metafile(ms, ipHdc);
grfx.ReleaseHdc(ipHdc);
grfx.Dispose();
grfx = Graphics.FromImage(mf);
grfx.DrawLine(new Pen(Brushes.Black), 10, 10, 300, 10);
grfx.DrawRectangle(new Pen(Brushes.Black), new Rectangle(20, 20, 400, 400));
return mf;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Metafile mf = printMF();
e.Graphics.DrawImage(mf, 20, 20,500,500);
}
Is there any obvious mistake or is it just impossible and I have to create two interpreters.
Thanks a lot for your help!

CSharp Windows Form Picturebox Draw Small Image Without Quality Loss

I'm trying to create a level editor using Windows Forms for my monogame project and need to draw small pixel based images to a picture box with no quality loss when scaled. In monogame when I need to do this I can just set the draw type to PointClamp and then each pixel is drawn as is instead of being pixelated when zoomed; I was hoping for something like this via a picturebox. Right now it looks like this But I'd prefer a more crisp and clean image like this (The second is how it'll appear in monogame). I haven't uploaded any code for this, but just assume I grabbed an image from the filestream and used the bitmap constructor to scale it up (don't think that's relevent but I'll just put it out there).
Image croppedImage, image = tileMap.tileBox.Image;
var brush = new SolidBrush(Color.Black);
try { croppedImage = CropImage(image, tileMap.highlightedRect); } catch {
return; // If crop target is outside bounds of image then return
}
float scale = Math.Min(higlightedTileBox.Width / croppedImage.Width, higlightedTileBox.Height / image.Height);
var scaleWidth = (int)(higlightedTileBox.Width * scale);
var scaleHeight = (int)(higlightedTileBox.Height * scale);
try { higlightedTileBox.Image = new Bitmap(croppedImage, new Size(scaleWidth, scaleHeight)); } catch {
return; // Image couldn't be scaled or highlighted tileBox couldn't be set to desired image
}
CropImage:
private static Image CropImage(Bitmap img, Rectangle cropArea) {
return img.Clone(cropArea, img.PixelFormat);
}
private static Image CropImage(Image img, Rectangle cropArea) {
return CropImage(new Bitmap(img), cropArea);
}
The code above is my current method in it's entirety. tileMap is a form and tilebox is the picturebox within that form.image is the full spritesheet texture before being cropped to what the user has highlighted. After being cropped I attempt to set the current picturebox (highlightedTileBox's) image to a scaled up version of the cropped image.
So I got a solution by trying around a bit.
It looks like scaling images directly by size is using some sort of interpolation.
To try different interpolation modes supported by Winforms, I created a little demo.
As you can see, every label contains the name of the InterpolationMode and is followed by its resulting image. The original bitmap I used is the small one at the top.
From your question, it looks like you would like to achieve something like NearestNeighbour.
Following code scales bmp and the result is stored in bmp2. Try if that's what you want. Consider building a proper implementation if you're using this as solution (disposing unused bitmaps etc.).
I hope it helps.
Bitmap bmp = new Bitmap("test.bmp");
Bitmap bmp2;
Graphics g = Graphics.FromImage(bmp2=new Bitmap(bmp.Width * 2, bmp.Height * 2));
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, bmp.Width * 2, bmp.Height * 2);
g.Dispose();

how to extend draw area in Graphics.DrawImage c#

I have a Rectangle (rec) that contains the area in which a smaller image is contained within a larger image. I want to display this smaller image on a Picturebox. However, what I really am doing is using the smaller image as a picture detector for a larger image that is 333x324. So what I want to do is use the coordinates of the smaller image rectangle, and then draw to the Picturebox, starting from lefthand side of the rectangle, going outwards by 333 width and 324 height.
Currently my code works but it only displays the small image that was being used for detection purposes. I want it to display the smaller image + 300 width and + 300 height.
I fiddled with this code for hours and I must be doing something extremely basic wrong. If anyone can help me I would appreciate it so much!
My code for the class:
public static class Worker
{
public static void doWork(object myForm)
{
//infinitely search for maps
for (;;)
{
//match type signature for Threading
var myForm1 = (Form1)myForm;
//capture screen
Bitmap currentBitmap = new Bitmap(CaptureScreen.capture());
//detect map
Detector detector = new Detector();
Rectangle rec = detector.searchBitmap(currentBitmap, 0.1);
//if it actually found something
if(rec.Width != 0)
{
// Create the new bitmap and associated graphics object
Bitmap bmp = new Bitmap(rec.X, rec.Y);
Graphics g = Graphics.FromImage(bmp);
// Draw the specified section of the source bitmap to the new one
g.DrawImage(currentBitmap, 0,0, rec, GraphicsUnit.Pixel);
// send to the picture box &refresh;
myForm1.Invoke(new Action(() =>
{
myForm1.getPicturebox().Image = bmp;
myForm1.getPicturebox().Refresh();
myForm1.Update();
}));
// Clean up
g.Dispose();
bmp.Dispose();
}
//kill
currentBitmap.Dispose();
//do 10 times per second
System.Threading.Thread.Sleep(100);
}
}
}
If I understand correctly, the rec variable contains a rectangle with correct X and Y which identifies a rectangle with Width=333 and Height=324.
So inside the if statement, start by setting the desired size:
rec.Width = 333;
rec.Height = 324;
Then, note that the Bitmap constructor expects the width and height, so change
Bitmap bmp = new Bitmap(rec.X, rec.Y);
to
Bitmap bmp = new Bitmap(rec.Width, rec.Height);
and that's it - the rest of the code can stay the way it is now.

C# Out of Memory when Creating Bitmap

I'm creating an application (Windows Form) that allows the user to take a screenshot based on the locations they choose (drag to select area). I wanted to add a little "preview pane" thats zoomed in so the user can select the area they want more precisely (larger pixels). On a mousemove event i have a the following code...
private void falseDesktop_MouseMove(object sender, MouseEventArgs e)
{
zoomBox.Image = showZoomBox(e.Location);
zoomBox.Invalidate();
bmpCrop.Dispose();
}
private Image showZoomBox(Point curLocation)
{
Point start = new Point(curLocation.X - 50, curLocation.Y - 50);
Size size = new Size(100, 90);
Rectangle rect = new Rectangle(start, size);
Image selection = cropImage(falseDesktop.Image, rect);
return selection;
}
private static Bitmap bmpCrop;
private static Image cropImage(Image img, Rectangle cropArea)
{
if (cropArea.Width != 0 && cropArea.Height != 0)
{
Bitmap bmpImage = new Bitmap(img);
bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat);
bmpImage.Dispose();
return (Image)(bmpCrop);
}
return null;
}
The line that fails and has the Out of Memory exception is:
bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat);
Basically what this does is it takes a 100x90 rectangle around the mouse pointer and pulls that into the zoomBox, which is a picturebox control. However, in the process, i get an Out Of Memory error. What is it that i am doing incorrectly here?
Thanks for your assistance.
Out of memory in C# imaging, is usually sign of wrong rect or point - a bit of red herring. I bet start has negative X or Y when error happens or the Size.Hight + Y or Size.Width + X is bigger than Hight or width of the image.
MSDN explains that an OutOfMemoryException means
rect is outside of the source bitmap bounds
where rect is the first parameter to the Bitmap.Clone method.
So check that the cropArea parameter is not larger than your image.
In GDI+ an OutOfMemoryException does not really mean "out of memory"; the GDI+ error code OufOfMemory has been overloaded to mean different things. The reasons for this are historic and a well described by Hans Passant in another answer.
Use the Bitmap object like this:
using (Bitmap bmpImage = new Bitmap(img))
{
// Do something with the Bitmap object
}
you should check if curLocation.X is larger than 50, otherwise your rectangle will start in the negative area (and of course curLocation.Y)
If the zoom box goes off the edge of the desktop area, then when you try to crop, you are asking the system to make a new image that includes pixels outside of the video memory area. Make sure to limit your zoom box so that none of its extents is less than 0 or greater than the screen edges.
If you are creating new bitmaps over and over, you might need to call GC.Collect(); which will force C# to garbage collect

How do I display an image within a region defined in a Picturebox?

As a follow on to my previous question I have gotten my region but spent the last two hours trying to display tiny pictures wihing that region alone; with the end goal being to be able to arbitarily display any number of images I choose.
so far this is my code:
void OnPaintRadar(Object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Bitmap blip = new Bitmap(tst_Graphics.Properties.Resources.dogtag);
Rectangle radar_rect = new Rectangle(myRadarBox.Left + 80, myRadarBox.Left + 7, myRadarBox.Width - 200, myRadarBox.Height + 200);
using (Pen drw_pen = new Pen(Color.White, 1) )
{
using (GraphicsPath path = new GraphicsPath())
{
path.AddPie(radar_rect, 180, 180);
path.CloseFigure();
using (Region rdRegion = new Region(path) )
{
g.DrawPath(drw_pen, path);
g.Clip = rdRegion;
PictureBox pb = new PictureBox();
pb.Image = (blip);
pb.Size = blip.Size;
g.DrawImage(blip, radar_rect);
}
}
}
}// end paint method
I have tried the DrawImageUnscaled method also but I either get a my tiny picture blown to fill the pie region or nothing is displayed.
Click here to run a sample application that demonstrates the basics of how to do radar (or one way, at least). Note: this application does not do double-buffering or transparency of the tiny image.
Source code for the project is here.
This line:
pb.Image = (blip);
is what's causing the tiny image to appear large. Basically, you pulling a tiny bitmap out of resources, and then setting the PictureBox's Image property to that bitmap (I'm assuming "pb" is a PictureBox on your form). Try commenting out that line and the line below it.

Categories

Resources