Les transitions entre images sous Lazarus avec BGRABitmap (XVIII) - Utilisation des splines (suite)
par
, 16/04/2018 à 20h28 (1157 Affichages)
Dans le dernier billet, nous avons vu de nouvelles méthodes en lien avec les splines. Ces nouvelles méthodes introduites méritent que nous nous y arrêtions un instant.
Application étendue des splines
Examen des nouvelles méthodes
La première de ces méthodes est ComputeOpenedSpline qui, comme son nom l'indique, calcule une spline pour une courbe ouverte.
Sa déclaration se présente ainsi :
Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part function ComputeOpenedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; override;
Nous voyons qu'elle prend pour paramètres un tableau de points (de type TPointF avec des coordonnées flottantes) et un style de type TSplineStyle. Ce dernier permet de construire la courbe selon l'effet recherché en fonction de l'enveloppe du polygone de départ, c'est-à-dire le polygone obtenu en suivant le contour extérieur du polygone de référence :
- ssInside : la courbe est dessinée à l'intérieur de l'enveloppe du polygone sans inclure les points de début et de fin ;
- ssInsideWithEnds : idem mais en incluant les points de début et de fin ;
- ssCrossing : la courbe traverse l'enveloppe du polygone sans inclure les points de début et de fin ;
- ssCrossingWithEnds : idem mais en incluant les points de début et de fin ;
- ssOutside : la courbe est dessinée à l'extérieur de l'enveloppe du polygone en incluant les points de début et de fin ;
- ssRoundOutside : la courbe s'étend à l'extérieur de l'enveloppe du polygone en incluant les points de début et de fin ;
- ssVertexToSide : la courbe est située à l'extérieur de l'enveloppe du polygone et est calculée à partir des tangentes aux points choisis en incluant les points de début et de fin.
Certains des styles ne permettent pas d'obtenir un recouvrement complet de l'image d'origine. Par exemple, avec nos formules et les coordonnées choisies, les quatre premiers styles du tableau ne conviennent pas.
Un peu plus loin, dans le prochain exemple, nous verrons en action les différences entre ces différentes options.
La valeur retournée par la fonction est un tableau qui contient les points de la courbe arrondie : c'est ce tableau qui sera finalement dessiné.
ComputeOpenedSpline a pour pendant la méthode ComputeClosedSpline qui ferme la spline. Dans l'exemple choisi, la différence n'était pas perceptible puisque nous nous appuyions sur le bord supérieur de l'image pour former une frontière à la surface de la courbe.
La seconde méthode nouvelle est DrawPolyLineAntialias. Comme son nom l'indique, elle dessine avec anticrénelage une ligne continue composée d'un ou de plusieurs segments de ligne. Comme aussi bon nombre des méthodes présentées, elle a de multiples variétés grâce à la surcharge des déclarations :
Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 {** Draws a polyline using current pen style/cap/join } procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override; {** Draws a polyline using current pen style/cap/join. ''texture'' specifies the source color to use when filling the line } procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override; {** Draws a polyline using current pen style/cap/join. ''Closed'' specifies if the end of the line is closed. If it is not closed, a space is left so that the next line can fit } procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; ClosedCap: boolean); override; procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single; ClosedCap: boolean); override; {** Draws a polyline using current pen style/cap/join. ''fillcolor'' specifies a color to fill the polygon formed by the points } procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override;
Nous voyons que nous pouvons jouer sur les points, les couleurs, les textures, les jointures, la couleur de remplissage, l'épaisseur du trait (paramètre w) ou l'apparence de la fin de la ligne pour un éventuel raccord. Pour le masque, nous avons choisi la dernière mouture de la méthode puisque nous désirions peindre en blanc la surface délimitée par la courbe calculée.
Ces procédures mériteraient à elles seules un développement particulier qui nous conduirait bien loin de notre préoccupation première !
Application étendue des splines
Les transitions décrites jusqu'à présent reposaient la plupart du temps sur l'utilisation d'un ou de plusieurs polygones (triangles et rectangles pour l'essentiel). En appliquant l'outil de la spline à ces transitions, nous pouvons espérer obtenir des animations proches de l'original mais aux formes adoucies.
Reprenons par exemple la transition RightBottomExpand qui agrandit un rectangle en direction du point inférieur droit de l'image finale, recouvrant peu à peu l'image d'origine par celle de destination. Nous pouvons stocker dans un tableau les coordonnées du rectangle qui sert au dessin, calculer une spline à partir d'elles, avant de dessiner le résultat sur notre masque.
Le code donnera alors ceci :
Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 procedure TMainForm.btnGoClick(Sender: TObject); // *** dessin *** var LBGRAFrom, LBGRATo, LBGRAMask: TBGRABitmap; LY, LX: Integer; LPts, LSpline: array of TPointF; begin btnGo.Enabled := False; LBGRAFrom := TBGRABitmap.Create(imgResult.ClientWidth, imgResult.ClientHeight, BGRABlack); try LBGRATo := TBGRABitmap.Create(imgResult.ClientWidth, imgResult.ClientHeight, BGRABlack); try LBGRAMask := TBGRABitmap.Create(imgResult.ClientWidth, ClientHeight, BGRABlack); try fStep := 0; repeat Inc(fStep); LX := 0; LY := 0; LBGRAFrom.FillRect(ClientRect, BGRABlack); LBGRAFrom.PutImage(LX, LY, fBGRAFrom, dmDrawWithTransparency, Opacity(False)); SetLength(LPts, 4); LBGRAMask.FillRectAntialias(0, 0, imgResult.ClientWidth, imgResult.ClientHeight, BGRABlack); //LBGRAMask.FillRectAntialias(0, 0, imgResult.ClientWidth * fStep div 100, // imgResult.ClientHeight * fStep div 100, BGRAWhite); LPts[0] := PointF(0, 0); LPts[1] := PointF(imgResult.ClientWidth * fStep div 100, 0); LPts[2] := PointF(imgResult.ClientWidth * fStep div 100, imgResult.ClientHeight * fStep div 100); LPts[3] := PointF(0, imgResult.ClientHeight * fStep div 100); LSpline := LBGRAMask.ComputeClosedSpline(LPts, ssVertexToSide); LBGRAMask.DrawPolyLineAntialias(LSpline, BGRAWhite, 1, BGRAWhite); LBGRATo.PutImage(0, 0, fBGRATo, dmSet); LBGRATo.ApplyMask(LBGRAMask); LBGRAFrom.PutImage(0, 0, LBGRATo, dmDrawWithTransparency, Opacity); LBGRAFrom.Draw(imgResult.Canvas, 0, 0); imgResult.Repaint; sleep(100 - fSpeed); until fStep = 100; finally LBGRAMask.Free; end; finally LBGRATo.Free; end; finally LBGRAFrom.Free; btnGo.Enabled := True; end; end;
Les deux lignes de calculs mises en commentaires sont les deux lignes originales écrites pour la transition sans spline.
La transition dessine désormais une forme arrondie proche de l'ellipse.
Afin de visualiser les différences entre les options associées au calcul de la spline, nous ajouterons une ligne provisoire à notre code :
Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 LBGRATo.PutImage(0, 0, fBGRATo, dmSet); LBGRATo.ApplyMask(LBGRAMask); LBGRAFrom.PutImage(0, 0, LBGRATo, dmDrawWithTransparency, Opacity); LBGRAFrom.DrawPolylineAntialias(LPts,BGRA(255,0,0,255),3); // nouvelle ligne LBGRAFrom.Draw(imgResult.Canvas, 0, 0); imgResult.Repaint;
La forme d'origine de la ligne multiple est reproduite au fur et à mesure de la transition. Elle est d'autant plus visible que sa couleur de dessin est le rouge. Il ne nous reste qu'à modifier le dernier paramètre de la méthode ComputeClosedSpline pour voir les résultats obtenus.
Code pascal : Sélectionner tout - Visualiser dans une fenêtre à part LSpline := LBGRAMask.ComputeClosedSpline(LPts, ssVertexToSide);
Nous verrons ainsi ce que signifie tel ou tel paramètre. Nous vérifierons aussi que certaines options ne couvrent pas entièrement l'image.
Voici par exemple un instantané pris avec l'option ssCrossing :
Il est clair que la courbe finale « traverse » bien la courbe d'origine (le rectangle) !