I need to write in a form using a bitmap font at a specific point size, without having Windows do any antialiasing or otherwise "helping" the display of the text. The reason is that the text will be saved out as a bitmap for display on a low-resolution display (eg a Netduino-driven bitmap with space for 120 pixels wide and 40 pixels high) , so if I want a black "A" on the screen I can't have grey pixels added in and arund the letters.
I need to use a font like this
http://robey.lag.net/2010/01/23/tiny-monospace-font.html
Although I know Windows doesn't do BDF I included that as a reference to the kind of no-nonsense super small typeface that I need to use in Windows.
Using C#, Franework 4.5.2, what can I do to make .NET emit a typeface as a pure unscaled bitmap?
I built a bitmap using the "Tiny" TrueType font at 6 point. Notice in the generated bitmap image that the text is all not pure white, although I specified it that way.
using (var gb = Graphics.FromImage(mybitmap))
{
gb.PixelOffsetMode = PixelOffsetMode.Half;
gb.SmoothingMode = SmoothingMode.None;
gb.InterpolationMode = InterpolationMode.NearestNeighbor;
gb.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
gb.TextContrast = 0;
gb.Clear(colorbg);
var fontSize = Convert.ToSingle(6);
var nowFont = new Font( myfont , fontSize, GraphicsUnit.Pixel );
TextRenderer.DrawText(gb, "pack my box..." , nowFont , new Point(0, 0), colorforeground);
}
Thanks.
Set the Graphics.TextRenderingHint property to TextRenderingHint.SingleBitPerPixelGridFit. If the font has no TrueType hinting at all then SingleBitPerPixel is probably your preferred choice. Either renders text without any anti-aliasing.
Related
I use RichTextBox to display small texts which is much sharper without anti-aliasing. Texts in TextBox is not anti-aliased which gives a sharp outline. But texts in RichTextBox is anti-aliased which is blured. So I want to prevent RichTextBox from anti-aliasing the texts.
I think this is only possible if RichTextBox can also render bitmap-text since for small text, if it is rendered without anti-aliasing, the result won't be readable. So the question is indeed can RichTextBox render text in bitmap mode instead of vector mode?
Environment: Windows 10 x64, VS2017
This question's Disable Anti-aliasing on WinForms text rendering answer doesn't affect RichTextBox.
It turns out that RichTextBox can render bitmap font, it depends on the font that you use, if you use a bitmap font, then RitchTextBox will just render bitmap font...
I use NetRtfWriter https://sourceforge.net/projects/netrtfwriter/ to generate RTF text then put it in RichTextBox's Rtf property.
Win forms test code (txtMain is a RichTextBox)
public Form1()
{
InitializeComponent();
var doc = new RtfDocument(PaperSize.A4, PaperOrientation.Portrait, Lcid.English);
string rtf;
for (int i = 1; i < 20; i++)
{
RtfCharFormat fmt;
//basic text use bitmap font "roman"
RtfParagraph par = doc.addParagraph();
par.setText("("+i+") This is paragraph");
par.DefaultCharFormat.FontSize = i;
//search in Windows, there is a "roman.fon" in the system
//which is a bitmap font
par.DefaultCharFormat.Font = doc.createFont("roman");
//this section use vector font
fmt = par.addCharFormat(14, 17);
fmt.FgColor = doc.createColor(new DW.RtfWriter.Color(0, 0, 255)); ;
fmt.FontSize = i+10;
fmt.Font = doc.createFont("Times New Roman");
}
rtf = doc.render();
txtMain.Rtf=rtf;
}
If you don't set font for a Rtf section, then RichEditBox will use sort of default font (perhaps the same as Windows common controls), for this defaulft font, it will render with bitmap for some range of font sizes, and render vector font for other sizes that is too small or too big to get a good result from bitmap font.
NetRtfWriter has a default built'in font setting which prevent the above to happen so first modify the source code of NetRtfWriter:
In RtfBasics.cs:
//delete public static string Font = "Times New Roman";
public static string Font = null;//add
In RtfDocument.cs:
//delete rtf.AppendLine(#"{\f" + i + " " + RtfUtility.unicodeEncode(_fontTable[i].ToString()) + ";}");
if (_fontTable[i]!=null) rtf.AppendLine(#"{\f" + i + " " + RtfUtility.unicodeEncode(_fontTable[i].ToString()) + ";}");//add
In the above Winforms test code, remove the font setting to use the "default font":
//delete par.DefaultCharFormat.Font = doc.createFont("roman");
Windows/.NET will decide which size will be rendered with bitmap text, so the final result may depends on system configuration. But whatever the configuration is, you can find (with your face very near the screen and use native resolution of the monitor...) some lines of the result rendered with bitmap (if not, expand the range of i).
For my system which is 4K resolution, 300% system enlarge setting, the default Winforms exe will render 9-13 with bitmap. But when enable "High DPI scaling behavior" and set it to "application" for the executable in Explorer, it will only render 3 and 4 with bitmap.
I have a canvas that contains an Image in which I dislay an existing BMP. I draw rectangles on the canvas and add these to the Children colllection. When I click save, I want to update the underlying BMP file.
The following code works, but the rectangle that gets drawn to the BMP is way smaller than what I drew. I guess there's some difference in the co-ordinates? Maybe I shouldn't be using System.Drawing?
using (Graphics g = Graphics.FromImage(image))
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
foreach (var child in canvas.Children)
{
if (child is System.Windows.Shapes.Rectangle)
{
var oldRect = child as System.Windows.Shapes.Rectangle;
// need to do something here to make the new rect bigger as the scale is clearly different
var rect = new Rectangle((int)Canvas.GetLeft(oldRect), (int)Canvas.GetTop(oldRect), (int)oldRect.Width, (int)oldRect.Height);
g.FillRectangle(Brushes.Black, rect);
}
}
... code to save bmp
All suggestions welcome!
Thanks
Try using the System.Windows.Media.Imaging.RenderTargetBitmap Class (an example here).
Wpf uses Device Independent Graphics so you have to compensate for the DPI :
RenderTargetBitmap bmp = new RenderTargetBitmap((int)Canvas1.Width, (int)Canvas1.Height, 96, 96, PixelFormats.Default);
bmp.Render(Canvas1);
From Third Link:
There are two system factors that determine the size of text and graphics on your screen: resolution and DPI. Resolution describes the number of pixels that appear on the screen. As the resolution gets higher, pixels get smaller, causing graphics and text to appear smaller. A graphic displayed on a monitor set to 1024 x 768 will appear much smaller when the resolution is changed to 1600 x 1200.
This seems to be the standard method to convert the pixel format of a bitmap but it's giving me a very poor result when converting to black and white format:
// convert to black & white bitmap
FormatConvertedBitmap monoBitmap = new FormatConvertedBitmap(bitmap, PixelFormats.BlackWhite, null, 0);
// save black & white bitmap to file
fileName = string.Format("{0}.bmp", data.SelectedProduct.Code);
bitmapEncoder = new BmpBitmapEncoder();
bitmapEncoder.Frames.Add(BitmapFrame.Create(monoBitmap));
using (Stream stream = File.Create(fileName))
{
bitmapEncoder.Save(stream);
}
The resulting image file is very grainy and pixelated. I need to send it to a line printer so I need sharp edges.
The original before conversion looks fine (its 32BPP color) and if I use something like IrfanView to manually convert the original to black and white it also comes out much better than whatever .NET is doing.
Is there another alternative for doing this in .NET instead of FormatConvertedBitmap?
It are using PixelFormats.BlackWhite, which uses only black and white colors in the resulting image, which probably can make it look grainy if there are light pixels surrounded by darker ones. I suppose the effect can be better, if you convert the image to use the shades of gray. In order to do this, you this line of code instead of the one in the code you've posted:
FormatConvertedBitmap monoBitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Gray32Float, null, 0);
This should convert the image to use only only shades of gray, so no color will be needed to print it.
Let me know if it's the effect you want to achieve.
You can find some more information about PixelFormats class here: http://msdn.microsoft.com/en-us/library/system.windows.media.pixelformats.aspx.
I am looking to render a DrawingVisual (visual in the example) to a bitmap using RenderTargetBitmap with the view to set this bitmap as the background to a Canvas as below:
var bmp = new RenderTargetBitmap(2000, 50, 120, 96, PixelFormats.Indexed2);
bmp.Render(visual);
var brush = new ImageBrush(bmp) { Stretch = Stretch.Fill };
Canvas.Background = brush;
When using PixelFormats.Default as the last argument to RenderTargetBitmap, the image renders as expected. However, when I choose PixelFormats.Indexed2 (or any of the PixelFormats.IndexedX), my code seems to exit the method without an exception, the bmp.Render line is never called and hence the image is not displayed on the Canvas.
How to use the IndexedX pixel formats with RenderTargetBitmap? Or are there other ways to reduce the memory footprint of the image? It only uses three colors, so using a palette rather than 32bit RGB seemed the way to go.
You can't. RenderTargetBitmap only supports the Pbgra32 pixel format. That's because WPF's rendering system works entirely in 32 bits per pixel. That's the format in which it generates images, and it's also the format in which it prefers images to be in if you want to render them. (If you provide it with a bitmap in any other format, it'll need to convert it into a 32 bit per pixel representation first.)
What are you planning to do with this bitmap? If you want to render it in a WPF application, it'll need to be converted to a 32bpp format first in any case, so you risk using more memory if you attempt to hold it internally in any other format. (You'll have your supposedly memory-efficient representation and the version WPF's actually able to work with.) Not to mention the extra CPU time spent converting between your chosen format and a format WPF can work with.
I have a print preview that displays a captured panel on a form 'Panel1.DrawToBitmap(memoryImage, bounds);'
I also save the image to my hard drive - 'memoryImage.Save("diary.png")'
The image in the print preview at any zoom level is blurry, the saved image is perfect (viewed in windows photo viewer & PS).
Id like the print preview to be as good as the saved image, any ideas?
here's the code:-
private void CaptureScreen()
{
int x = splitContainerDiary.Location.X;
int y = splitContainerDiary.Location.Y;
int SCwidth = splitContainerDiary.Panel1.Width;
int SCheight = splitContainerDiary.Panel1.Height;
Rectangle bounds = new Rectangle(x, y, SCwidth, SCheight);
memoryImage = new Bitmap(SCwidth, SCheight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
splitContainerDiary.Panel1.DrawToBitmap(memoryImage, bounds);
memoryImage.Save("diary.png");
}
private void printDocumentDiary_PrintPage(object sender, PrintPageEventArgs e)
{
CaptureScreen();
Font HeaderFont = new Font("Consolas", 16, FontStyle.Bold);
e.Graphics.DrawString(selectedYear.ToString() + " - " + name, HeaderFont, Brushes.Black, 15, 15);
e.Graphics.DrawImage(Image.FromFile("diary.png"), 5, 5);
// e.Graphics.DrawImage(memoryImage, 0, 40);
PrintDoodle(e);
}
I have tried to draw the image from memory (e.Graphics.DrawImage(memoryImage, 0, 40) and also from the saved image 'e.Graphics.DrawImage(Image.FromFile("diary.png"), 5, 5);' They are both blurry in print preview.
I have tried different Pixel formats with no joy either.
I have tried saving the image as BMP, JPG, PNG with no joy either (when drawing image fromFile).
I have tried using BitBlt routine also with the same results.
Tino
This is an inevitable consequence of the dramatic difference between the device resolution of a printer vs a monitor. A printer typically can print with a resolution of 600 dots per inch. A monitor is typically set to 96 DPI. So when you print an image that's razor sharp on a monitor, each pixel of the image requires printing a blob of 6 x 6. Short from the blockiness this produces, anything that's drawn on screen with anti-aliasing will get those anti-aliasing pixels drawn 6 times larger as well. Completely ruining the effect. This is especially noticeable with any text that's drawn with ClearType anti-aliasing. The red and blue fringes become very noticeable on paper.
You can partly solve this by drawing the image one-to-one on the printer, ensuring that 1 pixel in the image becomes 1 pixel on paper. That ought to now look nice and sharp (minus the ClearType problem) but you'll be looking at a postage stamp. Growing your arms six times longer would have the same effect.
Well, this just doesn't work well. Use the PrintDocument class so you can draw stuff to the printer using its native resolution. Use the methods provided by e.Graphics in the PrintPage event handler. Avoid images unless they are photos, anything that doesn't have finely detailed line art will scale well.
I have encountered a similar "blurry font" problem, while trying to print out some custom text, which I've pre-arranged as Labels in the TableLayoutPanel.
My solution for the blurriness was as follows: I have created a panel and labels four times as big as the desired final size (by using font 44 instead of 11, and using width and height four times greater).
Then I've created a (large) bitmap, and downscaled it in the final step (DrawImage):
using (var bmp = new Bitmap(tableLayout.Width, tableLayout.Height))
{
tableLayout.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
printPageEventArgs.Graphics.DrawImage(
bmp,
printPageEventArgs.MarginBounds.X,
printPageEventArgs.MarginBounds.Y,
bmp.Width / 4,
bmp.Height / 4);
}
The resulting text looks much sharper both in the preview and in the actual printed page.
Of course, such an approach can only work if you can manipulate the Control's size, for example by creating it "off screen". But it will not work if you require the actual displayed control to be printed.