32 votes

Pourquoi tracer une ligne de moins de 1,5 pixel d'épaisseur deux fois plus lentement que de tracer une ligne de 10 pixels d'épaisseur?

Je suis en train de jouer avec FireMonkey pour voir si le graphique de la peinture est plus vite que le GDI ou Graphics32 (ma bibliothèque de choix pour le moment).

Pour voir comment il est rapide, j'ai effectué quelques tests, mais je croise certains des comportements bizarres:

Tracez des lignes fines (<1.5 pixel de large) semble extrêmement lent comparativement lignes plus épaisses: Performance

  • Axe Vertical: les cycles de l'uc à la peinture de 1000 lignes
  • Axe Horizontal: ligne tickness*

Les résultats sont assez stables; le dessin a toujours devient beaucoup plus rapide une fois que l'épaisseur de la ligne est plus que de 1 pixel de large.

Dans d'autres bibliothèques, il semble y avoir algorithmes rapides pour des lignes simples et des lignes épaisses sont plus lents car un polygone est créé en premier, alors pourquoi est-FireMonkey dans l'autre sens?

J'ai surtout besoin d'un pixel lignes, de sorte que je devrais peindre des lignes dans une autre façon, peut-être?

Les tests ont été exécutés avec ce code:

// draw random lines, and copy result to clipboard, to paste in excel
procedure TForm5.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var
  i,iWidth:Integer;
  p1,p2: TPointF;
  sw:TStopWatch;
const
  cLineCount=1000;
begin
  Memo1.Lines.Clear;
  // draw 1000 different widths, from tickness 0.01 to 10
  for iWidth := 1 to 1000 do
  begin
    Caption := IntToStr(iWidth);
    Canvas.BeginScene;
    Canvas.Clear(claLightgray);
    Canvas.Stroke.Kind := TBrushKind.bkSolid;
    Canvas.Stroke.Color := $55000000;
    Canvas.StrokeThickness :=iWidth/100;
    sw := sw.StartNew;
    // draw 1000 random lines
    for I := 1 to cLineCount do
    begin
      p1.Create(Random*Canvas.Width,Random*Canvas.Height);
      p2.Create(Random*Canvas.Width,Random*Canvas.Height);
      Canvas.DrawLine(p1,p2,0.5);
    end;
    Canvas.EndScene;
    sw.Stop;
    Memo1.Lines.Add(Format('%f'#9'%d', [Canvas.StrokeThickness,  Round(sw.ElapsedTicks / cLineCount)]));
  end;
  Clipboard.AsText := Memo1.Text;
end;

Mise à jour

@Steve Wellens: En effet, les lignes verticales et les lignes horizontales sont beaucoup plus rapide. Il y a effectivement une différence entre horizontales et verticales:

Difference between Diagonal, Horitonzal and Vertical linesLes lignes diagonales: en bleu, les lignes Horizontales: vert, les lignes Verticales: rouge

Avec des lignes verticales, il y a une différence nette entre les lignes qui sont à moins de 1 pixel de large. Avec des lignes diagonales il y a une pente comprise entre 1.0 et 1.5.

La chose étrange est qu'il n'y a pratiquement aucune différence entre la peinture d'une ligne horizontale de 1 pixel et une peinture de 20 pixels. Je suppose que c'est là l'accélération matérielle commence à faire une différence?

28voto

dthorpe Points 23314

Résumé: l'Anticrénelage sous-pixel de l'épaisseur des lignes est difficile et nécessite un certain nombre de sales tours à la sortie de ce qu'intuitivement, nous attendons de voir.

L'effort supplémentaire que vous voyez n'est presque certainement en raison de l'anticrénelage. Lorsque la ligne est inférieure à l'épaisseur d'un pixel et la ligne n'a pas s'asseoir directement au centre d'une rangée de dispositif de pixels, chaque pixel à en tirer pour la ligne sera partielle de la luminosité des pixels. Pour vous assurer que les valeurs partielles sont assez vif pour que la ligne ne disparaît pas, plus de travail est nécessaire.

Depuis la vidéo de signaux fonctionnent sur un balayage horizontal (pensez à tube CATHODIQUE, pas de LCD), les opérations graphiques mettent traditionnellement l'accent sur le traitement des choses à l'horizontale scanline à la fois.

Voici ma conjecture:

Pour résoudre certains collant problèmes, rasterizers parfois "coup de pouce" lignes de sorte que plus de leurs pixels virtuels aligner avec dispositif de pixels. Si un .25 pixel d'épaisseur de la ligne horizontale est exactement à mi-chemin entre l'appareil scanline A et B, cette ligne peut disparaître complètement, car il ne s'enregistre pas assez fortement pour éclairer tous les pixels dans scanline A ou B. Ainsi, le rasterizer pourrait décaler la ligne "vers le bas" un tout petit peu en virtuel coordonnées afin qu'il s'harmonise avec scanline B dispositif de pixels et de produire une belle fortement éclairé ligne horizontale.

La même chose peut être fait pour les lignes verticales, mais n'est probablement pas si votre carte graphique/pilote est hyperfocused horizontal scanline opérations (comme le sont plusieurs).

Donc, dans ce scénario, une ligne horizontale rendrait très rapide car il n'y a pas d'anticrénelage être effectuée à tout, et il peut être fait dans un scanline.

Une ligne verticale nécessiterait l'anticrénelage analyse pour chaque horizontal scanline qui franchit la ligne. Le rasterizer peut avoir un cas spécial pour les lignes verticales à prendre en compte uniquement la gauche et la droite pixels à calculer l'anticrénelage valeurs.

Une ligne diagonale n'a pas de raccourcis. Il a jaggies partout, donc il y a beaucoup d'anticrénelage de travail à faire ensemble. L'antialias calcul doit tenir compte (sous-échantillon) une matrice de points (au moins 4, probablement 8) autour du point cible de décider dans quelle mesure partielle de la valeur à donner à l'appareil de pixel. La matrice peut être simplifiée ou d'éliminer totalement les pour les lignes verticales ou horizontales, mais pas pour les diagonales.

Il y a un autre élément qui est vraiment seulement une préoccupation pour les sous-pixel de l'épaisseur des lignes: comment faire pour éviter la sous-pixel de l'épaisseur de la ligne de disparaître totalement ou ayant des lacunes notables, où la ligne ne passe pas par le centre d'un dispositif de pixel? Il est probable qu'après la lisser les valeurs sont calculées sur une scanline, si il n'y a pas de "signal" ou suffisamment éclairé de pixels de l'équipement causés par la ligne virtuelle, le rasterizer as de revenir en arrière et "try harder" ou appliquer un peu de stimuler l'heuristique pour obtenir un signal plus fort à l'étage le ratio de sorte que le dispositif de pixels représentant la ligne virtuelle sont tangibles et continue.

Deux adjacentes dispositif de pixels à 40% de luminosité est ok. Si la seule rasterizer de sortie pour le scanline est de deux pixels adjacents à 5%, l'œil percevra une brèche dans la ligne. Pas ok.

Lorsque la ligne est plus que de 1,5 dispositif de pixels d'épaisseur, vous aurez toujours au moins un bien éclairée appareil pixel sur chaque ligne de numérisation et n'ont pas besoin de revenir en arrière et essayer plus difficile.

Pourquoi est-1.5 le nombre magique pour l'épaisseur de la ligne? Demandez À Pythagore. Si votre appareil pixel est de 1 unité de largeur et de hauteur, la longueur de la diagonale du carré de pixels de l'équipement correspond à la racine carrée(1^2 + 1^2) = sqrt(2) = 1.41 ish. Lorsque votre épaisseur de la ligne est supérieure à la longueur de la diagonale d'un pixel appareil, vous devez toujours avoir au moins un "bien éclairée" pixel dans le scanline de sortie de n'importe quel angle de la ligne.

C'est ma théorie, de toute façon.

6voto

Mattias Andersson Points 153

Dans d'autres bibliothèques, il semble y avoir algorithmes rapides pour des lignes simples et des lignes épaisses sont plus lents car un polygone est créé en premier, alors pourquoi est-FireMonkey dans l'autre sens?

Dans Graphics32, Bresenham ligne de l'algorithme est utilisé pour accélérer les lignes avec un 1px de largeur et qui devrait certainement être rapide. FireMonkey ne dispose pas de sa propre natif rasterizer, au lieu de déléguer les opérations de peinture à d'autres Api (dans Windows, il va déléguer soit Direct2D ou GDI+.)

Ce que vous observez est en effet la performance de la Direct2D rasterizer et je peux confirmer que j'ai fait des observations similaires précédemment (j'ai comparé beaucoup de différents rasterizers.) Voici un post qui parle spécifiquement sur les performances de la Direct2D rasterizer (btw, c'est pas une règle générale que de fines lignes sont tirées plus lent, surtout pas dans mon propre rasterizer):

http://www.graphics32.org/news/newsgroups.php?article_id=10249

Comme vous pouvez le voir sur le graphique, Direct2D a de très bonnes performances pour des ellipses et des traits épais, mais bien pire performance dans les autres critères (d'où mon propre rasterizer est plus rapide.)

J'ai surtout besoin d'un pixel lignes, donc dois-je peindre des lignes dans une autre façon, peut-être?

J'ai mis en place un nouveau FireMonkey backend (une nouvelle TCanvas descendant), qui s'appuie sur ma propre rasterizer moteur de VPR. Il devrait être plus rapide que Direct2D pour les lignes fines et pour le texte (même si c'est à l'aide de forme polygonale tramage techniques.) Il reste peut-être quelques mises en garde qui doivent être abordés afin de le faire fonctionner à 100% de façon transparente comme un Firemonkey backend. Plus d'infos ici:

http://graphics32.org/news/newsgroups.php?article_id=11565

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X