GLS.Canvas.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Canvas;
  5. (*
  6. Implements a basic Canvas-like interface over for OpenGL.
  7. This class can be used for generic OpenGL applications and has no dependencies
  8. to the GLScene core units (only to base units).
  9. *)
  10. interface
  11. {$I GLScene.inc}
  12. uses
  13. Winapi.OpenGL,
  14. System.Types,
  15. System.Classes,
  16. System.Math,
  17. Vcl.Graphics,
  18. GLS.OpenGLTokens,
  19. GLS.VectorGeometry,
  20. GLS.Color,
  21. GLS.Context,
  22. GLS.VectorTypes,
  23. GLS.State;
  24. type
  25. TGLArcDirection = (adCounterClockWise, adClockWise);
  26. (* A simple Canvas-like interface for OpenGL.
  27. This class implements a small "shell" for 2D operations in OpenGL,
  28. it operates over the current OpenGL context and provides methods
  29. for drawing lines, ellipses and points.
  30. This class is typically used by creating an instance, using it for drawing,
  31. and freeing the instance. When drawing (0, 0) is the top left corner.
  32. All coordinates are internally maintained with floating point precision.
  33. Several states are cached and it is of primary importance not to invoke
  34. OpenGL directly throughout the life of an instance (at the cost of
  35. unespected behaviour). *)
  36. TGLCanvas = class
  37. private
  38. FBufferSizeX, FBufferSizeY: Integer;
  39. FLastPrimitive: Integer;
  40. FCurrentPos: TAffineVector;
  41. FPenColor: TColor;
  42. FPenWidth: Integer;
  43. FCurrentPenColorVector: TGLVector;
  44. FArcDirection: TGLArcDirection;
  45. protected
  46. procedure BackupOpenGLStates;
  47. procedure StartPrimitive(const primitiveType: Integer);
  48. procedure EllipseVertices(x, y, xRadius, yRadius: Single);
  49. procedure SetPenColor(const val: TColor);
  50. function GetPenAlpha: Single;
  51. procedure SetPenAlpha(const val: Single);
  52. procedure SetPenWidth(const val: Integer);
  53. procedure SwapSingle(pX, pY: PSingle);
  54. procedure NormalizePoint(const x1, y1, x2, y2: Single;
  55. const x, y: Single; pX, pY: PSingle);
  56. procedure DrawArc(x1, y1, x2, y2, x3, y3, x4, y4: Single;
  57. UpdateCurrentPos: Boolean); overload;
  58. procedure DrawArc(x1, y1, x2, y2: Single;
  59. AngleBegin, AngleEnd: Single;
  60. UpdateCurrentPos: Boolean); overload;
  61. public
  62. constructor Create(bufferSizeX, bufferSizeY: Integer;
  63. const baseTransform: TGLMatrix); overload;
  64. constructor Create(bufferSizeX, bufferSizeY: Integer); overload;
  65. destructor Destroy; override;
  66. (* Stops the current internal primitive.
  67. This function is invoked automatically by TGLCanvas when changeing
  68. primitives, you should directly call if you want to render your
  69. own stuff intertwined with TGLCanvas drawings. In that case, call
  70. it before your own OpenGL calls. *)
  71. procedure StopPrimitive;
  72. (* Inverts the orientation of the Y Axis.
  73. If (0, 0) was in the top left corner, it will move to the bottom
  74. left corner or vice-versa. *)
  75. procedure InvertYAxis;
  76. property CanvasSizeX: Integer read FBufferSizeX;
  77. property CanvasSizeY: Integer read FBufferSizeY;
  78. // Current Pen Color.
  79. property PenColor: TColor read FPenColor write SetPenColor;
  80. // Current Pen Alpha channel (from 0.0 to 1.0)
  81. property PenAlpha : Single read GetPenAlpha write SetPenAlpha;
  82. // Current Pen Width.
  83. property PenWidth: Integer read FPenWidth write SetPenWidth;
  84. // Updates the current position (absolute coords).
  85. procedure MoveTo(const x, y: Integer); overload;
  86. procedure MoveTo(const x, y: Single); overload;
  87. // Updates the current position (relative coords).
  88. procedure MoveToRel(const x, y: Integer); overload;
  89. procedure MoveToRel(const x, y: Single); overload;
  90. (* Draws a line from current position to given coordinate.
  91. Current position is updated. *)
  92. procedure LineTo(const x, y: Integer); overload;
  93. procedure LineTo(const x, y: Single); overload;
  94. procedure LineToRel(const x, y: Integer); overload;
  95. procedure LineToRel(const x, y: Single); overload;
  96. (* Draws a line from (x1, y1) to (x2, y2).
  97. The current position is NOT updated. *)
  98. procedure Line(const x1, y1, x2, y2: Integer); overload;
  99. procedure Line(const x1, y1, x2, y2: Single); overload;
  100. (* Draws the set of lines defined by connecting the points.
  101. Similar to invoking MoveTo on the first point, then LineTo
  102. on all the following points. *)
  103. procedure Polyline(const points: array of TPoint);
  104. // Similar to Polyline but also connects the last point to the first.
  105. procedure Polygon(const points: array of TPoint);
  106. (* Plots a pixel at given coordinate. PenWidth affects pixel size.
  107. The current position is NOT updated. *)
  108. procedure PlotPixel(const x, y: Integer); overload;
  109. procedure PlotPixel(const x, y: Single); overload;
  110. // Draw the (x1,y1)-(x2, y2) rectangle's frame (border).
  111. procedure FrameRect(const x1, y1, x2, y2: Integer); overload;
  112. procedure FrameRect(const x1, y1, x2, y2: Single); overload;
  113. // Draw the (x1,y1)-(x2, y2) rectangle (filled with PenColor).
  114. procedure FillRect(const x1, y1, x2, y2: Integer); overload;
  115. procedure FillRect(const x1, y1, x2, y2: Single); overload;
  116. // Draw the (x1,y1)-(x2, y2) rectangle (filled with given gradient's color).
  117. procedure FillRectGradient(const x1, y1, x2, y2: Single;
  118. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector); overload;
  119. procedure FillRectGradient(const x1, y1, x2, y2: Integer;
  120. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector); overload;
  121. // Draws an ellipse with (x1,y1)-(x2, y2) bounding rectangle.
  122. procedure EllipseBB(const x1, y1, x2, y2: Integer); overload;
  123. procedure EllipseBB(const x1, y1, x2, y2: Single); overload;
  124. // Draws and ellipse centered at (x, y) with given radiuses.
  125. procedure Ellipse(const x, y: Integer; const xRadius, yRadius: Single); overload;
  126. procedure Ellipse(const x, y: Single; const xRadius, yRadius: Single); overload;
  127. procedure Ellipse(const x, y: Single; const Radius: Single); overload;
  128. // Draw a filled ellipse.
  129. procedure FillEllipse(const x, y: Integer; const xRadius, yRadius: Single); overload;
  130. procedure FillEllipse(const x, y: Single; const xRadius, yRadius: Single); overload;
  131. procedure FillEllipse(const x, y: Single; const Radius: Single); overload;
  132. (* Draw a filled gradient ellipse.
  133. OpenGL will use the last PenColor and PenAlpha as the center color and do gradient
  134. to edge of ellipse using the edgeColor parameter. *)
  135. procedure FillEllipseGradient(const x, y, xRadius, yRadius: Single;
  136. const edgeColor: TGLColorVector); overload;
  137. procedure FillEllipseGradient(const x, y: Integer;
  138. const xRadius, yRadius: Integer; const edgeColor: TGLColorVector); overload;
  139. procedure FillEllipseGradient(const x, y, Radius: Single;
  140. const edgeColor: TGLColorVector); overload;
  141. (* Draw an elliptical arc.
  142. The points (x1, y1) and (x2, y2) specify the bounding rectangle.
  143. An ellipse formed by the specified bounding rectangle defines the curve of the arc.
  144. The arc extends in the current drawing direction from the point where it intersects the radial from the center of the bounding rectangle to the (x3, y3) point.
  145. The arc ends where it intersects the radial from the center of the bounding rectangle to the (x4, y4) point.
  146. If the starting point and ending point are the same, a complete ellipse is drawn.
  147. Use the ArcDirection property to get and set the current drawing direction for a device context.
  148. The default drawing direction is counterclockwise. *)
  149. procedure Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Integer); overload;
  150. procedure Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Single); overload;
  151. procedure Arc(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single); overload;
  152. // Same as Arc but update the current position.
  153. procedure ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Integer); overload;
  154. procedure ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Single); overload;
  155. procedure ArcTo(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single); overload;
  156. procedure RoundRect(const x1, y1, x2, y2, xr, yr: Integer); overload;
  157. procedure RoundRect(const x1, y1, x2, y2, xr, yr: Single); overload;
  158. property ArcDirection: TGLArcDirection read FArcDirection write FArcDirection;
  159. end;
  160. //-------------------------------------------------------------
  161. implementation
  162. //-------------------------------------------------------------
  163. const
  164. cNoPrimitive = MaxInt;
  165. pion2 = pi/2;
  166. pi3on2 = 3*pion2;
  167. // ------------------
  168. // ------------------ TGLCanvas ------------------
  169. // ------------------
  170. constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer;
  171. const baseTransform: TGLMatrix);
  172. var
  173. PM: TGLMatrix;
  174. begin
  175. FBufferSizeX := bufferSizeX;
  176. FBufferSizeY := bufferSizeY;
  177. gl.MatrixMode(GL_PROJECTION);
  178. gl.PushMatrix;
  179. PM := CreateOrthoMatrix(0, bufferSizeX, bufferSizeY, 0, -1, 1);
  180. gl.LoadMatrixf(@PM);
  181. gl.MatrixMode(GL_MODELVIEW);
  182. gl.PushMatrix;
  183. gl.LoadMatrixf(@baseTransform);
  184. BackupOpenGLStates;
  185. FLastPrimitive := cNoPrimitive;
  186. FArcDirection := adCounterClockWise;
  187. end;
  188. constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer);
  189. begin
  190. Create(bufferSizeX, bufferSizeY, IdentityHmgMatrix);
  191. end;
  192. destructor TGLCanvas.Destroy;
  193. begin
  194. StopPrimitive;
  195. gl.MatrixMode(GL_PROJECTION);
  196. gl.PopMatrix;
  197. gl.MatrixMode(GL_MODELVIEW);
  198. gl.PopMatrix;
  199. end;
  200. procedure TGLCanvas.BackupOpenGLStates;
  201. begin
  202. with CurrentGLContext.GLStates do
  203. begin
  204. Disable(stLighting);
  205. Disable(stFog);
  206. Disable(stCullFace);
  207. Disable(stColorMaterial);
  208. Disable(stDepthTest);
  209. Disable(stLineSmooth);
  210. Disable(stLineStipple);
  211. Disable(stPointSmooth);
  212. Enable(stBlend);
  213. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  214. // Setup and backup pen stuff
  215. FPenColor := clBlack;
  216. SetVector(FCurrentPenColorVector, NullHmgPoint);
  217. gl.Color4fv(@FCurrentPenColorVector);
  218. FPenWidth := 1;
  219. LineWidth := 1;
  220. PointSize := 1;
  221. end;
  222. end;
  223. procedure TGLCanvas.StartPrimitive(const primitiveType: Integer);
  224. begin
  225. if primitiveType <> FLastPrimitive then
  226. begin
  227. if FLastPrimitive <> cNoPrimitive then
  228. gl.End_;
  229. if primitiveType <> cNoPrimitive then
  230. gl.Begin_(primitiveType);
  231. FLastPrimitive := primitiveType;
  232. end;
  233. end;
  234. procedure TGLCanvas.StopPrimitive;
  235. begin
  236. StartPrimitive(cNoPrimitive);
  237. end;
  238. procedure TGLCanvas.InvertYAxis;
  239. var
  240. mat: TGLMatrix;
  241. begin
  242. mat := IdentityHmgMatrix;
  243. mat.Y.Y := -1;
  244. mat.W.Y := FBufferSizeY;
  245. gl.MultMatrixf(@mat);
  246. end;
  247. procedure TGLCanvas.SetPenColor(const val: TColor);
  248. begin
  249. SetVector(FCurrentPenColorVector, ConvertWinColor(val,
  250. FCurrentPenColorVector.W));
  251. FPenColor := val;
  252. gl.Color4fv(@FCurrentPenColorVector);
  253. end;
  254. procedure TGLCanvas.SetPenAlpha(const val: Single);
  255. begin
  256. FCurrentPenColorVector.W := val;
  257. gl.Color4fv(@FCurrentPenColorVector);
  258. end;
  259. procedure TGLCanvas.SetPenWidth(const val: Integer);
  260. begin
  261. if val < 1 then
  262. Exit;
  263. if val <> FPenWidth then
  264. with CurrentGLContext.GLStates do
  265. begin
  266. FPenWidth := val;
  267. StopPrimitive;
  268. LineWidth := val;
  269. PointSize := val;
  270. end;
  271. end;
  272. procedure TGLCanvas.MoveTo(const x, y: Integer);
  273. begin
  274. FCurrentPos.X := x;
  275. FCurrentPos.Y := y;
  276. end;
  277. procedure TGLCanvas.MoveTo(const x, y: Single);
  278. begin
  279. FCurrentPos.X := x;
  280. FCurrentPos.Y := y;
  281. end;
  282. procedure TGLCanvas.MoveToRel(const x, y: Integer);
  283. begin
  284. FCurrentPos.X := FCurrentPos.X + x;
  285. FCurrentPos.Y := FCurrentPos.Y + y;
  286. end;
  287. procedure TGLCanvas.MoveToRel(const x, y: Single);
  288. begin
  289. FCurrentPos.X := FCurrentPos.X + x;
  290. FCurrentPos.Y := FCurrentPos.Y + y;
  291. end;
  292. procedure TGLCanvas.LineTo(const x, y: Integer);
  293. begin
  294. StartPrimitive(GL_LINES);
  295. gl.Vertex2fv(@FCurrentPos);
  296. MoveTo(x, y);
  297. gl.Vertex2fv(@FCurrentPos);
  298. end;
  299. procedure TGLCanvas.LineTo(const x, y: Single);
  300. begin
  301. StartPrimitive(GL_LINES);
  302. gl.Vertex2fv(@FCurrentPos);
  303. MoveTo(x, y);
  304. gl.Vertex2fv(@FCurrentPos);
  305. end;
  306. procedure TGLCanvas.LineToRel(const x, y: Integer);
  307. begin
  308. LineTo(FCurrentPos.X + x, FCurrentPos.Y + y);
  309. end;
  310. procedure TGLCanvas.LineToRel(const x, y: Single);
  311. begin
  312. LineTo(FCurrentPos.X + x, FCurrentPos.Y + y);
  313. end;
  314. procedure TGLCanvas.Line(const x1, y1, x2, y2: Integer);
  315. begin
  316. StartPrimitive(GL_LINES);
  317. gl.Vertex2i(x1, y1);
  318. gl.Vertex2i(x2, y2);
  319. end;
  320. procedure TGLCanvas.Line(const x1, y1, x2, y2: Single);
  321. begin
  322. StartPrimitive(GL_LINES);
  323. gl.Vertex2f(x1, y1);
  324. gl.Vertex2f(x2, y2);
  325. end;
  326. procedure TGLCanvas.Polyline(const points: array of TPoint);
  327. var
  328. i, n: Integer;
  329. begin
  330. n := Length(Points);
  331. if n > 1 then
  332. begin
  333. StartPrimitive(GL_LINE_STRIP);
  334. gl.Vertex2iv(@points[Low(points)]);
  335. for i := Low(points) + 1 to High(points) do
  336. gl.Vertex2iv(@points[i]);
  337. StopPrimitive;
  338. end;
  339. end;
  340. procedure TGLCanvas.Polygon(const points: array of TPoint);
  341. var
  342. i, n: Integer;
  343. begin
  344. n := Length(Points);
  345. if n > 1 then
  346. begin
  347. StartPrimitive(GL_LINE_LOOP);
  348. gl.Vertex2iv(@points[Low(points)]);
  349. for i := Low(points) + 1 to High(points) do
  350. gl.Vertex2iv(@points[i]);
  351. StopPrimitive;
  352. end;
  353. end;
  354. procedure TGLCanvas.PlotPixel(const x, y: Integer);
  355. begin
  356. StartPrimitive(GL_POINTS);
  357. gl.Vertex2i(x, y);
  358. end;
  359. procedure TGLCanvas.PlotPixel(const x, y: Single);
  360. begin
  361. StartPrimitive(GL_POINTS);
  362. gl.Vertex2f(x, y);
  363. end;
  364. procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Integer);
  365. begin
  366. StartPrimitive(GL_LINE_LOOP);
  367. gl.Vertex2i(x1, y1);
  368. gl.Vertex2i(x2, y1);
  369. gl.Vertex2i(x2, y2);
  370. gl.Vertex2i(x1, y2);
  371. StopPrimitive;
  372. end;
  373. procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Single);
  374. begin
  375. StartPrimitive(GL_LINE_LOOP);
  376. gl.Vertex2f(x1, y1);
  377. gl.Vertex2f(x2, y1);
  378. gl.Vertex2f(x2, y2);
  379. gl.Vertex2f(x1, y2);
  380. StopPrimitive;
  381. end;
  382. function TGLCanvas.GetPenAlpha: Single;
  383. begin
  384. Result := FCurrentPenColorVector.W;
  385. end;
  386. procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Integer);
  387. begin
  388. StartPrimitive(GL_QUADS);
  389. gl.Vertex2i(x1, y1);
  390. gl.Vertex2i(x2, y1);
  391. gl.Vertex2i(x2, y2);
  392. gl.Vertex2i(x1, y2);
  393. StopPrimitive;
  394. end;
  395. procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Single);
  396. begin
  397. StartPrimitive(GL_QUADS);
  398. gl.Vertex2f(x1, y1);
  399. gl.Vertex2f(x2, y1);
  400. gl.Vertex2f(x2, y2);
  401. gl.Vertex2f(x1, y2);
  402. StopPrimitive;
  403. end;
  404. procedure TGLCanvas.EllipseVertices(x, y, xRadius, yRadius: Single);
  405. var
  406. i, n: Integer;
  407. s, c: TSingleArray;
  408. begin
  409. n := Round(MaxFloat(xRadius, yRadius) * 0.1) + 5;
  410. SetLength(s, n);
  411. SetLength(c, n);
  412. Dec(n);
  413. PrepareSinCosCache(s, c, 0, 90);
  414. ScaleFloatArray(s, yRadius);
  415. ScaleFloatArray(c, xRadius);
  416. // first quadrant (top right)
  417. for i := 0 to n do
  418. gl.Vertex2f(x + c[i], y - s[i]);
  419. // second quadrant (top left)
  420. for i := n - 1 downto 0 do
  421. gl.Vertex2f(x - c[i], y - s[i]);
  422. // third quadrant (bottom left)
  423. for i := 1 to n do
  424. gl.Vertex2f(x - c[i], y + s[i]);
  425. // fourth quadrant (bottom right)
  426. for i := n - 1 downto 0 do
  427. gl.Vertex2f(x + c[i], y + s[i]);
  428. end;
  429. procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Integer);
  430. begin
  431. Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
  432. 0.5);
  433. end;
  434. procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Single);
  435. begin
  436. Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
  437. 0.5);
  438. end;
  439. procedure TGLCanvas.Ellipse(const x, y: Single; const Radius: Single);
  440. begin
  441. Ellipse(x, y, Radius, Radius);
  442. end;
  443. procedure TGLCanvas.Ellipse(const x, y: Integer; const xRadius, yRadius:
  444. Single);
  445. var
  446. sx, sy: Single;
  447. begin
  448. sx := x;
  449. sy := y;
  450. Ellipse(sx, sy, xRadius, yRadius);
  451. end;
  452. procedure TGLCanvas.Ellipse(const x, y: Single; const xRadius, yRadius: Single);
  453. begin
  454. StartPrimitive(GL_LINE_STRIP);
  455. EllipseVertices(x, y, xRadius, yRadius);
  456. StopPrimitive;
  457. end;
  458. procedure TGLCanvas.FillEllipse(const x, y: Integer; const xRadius, yRadius:
  459. Single);
  460. begin
  461. StartPrimitive(GL_TRIANGLE_FAN);
  462. gl.Vertex2f(x, y); // not really necessary, but may help with memory stride
  463. EllipseVertices(x, y, xRadius, yRadius);
  464. StopPrimitive;
  465. end;
  466. procedure TGLCanvas.FillEllipse(const x, y, xRadius, yRadius: Single);
  467. begin
  468. StartPrimitive(GL_TRIANGLE_FAN);
  469. gl.Vertex2f(x, y); // not really necessary, but may help with memory stride
  470. EllipseVertices(x, y, xRadius, yRadius);
  471. StopPrimitive;
  472. end;
  473. procedure TGLCanvas.FillEllipse(const x, y, Radius: Single);
  474. begin
  475. FillEllipse(x, y, Radius, Radius);
  476. end;
  477. procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Single;
  478. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector);
  479. begin
  480. StartPrimitive(GL_QUADS);
  481. gl.Color4f(x1y1Color.X, x1y1Color.Y, x1y1Color.Z, x1y1Color.W);
  482. gl.Vertex2f(x1, y1);
  483. gl.Color4f(x2y1Color.X, x2y1Color.Y, x2y1Color.Z, x2y1Color.W);
  484. gl.Vertex2f(x2, y1);
  485. gl.Color4f(x2y2Color.X, x2y2Color.Y, x2y2Color.Z, x2y2Color.W);
  486. gl.Vertex2f(x2, y2);
  487. gl.Color4f(x1y2Color.X, x1y2Color.Y, x1y2Color.Z, x1y2Color.W);
  488. gl.Vertex2f(x1, y2);
  489. StopPrimitive;
  490. // restore pen color
  491. gl.Color4fv(@FCurrentPenColorVector);
  492. end;
  493. procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Integer;
  494. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector);
  495. begin
  496. StartPrimitive(GL_QUADS);
  497. gl.Color4f(x1y1Color.X, x1y1Color.Y, x1y1Color.Z, x1y1Color.W);
  498. gl.Vertex2i(x1, y1);
  499. gl.Color4f(x2y1Color.X, x2y1Color.Y, x2y1Color.Z, x2y1Color.W);
  500. gl.Vertex2i(x2, y1);
  501. gl.Color4f(x2y2Color.X, x2y2Color.Y, x2y2Color.Z, x2y2Color.W);
  502. gl.Vertex2i(x2, y2);
  503. gl.Color4f(x1y2Color.X, x1y2Color.Y, x1y2Color.Z, x1y2Color.W);
  504. gl.Vertex2i(x1, y2);
  505. StopPrimitive;
  506. // restore pen color
  507. gl.Color4fv(@FCurrentPenColorVector);
  508. end;
  509. procedure TGLCanvas.FillEllipseGradient(const x, y: Integer; const xRadius, yRadius: Integer; const edgeColor: TGLColorVector);
  510. begin
  511. StartPrimitive(GL_TRIANGLE_FAN);
  512. // the center will use the last set PenColor and PenAlpha
  513. gl.Vertex2f(x, y); // really necessary now :)
  514. // then OpenGL will do a gradient from the center to the edge using the edgeColor
  515. gl.Color4f(edgeColor.X, edgeColor.Y, edgeColor.Z, edgeColor.W);
  516. EllipseVertices(x, y, xRadius, yRadius);
  517. StopPrimitive;
  518. // restore pen color
  519. gl.Color4fv(@FCurrentPenColorVector);
  520. end;
  521. procedure TGLCanvas.FillEllipseGradient(const x, y, xRadius, yRadius: Single; const edgeColor: TGLColorVector);
  522. begin
  523. StartPrimitive(GL_TRIANGLE_FAN);
  524. gl.Vertex2f(x, y); // really necessary now :)
  525. gl.Color4f(edgeColor.X, edgeColor.Y, edgeColor.Z, edgeColor.W);
  526. EllipseVertices(x, y, xRadius, yRadius);
  527. StopPrimitive;
  528. // restore pen color
  529. gl.Color4fv(@FCurrentPenColorVector);
  530. end;
  531. procedure TGLCanvas.FillEllipseGradient(const x, y, Radius: Single; const edgeColor: TGLColorVector);
  532. begin
  533. FillEllipseGradient(x, y, Radius, Radius, edgeColor);
  534. end;
  535. procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
  536. begin
  537. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
  538. end;
  539. procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
  540. begin
  541. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
  542. end;
  543. procedure TGLCanvas.Arc(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
  544. begin
  545. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, False);
  546. end;
  547. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
  548. begin
  549. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
  550. end;
  551. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
  552. begin
  553. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
  554. end;
  555. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
  556. begin
  557. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, True);
  558. end;
  559. procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Integer);
  560. var
  561. x2r, y2r, x, y: integer;
  562. begin
  563. x2r := 2*xr;
  564. y2r := 2*yr;
  565. x := x1 -1;
  566. y := y2 +1;
  567. Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
  568. Line(x1, y1 + yr, x1, y - yr);
  569. Arc(x, y, x + x2r, y - y2r, pi, pion2);
  570. Line(x + xr, y2, x2 - xr, y2);
  571. Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
  572. Line(x2, y1 + yr, x2, y - yr);
  573. Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
  574. Line(x + xr, y1, x2 - xr, y1);
  575. end;
  576. procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Single);
  577. var
  578. x2r, y2r, x, y: Single;
  579. begin
  580. x2r := 2*xr;
  581. y2r := 2*yr;
  582. x := x1 -1;
  583. y := y2 +1;
  584. Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
  585. Line(x1, y1 + yr, x1, y - yr);
  586. Arc(x, y, x + x2r, y - y2r, pi, pion2);
  587. Line(x + xr, y2, x2 - xr, y2);
  588. Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
  589. Line(x2, y1 + yr, x2, y - yr);
  590. Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
  591. Line(x + xr, y1, x2 - xr, y1);
  592. end;
  593. // wrapper from "ByPoints" method
  594. procedure TGLCanvas.DrawArc(x1, y1, x2, y2, x3, y3, x4, y4: Single; UpdateCurrentPos: Boolean);
  595. var
  596. x, y: Single;
  597. AngleBegin, AngleEnd: Single;
  598. begin
  599. if x1 > x2 then
  600. SwapSingle(@x1, @x2);
  601. if y1 > y2 then
  602. SwapSingle(@y1, @y2);
  603. NormalizePoint(x1, y1, x2, y2, x3, y3, @x, @y);
  604. AngleBegin := ArcTan2(y, x);
  605. NormalizePoint(x1, y1, x2, y2, x4, y4, @x, @y);
  606. AngleEnd := ArcTan2(y, x);
  607. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, UpdateCurrentPos);
  608. end;
  609. // Real work is here
  610. procedure TGLCanvas.DrawArc(x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single; UpdateCurrentPos: Boolean);
  611. var
  612. Xc, Yc, Rx, Ry, x, y, s, c: Single;
  613. AngleCurrent, AngleDiff, AngleStep: Single;
  614. begin
  615. // check that our box is well set (as the original Arc function do)
  616. if x1 > x2 then
  617. SwapSingle(@x1, @x2);
  618. if y1 > y2 then
  619. SwapSingle(@y1, @y2);
  620. if (x1 = x2) or (y1 = y2) then
  621. exit;
  622. Xc := (x1 + x2) * 0.5;
  623. Yc := (y1 + y2) * 0.5;
  624. Rx := Abs(x2 - x1) * 0.5;
  625. Ry := Abs(y2 - y1) * 0.5;
  626. // if ClockWise then swap AngleBegin and AngleEnd to simulate it.
  627. if FArcDirection = adClockWise then
  628. begin
  629. AngleCurrent := AngleBegin;
  630. AngleBegin := AngleEnd;
  631. AngleEnd := AngleCurrent;
  632. end;
  633. if (AngleEnd >= AngleBegin) then
  634. begin // if end sup to begin, remove 2*Pi (360°)
  635. AngleEnd := AngleEnd - 2 * Pi;
  636. end;
  637. AngleDiff := Abs(AngleEnd - AngleBegin); // the amount radian to travel
  638. AngleStep := AngleDiff / Round(MaxFloat(Rx, Ry) * 0.1 + 5); // granulity of drawing, not too much, not too less
  639. AngleCurrent := AngleBegin;
  640. StartPrimitive(GL_LINE_STRIP);
  641. while AngleCurrent >= AngleBegin - AngleDiff do
  642. begin
  643. SinCosine(AngleCurrent, s, c);
  644. x := Xc + (Rx * c);
  645. y := Yc + (Ry * s);
  646. gl.Vertex2f(x, y);
  647. AngleCurrent := AngleCurrent - AngleStep; // always step down, rotate only one way to draw it
  648. end;
  649. SinCosine(AngleEnd, s, c);
  650. x := Xc + (Rx * c);
  651. y := Yc + (Ry * s);
  652. gl.Vertex2f(x, y);
  653. StopPrimitive();
  654. if UpdateCurrentPos then
  655. MoveTo(x, y); //FCurrentPos := CurrentPos;
  656. end;
  657. // for internal need
  658. procedure TGLCanvas.NormalizePoint(const x1, y1, x2, y2: Single; const x, y: Single; pX, pY: PSingle);
  659. begin
  660. pX^ := (x - x1) / (x2 - x1) * 2.0 - 1.0;
  661. pY^ := (y - y1) / (y2 - y1) * 2.0 - 1.0;
  662. end;
  663. procedure TGLCanvas.SwapSingle(pX, pY: PSingle);
  664. var
  665. tmp: Single;
  666. begin
  667. tmp := pX^;
  668. pX^ := pY^;
  669. pY^ := tmp;
  670. end;
  671. end.