SFML: keep a RenderWindow's partial region unrefreshed - c#

I am looking to get the same effect as the GraphicsPath from Winforms which allows to keep some particular areas of myForm unrefreshed. f.i.:
myForm.Invalidate(new Region(graphicsPath));
My final goal is to draw things at the unrefreshed location, using a HDC (Device context handle) that I will provide to an external application. (This currently works using winforms).
I am using SFML.Net 2.4 and I create my window this way:
SFML.Graphics.RenderWindow mySfmlWindow = new RenderWindow(myForm.Handle, settings);
I can still create the HDC on myForm, however, even without calling :
mySfmlWindow.Clear(color);
, the things drawn by the external application are still instantly cleared.

Manual approach
You can draw your desired background yourself. I've got an example, where I draw a half of the window background manually, while the other half is not cleared.
The left half is "cleared" in gray just to show the point.
In the code, I use a sf::RectangleShape to clear the window, but you can use a sf::VertexArray if your shape is more complex.
Full Code
int main(){
sf::RenderWindow win(sf::VideoMode(640, 480), "SFML Test");
sf::RectangleShape r1;
r1.setOrigin(sf::Vector2f(25, 25));
r1.setPosition(50, 50);
r1.setSize(sf::Vector2f(50, 50));
r1.setFillColor(sf::Color::Red);
sf::RectangleShape r2;
r2.setOrigin(sf::Vector2f(25, 25));
r2.setPosition(400, 50);
r2.setSize(sf::Vector2f(50, 50));
r2.setFillColor(sf::Color::Blue);
sf::RectangleShape updatedRegion;
updatedRegion.setPosition(0, 0);
updatedRegion.setSize(sf::Vector2f(320, 480)); // Half window
updatedRegion.setFillColor(sf::Color(30,30,30)); // Dark gray just for the sake of the example
while (win.isOpen())
{
sf::Event event;
while (win.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
win.close();
}
}
//win.clear(); // We skip the clearing process
win.draw(updatedRegion); // And do our own "clear"
win.draw(r1);
win.draw(r2);
win.display();
// Just some movement to test the concept
r1.rotate(0.01);
r2.rotate(0.01);
}
return 0;
}

Related

C# Form Draws Slowly

I made an application on Visual Studio 2012 and im trying to speed up the draw times of the forms.
I have a main form and inside of it i have a container in which depending on the selection of a tool strip, the new form will show inside of it. It works like a charm, but the issue is, it takes a lot of time to draw, no matter how good the computer is (tried on different computers), and the issue seems to be the background.
I have set a background image for the main form, for the container inside that form, and for all the forms in my project, so when they show up, the background image isnt chopped and it continues the image. But, if instead of using a background for picture and i leave the back in white, for all, the main form, container, and forms, it works like a charm.
I've read around the internet about setting the double buffer inside the form and stuff to true, but it didnt do anything, it takes the same ammount of time.
Any advice? Thanks in advance!
You can squeeze a little more speed out of it by drawing the background manually. This helps because it allows you to disable the underlying background color, which just wastes time because it gets overwritten with the image anyway.
// Reference to manually-loaded background image
Image _bmp;
// In your constructor, set these styles to ensure that the background
// is not going to be automatically erased and filled with a color
public Form1() {
InitializeComponent();
SetStyle(
ControlStyles.Opaque |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
// Load background image
_bmp = Image.FromFile("c:\\path\\to\\background.bmp");
}
// Override OnPaint to draw the background
protected override void OnPaint(PaintEventArgs e) {
var g = e.Graphics;
var srcRect = new Rectangle(0, 0, _bmp.Width, _bmp.Height);
int startY = Math.Max(0, (e.ClipRectangle.Top / _bmp.Height) * _bmp.Height);
int startX = Math.Max(0, (e.ClipRectangle.Left / _bmp.Width) * _bmp.Width);
for (int y = startY; y < e.ClipRectangle.Bottom; y+= _bmp.Height)
for (int x = startX; x < e.ClipRectangle.Right; x += _bmp.Width)
{
var destRect = new Rectangle(x, y, _bmp.Width, _bmp.Height);
g.DrawImage(_bmp, destRect, srcRect, GraphicsUnit.Pixel);
}
base.OnPaint(e);
}

Moving a transparent image in a picturebox

For a project, I'm making a game and in it I have a scrolling map. The map moves left and right and is redrawn in a picturebox so that I can have a large map in a small picturebox. The top portion of the map is transparent so that I can change the sky colour later on. However when I move the map, the transparent part glitches out.
Original map before moving
After moving the map a bit
As you can see, everything above the tree line gets stretched, that is because that is where the transparency starts. The picturebox's parent is the form and the form is light blue, which is why the background is light blue.
Here is my code for moving the picture/redrawing it onto the picturebox:
private void timerTick_Tick(object sender, EventArgs e)
{
move();
//Draws new portion of the map
g.DrawImage(image, new Rectangle(0, 0, pbMap.Width, pbMap.Height), new Rectangle(imageX, imageY, pbMap.Width, pbMap.Height), GraphicsUnit.Pixel);
//Refreshes
pbMap.Image = bmp;
}
private void move()
{
//Right arrow events
if (right)
{
imageX += mapSpeed;
//Makes sure the picture stays within borders
if (imageX >= (imageWidth - pbMap.Width))
{
imageX = imageWidth - pbMap.Width;
}
}
//Left arrow events
if (left)
{
imageX -= mapSpeed;
//Makes sure the picture stays within borders
if (imageX <= 0)
{
imageX = 0;
}
}
}
Can anyone help explain the glitching?
Try calling g.Clear() with your sky color before the g.DrawImage() call. I think it's just drawing on top of itself and that's causing the smearing.
To me it seems like you are redrawing over and over without clearing the display from the previous draw! What type of framework are you using to develop that? Does it have a custom drawing class? As tesserex suggested more specifically call g.Clear() and u will be fine.
You shouldn't need g.Clear in this case because you're Re-Drawing a new image every time.
My bet is that imageX is greater then (imageWidth - pbMap.Width) so it will not enter the IF, therefore it will redraw the same as before.
Note: I don't know how you create g but if you use .CreateGraphics() don't forget to Dispose()
Cheers

Custom control onPaint event not working

Hey people I have a problem I am writing a custom control. My control inherits from Windows.Forms.Control and I am trying to override the OnPaint method. The problem is kind of weird because it works only if I include one control in my form if I add another control then the second one does not get draw, however the OnPaint method gets called for all the controls. So what I want is that all my custom controls get draw not only one here is my code:
If you run the code you will see that only one red rectangle appears in the screen.
public partial class Form1 : Form
{
myControl one = new myControl(0, 0);
myControl two = new myControl(100, 0);
public Form1()
{
InitializeComponent();
Controls.Add(one);
Controls.Add(two);
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
public class myControl:Control
{
public myControl(int x, int y)
{
Location = new Point(x, y);
Size = new Size(100, 20);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Pen myPen = new Pen(Color.Red);
e.Graphics.DrawRectangle(myPen, new Rectangle(Location, new Size(Size.Width - 1, Size.Height - 1)));
}
}
I'm guessing you are looking for something like this:
e.Graphics.DrawRectangle(Pens.Red, new Rectangle(0, 0,
this.ClientSize.Width - 1,
this.ClientSize.Height - 1));
Your Graphic object is for the interior of your control, so using Location isn't really effective here. The coordinate system starts at 0,0 from the upper-left corner of the client area of the control.
Also, you can just use the built-in Pens for colors, otherwise, if you are creating your own "new" pen, be sure to dispose of them.
LarsTech beat me to it, but you should understand why:
All drawing inside of a control is made to a "canvas" (properly called a Device Context in Windows) who coordinates are self-relative. The upper-left corner is always 0, 0.
The Width and Height are found in ClientSize or ClientRectangle. This is because a window (a control is a window in Windows), has two areas: Client area and non-client area. For your borderless/titlebar-less control those areas are one and the same, but for future-proofing you always want to paint in the client area (unless the rare occasion occurs where you want to paint non-client bits that the OS normally paints for you).

Can Cairo draw directly onto a Gtk.Pixbuf?

I have a fine, working system in C# that draws with Cairo commands in a render method. However, sometimes I would like to draw into a pixmap, rather than dynamically when the screen needs to be updated. For example, currently I have:
public override void render(Cairo.Context g) {
g.Save();
g.Translate(x, y);
g.Rotate(_rotation);
g.Scale(_scaleFactor, _scaleFactor);
g.Scale(1.0, ((double)_yRadius)/((double)_xRadius));
g.LineWidth = border;
g.Arc(x1, y2, _xRadius, 0.0, 2.0 * Math.PI);
g.ClosePath();
}
But I would like, if I choose, to render the Cairo commands to a Gtk.Pixbuf. Something like:
g = GetContextFromPixbuf(pixbuf);
render(g);
Is that possible? It would be great if I didn't have to turn the context back into a pixbuf, but that the cairo drawing would go directly to the pixbuf. Any hints on this would be appreciated!
The answer is actually quite easy: when you render the objects, render them to a context created from a saved surface. Then when you render the window, insert a context based on the same saved surface.
Create a surface:
surface = new Cairo.ImageSurface(Cairo.Format.Argb32, width, height);
Render a shape to the surface:
using (Cairo.Context g = new Cairo.Context(surface)) {
shape.render(g); // Cairo drawing commands
}
Render the window:
g.Save();
g.SetSourceSurface(surface, 0, 0);
g.Paint();
g.Restore();
... // other Cairo drawing commands
That's it!

Drawn content lost on window overlap in GTK#

I am using Cairo in a GTK# application for drawing. When another window covers part of the drawn content, the overlapped part of the drawn content is lost. Is there a way to make it permanent?
Here is my simplified method for drawing the content:
void UpdateConnectionLines ()
{
GdkWindow myWindow = GetGdkWindow();
myWindow.Clear ();
using (Context g = Gdk.CairoHelper.Create (myWindow))
{
g.Save ();
g.MoveTo (0, 20);
g.LineTo (100, 20);
g.Restore ();
g.Color = new Color (0, 0, 0);
g.LineWidth = 1;
g.Stroke();
}
}
If you are drawing directly on the form, then you need to do it in the Form's paint event, to ensure it is there every time the form get's painted (i.e. when another window covers it and then moves, when it is resized, ...)
Evaluating John Koerner's answer, I have found a solution, that works for every GTK# widget. I use the generic WidgetEvent ExposeEvent (thanks, ptomato) and redraw.
I append my event handler with
this.ExposeEvent += new global::Gtk.ExposeEventHandler (this.Handle_ExposeEvent);
and then the handler just calls my method:
protected virtual void Handle_ExposeEvent (object o, Gtk.ExposeEventArgs args)
{
UpdateConnectionLines();
}
EDIT:
Actually, I have not RTFM correctly, as it explicitely states:
The best place to create and use the
Context is the ExposeEvent for the
given widget. Usually you'll want to
use the Gtk.DrawingArea for this task.
An example implementation of the
Expose event method:

Categories