TextOut to the center of Device Context - c++

It frustrates me how difficult this is for me to figure out. I have an MFC application with a print feature. When I hit print I want to create a pdf file with a single word in the center of the A4 page. I am using a CPrintDialog and I actually managed to obtain my result on my machine (by hardcoding the coordinates for TextOut).
I want this software to work on any type of device context. My guess is that there has to be a way to get the logical coordinates (which TextOut expects) out of the CDC. I was playing around with GetDeviceCaps (with HORZSIZE, HORZRES, LOGPIXELSX, ASPECTX, PHYSICALWIDTH) but none of these values even gets close to my hardcoded values. The fact that the y coordinate I use is a negative value baffles me even more.
Is there a generic way to find the center of the device context?
Here are the values I see:
const int logPixY = dcPrinter.GetDeviceCaps(LOGPIXELSY); //600
const int logPixX = dcPrinter.GetDeviceCaps(LOGPIXELSX); //600
const int horzSize = dcPrinter.GetDeviceCaps(HORZSIZE); //216
const int vertSize = dcPrinter.GetDeviceCaps(VERTSIZE); //279
const int horzRez = dcPrinter.GetDeviceCaps(HORZRES); //5100
const int vertRez = dcPrinter.GetDeviceCaps(VERTRES); //6600
const int pWidth = dcPrinter.GetDeviceCaps(PHYSICALWIDTH); //5100
const int pHeight = dcPrinter.GetDeviceCaps(PHYSICALHEIGHT);//6600
const int aspectX = dcPrinter.GetDeviceCaps(ASPECTX); //600
const int aspectY = dcPrinter.GetDeviceCaps(ASPECTY); //600
And the hardcoded DrawText that writes at the center of the page (kind of) is:
dcPrinter.TextOut(4200, -5000, L"Center");

If using GDI it would be far easier to do centering via
SetTextAlign(hdc, TA_VCENTER|TA_VCENTER);
then simply use the page's center point (width/2, height/2) as the TextOut reference point. Let the TextOut API do the work of calculating the text's display area.

Related

wxStyledTextCtrl - Size of AutoComp

I was just wondering if it is possible to find the size (in pixels) of the autocompletion control shown by the wxStyledTextCtrl.
My goal is to show a help window associated with the entry when a selection happens. Therefore, I need the location and also the width of the autocompletion control. It seems location can be found from m_STC->AutoCompPosStart() but there seems to be no way of finding the width. I am using the following code:
auto StartPos = m_STC->ToPhys(m_STC->PointFromPosition(m_STC->AutoCompPosStart()));
int MaxChars = m_STC->AutoCompGetMaxWidth(); //returns 0 unless set to a fixed value
int w, h;
m_STC->GetTextExtent(wxString("A", MaxChars), &w, &h);
return wxPoint(StartPos.x + w, StartPos.y);
I am using Windows and wxWidgets 3.2.
There is no way to get this information from the styled text control because the autocomp window is completely managed by Scintilla. And unfortunately, Scintilla doesn't make any methods available for getting this info.
As a hack-around, the popup is currently implemented as a child window of the styled text control. So you could do something like this:
const wxWindowList& childred = m_stc->GetChildren();
for ( auto it = childred.begin() ; it != childred.end() ; ++it )
{
// We're assuming the styled text control has at most 1 child -
// namely the autocomp popup. It might be better to check that
// the window found is in fact the auto comp popup somehow.
// win->GetPosition() will return screen coordinates, so to get client
// coordinates, ScreenToClient must be called.
wxPoint psn = m_stc->ScreenToClient(win->GetPosition());
wxSize sz = win->GetSize();
// Do something with size and position here.
}
However, this isn't guaranteed to always work. If in the future, the auto comp popup implementation is changed to use a top level window instead of a child of the control, this method will fail.

How to find middle of a button on screen

Ok I'm coding a button. I have done the box collision and all of the other stuff.
The problem I'm having is putting text in the middle of the button. No matter what I try it doesn't work :/ .
Please help I'm bad at math.
x = 120, y = 120, w = 120, h = 50
Screen dimensions = 480, 240
Is there an equation for this? I tried everything.
The best thing I have so far is
Brain.Screen.printAt(x + (w / 2, y + (h / 2), false, "Bruh");
// printAt args int x, int y, bool opaque, const char *text
The problem with that is the it's not at the exact center
is a little bit to the top right.
https://i.stack.imgur.com/vA2UQ.png
You can compute the center-point of the button easily enough:
const int buttonCenterX = x+(w/2);
const int buttonCenterY = y+(h/2);
... for the next step you'll need to center the text around that point. If your GUI API doesn't provide a way to center the text for you, you can calculate the appropriate x/y position by hand, assuming you know (or have a way to calculate) the pixel-width and pixel-height of the text:
const int textHeight = [text string's height, in pixels]
const int textWidth = [text string's width, in pixels]
const int textLeft = buttonCenterX-(textWidth/2);
const int textTop = buttonCenterY-(textHeight/2);
drawTextAt(textLeft, textTop, textString); // assuming drawTextAt() draws starting at the top-left of the string

SDL2 How to position a window on a second monitor?

I am using SDL_SetWindowPosition to position my window. Can I use this function to position my window on another monitor?
UPDATE
Using SDL_GetDisplayBounds will not return the correct monitor positions when the text size is changed in Windows 10. Any ideas how to fix this?
SDL2 uses a global screen space coordinate system. Each display device has its own bounds inside this coordinate space. The following example places a window on a second display device:
// enumerate displays
int displays = SDL_GetNumVideoDisplays();
assert( displays > 1 ); // assume we have secondary monitor
// get display bounds for all displays
vector< SDL_Rect > displayBounds;
for( int i = 0; i < displays; i++ ) {
displayBounds.push_back( SDL_Rect() );
SDL_GetDisplayBounds( i, &displayBounds.back() );
}
// window of dimensions 500 * 500 offset 100 pixels on secondary monitor
int x = displayBounds[ 1 ].x + 100;
int y = displayBounds[ 1 ].y + 100;
int w = 500;
int h = 500;
// so now x and y are on secondary display
SDL_Window * window = SDL_CreateWindow( "title", x, y, w, h, FLAGS... );
Looking at the definition of SDL_WINDOWPOS_CENTERED in SDL_video.h we see it is defined as
#define SDL_WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED_DISPLAY(0)
so we could also use the macro SDL_WINDOWPOS_CENTERED_DISPLAY( n ) where n is the display index.
Update for Windows 10 - DPI scaling issue
It seems like there is indeed a bug with SDL2 and changing the DPI scale in Windows (i.e. text scale).
Here are two bug reports relevant to the problem. They are both still apparently unresolved.
https://bugzilla.libsdl.org/show_bug.cgi?id=3433
https://bugzilla.libsdl.org/show_bug.cgi?id=2713
Potential Solution
I am sure that the OP could use the WIN32 api to determine the dpi scale, for scale != 100%, and then correct the bounds by that.
DPI scaling issue ("will not return the correct monitor positions when the text size is changed")
It's a known issue with SDL2 (I encountered it in those versions: 2.0.6, 2.0.7, 2.0.8, probably the older versions have this issue as well).
Solutions:
1) Use manifest file and set there:
<dpiAware>True/PM</dpiAware>
(you need to include the manifest file to your app distribution)
2) Try SetProcessDPIAware().
Yes, you can use SetWindowPosition, if you know the boundaries of the second monitor.
You can use the function SDL_GetDisplayBounds(int displayIndex,SDL_Rect* rect) to get them.

Change the unit for setting position in TextOut, C++

I'm currently working on a printing plugin with C++, and starting working with TextOut to print the text I want. It works great, but apparently, the positions that TextOut uses as params are in pixels. Is there a way to set them to be in cm or mm? or any other?.
Well, it's pretty simple. The coordinates are not in pixels, but they are in the coordinates of your mapping mode. It just so happens that the default mapping mode of a DC is MM_TEXT which has each coordinate unit to be one pixel on the device.
Change your mapping mode using SetMapMode() to the coordinate system you prefer to use. You can also play with window extents, viewport extents, and origins to customize it however you want. You might want to look at the documentation for SetMapMode() and the MM_LOMETRIC (or MM_HIMETRIC) mapping mode.
There should be special handling implemented for printing. Basically, you need to perform conversion based on HIMETRIC units. The paper size is in HIMETRIC units.
Here is the code that will help you get started (MFC-based):
if (pDC->IsPrinting())
{
// printable area in millimeters
int nWidth = pDC->GetDeviceCaps(HORZSIZE);
int nHeight = pDC->GetDeviceCaps(VERTSIZE);
CDC ScreenDC;
ScreenDC.CreateIC(_T("DISPLAY"), NULL, NULL, NULL);
int nPixelsPerInchX = ScreenDC.GetDeviceCaps(LOGPIXELSX);
int nPixelsPerInchY = ScreenDC.GetDeviceCaps(LOGPIXELSY);
// paper size is in HIMETRIC units. we need to convert
CSize PaperSize(MulDiv(nWidth,nPixelsPerInchX*100,HIMETRIC_PER_INCH),
MulDiv(nHeight,nPixelsPerInchY*100,HIMETRIC_PER_INCH));
// now we need to calculate zoom ratio so the layer content fits on page
double fZoomX = (double)PaperSize.cx/(double)m_DocSize.cx;
double fZoomY = (double)PaperSize.cy/(double)m_DocSize.cy;
m_PrintZoom = min(fZoomX, fZoomY);
ResetViewSize(TRUE);
if (pDC->IsKindOf(RUNTIME_CLASS(CPreviewDC)))
{
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(nPixelsPerInchX, nPixelsPerInchY);
pDC->SetViewportExt(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
pDC->SetViewportOrg(0,0);
pDC->SetWindowOrg(0,0);
}
}

How to rescale an image with c/c++ in windows?

What's the shortest solution in c/c++?
You didn't give too much information so I will go with StretchBlt
For an example, see Scaling an Image.
I won't give you a demo, but try to do the following:
create destination bitmap that is of your desired size
select that bitmap into device context
StretchBlt original bitmap onto device context previously mentioned
unselect bitmap from the device context
That recipe above needs no any library then GDI that is already present in windows. And if you plan to draw something in c++, you should get familiarity with that library anyway.
Look here:
http://www.ucancode.net/Free-VC-Draw-Print-gdi-example-tutorial/GDI-Object-VC-MFC-Tutorial.htm
or here:
http://www.olivierlanglois.net/clover.html
if you don't plan to use MFC for the task.
One of the easiest rescale algorithms is nearest-neighbour. Suppose your are rescaling from an image in an array of size x1 y1 to another size x2 y2. The idea is to find the nearest integer offset in original array to each target array position. So your rescale algorithm ends for something like this:
const int x1 = 512;
const int y1 = 512;
const int x2 = 64;
const int y2 = 64;
unsigned char orig[x1*y1]; /* Original byte array */
unsigned char target[x2*y2] /* Target byte array */
for(int i=0;i<x2;i++)
{
for(int j=0;j<y2;j++)
{
xoff = (i*x2)/x1;
yoff = (j*y2)/y1;
target[i+j*x2] = orig[xoff+yoff*x1]
}
}
This will give a blocky resized image. For better results you can use average or any other fancier polynomial based interpolators.
What libraries are you using? How do you represent images? Most image libraries should already be able to do that, e.g. Qt has QPixmap with scaled() and GDI has StretchBlt.
Or you could code it yourself with bicubic interpolation.