Rotation de Bitmap



Télécharger les Sources de l'exemple (10.5 ko)

La rotation d'une image n'est en fait pas si compliquée que ca en a l'air. Il suffit de partir de formules de géométries simples, que l'on peut trouver dans n'importe quel ouvrage traitant de ce sujet:

Soient Xs et Ys les coordonnées de la source
Soient Xd et Yd les coordonnées de la destination
Soit Angle l'angle de la rotation

Xd = Xs * Sin(Angle) + Ys * Cos(Angle)
Yd = Xs * Cos(Angle) - Ys * Sin(Angle)


<center>

<br>L'image de départ...</center>

Seulement si on transpose cette formule directement dans notre programme, on aura un problème: des "trous" apparaissent entre les pixels, du fait du manque de précision du calcul avec les entiers. Pour contourner ce problème, il suffit de chercher les coordonnées des points sources à partir des points de destination. En adaptant la formule, on obtient:

Soient Xs et Ys les coordonnées de la source
Soient Xd et Yd les coordonnées de la destination
Soient Xm et Ym les coordonnées du milieu de l'image
Soit Angle l'angle de la rotation

Xs = (Yd - Ym) * Sin(Angle) + (Xd - Xm) * Cos(Angle) + Xm
Ys = (Yd - Ym) * Cos(Angle) - (Xd - Xm) * Sin(Angle) + Ym


<center>

<br>... et après rotation de 45 degrés</center>


Il ne nous reste plus qu'à optimiser un peu cette formule. Quand on regarde bien cette formule, on se rend compte que l'angle, son cosinus et son sinus sont constants pour tous les points de l'image. On a donc intêret à les calculer en dehors des boucles pour alléger le calcul. Il en est de même pour le calcul des coordonnées du centre de l'image.

Il ne reste plus qu'un petit problème à régler: si les coordonnées du point source se trouve en dehors de l'image, on obtiendra un résultat étrange si on recopie le point tel quel sur l'image de destination. Il faut donc surveiller si la couleur est égale à -1, auquel cas il faut la convertir en couleur de fond. Et voilà, tout est prêt pour commencer le calcul :

Rotation

procedure TForm1.Rotate(angle: integer);
var
  RadAngle: extended;
  SinAngle, CosAngle: extended;
  CenterX, CenterY: extended;
  X, Y: extended;
  Xi, Yi: integer;
  i, j: integer;
  c: TColor;
begin
  RadAngle := angle * pi / 180;
  SinAngle := sin(RadAngle);
  CosAngle := cos(RadAngle);
  CenterX := Bitmap.Width / 2;
  CenterY := Bitmap.Height / 2;
  for i := 0 to Pred(RotatedBitmap.Width) do
    for j := 0 to Pred(RotatedBitmap.Height) do
      begin
        X := (j - CenterY) * SinAngle + (i - CenterX) * CosAngle + CenterX;
        Y := (j - CenterY) * CosAngle - (i - CenterX) * SinAngle + CenterY;
        Xi := Round(X);
        Yi := Round(Y);
        c := Bitmap.Canvas.Pixels[Xi, Yi];
        if (c = -1) then c := clBlack;
        RotatedBitmap.Canvas.Pixels[i, j] := c;
      end;
end;



Si vous voulez, vous pouvez télécharger les Sources de l'exemple (10.5 ko)

Attention, la rotation telle qu'elle est implémentée ici est un processus extrèment lent, du fait de l'accès aux pixels par la propriété Canvas.Pixels du TBitmap. Il faudrait l'optimiser en utilisant la propriété ScanLine, introduite depuis Delphi 3, mais cela pose d'autres problèmes, car en fonction de la résolution de l'image (2, 4, 8, 15, 16, 24 ou 32 bits), il faut utiliser un algorithme différent. Vous trouverez plus de détails à ce sujet sur le site Efg's Computer Lab (en Anglais).



3 requête(s) SQL executée(s) en 0.049 Secs - Temps total de génération de la page : 0.056 Secs