I've got a remote screen that takes mouse position then converts the position into the captured screens position but the position is always off. I'm not the greatest at math but I'm sure my conversion is correct. The image size I get in correlation with the screen along with the window size is correct. The only thing that ends up being off is my click point after conversion.
** Also I know I have no error checking. This is to help with readability.
EXAMPLE: I want to click on Screen point 1102x 999y. The Remote Window point is approx 676 584. After conversion the point gets translated to 1081.5 935.3. I would understand maybe a few pixels off because its hard to click on that exact spot through the RW but its getting translated 60 pixels up and 20 pixels left.
Lets say that my remote screen is 800 x 600 and the captured is 1280 by 1024.
to get the difference I use (1280 / 800)*Clickpointx and (1024 / 600)*Clickpointy.
My problem is sWidth and sHeight are always off by at least 20 and sometimes are off by upwards of 80 depending on where the click is inside the window.
POINT p;
float sWidth;
float sHeight;
GetCursorPos(&p);
RECT rect;
GetWindowRect(hWnd, &rect); // Get Window Size
ScreenToClient(hWnd, &p); // convert points relative to screen to points relative to window
float wWidth = rect.right - rect.left;
float wHeight = rect.bottom - rect.top;
Gdiplus::Image* image = Gdiplus::Image::FromStream(sRemote_Handler.istream); //Get Captured image Size;
float iWidth = image->GetWidth();
float iHeight = image->GetHeight();
INPUT input;
input.type = INPUT_MOUSE;
input.mi.time = 0;
float pointx = p.x;
float pointy = p.y;
sWidth = (iWidth / wWidth)*pointx; // divide image width by screen width
sHeight = (iHeight / wHeight)*pointy; // divide image height by screen height
input.mi.dx = sWidth*(65536.0f / iWidth); //convert to pixels
input.mi.dy = sHeight*(65536.0f / iHeight); // ^
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
SendInput(1, &input, sizeof(input)); // move mouse
I've tried changing the conversion method to (Clickpointx*1280)/800 and (Clickpointy*1240)/600. Still the same results.
sWidth = (pointx * iWidth) / wWidth;
sHeight = (pointy * iHeight) / wHeight;
Changed the conversion method once more. Same Results
sWidth = (pointx / iWidth)*wWidth;
sHeight = (pointy / iHeight)*wHeight;
EDIT
I've came to the conclusion that its the ScreenToClient windows function.
When clicking in the bottom right corner which should be damn near 800,600 both values are around 50 less then what they should be. When I click in the top left both values are where they should be. The numbers are reporting as 0,1. It seems as though the further I get from the initial 0,0 point the less accurate ScreenToClient gets.
Related
Suppose I have a 600 by 600 window created through glfwCreateWindow().
I have a few models rendered in the scene and to move around them in the 3D space I use a camera class which moves around with my cursor as well as AWSD/spacebar keys.
To make the movement "seamless" I use glfwSetInputMode(this->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED).
Now then the problem is that I am also trying to implent a mouse picker of sorts (casting ray from cursor position to the scene).
So I implemented the mouse picker from a video I found on yt, but it doesnt work properly. The reason being is that my mouse X and Y coordinates get bigger than width and height if for example I rotate on spot (and I can increase it this way indefinitely).
I understand that this is happening because I have no cursor to be bound within resolution limits due to glfwSetInputMode(this->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED). I am asking how I should correct this so that I can keep the seamlessness as well as be able to limit the cursor coordinates within width and height (otherwise the mouse picker function wont work because normalized mouse coordinates go over [1,1] which completely breaks it and so on).
I will be grateful for any answers.
EDIT:
#httpdigest's answer put into very elementary code:
//store offsetX and offsetY values (and dont forget to give them 0 as initial value)
overshootX = mouseX - offsetX;
overshootY = mouseY - offsetY;
if (overshootX > width) {
offsetX = offsetX + overshootX - width;
}
if (overshootX < 0) {
offsetX = offsetX + overshootX;
}
if (overshootY > height) {
offsetY = offsetY + overshootY - height;
}
if (overshootY < 0) {
offsetY = offsetY + overshootY;
}
float withinWindowCursorPosX = mouseX - offsetX;
float withinWindowCursorPosY = mouseY - offsetY;
One way of doing it is to store an offset (initially (0, 0)) by how much the cursor moved beyond the bounds of the window/viewport (separate for X and Y).
Essentially, everytime when you either read the current cursor position or are being told of an update by an event, you calculate overshoot = cursorPos - offset (separate for X and Y). If overshoot exceeds (width, height) you increment offset by the excess it exceeded (width, height). Likewise, if overshoot is less than (0, 0) you decrement offset by the negative overshoot.
That way, you have a "sliding" window/rectangle which gets slided around as you keep on moving the mouse cursor beyond either edge of the screen.
Now, whenever you want to obtain the corrected cursor position (within your window), you simply use withinWindowCursorPos = cursorPos - offset.
You can then use withinWindowCursorPos for your picking calculations that require a cursor within the window/viewport.
I'm using direct2d to draw a bitmap (play a video) in a window, and I want to get the absolute coordinates for any position in the playing space, whether transformations are applied or not. So if the resolution is 1280x720, then by hovering the cursor over the image, I should get values like x = 0 ... 1280, y = 0 ... 720.
The positions of the total video area are in the variable m_rcLiveWindowPos, while the variable m_rcDstVideoRect contains the positions of the actual video after adjusting for the aspect ratio. Finally, m_rcSrcVideoRect is just the video resolution (ex: left=0, top=0, right=1280, bottom=720).
Below, I applied a translation and then a scale to the renderTarget. The rawScaleFactor is a number representing the amount to scale the video: if rawScaleFactor=1, then the video should be played at 100%. If 2, then at 200%.
This all works great -- the video zooms in properly and I can click and drag the video around. The problem is that I want to get the absolute x and y coordinates of the video resolution while my cursor is hovering over the video. The first definitions of mousePosInImage work for videos with no zoom/panning with the m_rcDstVideoRect sitting in a "fitted" position, but the values are incorrect for a zoomed-in video.
if (rawScaleFactor != 0)
{
// Make the dragging more precise based on the scaling factor.
float dragPosX = (float)m_rawScaleOffsetX / (rawScaleFactor * 2.0f);
float dragPosY = (float)m_rawScaleOffsetY / (rawScaleFactor * 2.0f);
D2D1_MATRIX_3X2_F translation = D2D1::Matrix3x2F::Translation(dragPosX, dragPosY);
// Get the center point of the current image.
float centerPointX = float(m_rcLiveWindowPos.Width()) / 2;
float centerPointY = float(m_rcLiveWindowPos.Height()) / 2;
// Calculate the amount that the image must scaled by.
D2D1ScaleFactor = ((float)m_videoResolution.width / (float)(m_rcDstVideoRect.right - m_rcDstVideoRect.left)) * (float)rawScaleFactor;
D2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale(D2D1::Size(D2D1ScaleFactor, D2D1ScaleFactor),
D2D1::Point2F(centerPointX, centerPointY));
// First translate the image, then scale it.
m_pJRenderTarget->SetTransform(translation * scale);
int32_t width = ((int32_t)m_videoResolution.width);
int32_t height = ((int32_t)m_videoResolution.height);
// This works for non-zoomed in video:
m_mousePosInImageX = int32_t(width * (rawMousePosX - m_rcDstVideoRect.left) / (m_rcDstVideoRect.right - m_rcDstVideoRect.left));
m_mousePosInImageY = int32_t(height * (rawMousePosY - m_rcDstVideoRect.top) / (m_rcDstVideoRect.bottom - m_rcDstVideoRect.top));
// Does not work for all cases...
m_mousePosInImageX = int32_t((centerPointX * D2D1ScaleFactor) - (centerPointX) + (m_mousePosInImageX / D2D1ScaleFactor));
m_mousePosInImageY = int32_t((centerPointY * D2D1ScaleFactor) - (centerPointY) + (m_mousePosInImageY / D2D1ScaleFactor));
}
m_pJRenderTarget-> DrawBitmap(m_pJVideoBitmap,
m_rcDstVideoRect,
1.0f,
D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
m_rcSrcVideoRect);
I need a way to "reflect" the changes that SetTransform() did in the mousePosInImage variables.
I'm working with an older C++ application and I need to draw text centered and rotated on an hDC. I've got the centered & rotated part working but the font that gets written to the Image is smaller than I'm expecting. If you take an Image and edit it or a word document and add a line of of Text "COPY" in font size 128 it fills up most of the width of the page. With my code it only covers about 1/3 of the page. What am I missing or doing wrong?
UINT nOptions = 0;//DT_CENTER;
RECT rect = {0,0, BITMAPWIDTH(&WatermarkHandle), BITMAPHEIGHT(&WatermarkHandle)}; //{0,0,FileInfo.Width, FileInfo.Height};
SetMapMode(hdcWatermark, MM_TEXT);
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lstrcpy(lf.lfFaceName, &sWatermarkFontName[0]);
lf.lfHeight = -MulDiv(lWatermarkFontSize, GetDeviceCaps(hdcWatermark, LOGPIXELSY), 72);
lf.lfEscapement = lWatermarkAngle * 10;
lf.lfOrientation = lf.lfEscapement;
lf.lfClipPrecision = CLIP_LH_ANGLES;// || CLIP_TT_ALWAYS;
lf.lfWeight = FW_THIN;
SetTextColor(hdcWatermark, black);
DrawRotatedText(hdcWatermark, &sWatermarkText[0], &rect, lf, lWatermarkAngle, nOptions);
void DrawRotatedText(HDC hdc, char *str, LPRECT rect, LOGFONT lf, double angle, UINT nOptions)
{
// convert angle to radian
double pi = 3.141592654;
double radian = (angle / 180 * pi);
HFONT hFontText = CreateFontIndirect(&lf);
HFONT hOldFontText = (HFONT)SelectObject(hdc, hFontText);
SIZE TextSize;
GetTextExtentPoint32(hdc, str, strlen(str), &TextSize);
// get the center of a not-rotated text
POINT center;
center.x = TextSize.cx / 2;
center.y = TextSize.cy / 2;
POINT rcenter;
rcenter.x = long(cos(radian) * center.x - sin(radian) * center.y);
rcenter.y = long(sin(radian) * center.x + cos(radian) * center.y);
// finally draw the text and move it to the center of the rectangle
SetTextAlign(hdc, TA_BASELINE);
SetBkMode(hdc, TRANSPARENT);
long lx = rect->left + ((rect->right - rect->left) / 2) - rcenter.x;
long ly = rect->top + ((rect->bottom - rect->top) / 2) + rcenter.y;
SetGraphicsMode(hdc,GM_ADVANCED);
ExtTextOut(hdc, lx, ly, nOptions, rect, str, strlen(str), NULL);
DeleteObject(hFontText);
SelectObject(hdc, hOldFontText);
return;
}
It all depends on what you mean by a "page".
A point is a physical unit of measure, just like an inch or a millimeter. In most modern contexts it's equal to 1/72 of an inch.
A page in Word corresponds to an actual piece of paper with physical dimensions. After subtracting out the margins it might be 6 inches wide, or 432 points. This will be zoomed by an amount to fill the screen.
Sizing a screen is a little more nebulous, since monitor sizes and resolutions may differ. Long ago Windows standardized on 96 pixels per inch for a default, as this was close enough to the typical monitor connected to a Windows system. This means a 1920x1080 monitor is considered by Windows to be 20 inches wide, or 1440 points, no matter how physically large it is.
In Word's case the screen is 432 points wide, while your program considers it to be 1440 points wide. This is very close to the 1/3 ratio you report. If you set Word to an exact 100% zoom the rendered text should be identical because the page will shrink.
The answer is to do as Word does, and zoom in by making everything 3x larger.
Edit: In your case it seems you want to emulate what Word is doing and show a facsimile of a page. That makes it even easier.
The formula given by Microsoft was from a time when floating point arithmetic was slow and avoided whenever possible. That's not the case today. The equivalent is:
lfHeight = -(int)(points * dpi / 72);
The question is what to use for dpi? You can calculate it directly! Since you know the size of the page you're displaying and the width of the window in pixels, it's a simple division:
double dpi = pixelWidth / (double)inches;
If you need to know what the effective zoom is, that's also a simple division: zoom = dpi / GetDeviceCaps(hdc, LOGPIXELSY).
I'm making a simple tutorial game with openGL and have a question about touch method. Please checkout my code:
My (0,0) point is in center of a screen:
void Init()
{
glClearColor(0.3,0.3,0.3,0.0);
glMatrixMode(GL_PROJECTION);
glOrtho(-400.0,400.0,-300.0,300.0,0,1.0); //сетка, середина в точке 0
}
Before this i'm call mouse methods:
glutPassiveMotionFunc(Mouse);
glutMouseFunc(MousePress);
And in method MousePress when touch is coming, it's another system coordinate with (0,0) point in top left corner of a screen. Please can you tell me better approach then make something like x-300;y-400 in MousePress method.
Given the simpler orthographic projection, your "x-300;y-400" is the correct approach, although you might want to do some scaling too...
float x = mouseX/(float)windowWidth;
float y = 1.0f - mouseY/(float)windowHeight; //flip since y=0 is at the top
//x and y are now 0 to 1, bottom left to top right
x = left + x * (right - left);
y = bottom + y * (top - bottom);
//x and y are now in 3D coordinates
Here, left/right/bottom/top are from glOrtho, which in your case can be substituted as follows (but of course storing in a variable is better)...
x = -400 + x * (400 - (-400));
y = -300 + y * (300 - (-300));
If you were using a perspective projection it gets a bit more complicated, as I've described here.
[EDIT]
Assuming the window size is 800x600, the above cancels to {x-400,300-y}. For example,
mouseY = 50;
windowHeight = 600;
float y = 1.0f - (50/(float)600); //1.0 - 0.08333 = 0.91667
y = -300 + y * (300 - (-300)); //-300 + 0.91667 * 600 = 250, also 300-50
Part 1 : In one of my projects, I want to display a Image in center of a Custom Control using GDI+ while maintaining the aspect ratio of the Image. Here is my code
Gdiplus::Image image(imagePathWStr); // imagePathWStr is the Image Path
int originalWidth = image.GetWidth();
int originalHeight = image.GetHeight();
// This code calculates the aspect ratio in which I have to draw the image
int16 cntrlwidth = controlPosition.right - controlPosition.left; // controlPosition is the custom Control Rect
int16 cntrlheigth = controlPosition.bottom - controlPosition.top;
float percentWidth = (float)cntrlwidth / (float)originalWidth;
float percentHeight = (float)cntrlheigth / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
int newImageWidth = (int)(originalWidth * percent);
int newImageHeight = (int)(originalHeight * percent);
Gdiplus::RectF imageContainer;
imageContainer.X = controlPosition.left;
imageContainer.Y = controlPosition.top;
imageContainer.Width = controlPosition.right;
imageContainer.Height = controlPosition.bottom;
Gdiplus::Graphics gdiGraphics(hDC);
Gdiplus::Unit scrUnit = Gdiplus::UnitPixel;
gdiGraphics.DrawImage(&image, imageContainer, 0, 0, originalWidth, originalHeight, scrUnit);
However when the Control is resized vertically the image is moving to the right and not always stay in center, Also when Control is resized Horizontally, it is moving to the bottom. I am not able to figure out why.
I am using C++ on Windows.
Part 2: Now I have got a Rectangle drawn as well on top of this
Gdiplus::RectF cropRect;
cropRect.X = 100;
cropRect.Y = 100;
cropRect.Width = 300;
cropRect.Height = 300;
Gdiplus::Pen* myPen = new Gdiplus::Pen(Gdiplus::Color::White);
myPen->SetWidth(3);
gdiGraphics.DrawRectangle(myPen, cropRect);
Now When I resize the Control, Image is getting resized correctly but this rectangle is not, I multiplied Width and Height accordingly
Gdiplus::RectF cropRectangle;
cropRectangle.X = cropRect.GetLeft();
cropRectangle.Y = cropRec.GetTop();
cropRectangle.Width = (cropRec.Width)* percent;
cropRectangle.Height = (cropRec.Height ) * percent;
My Part 1 has been solved after Adrians Answer now I am stuck on Part 2 of my problems
Thanks
-Pankaj
when the size of the control changes you'll have a WM_SIZE message posted to your window. you must refresh your aspect ratio according to new size.