I have this code in SharpGL where zoom and pan were implemented. This is de draw method:
private void OpenGLDraw(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
// Enable finish button
if (openFileOk && dispCBOk) { finish_btn.IsEnabled = true; }
else { finish_btn.IsEnabled = false; }
// Get the OpenGL object
OpenGL gl = GLControl.OpenGL;
// Set The Material Color
float[] glfMaterialColor = new float[] { 0.4f, 0.2f, 0.8f, 1.0f };
float w = (float)GLControl.ActualWidth;
float h = (float)GLControl.ActualHeight;
// Perspective projection
gl.Viewport(0, 0, (int)w, (int)h);
gl.Ortho(0, w, 0, h, -100, 100);
// Clear The Screen And The Depth Buffer
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT_AND_DIFFUSE, glfMaterialColor);
gl.MatrixMode(MatrixMode.Modelview);
gl.LoadIdentity();
gl.Scale(scale,scale,1);
// Camera Position
gl.LookAt(_position[0], _position[1], _position[2],
_viewPoint[0], _viewPoint[1], _viewPoint[2],
_upVector[0], _upVector[1], _upVector[2]);
// Draw the helix
float[][] points = vertexes.ToArray();
Helix(points.Length, points);
}
What I have here is a perspective projection using Viewport (the ortho projection and scale are used for zooming).
This is the code for panning whatever is drawn on the screen:
// pan
// y axis
// Up
if (keyCode == 1)
{
_viewPoint[1] += _moveDistance;
_position[1] += _moveDistance;
}
// Down
else if (keyCode == 2)
{
_viewPoint[1] += -_moveDistance;
_position[1] += -_moveDistance;
}
// x axis
// Left
else if (keyCode == 3)
{
_viewPoint[2] += _moveDistance;
_position[2] += _moveDistance;
}
// Right
else if (keyCode == 4)
{
_viewPoint[2] += -_moveDistance;
_position[2] += -_moveDistance;
}
The thing is that when I move too much to any one of the sides (up, down, left, right), the drawing gets really distorted. I didn't notice it at first because I was working with the image very zoomed in and couldn't see the difference, but now I see that the distortion always happens, it's just more intense the more you move away from the image.
All I want to know is why this happens, and if possible, a solution for it, thank you.
Here is an example of what happens:
Normal view
Distorted view
Related
I have a picturebox with an image in it. The image contains two ellipses face to face (black & blue).
What I want is to rotate the picturebox in a timer (for the effect) so the image to be "upside down" would look much more like they've changed place, which basically it's just rotating the picturebox like how the erath is moving around it's axis.
There are various kinds of rotations from a globe, depending on how you look at it.
If you look at it from above the poles it spins like a disk or a gear and you can find code for it here. This has the advantage that you can use any image and rotate it.
If you look at it from the side, facing the equator you can't easily use bitmaps, but using just two colors it will still look nice..
Here is an example of a 'globe-like' spinning rotation:
float angle = 0f;
float aSpeed = 4f; // <-- set your speed
Brush brush1 = Brushes.CadetBlue; // and your..
Brush brush2 = Brushes.DarkSlateBlue; // ..colors
private void timer1_Tick(object sender, EventArgs e)
{
angle += aSpeed;
if (angle + aSpeed > 360)
{
angle -= 360f;
Brush temp = brush1;
brush1 = brush2;
brush2 = temp;
}
pictureBox1.Invalidate();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Rectangle r = pictureBox1.ClientRectangle;
Rectangle r2 = r; // see below..
r.Inflate(-20, -20); // a little smaller than the panel or pBox
if (angle < 180)
{
e.Graphics.FillEllipse(brush1, r);
e.Graphics.FillPie(brush2, r, 270, 180);
r.Inflate(-(int)(r.Width * angle / 360f), 0);
e.Graphics.FillEllipse(brush2, r);
}
else
{
e.Graphics.FillEllipse(brush2, r);
e.Graphics.FillPie(brush1, r, 90, 180);
r.Inflate(-(int)(r.Width * angle / 360f), 0);
e.Graphics.FillEllipse(brush1, r);
}
}
}
This is created by three DrawXXX calls: a circle of one color and an ellipse and an arc, set to display a half circle of the same, second color.
Note: To make the angular speed uniform you may want to play with a little Math.Sin and/or an angle table..
If you look at it from any other angle and if you need to show rotating bitmaps in 3D you can't easily draw it but will need to resort to displaying frames..
But you can combine the disk rotation from the link with the code above and will get rather complex rotations, that look a lot like a 3D sphere.. Simply add the code before the drawing..
float bw2 = r2.Width / 2f;
float bh2 = r2.Height / 2f;
e.Graphics.TranslateTransform(bw2, bh2);
e.Graphics.RotateTransform(angle / 3);
e.Graphics.TranslateTransform(-bw2, -bh2);
..use the drawing from above instead of the DrawImage line and move the ResetTransform to the end. You will want to use a different or scaled angle!
I am trying to make a triangle move back and forth over an arc, the triangle shoud rotate while moving.
I have made a picture to explain it better:
https://app.box.com/s/mt9p66zlmtkkgkdvtb5h
The math looks right to me, can anyone tell me what I am doing wrong?
public partial class Form1 : Form
{
bool turn = false;
double angle = 0;
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush solidBlackBrush = new SolidBrush(Color.Black); //En solid svart brush som brukes flere steder
Pen solidBackPen = new Pen(solidBlackBrush);//En solid svart pen som brukes flere steder
//Trekant = Norwegian for Triangle, Trekant is a class that draws a polygon shaped as a Triangle.
Trekant tre = new Trekant();
e.Graphics.DrawArc(solidBackPen, new Rectangle(new Point(50,50), new Size(100,100)) , 180, 180);
//X = a + r*Cos(angle) | Y = b + r*Sin(angle)
double x = (50+(100/2)) + (100/2) * Math.Cos(Trekant.DegreeToRadian(angle));
double y = (50+(100/2)) - (100/2) * Math.Sin(Trekant.DegreeToRadian(angle));
e.Graphics.TranslateTransform((float)x - 15, (float)y - 40);//Flytter 0 slik at pistolen havner på rett sted
e.Graphics.RotateTransform((float)-Trekant.RadianToDegree(Trekant.DegreeToRadian(angle-90)));
tre.Draw(e.Graphics);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (angle == 0)
{
turn = false;
}
if (angle == 180)
{
turn = true;
}
if (turn)
{
angle -= 10;
}
if (!turn)
{
angle += 10;
}
this.Invalidate();
}
}
Without going into coding let's first set up the math..
Let say the half ellipse in the picture has a width of 2w and a height of h. And lets assume you want the movement to happen in n steps.
Then at each step s the rotation angle is s * 180f/n. The rotation point's x stays at w plus whatever offset ox the ellipse has, but will have to move its y vertically from offset oy, first by (w-h) * 2f / n down on each step and then up again by the same amounts..
The Drawing itself moves accordingly.
So you have a TranslateTransform for the rotation point, the RotateTransform, then another TranslateTransform to place the image, then the DrawImage and finally a ResetTransform.
I hope that helps. If that doesn't work, please update the question and we'll can get it right, I'm sure..
I have VS2012 and try to make a simple xna app for Windows Phone 7/8.
I have something like this:
public partial class GamePage : PhoneApplicationPage
{
ContentManager contentManager;
GameTimer timer;
SpriteBatch spriteBatch;
public Texture2D firstSprite { get; set; }
public Vector2 transition = new Vector2(0, 0);
public GamePage()
{
InitializeComponent();
contentManager = (Application.Current as App).Content;
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
firstSprite = this.contentManager.Load<Texture2D>("ikona");
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
timer.Stop();
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
float d = 0;
private void OnUpdate(object sender, GameTimerEventArgs e)
{
TouchCollection touchCollection = TouchPanel.GetState();
foreach (TouchLocation tl in touchCollection)
{
if (tl.State == TouchLocationState.Pressed && tl.Position.X < 240)
{
d = -10;
}
if (tl.State == TouchLocationState.Pressed && tl.Position.X > 240)
{
d = 10;
}
if (tl.State == TouchLocationState.Released)
d = 0;
}
transition += new Vector2(d, 0);
}
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(firstSprite, transition, null, Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
spriteBatch.End();
}
}
Is it a good way do animate sprite? Will it be animated same way on every device? When I was testing it on lumia920 it wasn't very smooth, so I think I do it badly.
And second thing. I want to move picture left when I press left half of the screen and move right when I press right side. It partly works but when I press left (it moves left), then at the same time press right (it moves right), and then release right, picture stops. I thought it would move again left. How can I achieve this?
/edit/
And what about touch handling?
I'm not sure how it's animated as I'm not familiar with TouchCollection.
But using XNA to animate works like this for most of the solutions out there:
You have your texture which is a spritesheet I assume containing all the frames for your animated sprite.
You use a Rectangle which will hold Position and size on the screen.
You also use something called a source rectangle, which is a rectangle that represents an area inside your sprite, which will stretch in your position rectangle. Here is an image:
Rectangle A is your position + size rectangle, and rectangle B is your source rectangle.
To create an animation you say the following ( pseudo code ):
int timer = 0;
Rectangle position = new Rectangle(100, 100, 80, 80); // position 100x, 100y, and size is 80 width and height.
Rectangle source = new Rectangle(0, 0, 80, 80); // Position on the spritesheet is 0, 0 which should be first frame, and the frames are all 80*80 pixels in size.
private void OnUpdate(object sender, GameTimerEventArgs e)
{
timer += e.ElapsedTime.TotalMilliseconds;
if(timer > 30) // If it has been 0.03 seconds = 33 frames per second
{
timer = 0; // reset timer
source.X += 80; // Move x value to next frame
if(source.X > widthOfYourSpriteSheet) source.X = 0; // Reset the animation to the beginning when we are at the end
}
}
Your draw function will look pretty much the same, except for the sourcerectangle argument you will put in it.
I'm trying to create a resizable image overlay (for cropping purposes). It seems pretty easy to resize the overlay if I ignore the aspect ratio, but I can't figure out how to perform a constrained resize that respects the AR. I figure that I obviously can't obey the overlay's "grip" positions (or even borders) unless I force the mouse to follow it, but that seems unnatural, so I'll just have to rely on the mouse gesture (which I don't mind doing).
I can also easily resize the overlay and then force it into the proper dimensions afterwards (like every other question about this topic on this site is about), but it's not very intuitive when using a mouse.
This is sort of what I'm going for:
http://deepliquid.com/projects/Jcrop/demos.php?demo=live_crop
I've written an application like this before but it was browser-based so I used a javascript library. This is a desktop application and I haven't found a suitable library for this.
I've left a lot of details out of this code snippet and simplified some conditions with booleans.
private void pbImage_Paint(object sender, PaintEventArgs e)
{
//Overlay
e.Graphics.FillRectangle(brushRect, overlayRect);
// Grips
e.Graphics.FillRectangle(gripRect, leftTopGrip);
e.Graphics.FillRectangle(gripRect, rightTopGrip);
e.Graphics.FillRectangle(gripRect, leftBottomGrip);
e.Graphics.FillRectangle(gripRect, rightBottomGrip);
AdjustGrips();
base.OnPaint(e);
}
public void AdjustGrips()
{
// The next section only causes the grips to partly obey
// the AR - the rest of the overlay ignores it
if (overlayRect.Height * arWidth <= overlayRect.Width)
overlayRect.Width = overlayRect.Height * arWidth;
else if (overlayRect.Width * arHeight <= overlayRect.Height)
overlayRect.Height = overlayRect.Width * arHeight;
leftTopGrip.X = overlayRect.Left;
leftTopGrip.Y = overlayRect.Top;
rightTopGrip.X = overlayRect.Right - rightTopGrip.Width;
rightTopGrip.Y = overlayRect.Top;
leftBottomGrip.Y = overlayRect.Bottom - leftBottomGrip.Height;
leftBottomGrip.X = overlayRect.Left;
rightBottomGrip.X = overlayRect.Right - rightBottomGrip.Width;
rightBottomGrip.Y = overlayRect.Bottom - rightBottomGrip.Height;
}
private void pbImage_MouseMove(object sender, MouseEventArgs e)
{
Point pt = new Point(e.X, e.Y);
// Details elided
if (e.Button == MouseButtons.Left && mouseinGrip)
{
if (bottomRightIsGripped)
{
newOverlayRect.X = overlayRect.X;
newOverlayRect.Y = overlayRect.Y;
newOverlayRect.Width = pt.X - newOverlayRect.Left;
newOverlayRect.Height = pt.Y - newOverlayRect.Top;
if (newOverlayRect.X > newOverlayRect.Right)
{
newOverlayRect.Offset(-width, 0);
if (newOverlayRect.X < 0)
newOverlayRect.X = 0;
}
if (newOverlayRect.Y > newOverlayRect.Bottom)
{
newOverlayRect.Offset(0, -height);
if (newOverlayRect.Y < 0)
newOverlayRect.Y = 0;
}
pbImage.Invalidate();
oldOverlayRect = overlayRect = newOverlayRect;
Cursor = Cursors.SizeNWSE;
}
// Code for other grips elided
}
AdjustGrips();
pbImage.Update();
base.OnMouseMove(e);
}
// Mouse up and down elided
You have complete control over the new size for the overlay as it drags.
The example link that you've given, is simply selecting a starting point based on the click down, then selecting Max(Abs(pt.x - start.x), Abs(pt.y - start.y)), and basing the crop square off of that.
To use a non square ratio, normalize the distances first.
// given known data
//
// Point start;
// The starting location of the mouse down for the drag,
// or the top left / bottom right of the crop based on if the mouse is
// left/above the starting point
//
// Size ratio;
// The ratio of the result crop
//
// pt = (20)x(-20)
// start = (0),(0)
// ratio = (1)x(2)
var dist = new Point(pt.X - start.X, pt.Y - start.Y);
// "normalize" the vector from the ratio
// normalized vector is the distances with respect to the ratio
// ratio is (1)x(2). A (20)x(-20) is normalized as (20),(-10)
var normalized = new Point(dist.X / ratio.Width, dist.Y / ratio.Height);
// In our (20),(-10) example, we choose the ratio's height 20 as the larger normal.
// we will base our new size on the height
var largestNormal = (Math.Abs(normalized.X) > Math.Abs(normalized.Y)
? Math.Abs(normalized.X) : Math.Abs(normalized.Y);
// The calcedX will be 20, calcedY will be 40
var calcedOffset = (largestNormal * ratio.Width, largestNormal * ratio.Height);
// reflect the calculation back to the correct quarter
// final size is (20)x(-40)
if (distX < 0) calcedOffset.X *= -1;
if (distY < 0) calcedOffset.Y *= -1;
var newPt = new Point(start.X + calcedOffset.X, start.Y + calcedOffset.Y);
Notice that one of the lengths can grow greater than the mouse location, but it will never be less. This will have the effect of the mouse traveling along the edge of the new crop box, and the box maintaining ratio.
I've figured out what was causing the original problems in my code. Unlike a static image resize, the aspect ratio code depends on which grip you're "holding", so putting it in a common location for all cases (eg. when the grip positions are set) will not work. You can easily calculate the size of the what the rect should be on the next update, but the position should be set depending on which grip is being held.
If, for example, you're resizing by holding the top left grip, then the bottom and right sides of the cropping rectangle should remain stationary. If you leave the code the same, then the rectangle resizes correctly, but it moves around the canvas and/or the grips go out of sync with the corners of the rect. There is probably a better way to do this but here's some crude code that works. I've only included code for the bottom right and top left grips to illustrate the differences. Extraneous things like setting the mouse pointer and error checking omitted.
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Point mousePosition = new Point(e.X, e.Y);
if (e.Button == MouseButtons.Left)
{
// This resizeMode, moveMode and other booleans
// are set in the MouseUp event
if (resizeBottomLeft)
{
// Top and Right should remain static!
newCropRect.X = mousePosition.X;
newCropRect.Y = currentCropRect.Y;
newCropRect.Width = currentCropRect.Right - mousePosition.X;
newCropRect.Height = mousePosition.Y - newCropRect.Top;
if (newCropRect.X > newCropRect.Right)
{
newCropRect.Offset(cropBoxWidth, 0);
if (newCropRect.Right > ClientRectangle.Width)
newCropRect.Width = ClientRectangle.Width - newCropRect.X;
}
if (newCropRect.Y > newCropRect.Bottom)
{
newCropRect.Offset(0, -cropBoxHeight);
if (newCropRect.Y < 0)
newCropRect.Y = 0;
}
// Aspect Ratio + Positioning
if (newCropRect.Width > newCropRect.Height)
{
newCropRect.Height = (int)(newCropRect.Width / ASPECT_RATIO);
}
else
{
int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
newCropRect.X = newCropRect.Right - newWidth;
newCropRect.Width = newWidth;
}
}
else if (resizeTopRight)
{
// Bottom and Left should remain static!
newCropRect.X = oldCropRect.X;
newCropRect.Y = mousePosition.Y;
newCropRect.Width = mousePosition.X - newCropRect.Left;
newCropRect.Height = oldCropRect.Bottom - mousePosition.Y;
if (newCropRect.X > newCropRect.Right)
{
newCropRect.Offset(-cropBoxWidth, 0);
if (newCropRect.X < 0)
newCropRect.X = 0;
}
if (newCropRect.Y > newCropRect.Bottom)
{
newCropRect.Offset(0, cropBoxHeight);
if (newCropRect.Bottom > ClientRectangle.Height)
newCropRect.Y = ClientRectangle.Height - newCropRect.Height;
}
// Aspect Ratio + Positioning
if (newCropRect.Width > newCropRect.Height)
{
int newHeight = (int)(newCropRect.Width / ASPECT_RATIO);
newCropRect.Y = newCropRect.Bottom - newHeight;
newCropRect.Height = newHeight;
}
else
{
int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
newCropRect.Width = newWidth;
}
}
else if (moveMode) //Moving the rectangle
{
newMousePosition = mousePosition;
int dx = newMousePosition.X - oldMousePosition.X;
int dy = newMousePosition.Y - oldMousePosition.Y;
currentCropRect.Offset(dx, dy);
newCropRect = currentCropRect;
oldMousePosition = newMousePosition;
}
if (resizeMode || moveMode)
{
oldCropRect = currentCropRect = newCropRect;
// Set the new position of the grips
AdjustGrips();
pictureBox1.Invalidate();
pictureBox1.Update();
}
}
}
I got code from http://support.microsoft.com/kb/314945 to draw a reversible/rubber band rectangle. I added code to it so that when i leave the left mouse button a rectangle is also created on the image and then i use that for cropping the image.
This works superb. the only problem is that the rubberband rectangle doesnot start or end from where the mouse is... there is very little diference but still it is quite notable. i use the same co-ords to draw the rectangle afterwards which is drawn exactly where my mouse started and where is ended. help would be appreciated.
Here is the code: (Got issue fixed - adding code that others can benefit from it)
I have made it a control and i use it wherever i need it!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace CroppingControl
{
public partial class CroppingImage : UserControl
{
Rectangle rc = new Rectangle();
Boolean bHaveMouse;
Point ptOriginal = new Point();
Point ptLast = new Point();
Image Pic;
public CroppingImage()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(MyMouseDown);
pictureBox1.MouseUp += new MouseEventHandler(MyMouseUp);
pictureBox1.MouseMove += new MouseEventHandler(MyMouseMove);
bHaveMouse = false;
}
public Image Image
{
set
{
pictureBox1.Image = value;
Pic = value;
}
get
{
return pictureBox1.Image;
}
}
public void MyMouseDown(Object sender, MouseEventArgs e)
{
pictureBox1.Image = Pic;
// Make a note that we "have the mouse".
bHaveMouse = true;
// Store the "starting point" for this rubber-band rectangle.
ptOriginal.X = e.X;
ptOriginal.Y = e.Y;
// Special value lets us know that no previous
// rectangle needs to be erased.
ptLast.X = -1;
ptLast.Y = -1;
}
// Convert and normalize the points and draw the reversible frame.
private void MyDrawReversibleRectangle(Point p1, Point p2)
{
Point px = p1;
Point py = p2;
// Convert the points to screen coordinates.
p1 = PointToScreen(p1);
p2 = PointToScreen(p2);
// Normalize the rectangle.
if (p1.X < p2.X)
{
rc.X = p1.X;
rc.Width = p2.X - p1.X;
}
else
{
rc.X = p2.X;
rc.Width = p1.X - p2.X;
}
if (p1.Y < p2.Y)
{
rc.Y = p1.Y;
rc.Height = p2.Y - p1.Y;
}
else
{
rc.Y = p2.Y;
rc.Height = p1.Y - p2.Y;
}
// Draw the reversible frame.
ControlPaint.DrawReversibleFrame(rc, Color.Black, FrameStyle.Dashed);
rc.X = px.X;
rc.Y = px.Y;
}
// Called when the left mouse button is released.
public void MyMouseUp(Object sender, MouseEventArgs e)
{
// Set internal flag to know we no longer "have the mouse".
bHaveMouse = false;
// If we have drawn previously, draw again in that spot
// to remove the lines.
if (ptLast.X != -1)
{
Point ptCurrent = new Point(e.X, e.Y);
MyDrawReversibleRectangle(ptOriginal, ptLast);
Graphics graphics = pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Gray, 2);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
graphics.DrawRectangle(pen, rc);
}
// Set flags to know that there is no "previous" line to reverse.
ptLast.X = -1;
ptLast.Y = -1;
ptOriginal.X = -1;
ptOriginal.Y = -1;
}
// Called when the mouse is moved.
public void MyMouseMove(Object sender, MouseEventArgs e)
{
Point ptCurrent = new Point(e.X, e.Y);
// If we "have the mouse", then we draw our lines.
if (bHaveMouse)
{
// If we have drawn previously, draw again in
// that spot to remove the lines.
if (ptLast.X != -1)
{
MyDrawReversibleRectangle(ptOriginal, ptLast);
}
// Update last point.
ptLast = ptCurrent;
// Draw new lines.
MyDrawReversibleRectangle(ptOriginal, ptCurrent);
}
}
}
}
ControlPaint.DrawReversibleFrame uses screen co-ordinates to draw a rectangle on the screen (i.e. without respect to your application's windows) which is useful when acting upon drag mouse actions as the mouse may move outside of the application window. If you are using these very same co-ordinates raw to paint in your application, then they will be out as the co-ordinates on the control upon which you are painting are with respect to the control's origin (usually its top-left corner).
To use the screen co-ordinates, you first need to convert them into control co-ordinates using the PointToClient() or RectangleToClient() methods on the control upon which you are painting, e.g.
// panel`s OnPaint
Rectangle screenRectangle = ...
Rectangle clientRectangle = panel.RectangleToClient(screenRectangle);
graphics.DrawRectangle(Pens.Red, clientRectangle);