Messen von Bildschirmweite

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Marserdling
Beiträge: 15
Registriert: 02.05.2009, 11:45
Benutzertext: Erdling vom Mars.

Messen von Bildschirmweite

Beitrag von Marserdling »

Hi,
mein Bitmapfontsystem hat wohl ein paar schlecht zu findende Bugs, und ich würde gerne genau abmessen können welche Buchstaben jetzt richtig gerendert wurden oder nicht. Also im Code ist angeblich alles richtig, aber ich würde gerne kontrollieren ob das stimmt, indem ich "auf den Bildschirm" ein Lineal anlege und die im Code als Points gemessene Schrift in Millimeter umrechne und dann mit der gemessenen Länge des Lineals vergleiche.

Nur wie? Ich muss verschiedene Auflösungen unterstützen. Woher weiß ich wieviel Millimeter ein Pixel ist? Muss ich da den Bildschirm in Zoll nehmen und dann runterechnen ? Ist das nicht super ungenau, ist ja sicher vom Hersteller nicht so streng genommen.? Es geht bei meinem Fontsystem echt um Millimeter die hin und wieder nicht passen, und da ich es nicht selbst geschrieben hab, aber das ausbaden muss, wäre sowas eine echte Hilfe.

Danke für Tipps schonmal.
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Messen von Bildschirmweite

Beitrag von Krishty »

Hi,

Als ich mich zuletzt (wegen Head-Tracking) mit der Materie beschäftigt hatte, gab es noch keine Lösung für dieses Problem. Rein theoretisch müsste jedes System die DPI korrekt eingestellt haben, so dass du mit einer DPI-Abfrage × Monitorauflösung richtig fährst. Leider stehen die DPI bei 99,999 % aller Systeme fälschlicherweise auf 96.

Ich persönlich habe allerdings das Glück, dass mein 23-Zoll-TFT in 1920×1080 präzise 96 DPI entspricht – die Berechnungen also zumindest auf meinem System milimetergenau zutreffen. Ich hoffe zwar, dass hier noch jemand eine bessere Lösung kennt, aber ich würde den Bildschirm möglichst genau abmessen und das Ergebnis hardcoden. Dass das nicht portabel ist sollte klar sein, aber zum Debuggen müsste es reichen.

Gruß, Ky

Edit: Falls du dich auf die Systemangabe betreffend der DPI verlassen möchtest und C++ benutzt, ist hier ein wenig Code. Kann man ja immer mal gebrauchen.

Code: Alles auswählen

// Compute the physical resolution of the screen.
// The physical resolution of the screen can be obtained via "::GetDeviceCaps()", which needs a device
//	context handle for the desktop. Using the values "LOGPIXELSX" and "LOGPIXELSY" (number of pixels per
//	logical inch), we compute the meters per pixel. Multiplying this value with a window size, in pixels,
//	will yield to its physical size.

// Open a device context for the desktop. See msdn.microsoft.com/en-us/library/dd144871.
::HDC ScreenDeviceContext = ::GetDC(nullptr);
if_unlikely(!ScreenDeviceContext)
	WinAPI::ThrowErrorOfLastCall("GetDC");

// Get the number of pixels per logical inch. Since this value will later be used as a divisor, ensure that
//	it is non-zero. See msdn.microsoft.com/en-us/library/dd144877.
int DotsPerInchX = ::GetDeviceCaps(ScreenDeviceContext, LOGPIXELSX);
int DotsPerInchY = ::GetDeviceCaps(ScreenDeviceContext, LOGPIXELSY);
if_unlikely(!IsInRange(1, DotsPerInchX, 1200)) {
	::std::cerr << "resetting the horizontal DPI (" << DotsPerInchX << ") to 96" << ::std::endl;
	DotsPerInchX = 96;
}
if_unlikely(!IsInRange(1, DotsPerInchY, 1200)) {
	::std::cerr << "resetting the vertical DPI (" << DotsPerInchY << ") to 96" << ::std::endl;
	DotsPerInchY = 96;
}

// 1 inch = 0.0254 m (http://en.wikipedia.org/wiki/Inch).
MyMetersPerPixelX = static_cast<CFloatingPoint4Bytes>(0.0254 / DotsPerInchX);
MyMetersPerPixelY = static_cast<CFloatingPoint4Bytes>(0.0254 / DotsPerInchY);

// "::DeleteDC()" must not be used here. See msdn.microsoft.com/en-us/library/dd162920.
::ReleaseDC(nullptr, ScreenDeviceContext);

// Print some debugging information on the screen's size (msdn.microsoft.com/en-us/library/ms724385).
int const PixelsX = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
int const PixelsY = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
CFloatingPoint8Bytes const ScreensSizeX = CFloatingPoint8Bytes(PixelsX) * MyMetersPerPixelX;
CFloatingPoint8Bytes const ScreensSizeY = CFloatingPoint8Bytes(PixelsY) * MyMetersPerPixelY;
::std::clog << "desktop size is " << PixelsX << "\xc3\x97" << PixelsY << " pixels @ " << DotsPerInchX
			<< "\xc3\x97" << DotsPerInchY << " DPI = " << ScreensSizeX << "\xc3\x97" << ScreensSizeY // "\xc3\x97" = "×" UTF-8
			<< " meters" << ::std::endl;
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Marserdling
Beiträge: 15
Registriert: 02.05.2009, 11:45
Benutzertext: Erdling vom Mars.

Re: Messen von Bildschirmweite

Beitrag von Marserdling »

Danke für deine Antwort.
Die Sache scheint unmöglich wenn ich nicht im Fullscreen Modus bin. Ich frage mich wie man von Points in Software reden kann, wo doch Points genau in Millimeter definiert sind und die Größe der Schrift sich softwaretechnisch schon allein durch eine Veränderung der Größe des Fensters verkleinert/vergrößert. Im Code steht dann noch immer 12 points, was aber beileibe keine echten 4.2324mm sein können. Ich schätze man muss dann mit dem Faktor multiplizieren der den Unterschied zwischen nativer und tatsächlicher Auflösung beschreibt, wenn man denn mit einer anderen Auflösung spielen will.

Zum Abmessen:
Die Größe des Monitors abzumessen ist schon sehr fehleranfällig. Der Monitor hat einen dünnen schwarzen Pixelrand, soll man den in der Messung dazurechnen oder nicht? ~1mm ist das. Ich fürchte die Sache gestaltet sich als unmöglich weil zuviele "undefinierte" Fehlerquellen da sind.
...Wahrscheinlich aber meinst du das Abmessen mit GetDeviceCaps(HORZSIZE/VERTSIZE), richtig? (das schließt dann Fensteranwendungen schonmal aus) Kombiniert mit dem Wissen um die Anzahl der Pixels lässt sich dann schon eine Lösung für Fullscreen erarbeiten.

Was ich mich auch ständig frage ist, was "logical inch" von einem normalen inch unterscheidet. Sehe ich sehr oft in offziellen Dokumentationen, sogar die Bezeichnung "logische Einheit", was auch immer das bedeutet!
Leider stehen die DPI bei 99,999 % aller Systeme fälschlicherweise auf 96.
Wie überprüfe ich ob die DPI richtig sind?


Übrigens bin ich momentan in C# unterwegs, aber das heißt nicht dass ich den Code nicht benutzen könnte, danke!

edit: eventuell bringe ich hier einiges durcheinander: Das erzeugte Fullscreenlineal kann man natürlich über die Fensterappliaktion schieben, was völlig ausreicht um die Länge von Buchstaben zu messen. Der Rest des Problems beschränkt sich eher darauf wie man die Größe des Buchstabens in der Fensteranwendung umrechnet auf die Größe die er haben würde wenn er nicht eine andere Auflösung hätte und "jeder Softwarepixel einen physischen Pixelplatz bekommen würde." Nur mit dem oben angesprochenen Faktor zu multiplizieren reicht nicht. Kennst du vielleicht eine Funktion die mir die Weite eines Fensters in "Desktoppixel" angibt? Also wieviele Pixel das Ding vom Desktop versteckt. Daraus könnte man auf alles andere schließen und auch für Fensteranwendungen genau messen. (weil man dann den Anteil ausrechnen kann den das Fenster auf den gesamten Desktop hat, und damit auch die Weite in Millimetern, da man die Millimeter von GetDeviceCaps() kennt. Natürlich muss man die Border irgendwie rausrechnen. Oder man macht entfernt temporär eben die Border. Je nachdem wie bequem man ist ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Messen von Bildschirmweite

Beitrag von Krishty »

Marserdling hat geschrieben:… die Größe der Schrift sich softwaretechnisch schon allein durch eine Veränderung der Größe des Fensters verkleinert/vergrößert.
Das dürfte sie nicht. Wie renderst du den Text?
Marserdling hat geschrieben:Was ich mich auch ständig frage ist, was "logical inch" von einem normalen inch unterscheidet. Sehe ich sehr oft in offziellen Dokumentationen, sogar die Bezeichnung "logische Einheit", was auch immer das bedeutet!
Die logischen DPI sind die, von denen das System annimmt, dass sie stimmen – im Gegensatz zu den physischen DPI, also die, mit denen der Monitor tatsächlich gerade arbeitet. Siehe nächster Punkt …
Marserdling hat geschrieben:Wie überprüfe ich ob die DPI richtig sind?
Höchstens, indem du den User fragst, ob er die Einstellung persönlich korrigiert hat. Es gibt an keinem Punkt der Windows-Installation einen Schritt, in dem man die DPI einrichten muss – ergo gibt es keine verlässliche Angabe dafür. Das einzige, was ich mir vorstellen könnte, wäre, dass die DPI der nativen Auflösung irgendwo im Bildschirmtreiber herumliegen könnten, falls ein auf den Bildschirm zugeschnittener Treiber installiert ist (was ja auch bei der Hälfte der Systeme nicht der Fall ist). Schreib den Gedanken, die Pixeldichte automatisch genau zu erkennen, am besten ab.

Zum Rest: Sag erstmal, wie du die Schrift renderst. Dass sie sich beim verkleinern / vergrößern des Fensters ändert ist, naja, sehr ungewöhnlich. Renderst du per GDI, Direct3D, WDDM-2.0-Zeug …?

Achja, ist alles Dreiviertelwissen, was ich hier posten kann. Falls jemand Ahnung von der Materie hat wäre es schön, wenn er uns aufklären könnte.
Marserdling hat geschrieben:Der Monitor hat einen dünnen schwarzen Pixelrand, soll man den in der Messung dazurechnen oder nicht? ~1mm ist das.
Von der Mitte des ganz linken Pixels bis zur Mitte des ganz Rechten ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Marserdling
Beiträge: 15
Registriert: 02.05.2009, 11:45
Benutzertext: Erdling vom Mars.

Re: Messen von Bildschirmweite

Beitrag von Marserdling »

Höchstens, indem du den User fragst, ob er die Einstellung persönlich korrigiert hat.
Ich dachte du meintest dass die DPI Einstellung in Windows sowieso nie korrekt ist, inwiefern würde es dann Sinn machen den User nach der eingestellten DPI zu fragen? Bei mir ist 96dpi eingestellt, die Größe meines Bildschirms 20 Zoll in etwa. (keine Ahnung wieviel genau, der ist geliehen)
Das dürfte sie nicht. Wie renderst du den Text?
Du hast recht, das war bloß ein "Feature" (*bug* *hust*) des GUI System, das ich gerade fixen will. Man wollte ein GUI System machen das automatisch alle Komponenten anpasst an den Bildschirm.

Gerendert wird per DrawUserIndexedPrimitives() im Xna Framework. Ich werde das Lineal tatsächlich implementieren müssen, also werden wir dann sehen wo die Probleme liegen :)
Zuerst muss ich ein paar andere Dinge im GUI System fixen, zb. funktioniert nichtmal das Snapping an eine bestimmte Bildecke momentan.
Von der Mitte des ganz linken Pixels bis zur Mitte des ganz Rechten
Ach, ich sollte also doch per Maßband messen zur Sicherheit? GetDeviceCaps() würde schon in Millimeter zurückgeben wenn ich die Doku lese.
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Messen von Bildschirmweite

Beitrag von Krishty »

Marserdling hat geschrieben:
Höchstens, indem du den User fragst, ob er die Einstellung persönlich korrigiert hat.
Ich dachte du meintest dass die DPI Einstellung in Windows sowieso nie korrekt ist, inwiefern würde es dann Sinn machen den User nach der eingestellten DPI zu fragen?
Insofern, als dass der User dann darauf aufmerksam gemacht wird, dass er für eine vernünftige Funktionsweise des Programms auch die DPI vernünftig kalibrieren müsste. Wenn dein Programm an professionelle User – wie Print- und Mediengestalter geht – werden sie die DPI entweder schon eingestellt haben oder es auf Anfrage tun können. Wenn es unprofessionelle User sind, kannst du eine manuelle Abfrage anbieten, die zwar auch ungenau wäre, aber die schlimmsten Inkonsistenzen ausmerzen würde (ich erinnere mich, vor langer Zeit mal ein Office-Programm gehabt zu haben, das mich bei der Installation bat, ein DIN-A4-Blatt an den Bildschirm zu halten und so lange am Regler zu drehen, bis es ungefähr mit dem auf dem Bildschirm übereinstimmte).
Marserdling hat geschrieben:Du hast recht, das war bloß ein "Feature" (*bug* *hust*) des GUI System, das ich gerade fixen will. Man wollte ein GUI System machen das automatisch alle Komponenten anpasst an den Bildschirm.
Ah, okay. Wenn es so ist, musst du wohl tatsächlich den Weg über Umrechnunsfaktoren gehen … die aktuelle Größe des Fensters (ohne störenden Rahmen), in Pixeln, liefert dir GetClientRect(). Die Desktopauflösung bekommst du durch GetSystemMetrics().
Marserdling hat geschrieben:Ach, ich sollte also doch per Maßband messen zur Sicherheit? GetDeviceCaps() würde schon in Millimeter zurückgeben wenn ich die Doku lese.
Ja, aber wer sagt, dass GetDeviceCaps() auch einen richtigen Wert zurückgibt? Der Wert stammt wahlweise aus dem Bildschirmtreiber, dem Grafiktreiber oder der DPI-Voreinstellung. Nur die erste Quelle hat überhaupt eine Chance, zuverlässig zu sein, und das auch nur auf einem Bruchteil der Systeme. Wie man hier lesen kann können selbst die Werte, die die Funktion für die horizontale Größe, die horizontale Auflösung und die horizontalen DPI zurückgibt untereinander inkonsistent sein. Ich bleibe vorerst dabei, dass du keine Möglichkeit hast, ohne Maßband und manuelle Abstimmung die auf wenige Milimeter korrekten Ausmaße zu bestimmen – es ist einfach unmöglich.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten