CoreGraphics: Points und Pixels

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • CoreGraphics: Points und Pixels

    Liebe Community,

    ich habe ein kleines Problem mit den Points und Pixels. Wenn ich eine CGSize-Instanz erstelle, muss ich die width und heigtht in Points angeben. Wenn ich aber beispielsweise die Breite von einem UIView bekommen will (bounds.width), dann bekomme ich diese in Pixel.

    Wieso gibt es diese Inkonsistenz? Da seit iOS 7 eh nur mehr Retina-Geräte unterstützt werden, könnte ich ja durch zwei teilen um an die Points zu kommen. Aber das ist hässlicher Code und ich will gar nicht erst damit anfangen. Gibt es eine andere Möglichkeit diesen Wert in Points zu bekommen? Immerhin setze ich ihn auch in Points.

    Mit freundlichen Grüßen

    TheFuriousLion
  • Entschuldigung. Aber das ist doch Unsinn.

    Auf dem iPhone 4 als Beispiel ist der "frame" und "bounds" immer 320x480 in Hochkant.
    Der contentScale Faktor sagt wie viele Pixel das sind.
    Du kannst natürlich die bounds dann auch 640x960 setzen um auf ganzzahligen Koordinaten Grafik zu generieren.
  • TheFuriousLion schrieb:

    Wenn ich eine CGSize-Instanz erstelle, muss ich die width und heigtht in Points angeben. Wenn ich aber beispielsweise die Breite von einem UIView bekommen will (bounds.width), dann bekomme ich diese in Pixel.

    CoreGraphics rechnet immer in Pixeln und das UIKit in Punkten. Das liegt im Prinzip daran, dass CoreGraphics als Low-Level-Framework nichts von der Auflösung (Retina vs. Non-Retina) weiß. Der Layer regelt den Übergang (die Umrechnung) von Retina nach CoreGraphics
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    CoreGraphics rechnet immer in Pixeln und das UIKit in Punkten. Das liegt im Prinzip daran, dass CoreGraphics als Low-Level-Framework nichts von der Auflösung (Retina vs. Non-Retina) weiß.

    Hmm, das widerspricht sich doch. Wenn CoreGraphics in Pixeln rechnet, dann müsste ich ja Koordinaten als Pixel übergeben, was ich ja nicht mache. Ob ich jetzt mit CoreGraphics oder mit UIKit/AppKit eine Linie der Länge x zeichne, führt immer zu einer Linie der Länge x.

    macmoonshine schrieb:

    Der Layer regelt den Übergang (die Umrechnung) von Retina nach CoreGraphics

    Und wie macht OS X das ohne Layer?
  • Michael schrieb:

    Hmm, das widerspricht sich doch.

    Wieso?

    Michael schrieb:

    Wenn CoreGraphics in Pixeln rechnet, dann müsste ich ja Koordinaten als Pixel übergeben, was ich ja nicht mache. Ob ich jetzt mit CoreGraphics oder mit UIKit/AppKit eine Linie der Länge x zeichne, führt immer zu einer Linie der Länge x.

    Wenn Du im UIKit zeichnest, zeichnest Du ja letztendlich in einem CGContext, den Du vom UIKit bekommst. Der ist pixelbasiert. Wie der allerdings konfiguriert ist, wenn Du da rein malst, legt Cocoa fest. Bei Retina-Displays skaliert Cocoa entsprechend. Hier erweist sich der stapelbasierte Ablage von Konfigurationen im Kontext als sehr nützlich.

    CoreGraphics macht da aber nichts selbständig: Wenn Du beispielsweise einen Image-Kontext erstellst, entspricht immer ein Punkt einem Pixel.

    Michael schrieb:

    Und wie macht OS X das ohne Layer?

    Ich habe nur vom UIKit gesprochen. Vermutlich geht OSX änhlich vor, nur eben ohne Layer.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Michael schrieb:

    Hmm, das widerspricht sich doch.

    Wieso?

    Ok, wenn man der Meinung ist, CoreGraphics rechnet nur in Pixeln, dann vielleicht nicht.

    macmoonshine schrieb:

    Michael schrieb:

    Wenn CoreGraphics in Pixeln rechnet, dann müsste ich ja Koordinaten als Pixel übergeben, was ich ja nicht mache. Ob ich jetzt mit CoreGraphics oder mit UIKit/AppKit eine Linie der Länge x zeichne, führt immer zu einer Linie der Länge x.

    Wenn Du im UIKit zeichnest, zeichnest Du ja letztendlich in einem CGContext, den Du vom UIKit bekommst. Der ist pixelbasiert. Wie der allerdings konfiguriert ist, wenn Du da rein malst, legt Cocoa fest. Bei Retina-Displays skaliert Cocoa entsprechend. Hier erweist sich der stapelbasierte Ablage von Konfigurationen im Kontext als sehr nützlich.

    Ja, im CGContext sind alle Informationen enthalten, die CoreGraphics braucht um zu zeichnen. Dazu gehören eben auch die Device spezifischen Informationen.

    macmoonshine schrieb:

    CoreGraphics macht da aber nichts selbständig: Wenn Du beispielsweise einen Image-Kontext erstellst, entspricht immer ein Punkt einem Pixel.

    Stimmt nicht. Bei UIGraphicsBeginImageContextWithOptions() kannst du einen Skalierungsfaktor angeben. Und mit CGContextScaleCTM() kannst du den auch jederzeit ändern.
  • Michael schrieb:

    Dazu gehören eben auch die Device spezifischen Informationen.

    Für den Kontext ist das aber nur ein Skalierungsfaktor.

    Michael schrieb:

    Stimmt nicht. Bei UIGraphicsBeginImageContextWithOptions() kannst du einen Skalierungsfaktor angeben. Und mit CGContextScaleCTM() kannst du den auch jederzeit ändern.

    Aus welchem Framework stammt UIGraphicsBeginImageContextWithOptions? Ich habe außerdem nie behauptet, dass CoreGraphics bzw. der Kontext nicht skalieren kann.
    „Meine Komplikation hatte eine Komplikation.“
  • macmoonshine schrieb:

    Michael schrieb:

    Dazu gehören eben auch die Device spezifischen Informationen.

    Für den Kontext ist das aber nur ein Skalierungsfaktor.

    Wozu ist dann Funktion CGContextGetUserSpaceToDeviceSpaceTransform() gut?

    macmoonshine schrieb:

    Michael schrieb:

    Stimmt nicht. Bei UIGraphicsBeginImageContextWithOptions() kannst du einen Skalierungsfaktor angeben. Und mit CGContextScaleCTM() kannst du den auch jederzeit ändern.

    Aus welchem Framework stammt UIGraphicsBeginImageContextWithOptions?

    Aus UIKit. Die UIKit Funktion UIGraphicsGetCurrentContext() liefert dir aber auch nur eine CGContextRef zurück.

    Michael schrieb:

    Ich habe außerdem nie behauptet, dass CoreGraphics bzw. der Kontext nicht skalieren kann.

    Nichts anderes muss CoreGraphics machen, um vom User Space Koordinatensystem auf Pixel umzurechnen. Noch was aus der Doku:
    Quartz 2D Coordinate Systems
    A coordinate system, shown in Figure 1-4, defines the range of locations used to express the location and sizes of objects to be drawn on the page. You specify the location and size of graphics in the user-space coordinate system, or, more simply, the user space. Coordinates are defined as floating-point values.
  • Noch mal: Ich spreche CoreGraphics nicht die Fähigkeit ab, Koordinaten zu transformieren.

    Michael schrieb:

    Aus UIKit. Die UIKit Funktion UIGraphicsGetCurrentContext() liefert dir aber auch nur eine CGContextRef zurück.

    Das ist aber genau der Punkt: Das UIKit kümmert sich darum, den Grafikkontext für das entsprechende Gerät anzupassen.

    Michael schrieb:

    Noch was aus der Doku: [...]

    Auch das ist kein Widerspruch zu meiner Aussage, da der User Space eine beliebige Skalierung, Drehung oder Translation des physikalischen Raums ist. Das Konzept von CoreGraphics ist glücklicherweise allgemeiner als nur die Möglichkeit Retina-Darstellungen zu ermöglichen.

    Im Gegensatz zum UIKit kennt CoreGraphics nicht automatisch die richtige Auflösung, da ist erstmal Pixel gleich Punkt. Das lässt sich aber beliebig umkonfigurieren, und das nutzen OSX und das UIKit eben aus.
    „Meine Komplikation hatte eine Komplikation.“
  • Vielleicht meinen wir beide im Prinzip auch das Gleiche und wir betreiben hier nur Wortklauberei. Es ist doch so, wenn du beispielsweise CoreGraphics sagst, zeichne mir ein Rechteck mit 1.0x1.0 Kantenlänge, kommt auf einem Retina Device ein Rechteck von 2x2 Pixeln raus oder auf einem Drucker, der mit 600dpi druckt ca. 8x8 Pixel. Also rechnet, aus meiner Sicht der Dinge, CoreGraphics die „normierten“ Koordinaten auf das Ziel-Device um. Die Information, wie CoreGraphics umrechnen muss, kann ja von außen kommen.

    macmoonshine schrieb:

    Im Gegensatz zum UIKit kennt CoreGraphics nicht automatisch die richtige Auflösung, da ist erstmal Pixel gleich Punkt. Das lässt sich aber beliebig umkonfigurieren, und das nutzen OSX und das UIKit eben aus.

    Du sagst es. OS X und UIKit konfigurieren um, CoreGraphics rechnet um. Die Defaultauflösung in CoreGraphics ist 72dpi.
  • Ich wollte ja noch was schreiben, aber ich lass Euch das mal ausdiskutieren.
    Aber ich kann mich nicht zurückhalten, hier kommt es:

    CoreGraphics ist die API, die Vektor-basierte API Funktionen in Rastergrafiken umwandelt.
    Das dazu verwendete Koordinatensystem benutzt Affine Transformationen auf Basis von Matrix Operationen.

    Der Ziel-Context (Bitmap, PDF, oder was auch immer) definiert die Bedeutung dieser Koordinaten für das Ausgabe Medium.
    (Dazu gehört auch der Farbraum. Aber egal.)

    Das war es eigentlich schon alles. Der ganze andere Rest wie "contentsScale" und "frame" und "bounds" in UIKit sind eigentlich nur
    die Zwischenschicht, die der Applikation eine einheitliche Sicht / API auf die darunter liegende CoreGraphics Implementierung.

    Und genau irgendwo bei CGimage (UIImage) und CALayer ist eben der "Bruch".
    An dieser Stelle muss man wissen mit was man es zu tun hat, da man als Programmierer selber diese Grenze überschreitet.

    Wenn ich in einem UIView frame auf "320x480" setze, "bounds" auf "640x960" dann habe ich mir als Programmierer eine Umgebung konfiguriert in der ein Punkt einem Pixel auf genau einem Retina iPhone Screen entsprechen.

    Genauso kann ich aber "bounds" auch auf 0 bis 2 PI auf der X Achse und -1 bis 1 auf der Y-Achse setzen und eine Sinus-Kurve malen,
    die den konfigurierten "frame" ausfüllt.

    Der "frame" ist der Bilderrahmen des Ausgabegeräts, die "bounds" sind die "Positionen" innerhalb derer ich mich innerhalb des Rahmens bewegen kann.

    Bei UIView und Subviews gilt diese Transformation auch für verschachtelte Instanzen. Das heisst eine Änderung der "bounds" ändert die Bedeutung von "frame" im Subview.

    Das ist bei CALayer nicht der Fall. Die besiten nur "bounds" und arbeiten alle auf demselben Koordinatensystem.
    Das ist das Koordinatensystem des CGContext (sozusagen) ohne die affinen Transformationen, die bei der API schon angewandt werden.

    Das muss man wissen, mehr nicht. (Das reicht aber schon aus um Verwirrung zu stiften).

    Ich weiss das viel von dem Beitrag schon gesagt wurde, aber ich wollte es noch mal schreiben.
    Danke für's Lesen.