How to Do a Std Cout Print of Bitmap Letter Font Art in C
- Download Graphics3 - 86.07 KB
Introduction
A few weeks ago, I decided I wanted to create a screen saver that fades quotes on/off the screen. I've subsequenty lost interest in the project, but I learned some handy things in the process of becoming bored. This certainly isn't rocket science, but I've seen a few people inquire about it, so I thought I'd share my findings.
Putting Text on a Bitmap
This is so ridiculously simple, it almost hurts to describe. Essentially, you lot only demand five lines of lawmaking (with three optional lines shown for completeness and making the text "pretty".
Bitmap bitmap = new Bitmap(800, 600); Graphics g = Graphics.FromImage(bitmap); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; thou.TextRenderingHint = Organisation.Cartoon.Text.TextRenderingHint.ClearTypeGridFit; thou.Clear(Colour.Transparent); TextRenderer.DrawText(g, " This is my text", myFont, new Betoken(0, 0), Color.Cherry); m_bitmap = bitmap; chiliad.Dispose();
And so there you accept it. Simple eh? Yes - I remember so, also. However, if I stopped here, this commodity really wouldn't be very interesting, at present would it? For that reason, I'yard going to talk a petty most the sample application provided with this article.
The sample app always runs maximized, and displays randomly selected quotes at random positions on the screen. This ways we need to be cognizant of the width and height of the quote bitmap so that we tin can display it without any office of information technology being cut off by an edge of the screen. Some of the quotes are quite long, and some are quite short, so that means we have to dynamically calculate the size of the rectangle for each quote before it'southward displayed. On top of all that, the app suppports a width constraint as specified by the programmer (or user if a settings class is provided). For this app, it's difficult-coded for the sake of example.
Later on a quote is selected, the app calls a method called GetConstrainedTextHeight. This function accepts a font, the original text string, and a ref parameter that will contain the modified version of the original text string. The method returns a rectangle object that is large enough to incorporate the rendered text. Just follow the coments to meet what we're doing.
private Rectangle GetConstrainedTextHeight(Font font, string textToParse, ref string resultText) { int quoteAreaWidth = m_saverSettings.QuoteAreaWidth; Bitmap bitmap = new Bitmap(100, quoteAreaWidth); Graphics one thousand = Graphics.FromImage(bitmap); SizeF sz = g.MeasureString(textToParse, font); if (sz.Width <= quoteAreaWidth) { resultText = textToParse; g.Dispose(); bitmap.Dispose(); return new Rectangle(new Point(0, 0), new Size((int)sz.Width+v, (int)sz.Elevation)); } resultText = " "; string[] words = textToParse.Trim().Split(' '); string nextLine = " "; string give-and-take = " "; for (int i = 0; i < words.Length; i++) { word = words[i]; SizeF lineSize = grand.MeasureString(nextLine, font); SizeF wordSize = g.MeasureString(" " + discussion, font); if (lineSize.Width + wordSize.Width < quoteAreaWidth) { nextLine = cord.Format(" {0} {ane}", nextLine, discussion); if (i == words.Length - 1) { resultText += nextLine; } } else { resultText += (nextLine + " \northward"); nextLine = word; if (i == words.Length - 1) { resultText += nextLine; } } } sz = 1000.MeasureString(resultText, font); one thousand.Dispose(); bitmap.Dispose(); return new Rectangle(new Bespeak(0, 0), new Size((int)sz.Width, (int)sz.Height)); }
We telephone call the GetConstrainedTextHeight() method twice - in one case for the quote text itself, and once for the author text. After all, there's really no point in leaving anything to risk. As well, we demand the writer'south rectangle to complete the positioning calculation. Afterwards getting the two rectangles, we can take intendance of positioning the bitmap on the screen. Remember, in the sample app it's always running in a maximized window state. For this reason, nosotros simply use the screen resolution equally the constraining rectangle.
string newQuoteText = " "; Rectangle quoteRect = GetConstrainedTextHeight(m_saverSettings.QuoteFont, quoteItem.Text, ref newQuoteText); string newAuthorText = " "; Rectangle authorRect = GetConstrainedTextHeight(m_saverSettings.AuthorFont, quoteItem.Author, ref newAuthorText); authorRect.X = quoteRect.Right - authorRect.Width; authorRect.Y = quoteRect.Lesser;
Finally, some quality properties are set, and the text is actually rendered to the bitmap.
Bitmap bitmap = new Bitmap(quoteRect.Width, quoteRect.Top + authorRect.Height + 2); Graphics chiliad = Graphics.FromImage(bitmap); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = Organisation.Drawing.Text.TextRenderingHint.ClearTypeGridFit; thou.Clear(Color.Transparent); TextRenderer.DrawText(g, newQuoteText, m_saverSettings.QuoteFont, new Signal(quoteRect.X, quoteRect.Y), m_saverSettings.QuoteColor); TextRenderer.DrawText(g, newAuthorText, m_saverSettings.AuthorFont, new Point(authorRect.Ten, authorRect.Y+2), m_saverSettings.AuthorColor); m_bitmap = bitmap; one thousand.Dispose();
Other Points of Involvement
This article features a simple windows course application that I used to test the core functionality. Too demonstrating the dynamic creation and display of a bitmap, this article illustrates a method for making your Windows class application go full screen, the employ of a BackgroundWorker object that'southward used to do the real work, equally well every bit some rudimentary use of LINQ when loading and saving XML files.
Full-screen Mode
At work, I needed to brand an application eat the unabridged screen, including the job bar. This entailed irresolute the border style to "None", make the formthe top-well-nigh window, and finally, using up the unabridged screen. The but way I know of to *truly* get total-screen is to use the Interop features of .Internet, and then I found this class on the net that does exactly what nosotros need:
public course WinAPI { [DllImport(" user32.dll", EntryPoint = " GetSystemMetrics")] public static extern int GetSystemMetrics(int which); [DllImport(" user32.dll")] public static extern void SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int X, int Y, int width, int height, uint flags); private const int SM_CXSCREEN = 0; private const int SM_CYSCREEN = 1; private static IntPtr HWND_TOP = IntPtr.Goose egg; private const int SWP_SHOWWINDOW = 64; public static int ScreenX { go { render GetSystemMetrics(SM_CXSCREEN);} } public static int ScreenY { get { render GetSystemMetrics(SM_CYSCREEN);} } public static void SetWinFullScreen(IntPtr hwnd) { SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW); } }
To make all this happen, we write the following code:
WinAPI.SetWinFullScreen(this.Handle); this.TopMost = true; this.FormBorderStyle = FormBorderStyle.None;
Of class, this doesn't keep the user from alt-tabbing out of the app, but in that location's a highly-regarded commodity hither on CodeProject that discusses hooking the keyboard using C#, and I sugggest that you lot showtime in that location if you want to address this issue.
The Bones Framework
We first out with a standard Windows Form application. Since we're but dealing with a unmarried form with no controls, in that location'due south no point in setting the class properties in the using IDE. So, information technology's off to the form's constructor, on a transmission-typing adventure (c'mon - I know you lot'd rather practice information technology this way).
First upwards, we want to set some styles regarding painting within the form. For some reason, I could never become this to work completely when setting these properties in the IDE, so I moved them to the object constructor:
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); We also need to override the Paint method, so nosotros tin can brandish the bitmap.
private void Form1_Paint(object sender, PaintEventArgs due east) { if (m_bitmap != null) { attempt { east.Graphics.DrawImage(m_bitmap, m_rect, 0, 0, m_rect.Width, m_rect.Height, GraphicsUnit.Pixel, m_attributes); } take hold of (Exception ex) { if (ex != zippo) {} } } }
Finally, nosotros employ a BackgroundWorker object to control the fading of the bitmaps.
private void fader_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; if (m_bitmap != null) { m_bitmap.Dispose(); } m_bitmapType = BitmapType.Quote; switch (m_saverSettings.QuoteOrder) { case OrderSelector.Random: { m_nextQuoteIndex = RandomQuoteIndex(); } break; default: { m_nextQuoteIndex = NextQuoteIndex(); } break; } if (m_nextQuoteIndex < 0 || m_nextQuoteIndex > m_quotes.Count) { return; } PrepareQuoteImage(m_quotes[m_nextQuoteIndex]); int x = m_random.Next(0, Math.Max(0, WinAPI.ScreenX - m_bitmap.Width)); int y = m_random.Next(0, Math.Max(0, WinAPI.ScreenY - m_bitmap.Height)); m_rect = new Rectangle(x, y, m_bitmap.Width, m_bitmap.Height); float delta = 0.05f; m_alpha = 0.0f; while (m_alpha != one.0f && !worker.CancellationPending) { m_colorMatrix.Matrix33 = m_alpha; try { m_attributes.SetColorMatrix(m_colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); } catch (Exception ex) { if (ex != zip) {} } m_alpha += delta; m_alpha = Math.Min(one.0f, m_alpha); this.Invalidate(m_rect); Thread.Sleep(35); } int sleeper = 0; int sleepTime = 100; while (sleeper <= m_saverSettings.OnDuration && !worker.CancellationPending) { Thread.Sleep(sleepTime); sleeper += sleepTime; } m_alpha = one.0f; while (m_alpha != 0.0f && !worker.CancellationPending) { m_alpha -= delta; m_alpha = Math.Max(0.0f, m_alpha); m_colorMatrix.Matrix33 = m_alpha; m_attributes.SetColorMatrix(m_colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); this.Invalidate(m_rect); Thread.Sleep(35); } }
Loading The Data - LINQ to the Rescue!
I similar patriotic quotes. Despite most of them existence 200 years old, and some being of questionable origin, I similar to read them. It makes me experience good to know I'one thousand a role of what was an uncommon (and even unpopular) thought. I can understand that this kind of stuff isn't necessarily everyone's cup of tea, but since this is my article, I claim the right of creative license.
As far as quotes are concerned, Our needs are quite simple - we have the quote text itself, and the author of that quote. While in the process of writing this commodity, I had cause to first using LINQ at piece of work, and decided it might exist fun to add LINQ to this sample application. Initially, I was using a simple comma-delimited text file to store the quotes, and everything was right with the world. However, to really experience LINQ, I decided I would utilise an XML file instead because that's currently how most people load data that's non in a database. Here's the QuoteItem class:
public class QuoteItem { private string m_text; private cord m_author; public string Text { get { return m_text; } } public string Author { go { return m_author; } } public XElement XmlElement { get { return new XElement(" QUOTE", new XElement(" TEXT", Text), new XElement(" AUTHOR", Writer)); } } public QuoteItem(cord text, string author) { m_text = text; m_author = author; } public QuoteItem (XElement element) { m_text = element.Chemical element(" TEXT").Value; m_author = element.Element(" Writer").Value; } }
Notice that there's a belongings that returns the data as an XElement. This helps to proceed the code outside this class a little cleaner. Toward that same end, there's a constructor overload that accepts an XElement object.
Having started with a .txt file, I start wanted to automate it's conversion into XML, but beginning, I had to load the text file. Since the code was already written to do that, I decided to merely modify it enough to cause a conversion to take identify.
private void LoadQuotes() { m_quotes.Clear(); if (File.Exists(m_quotesFileName)) { LoadQuotesFromXML(); } if (m_quotes.Count == 0) { if (m_quotes.Count > 0) { SaveQuotesToXML(); m_quotes.Clear(); LoadQuotesFromXML(); } }
First, I check for the exstance of the XML file, and load the data from it if it's there. If after loading the XML file, there are no quotes loaded, it tries to read the .txt version of the file. Finally, if nosotros loaded some quotes, we relieve them to the XML file, articulate the list, and repopulate it from the XML file. This allows us to verify that the XML file was indeed created.
Loading XML data is a slice of cake with LINQ. Our LoadQuotes() part required more than two-dozen lines of code to load the data from a comma-delimited text file, and this doesn't even count the code in the string parsing course. With LINQ, the line-count is reduced to only six lines of lawmaking:
individual void LoadQuotesFromXML() { XDocument quotes = XDocument.Load(m_quotesFileName); var dataSet = from data in quotes.Descendants(" QUOTE") select data; foreach (var quote in dataSet) { m_quotes.Add(new QuoteItem(quote)); } }
Saving the data as XML is fairly straightforward using LINQ:
private void SaveQuotesToXML() { XDocument quotes = new XDocument(new XDeclaration(" 1.0", " utf-8", " yes"), new XComment(" Quotes file")); var root = new XElement(" QUOTES", from q in m_quotes select q.XmlElement); quotes.Add(root); quotes.Save(m_quotesFileName); }
I seem to remember it existence a complete pain-in-the-ass to create XML and save it to a file before LINQ came along. Now, it's but four lines of lawmaking Call up when I said hiding details in black-box classes kept your code clean? This is a perfect example of what I mean.
Closing
The sample app serves no useful purpose beyond letting me test the code I was going to use in the screensaver I intended to write. On the way, I use a couple of techniques and classes that well-nigh of the residual of you tin can make use of. If you lot desire to footstep through the code that creates the XML file, just delete the XML file from the program binder.
History
09/29/2008: Corrected some spelling errors, and modified some of the descriptive text.
09/28/2008: Posted original article.
Source: https://www.codeproject.com/Articles/29749/Render-Text-On-A-Bitmap-and-other-stuff
Postar um comentário for "How to Do a Std Cout Print of Bitmap Letter Font Art in C"