I am developing an OpenGL application that has two working modes: windowed mode and full screen.
The app displays several graphic objects using OpenGL and writes some text strings using that same API. The program displays the texts strings in its intended positions when running as a windowed application, but when running full screen the text strings are displayed in an upper position that its intended position.
The app creates the fonts using wglUseFontBitmap and displays the text strings with glCallLists (it sets the text position using glRasterPos2i). Before the text is displayed I adjust the text position adding an offset to the Y coord. I get that offset using the GetDCOrgEx Win32 API call.
I think you have to call GetDCOrgEx again after getting in fullscreen mode, or do you already do this? It would help if you could post the code where you call GetDCOrgEx and calculate the Y offset.
EDIT: Another idea: Could it be that you can use the same Y offset, but negative? Or perhaps calculate Y position and then use height-ypos? There's some source code here that uses glRasterPos2i different when in fullScreen:
if(!state->fullScreen)
// if fullScreen (don't forget the image/GL y-coord vertical flip)
glRasterPos2i((w - state->img->cols())/2, (h - state->img->rows())/2);
else
// for non-fullscreen images
glRasterPos2i(0,h);
Schnaader, thanks a lot for your answers.
I have solved the problem modifying how I calculate the offset that I need to add to the Y coord. Now the offset is calculated with the following code:
m_iYOffset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYBORDER);
The above code solves the problem for my application.
Here's the code to calculate the Y offset:
POINT vOffset;
m_hdc = GetDC(m_hWnd);
if (m_hdc)
{
GetDCOrgEx(m_hdc, &vOffset);
m_iYOffset = vOffset.y;
}
The above code is called once the program has set up full screen mode. I tried to call GetDCOrgEx every time I need to write a text string, but the text is written at the same positions that when GetDCOrgEx is called only once.
Related
Windows, using Win32 API calls directly.
I am trying to create a windows app that emulates the behavior of a console app. So I am using Cascadia Mono, the nice new font that comes with Windows Terminal (makes no difference to my problem if I use Courier New or Consolas). The app that is asking me to write things is trying to draw a box using the line drawing characters. The horizontal line char it uses is u2500, the standard light horizontal line
In my app I have calculated the width and height for the font and draw each character one at a time in my WM_PAINT message handling
for row in 0..ws.rows {
for col in 0..ws.cols {
let ch = sb.buffer[offset];
trace!("paint {:?}", ch);
offset += 1;
if ch.ch != ' ' {
if ch.fg == Color::Reset {
SetTextColor(hdc, RGB(255, 255, 255));
} else {
let rgb = color(ch.fg);
SetTextColor(hdc, rgb);
}
let u = ch.ch.encode_utf16(&mut b);
trace!("ch = {0:0x} ", u[0]);
assert!(
TextOutW(hdc, col * ws.ch_width, row * ws.ch_height, u.as_ptr(), 1)
!= 0
);
}
}
}
(sorry that this is rust , not C++, but you can see what calls I am making). All works fine but the horizontal lines come out wrong. Here is a screen shot of 3 screens stack on top of each other.
Top is my app,
next is the original console app in windows terminal
Last a cut and paste of the text from Windows terminal into notepad.
You can see that in my app the dashes are chopped off at either end so I get a dashed line. You can also see that the top left corner abuts right up to the first 'k' in the real things but in mine there is a definite gap. Also note that the right hand side of the last 'k' on mine is chopped off by the invisible bounding box of the first dash.
I cannot reduce the character pitch in my code to squash the dashes together because then the other characters will overlap.
I am very much a novice at font and character handling so I am at a loss. I have checked and I am for sure emitting 0x2500 as the character. My naive expectation is that all the characters in a fixed pitch font are the same and that they all stay nicely inside that bounding box.
NOTE : that rust utf16 call is taking the character thats in utf32 format (rusts standard char internal representation) and converting it to utf16.
EDIT: here is a simple no code winforms c# app screen, the same text is copied from the console app and pasted into the text box. Note that the glyphs are full width
And here is chrome displaying the same chars, full width \e2\94\80 is utf8 for 2500. Seem everybody knows a trick I dont
I am trying to scale images/text etc using MM_ANISTROPIC and what I've done is the following (by the way if the syntax is a little weird, it's originally from delphi so treat the following as pseudocode)
I would expect the following code to produce a rectangle that is 70% of the width of the PaintBox and 30% of the height, yet it doesn't, instead it it noticeably too small.
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,100,100,0);
SetViewportExtEx(hdc,70,30,0);
Rectangle(hdc, 0,0,PaintBox.width-1,PaintBox.Height-1);
if, on the other hand I change the code so that the SetWindowExtEx has 91 instead of 100 as its parameters (as shown below) then it works, which makes no sense to me at all...
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,91,91,0);
SetViewportExtEx(hdc,70,30,0);
Rectangle(hdc, 0,0,PaintBox.width-1,PaintBox.Height-1);
My sanity test case was to add the following pseudocode
SetMapMode(hdc,MM_TEXT);
DrawLine(hdc,Round(PaintBox.width*0.7),0,Round(PaintBox.width*0.7),PaintBox.Height-1);
DrawLine(hdc,0,Round(PaintBox.height*0.3),PaintBox.width-1,Round(PaintBox.height*0.3));
I would have expected this to overwrite the lower / bottom edges of my original Rectangle but it does not unless I uses that 91,91 SetWindowExtEx.
Can anyone duplicate this?
FURTHER EDIT: Here is my exact original code I had given pseudo code before to make the question more accessible to non-delphi users but one of my commenters wanted full code to see if my contention that it was a delphi quirk was true or not.
The entire project consisted of a VCL form with a rectangular paintbox dropped on it centered so there was space all around it, and its onPaint event was set to the code below resulting in this image::
procedure TForm11.PaintBox2Paint(Sender: TObject);
var
hdc:THandle;
res:TPoint;
procedure SetupMapMode;
begin
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,100,100,0);
SetViewportExtEx(hdc,70,30,0);
// These lines are required when we're painting to a TPaintBox but can't be used
// if we're paiting to a TPanel and they were NOT in my original question but only
// got added as part of the answer
// SetViewportOrgEx(hdc,PaintBox2.Left,PaintBox2.Top,#ZeroPoint);
// SetWindowOrgEx(hdc,0,0,#ZeroPoint);
end;
begin
//draw a rectangle to frame the Paintbox Surface
PaintBox2.Canvas.Pen.Style:=psSolid;
PaintBox2.Canvas.Pen.width:=2;
PaintBox2.Canvas.Pen.Color:=clGreen;
PaintBox2.Canvas.Brush.Style:=bsClear;
PaintBox2.Canvas.Rectangle(0,0,PaintBox2.Width-1,PaintBox2.Height-1);
PaintBox2.Canvas.Brush.Style:=bsSolid;
//initialize convenience variable
hdc:=PaintBox2.Canvas.Handle;
SetTextAlign(hdc,TA_LEFT);
//as doing things to the PaintBox2.Canvas via Delphi's interface tends to reset
//everything, I'm ensuring the map mode gets set **immediately** before
//each drawing call
SetupMapMode;
/// Draw Text at the bottom of the PaintBox2.Canvas (though as it's mapped it
/// SHOULD be 1/3 of the way down and much smaller instead)
TextOut(hdc,200,PaintBox2.Height-PaintBox2.Canvas.TextHeight('Ap'),'Hello, World!',13);
PaintBox2.Canvas.Pen.Color:=clblue;
PaintBox2.Canvas.Brush.Style:=bsClear;
//ensure it's set before doing the rectangle
SetupMapMode;
// Redraw the same rectangle as before but in the mapped mode
Rectangle(hdc, 0,0,PaintBox2.Width-1,PaintBox2.Height-1);
PaintBox2.Canvas.Brush.Style:=bsSolid;
//reset the map mode to normal
SetMapMode(hdc,MM_Text);
//draw text at the "same" position as before but unmapped...
TextOut(hdc,200,PaintBox2.Height-PaintBox2.Canvas.TextHeight('Ap'),'Goodbye, World!',15);
//Draw lines exactly at 70% of the way across and 30% of the way down
//if this works as expected they should overwrite the right and bottom
//borders of the rectangle drawn in the mapped mode
PaintBox2.Canvas.Pen.Color:=RGB(0,255,255);
PaintBox2.Canvas.MoveTo(Round(PaintBox2.Width*0.7),0);
PaintBox2.Canvas.LineTo(Round(PaintBox2.Width*0.7),PaintBox2.Height);
PaintBox2.Canvas.MoveTo(0,Round(PaintBox2.Height*0.3));
PaintBox2.Canvas.LineTo(PaintBox2.Width,Round(PaintBox2.Height*0.3));
end;
Okay, I don't know WHY the following is necessary -- it may be a Delphi quirk, the fact that I'm using a TPaintBox with is a TGraphicControl rather than a Component, or if I'm missing out on some fundamental concept on how this whole mapping mode works, BUT if I add the following code:
ZeroPoint:=TPoint.Zero;
SetViewportOrgEx(hdc,PaintBox1.Left,PaintBox1.Top,#ZeroPoint);
SetWindowOrgEx(hdc,0,0,#ZeroPoint);
Then it all displays as expected. Anyone have any explanations as to why this is necessary?
EDIT: Okay, I've got a PARTIAL explanation. It has to do with the control I was painting on being a TPaintBox, which is a TGraphic control rather than a TWinControl. To wit:
TGraphicControl is the base class for all lightweight controls.
TGraphicControl supports simple lightweight controls that do not need the ability to accept keyboard input or contain other controls. Since lightweight controls do not wrap Windows screen objects, they are faster and user fewer resources than controls based on TWinControl.
As such, although they APPEAR to have a separate canvas, I have this sneaking feeling that they are really sharing the form's canvas which is why, when I switched to a TWinControl descendant, which DOES own its own Windows DC, then the display worked as expected without setting the ViewpointOrg.
So it was a Delphi quirk after all...!
I am trying to right justify a bar code I am printing using ZPL from a .Net program. Here is my current code:
^FO10,50^FB500,1,0,R,0^AO,40,40^BY3^BCN,100,Y,N,N^FD1234567^FS^XZ
The Field Block seems to be ignored when working with a bar code, but for text it justifies it correctly. Any ideas?
Newer Zebra firmware supports a third parameter on ^FO and ^FT. If you want to right justify something, specify the right edge as the x parameter, and include a 1 as the third parameter.
^XA
^BY3
^FO85,30^GB500,450,3^FS
^FO100,50^BCN,30,Y,N,N
^FD>;12345678^FS
^FO100,120N^BCN,30,Y,N,N
^FD>;123456^FS
^FO100,190^BCN,30,Y,N,N
^FD>;1234^FS
^FO570,260,1^BCN,30,Y,N,N
^FD>;12345678^FS
^FO570,330,1^BCN,30,Y,N,N
^FD>;123456^FS
^FO570,400,1^BY3^BCN,30,Y,N,N
^FD>;1234^FS
^XZ
We had the same issue at our company. The barcode doesn't respond to the Field Block. Based on your example since you are using ZPL commands directly to generate your barcode (as opposed to an image of a barcode), you could do something similar to how we did. We know roughly how wide the barcode is going to be based on the data we write to it. So we created a method to determine the x-axis location of the barcode based on our also known label widths.
For example: We use the s4M printers and estimate the full label width to be about 780 dots wide. So in order to left/center/right justify, we take the rough barcode (estimated) width and calculate accordingly (left is set to xaxis = 0, center is set to xaxis = ((780 - barcodeWidth) / 2), and right justified would be xaxis = (780 - barcodeWidth). It isn't a perfect solution but is very close at least for our needs. You could certainly be more precise in your algorithm by looking at more barcode setting information like the data square size, columns, rows, etc... depending on your needs.
If you were to come up with a solution of generating an image of a barcode (which is what we are also trying to come up with now) then you should be able to easily get the width of the barcode image using build in .net graphics functionality.
For my graphics class, I need to match an OpenGL sample output in a pixel-perfect way.
I figured it would be cool if I could spawn the sample, send it some input, then take a screenshot of the exact OpenGL area, do the same for mine, and then just compare those screenshots. I also figured something like AutoIT would be the easiest way to do something like this.
I know that I can use the screencapture function, but I'm unsure of how to get the exact coordinates and size of the OpenGL area of the window (not the title bar/surrounding window stuff).
If anybody could help me out that would be awesome.
Or if anybody can think of an easier solution than AutoIt, and can point me in the right direction, that'd be great too.
EDIT: I also don't have access to the source code of the sample output program.
AutoIt is a pretty good tool for this job. I think you already found the _ScreenCapture in the help file, it has parameters for: X left, Y top, X right and Y bottom coordinates. However, the _ScreenCapture function stores to a file. I've made a library where you can capture part of the screen, or a window, and save this to memory. Then you can get the pixel colors from the memory and compare them to your existing pixels. You can find it here: http://www.autoitscript.com/forum/topic/63318-get-or-read-pixel-from-memory-udf-pixelgetcolor-au3/
The part of a window which does not include the titlebar and the borders is called the 'client area'. You can get the width and the height of the client area with the WinGetClientSize. Alternatively, you can use ControlGetPos on the OpenGL control to get the X and Y relative to the window, and the width and height of the OpenGL control. Combined with WinGetPos you should be able to calculate the values you need for _ScreenCapture. You should be able to find out a good approach if you use the "AutoIt window info" tool.
Finally, a simple and short solution which gives you little control, but might be just what you need, is the PixelChecksum function. Once you have the coordinates of the OpenGL part, you can use PixelChecksum and get a value corresponding to the pixels of the screen (a checksum of the pixels). You can then compare this value to a pre-recorded value to tell whether the pixels on the screen are exactly the same. Check the Autoit help file of PixelChecksum for an example.
If you want to capture data from an OpenGL buffer, here is how you can do it with legacy OpenGL (version 1.2 or so)
glFinish();
glReadBuffer(GL_FRONT);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glReadPixels(ox, oy, w, h, mode, GL_UNSIGNED_BYTE, img);
where:
ox and oy are origin x and y
w and h are width and height
mode is the mode, like GL_BGR if you want to save to BMP
img is a pointer to unsigned int * to copy the image to
You can search for glReadPixels on the internet and see the reference for more information.
I am making a GUI for a program I have created, where I need to be able to change the position of a load along a beam. I have set up the axes and the slider properly, but I am unsure how to get the axes to update, as I can't find any examples that show how to do this on the internet.
At present, as I move the load, the position updates properly, but the old position stays on screen as well, which is quite annoying.
Can anyone recommend any good examples that show how to do this, or does anyone have a suggestion of how to go about refreshing the axes?
Here is the slider callback (I haven't included the create_fcn function). Also, theres a lot of comments in the code, as I used the Guide function to make the GUI.
Please note that the input to slider is a proportion of the overall beam length (as a decimal).
function PointLoadxx1posslider_Callback(hObject, eventdata, handles)
% hObject handle to PointLoadxx1posslider (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
PLxx1pos = get(handles.PointLoadxx1posslider,'value');
set(handles.PLxx1posedit, 'String', num2str(PLxx1pos));
l = 3000; % This is the Length of the beam
zpl1 = get(handles.PointLoadxx1posslider,'value')*l;
% Multiplies the position decimal by the overall length
LoadPlotter(hObject,zpl1,handles) % Sends the command to the plot plot function
guidata(hObject,handles);
function LoadPlotter(hObject,zpl1,handles)
% The following draws the beam supports as lines
SH = l/20; %Height of supports
line([0 l], [SH/2 SH/2])
line([-SH/2 SH/2], [0 0])
line([-SH/2 0], [0 SH/2])
line([0 SH/2], [SH/2 0])
line([l-SH/2 l+SH/2], [0 0])
line([l-SH/2 l], [0 SH/2])
line([l l+SH/2], [SH/2 0])
xlim([ -100 l+200])
ylim([-l/2 l/2])
%Draw Load position
% zpl1 = get(handles.PointLoadxx1posslider,'value')*l;
% zpl1 = 0.5*l;
zpl2 = 0.2*l;
PL1 = 50;
%This is the value of the point load applied to the beam, which will
be an input from another slider
PL1Draw = line([zpl1 zpl1],[SH/2 PL1*10]);
% refresh(handles.axes1);
guidata(hObject,handles);
Obviously, I would like to keep the other lines drawn, but change PL1Draw as the slider is moved. Please can you explain what I am supposed to tag to do this?
Many thanks in advance.
James
I assume that you have plotted a beam, which is supposed to bend as you change the slider value. Since you are able to plot the new position into the axes, I assume that you know how to write callbacks. I further assume that there are parts of the plot that should stay the same, and parts that should change.
To change the parts that need changing, the easiest method is just to delete them and then to redraw. In order to delete specific items from a plot, it's best to tag them. Thus, your plotting would go like this
%# remove the old position
%# find the handle to the old position by searching among all the handles of
%# the graphics objects that have been plotted into the axes
oldPosHandle = findall(handles.axes1,'Tag','position');
delete(oldPosHandle);
%# plot new position
PL1Draw = line([zpl1 zpl1],[SH/2 PL1*10]);
%# add the tag so that you can find it if you want to delete it
set(PL1Draw,'Tag','position');
Note 1
To make the GUI respond faster (if needed), do not delete and re-plot, but change the 'XData' and 'YData' property of the old position object.
Note 2
If you aren't already doing so, put the plotting function (the one that updates everything in the plot, not just the position of the load) in a separate function, not into the callback of the slider, and have instead the slider callback call the plot function. This way, you can call the same plotting function from several buttons and sliders, which makes the code a lot easier to maintain.
EDIT
I have updated the commands. Note that there is no special 'tagging' function. 'Tag' is a property of every graphics object, like 'Units', or 'Color'. It simply helps you to label the graphics objects so that you don't need to remember the handles.
Unrelated to the actual question, but related to the project:
http://www.mathworks.com/matlabcentral/fileexchange/2170
This book is still available used on Amazon, it might save you a lot of the coding of the Mechanics of Materials stuff. I wrote this about 12 years ago as an undergrad, but I think the MATLAB code should still be functional.