Is the behaviour of DrawLine predictable with out of range parameters? - c#

Can DrawLine handle coordinates outside the defined area?
For example myGraphics.DrawLine(MyPen, -20, -80, 20, 90);
I would expect this to produce a line correctly as though it had used an infinite canvas but plotting only the section within my graphic.
My code is as follows. I am plotting movement from coordinates recorded in a database. Occasionally the subject moves further than expected, beyond the edges of my bitmap. I do not check for this occurrence as I was relying on DrawLine to handle it.
Bitmap Border = new Bitmap(5000, 5000);
Border.SetResolution(254, 254);
Graphics MyGraphics= Graphics.FromImage(Border);
Pen MyPen = new Pen(Color.Black, 1);
for (Int32 Point = 1; Point <= Points; Point++)
{
XCoord2 = XCoord1;
YCoord2 = YCoord1;
XCoord1 = *READ FROM DATABASE*
YCoord1 = *READ FROM DATABASE*
if (Point > 1)
{
MyGraphics.DrawLine(MyPen, XCoord1, YCoord1, XCoord2, YCoord2);
}
}
In reality, my plots work most of the time. However I do get an occasional graphic with missing lines or with an obscure line originating from a strange coordinate.
In summary, should the behaviour of DrawLine predictable with unusual parameters. Should I introduce some trigonometry to force the plots to always be within my grid?

The actual limits are a billion positive or negative see this past question (that used .net )
What are the hard bounds for drawing coordinates in GDI+?
My guess is that your database pulls are wrong, this can happen if you are using a string data storage and forcing that to be parsed.
Add a thread.sleep() and have it Debug.WriteLine the new pulls (or just breakpoint things), likely a value is getting in there that is either odd or getting parsed oddly

After more experimentation, I finally cured my problem with...
SolidBrush WhiteBrush = new SolidBrush(Color.White);
myGraphics.FillRectangle(WhiteBrush,0,0,5000,5000);
i.e. I gave my graphics a solid white background before I drew any lines. Before I was drawing a black line on a NULL background. I have no idea why this would affect anything, but it did!

Related

Best way to draw text in MonoGame without using a SpriteFont?

Alright, so I'm working on a game in MonoGame which is set in a computer operating system. As expected, it does a lot of text rendering. The in-game OS allows users to customize almost every aspect of the operating system - people have made skins for the OS that make it look like Mac OS Sierra, almost every najor Windows release since 95, Xubuntu, Ubuntu, and way more.
This game used to be written in Windows Forms however there are features I want to implement that simply are not possible in WinForms. So, we decided to move from WinForms to MonoGame, and we are faced with one huge problem.
The skin format we've made allows the user to select any font installed on their computer to use for various elements like titlebar text, main UI text, terminal text etc. This was fine in WinForms because we could use System.Drawing to render text and that allows the use of any TrueType font on the system. If it can be loaded into a System.Drawing.Font, it can be rendered.
But, MonoGame uses a different technology for rendering text on-screen. SpriteFont objects. The problem is, there seems to be no way at all to dynamically generate a SpriteFont from the same data used to generate System.Drawing.Fonts (family, size, style, etc) in code.
So, since I seemingly can't create SpriteFonts dynamically, in my graphics helper class (which deals with drawing textures etc onto the current graphics device without needing copy-pasted code everywhere), I have my own DrawString and MeasureString methods which use System.Drawing.Graphics to composite text onto a bitmap and use that bitmap as a texture to draw onto the screen.
And, here's my code for doing exactly that.
public Vector2 MeasureString(string text, System.Drawing.Font font, int wrapWidth = int.MaxValue)
{
using(var gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1)))
{
var s = gfx.SmartMeasureString(text, font, wrapWidth); //SmartMeasureString is an extension method I made for System.Drawing.Graphics which applies text rendering hints and formatting rules that I need to make text rendering and measurement accurate and usable without copy-pasting the same code.
return new Vector2((float)Math.Ceiling(s.Width), (float)Math.Ceiling(s.Height)); //Better to round up the values returned by SmartMeasureString - it's just easier math-wise to deal with whole numbers
}
}
public void DrawString(string text, int x, int y, Color color, System.Drawing.Font font, int wrapWidth = 0)
{
x += _startx;
y += _starty;
//_startx and _starty are used for making sure coordinates are relative to the clip bounds of the current context
Vector2 measure;
if (wrapWidth == 0)
measure = MeasureString(text, font);
else
measure = MeasureString(text, font, wrapWidth);
using (var bmp = new System.Drawing.Bitmap((int)measure.X, (int)measure.Y))
{
using (var gfx = System.Drawing.Graphics.FromImage(bmp))
{
var textformat = new System.Drawing.StringFormat(System.Drawing.StringFormat.GenericTypographic);
textformat.FormatFlags = System.Drawing.StringFormatFlags.MeasureTrailingSpaces;
textformat.Trimming = System.Drawing.StringTrimming.None;
textformat.FormatFlags |= System.Drawing.StringFormatFlags.NoClip; //without this, text gets cut off near the right edge of the string bounds
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel; //Anything but this and performance takes a dive.
gfx.DrawString(text, font, new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B)), 0, 0, textformat);
}
var lck = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); //Lock the bitmap in memory and give us the ability to extract data from it so we can load it into a Texture2D
var data = new byte[Math.Abs(lck.Stride) * lck.Height]; //destination array for bitmap data, source for texture data
System.Runtime.InteropServices.Marshal.Copy(lck.Scan0, data, 0, data.Length); //cool, data's in the destination array
bmp.UnlockBits(lck); //Unlock the bits. We don't need 'em.
using (var tex2 = new Texture2D(_graphicsDevice, bmp.Width, bmp.Height))
{
for (int i = 0; i < data.Length; i += 4)
{
byte r = data[i];
byte b = data[i + 2];
data[i] = b;
data[i + 2] = r;
} //This code swaps the red and blue values of each pixel in the bitmap so that they are arranged as BGRA. If we don't do this, we get weird rendering glitches where red text is blue etc.
tex2.SetData<byte>(data); //Load the data into the texture
_spritebatch.Draw(tex2, new Rectangle(x, y, bmp.Width, bmp.Height), Color.White); //...and draw it!
}
}
}
I'm already caching heaps of textures created dynamically - window buffers for in-game programs, skin textures, etc, so those don't hit the performance hard if at all, but this text rendering code hits it hard. I have trouble even getting the game above 29 FPS!
So, is there a better way of doing text rendering without SpriteFonts, and if not, is there any way at all to create a spritefont dynamically in code simply by specifying a font family, font size and style (bold, italic, strikeout etc)?
I'd say I'm intermediate with MonoGame now but I have a hard enough time getting RenderTargets to work - so if you want to answer this question please answer it as if you were talking to a kindergarten student.
Any help would be greatly appreciated, and as this is a major hot-buttin' issue in my game's development team you may see yourself mentioned in the game's credits as a major help :P
You could create a custom spritefont using System.Drawing and use this one. It is basically every character that can be used, stored in a Dictionary with the corresponding Texture2D.
When you want to draw a text, you just draw every char next to eachother.
This is still slow (because drawing text without vector graphics is always slow) but at least you do not have to parse everything every frame.
Just specify somewhere what characters can be used and import them. Dictionaries are very fast in C# when it comes to indexing, so this shouldn't be a problem at all.
Hope this helps. Good luck.

Detect and draw shape inside of available space between lines

My goal is to detect the different regions within a simple drawing constructed of various lines. Please click the following link to view a visual example of my goal for clarification. I am of course able to get the position of the drawn lines, but since one line can cross multiple 'regions' I don't think this information alone will be sufficient.
Any ideas, suggestions or points to other websites are welcome. I am using C# in combination with WPF - I am not certain which search words might lead to an answer to this problem. I did come across this shape checker article from AForge, but it seems to focus on detecting shapes that are already there, not so much on regions that still have to be 'discovered'. As a side note, I hope to find a solution that works not only with rectangles but also with other types of shapes.
Thank you very much in advance.
Update:
foreach (Line canvasObject in DrawingCanvas.Children.OfType<Line>())
{
LineGeometry lineGeometry1 = new LineGeometry();
lineGeometry1.StartPoint = new Point(canvasObject.X1, canvasObject.Y1);
lineGeometry1.EndPoint = new Point(canvasObject.X2, canvasObject.Y2);
if (canvasObject.X1 != canvasObject.X2) {
foreach (Line canvasObject2 in DrawingCanvas.Children.OfType<Line>()) {
if (canvasObject.X1 == canvasObject2.X1 && canvasObject.X2 == canvasObject2.X2 &&
canvasObject2.Y1 == canvasObject2.Y2 && canvasObject.Y2 == canvasObject2.Y2) {
return;
// prevent the system from 'colliding' the same two lines
}
LineGeometry lineGeometry2 = new LineGeometry {
StartPoint = new Point(canvasObject2.X1, canvasObject2.Y1),
EndPoint = new Point(canvasObject2.X2, canvasObject2.Y2)
};
if (lineGeometry1.FillContainsWithDetail(lineGeometry2).ToString() != "Empty") {
//collision detected
Rectangle rectangle = new Rectangle {
Width = Math.Abs(canvasObject.X2 - canvasObject.X1),
Height = 20,
Fill = Brushes.Red
};
//rectangle.Height = Math.Abs(canvasObject.Y2 - canvasObject.Y1);
DrawingCanvas2.Children.Add(rectangle);
Canvas.SetTop(rectangle, canvasObject.Y1);
Canvas.SetLeft(rectangle, canvasObject.X1);
}
}
}
}
I have experimented with the following code - to give you an impression of how I tried to tackle this problem. Initially I thought I had found a partial solution, by checking for collision between lines. Unfortunately I just created a second line of each line (which of course collided 'with itself'). After I added a simple if check (see below) this no longer occurs, but now I don't get any collisions anymore.. so will probably need a new technique.
Update 2:
After some more digging and searching the internet for solutions, I have a new potential solution in mind. Hopefully this can also be of use to anyone looking for answers in the future. Using a flood-fill algorithm I am able to 'fill' each region with a specific color - much like the paint bucket tool in an image editing application. Summarized, this done by taking a 'screenshot' of the Canvas element, starting at a certain pixel and expanding over and over until a pixel with a different color is found (these would be the lines). It works pretty well and is able to return an image with the various regions. However - my current problem is accessing these regions as 'objects' in C#/WPF. I would like to draw the regions myself (using polyobject or something similar?) - making it possible to use the objects for further calculations or interactions.
I have tried saving the position of the smallest and largest X and Y positions in the FloodFill algorithm after each pixel check, but this makes the algorithm work very very slow. If anyone has an idea, I would love to know. :)

a texture that repeats across the world, based on X, Y coordinates

I'm using XNA/MonoGame to draw some 2D polygons for me. I'd like a Texture I have to repeat on multiple polygons, based on their X and Y coordinates.
here's an example of what I mean:
I had thought that doing something like this would work (assuming a 256x256 pixel texture)
verticies[0].TextureCoordinate = new Vector2(blockX / 256f, (blockY + blockHeight) / 256f);
verticies[1].TextureCoordinate = new Vector2(blockX / 256f, blockY / 256f);
verticies[2].TextureCoordinate = new Vector2((blockX + blockWidth) / 256f, (blockY + blockHeight) / 256f);
verticies[3].TextureCoordinate = new Vector2((blockX + blockWidth) / 256f, blockY / 256f);
// each block is draw with a TriangleStrip, hence the odd ordering of coordinates.
// the blocks I'm drawing are not on a fixed grid; their coordinates and dimensions are in pixels.
but the blocks end up "textured" with long-horizontal lines that look like the texture has been extremely stretched.
(to check if the problem had to do with TriangleStrips, I tried removing the last vertex and drawing a TriangleList of 1 - this had the same result on the texture, and the expected result of drawing only one half of my blocks.)
what's the correct way to achieve this effect?
my math was correct, but it seems that other code was wrong, and I was missing at least one important thing.
maybe-helpful hints for other people trying to achieve this effect and running into trouble:
GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
^ you need that code. but importantly, your SamplerState and other settings will get reset when you draw sprites (SpriteBatch's Begin()), so especially if you're abstracting your polygon-rendering code into little helper functions, be mindful of when and where you call them! ex:
spriteBatch.Begin();
// drawing sprites
MyFilledPolygonDrawer(args);
// drawing sprites
spriteBatch.End();
if you do this (assuming MyFilledPolygonDrawer uses 3D methods), you'll need to change all the settings (such as SamplerState) before you draw in 3D, and possibly after (depending on what settings you use for 2D rendering), all of which comes with a little overhead (and makes your code more fragile - you're more likely to screw up :P)
one way to avoid this is to draw all your 3D stuff and 2D stuff separately (all one, then all the other).
(in my case, I haven't got my code completely separated out in this way, but I was able to at least reduce some switching between 2D and 3D by using 2D methods to draw solid-color rectangles - Draw Rectangle in XNA using SpriteBatch - and 3D stuff only for less-regular and/or textured shapes.)

Determining the Position of a String After it is Drawn using WinForms (C#)

I am drawing a string on a big bounding box and using StringFormat to align the string appropriately. However I need the actual (X, Y) location of the string once it's drawn (not just the size given by MeasureString).
I'm using the code below:
CharacterRange[] ranges = { new CharacterRange(0, this.Text.Length) };
format.SetMeasurableCharacterRanges(ranges);
//Measure character range
Region[] region = g.MeasureCharacterRanges(this.Text, this.Font, layoutRect, format);
RectangleF boundsF = region[0].GetBounds(g);
bounds = new Rectangle((int)boundsF.Left, (int)boundsF.Top,
(int)boundsF.Width, (int)boundsF.Height);
It's a code segment so ignore any missing declarations. The point is that rectangle the code above gives is not the right size, the last character of the string is dropped and only the first line of strings are drawn.
Anyone know why, or perhaps a better way to go about this?
Thanks
Make sure you're using the right format for measuring your text. You haven't included the entire source code, so I can't tell if you are.
There are two 'standard' format values you can use:
StringFormat.GenericTypographic and StringFormat.GenericDefault. If memory serves, the default one selected is typically GenericDefault, but the one you want when rendering UI is GenericTypographic.
Therefore, instead of doing new StringFormat(), you want to do StringFormat.GenericTypographic.Clone() instead. That should correct the margins/spacing and give you measurement results that match what you see rendered on the Graphics surface.
The strategy I typically use is to construct a single StringFormat instance and use it for both text rendering and measurement to ensure things line up - I avoid any method that lets me omit the StringFormat since the default probably isn't what I want.
Hope this helps. If you're still having trouble, try posting a more complete code snippet so we can see how you're painting your text.
Use:
TextBox tb = new TextBox { Text = "Test", Multiline = true };
Size size = System.Windows.Forms.TextRenderer.MeasureText(tb.Text, tb.Font);
Point location = new Point( //Is this what you were looking for?
tb.Location.X + size.Width,
tb.Location.Y + size.Height);
Note that there are additional overloads to this method, please check out.
I am definitely using the same StringFormat object to measure and draw the text.
What I have ended up doing is I use the code above and then measure again with TextRenderer and adjust the size and location accordingly

Efficient Line Drawing In Direct3D (Transformation2D?)

Background: I'm using the SlimDX C# wrapper for DirectX, and am drawing many 2D sprites using the Sprite class (traditionally from the Direct3DX extension in the underlying dlls). I'm drawing multiple hundreds of sprites to the screen at once, and the performance is awesome -- on my quad core, it's using something like 3-6% of the processor for my entire game, including logic for 10,000+ objects, ai routines on a second thread, etc, etc. So clearly the sprites are being drawing using full hardware acceleration, and everything is as it should be.
Issue: The problem comes when I start introducing calls to the Line class. As soon as I draw 4 lines (for a drag selection box), processor usage skyrockets to 13-19%. This is with only four lines!
Things I have tried:
Turning line antialiasing off and on.
Turning GLLines off and on.
Manually calling the line.begin and line.end around my calls to draw.
Omitting all calls to line.begin and line.end.
Ensuring that my calls to line.draw are not inside a sprite.begin / sprite.end block.
Calling line.draw inside a sprite.begin / sprite.end block.
Rendering 4 lines, or rendering 300.
Turning off all sprite and text rendering, and just leaving the line rendering for 4 lines (to see if this was some sort of mode-changing issue).
Most combinations of the above.
In general, none of these had a significant impact on performance. #3 reduced processor usage by maybe 2%, but even then it's still 8% or more higher than it should be. The strangest thing is that #7 from above had absolutely zero impact on performance -- it was just as slow with 4 lines as it was with 300. The only thing that I can figure is that this is being run in software for some reason, and/or that it is causing the graphics card to continually switch back and forth between some sort of drawing modes.
Matrix Approach:
If anyone knows of any fix to the above issue, then I'd love to hear it!
But I'm under the assumption that this might just be an issue inside of directx in general, so I've been pursuing another route -- making my own sprite-based line. Essentially, I've got a 1px white image, and I'm using the diffuse color and transforms to draw the lines. This works, performance-wise -- drawing 300 of the "lines" like this puts me in the 3-6% processor utilization performance range that I'm looking for on my quad core.
I have two problems with my pixel-stretch line technique, which I'm hoping that someone more knowledgeable about transforms can help me with. Here's my current code for a horizontal line:
public void DrawLineHorizontal( int X1, int X2, int Y, float HalfHeight, Color Color )
{
float width = ( X2 - X1 ) / 2.0f;
Matrix m = Matrix.Transformation2D( new Vector2( X1 + width, Y ), 0f, new Vector2( width, HalfHeight ),
Vector2.Zero, 0, Vector2.Zero );
sprite.Transform = m;
sprite.Draw( this.tx, Vector3.Zero, new Vector3( X1 + width, Y, 0 ), Color );
}
This works, insofar as it draws lines of mostly the right size at mostly the right location on the screen. However, things appear shifted to the right, which is strange. I'm not quite sure if my matrix approach is right at all: I just want to scale a 1x1 sprite by some amount of pixels horizontally, and a different amount vertically. Then I need to be able to position them -- by the center point is fine, and I think that's what I'll have to do, but if I could position it by upper-left that would be even better. This seems like a simple problem, but my knowledge of matrices is pretty weak.
This would get purely-horizontal and purely-vertical lines working for me, which is most of the battle. I could live with just that, and use some other sort of graphic in locations which I am currently using angled lines. But it would be really nice if there was a way for me to draw lines that are angled using this stretched-pixel approach. In other words, draw a line from 1,1 to 7,19, for instance. With matrix rotation, etc, it seems like this is feasible, but I don't know where to even begin other than guessing and checking, which would take forever.
Any and all help is much appreciated!
it sounds like a pipeline stall. You're switching some mode between rendering sprites and rendering lines, that forces the graphics card to empty its pipeline before starting with the new primitive.
Before you added the lines, were those sprites all you rendered, or were there other elements on-screen, using other modes already?
Okay, I've managed to get horizontal lines working after much experimentation. This works without the strange offsetting I was seeing before; the same principle will work for vertical lines as well. This has vastly better performance than the line class, so this is what I'll be using for horizontal and vertical lines. Here's the code:
public void DrawLineHorizontal( int X1, int X2, int Y, float HalfHeight, Color Color )
{
float width = ( X2 - X1 );
Matrix m = Matrix.Transformation2D( new Vector2( X1, Y - HalfHeight ), 0f, new Vector2( width, HalfHeight * 2 ),
Vector2.Zero, 0, Vector2.Zero );
sprite.Transform = m;
sprite.Draw( this.tx, Vector3.Zero, new Vector3( X1, Y - HalfHeight, 0 ), Color );
}
I'd like to have a version of this that would work for lines at angles, too (as mentioned above). Any suggestions for that?

Categories