setNeedsDisplayInRect: zieht Striche

  • setNeedsDisplayInRect: zieht Striche

    Hi,

    Ich habe ein NSScrollView und darin ein normales NSView. Wenn ich nun mit setNeedsDisplayInRect: einen bereich des Views neu zeichnen will, schimmert am Rande des neugezeichneten Bereichs der Hintergrund des NSScrollViews durch. Weiss jemand von euch was ich dagegen tun könnte?

    Vielen Dank schon im Voraus und einen wunderschönen Sonntag,
    Fabian
  • RE: setNeedsDisplayInRect: zieht Striche

    Also vorab, um es gnau zu nehmen: -setNeedsDisplayInRect:(NSView) zeichnet nicht neu. sondern teilt OS X mit, dass demnächst mal wieder gezeichnet werden sollte, wenn es meint, dazu sei genügend Zeit vorhanden.

    Mutmaßlich setzt du das Rechteck nicht sauber.

    - Funktioniert ein -setNeedsDisplay?
    - Sind die Koordinaten wirklich richtig? Erhöhe die Breite mal?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Das Rechteck sollte eigentlich stimmen. Ich errechne is mit einem Haufen von Fliesskommazahlen, verwandle schlussendlich aber alle werte (x, y, width, height) unter Verwendung von floor() und ceil() zurück in ganze Zahlen.
    -setNeedsDisplay: hat immer geklappt, habe es bis jetzt verwendet und bin jetzt aus Performance-Gründen umgestiegen auf setNeedsDisplayInRect:
    Die Koordinaten variieren eigentlich permanent:
    Dabei geht es um ein Objekt auf einem Layout. Wird das Objekt bewegt wird die alte und die neue Stelle mit setNeedsDisplayInRect: aufgefrischt.

    Das Problem ist, dass ich keinen Screenshot machen kann, denn dann wird der View neu gezeichnet und es stimmt wieder alles.

    Was könnte das sein?

    Gruss,
    Fabian
  • Der Fehler liegt in deiner Int-Umwandlung! Die brauchst du auch nicht. NSRect ist in floats.

    Beispiel (nur x-Achse):

    0.9 - 4.7 -> 5.6
    0 - 5 -> 5

    Im zweiten Fall wird der Bereich von 5.0 bis 5.6 nicht gezeichnet, obwohl immer in die richtige Richtung gerundet wird.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Du hast ja in deinem D&D irgendwelche Koordinaten. Die gibst du aus. Dann gibst du die aus, die im setNeeds... übergeben werden, möglicherweise noch die, aus denen das Rect hergestellt wird.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Das Problem ist wieder da!

    Und zwar scheint es mit der Skalierung zusammenzuhängen. Man kann in meinen View zoomen. Bei 100% funktionierts, alles andere resultiert in Strichen (Screenshot hängt dran)

    Ich weiss hier keinen Rat. Könnte es sein, dass es nur mit dem Skalieren zu tun hat? Es entsteht nämlich eine Art Lücke um meinen neu gezeichneten Bereich, die 1 Pixel breit ist und durch diese Lücke schimmert alles von hinten durch.

    Grüsse,
    Fabian
  • Nein, sollte es eben nicht sein. Ich habe immer zwei Frame-Werte pro Objekt: frame und lastFrame. Wird ein neuer 'frame'-Wert gesetzt übernimmt lastFrame den alten 'frame'-Wert. In meiner Zeichenmethode errechne ich nun ein NSRect das beide frames umfasst und zeichne damit neu.
    Das komische ist ja, dass diese Schlieren NICHT auftreten, wenn das Zooming = 100% (keine Skalierung der Matrix) ist. Könnte doch damit zusammenhängen! Aber ich krieg irgendwie nicht raus, wie man das verbessern kann...

    Achja: Das NSRect das meine drawRect: methode bekommt enthält Fliesskommazahlen, nicht nur floats ohne Dezimalstelle:

    Quellcode

    1. 2005-05-01 22:31:39.423 Poster Print[3062] -drawRect: {{0, 0}, {600.98, 625.252}}


    Grüsse,
    Fabian
  • Also für mich sieht das immer noch wie ein Rundungsfehler aus, der natürlich bei höherem Zoom gewaltiger wird.

    Benutzt du auch zwischendurch keine Ints? Auch nicht für das Origin? Es sieht einfach verdammt so aus, weil auch immer der rechte und untere Rand "zu klein" ist. Ich nehme an, dass dein (0,0) oben links liegt (Flipped = YES)?

    ISt auch dein Origin richtig mit (0,0)? Stammt das Log vom alten oder vom neuen Rect?

    Ich mache jede Wette, dass das ein "Dämlichkeitsfehler" ist, über den du dich krank lachst, wenn du ihn gefunden hast.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Ja, Der View ist flipped, Origin stimmt perfekt und das Rect, dass da im Log angegeben ist ist das umfassende Rect, das den alten und neuen Bereich einschliesst.
    Was mir aufgefallen ist: Wenn im -drawRect: die Origin und Size ganze Zahlen sind, dann klappt es. Also bei 100% 200% 400% 800% Vergrössern, etc. ABER: Wenn ich mein Rect Runde und in ganze Zahlen umwandle, spuckt -drawRect: eine Fliesskommazahl aus.
    Liegt das evtl. an -scaleUnitBySquareToSize: liegen?

    Gruss
    Fabian
  • Was mir aufgefallen ist: Wenn im -drawRect: die Origin und Size ganze Zahlen sind, dann klappt es. Also bei 100% 200% 400% 800% Vergrössern, etc. ABER: Wenn ich mein Rect Runde und in ganze Zahlen umwandle, spuckt -drawRect: eine Fliesskommazahl aus.
    Liegt das evtl. an -scaleUnitBySquareToSize: liegen?

    Das ist ein Rundungsproblem. Das kann ja nun gar nicht mehr anders sein. Die Skalierung wird ja als Faktor dargestellt, also 1, 2, 4, 8. Berechnest du irgendwann mal Skalierung? Was ist der Variablentyp dafür?

    Wieso fummelst du daran überhaupt herum? Du kannst doch mit bounds/frame eine Skalierung erzeugen, die automatisch von OS X/RTE/Framework ausgeführt wird.

    Poste doch mal das drawRect.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Okay, also das sind mal die rohen zeichnungsfunktionen:

    Quellcode

    1. /*
    2. * MAIN DRAW FUNCTION
    3. */
    4. - (void)drawRect:(NSRect)_rect
    5. {
    6. NSLog(@"-drawRect: %@", NSStringFromRect(_rect));
    7. //Update our frame
    8. // -> If we had to change our frame, force ourself to redisplay and CANCEL the original call
    9. if ([self updateFrame])
    10. {
    11. [self display];
    12. return;
    13. }
    14. CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
    15. //Draw the editor
    16. [self draw];
    17. //Flush the context
    18. CGContextFlush(ctx);
    19. } //-drawRect:(NSRect)_rect
    20. - (void)setNeedsDisplayInRect:(NSRect)_rect
    21. {
    22. /*_rect.origin.x = floor(_rect.origin.x);
    23. _rect.origin.y = floor(_rect.origin.y);
    24. _rect.size.width = ceil(_rect.size.width);
    25. _rect.size.height = ceil(_rect.size.height);*/
    26. NSLog(@"-setNeedsDisplayInRect: %@", NSStringFromRect(_rect));
    27. [super setNeedsDisplayInRect:_rect];
    28. }
    Alles anzeigen

    Habe hier setNeedsDisplayInRect: abgefangen, damit ich sehen kann, mit welchen Parametern diese Funktion aufgerufen wird.

    In der Methode -draw ([self draw]) werden die Elemente neu gezeichnet, was den Rahmen sprengen würde (zu lange Funktion). Dort sehe ich aber auch nicht den Fehler.

    Dann kommt noch das hier:
    Diese Funktion errechnet aus einer Liste mit Objekten (die alle eine frame und lastFrame haben) das alles umschliessende Rectangle. Das Resultat dieser Funktion wird an setNeedsDisplayInRect: weitergegeben:

    Quellcode

    1. /*
    2. * CALCULATING THE BOUNDING AREA OF OBJECTS
    3. */
    4. - (NSRect)enclosingRectForObjects:(NSArray *)objects includeOriginalFrame:(BOOL)flag
    5. {
    6. OBPRECONDITION(([objects count] > 0));
    7. NSRect finalRect = [[objects objectAtIndex:0] frame];
    8. int i, n;
    9. for (i = 0; i < [objects count]; i++)
    10. {
    11. for (n = 0; n < 2; n++)
    12. {
    13. NSRect frame = (n == 1 && flag ? [[objects objectAtIndex:i] lastFrame] : [[objects objectAtIndex:i] frame]);
    14. finalRect = NSMakeRect(MIN(NSMinX(finalRect), NSMinX(frame)), MIN(NSMinY(finalRect), NSMinY(frame)),
    15. MAX(NSMaxX(finalRect), NSMaxX(frame)), MAX(NSMaxY(finalRect), NSMaxY(frame)));
    16. finalRect.size.width -= finalRect.origin.x;
    17. finalRect.size.height -= finalRect.origin.y;
    18. }
    19. }
    20. finalRect = millimeterToPixelRect(finalRect);
    21. finalRect.origin.x += origin.x;
    22. finalRect.origin.y += origin.y;
    23. //Add a security border
    24. finalRect = NSInsetRect(finalRect, -25, -25);
    25. return finalRect;
    26. }
    Alles anzeigen
  • Hmm auf anhieb sehe ich da auch nix -- leider.

    Du meintest, dass die Berechnung des umgebenden Rechteckes richtig ist? Ansonsten gibt es da die Funktion NSUnionRect mal verwenden anstelle deiner eigenen Abfrage. (Das solltest du ohnehin tun.)

    Ferner NSOutsetRect anstelle von NSInsectRect mit negativen Werten. Negative Zahlen machen Rects zuweilen Probleme, wie ich schon erfahren musste.

    Kannst du den Output für die einzelnen Rechtecke und drawRect auch mal posten? (Sorry, aber sonst sehe ich ja nichts!)

    BTW: Offenbar clipst du nur den Redraw? Das sorft nicht für eine Performance-Steigerung. Du solltest schon schauen, welche Objekte (deren Rechtecke) in dem neu zu zeichnenden Bereich liegn. Hier hilft NSIntersectRect.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Ja, die Überprüfung ob Objekte im gezeichneten Bereich liegen hab ich bewusst noch weggelassen.

    Also, das ist der Output der Rects bei einer Bewegung um ca. 1-2 Pixel:

    Quellcode

    1. 2005-05-02 09:27:38.906 Poster Print[881] -enclosingRectForObjects: {{137.014, 94.7529}, {223.789, 132.526}}
    2. 2005-05-02 09:27:38.906 Poster Print[881] -setNeedsDisplayInRect: {{137.014, 94.7529}, {223.789, 132.526}}
    3. 2005-05-02 09:27:38.932 Poster Print[881] -setNeedsDisplayInRect: {{137, 94}, {224, 134}}
    4. 2005-05-02 09:27:38.933 Poster Print[881] -drawRect: {{136.895, 94.1762}, {224.275, 133.983}}


    Seltsam dabei finde ich, dass das Rect, dass ich in -drawRect: mitbekomme leicht andere masse hat, als das, das ich bei setNeedsDisplayInRect: mitgebe...

    Das hat jetzt auch noch ein update gekriegt:

    Quellcode

    1. - (NSRect)enclosingRectForObjects:(NSArray *)objects includeOriginalFrame:(BOOL)flag
    2. {
    3. OBPRECONDITION(([objects count] > 0));
    4. NSRect finalRect = [[objects objectAtIndex:0] frame];
    5. int i;
    6. for (i = 0; i < [objects count]; i++)
    7. {
    8. NSRect frame = (flag ? NSUnionRect([[objects objectAtIndex:i] lastFrame], [[objects objectAtIndex:i] frame]) : [[objects objectAtIndex:i] frame]);
    9. finalRect = NSUnionRect(finalRect, frame);
    10. }
    11. finalRect = millimeterToPixelRect(finalRect);
    12. finalRect.origin.x += origin.x;
    13. finalRect.origin.y += origin.y;
    14. //Add a security border
    15. finalRect = NSInsetRect(finalRect, -25, -25);
    16. NSLog(@"-enclosingRectForObjects: %@", NSStringFromRect(finalRect));
    17. return finalRect;
    18. }
    Alles anzeigen