GLS.Canvas.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. //
  2. // The graphics engine 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 Stage.Defines.inc}
  12. uses
  13. Winapi.OpenGL,
  14. System.Types,
  15. System.Classes,
  16. System.Math,
  17. Vcl.Graphics,
  18. Stage.OpenGLTokens,
  19. Stage.VectorTypes,
  20. Stage.VectorGeometry,
  21. GLS.Color,
  22. GLS.Context,
  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. implementation //-------------------------------------------------------------
  161. const
  162. cNoPrimitive = MaxInt;
  163. pion2 = pi/2;
  164. pi3on2 = 3*pion2;
  165. // ------------------
  166. // ------------------ TGLCanvas ------------------
  167. // ------------------
  168. constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer;
  169. const baseTransform: TGLMatrix);
  170. var
  171. PM: TGLMatrix;
  172. begin
  173. FBufferSizeX := bufferSizeX;
  174. FBufferSizeY := bufferSizeY;
  175. gl.MatrixMode(GL_PROJECTION);
  176. gl.PushMatrix;
  177. PM := CreateOrthoMatrix(0, bufferSizeX, bufferSizeY, 0, -1, 1);
  178. gl.LoadMatrixf(@PM);
  179. gl.MatrixMode(GL_MODELVIEW);
  180. gl.PushMatrix;
  181. gl.LoadMatrixf(@baseTransform);
  182. BackupOpenGLStates;
  183. FLastPrimitive := cNoPrimitive;
  184. FArcDirection := adCounterClockWise;
  185. end;
  186. constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer);
  187. begin
  188. Create(bufferSizeX, bufferSizeY, IdentityHmgMatrix);
  189. end;
  190. destructor TGLCanvas.Destroy;
  191. begin
  192. StopPrimitive;
  193. gl.MatrixMode(GL_PROJECTION);
  194. gl.PopMatrix;
  195. gl.MatrixMode(GL_MODELVIEW);
  196. gl.PopMatrix;
  197. end;
  198. procedure TGLCanvas.BackupOpenGLStates;
  199. begin
  200. with CurrentGLContext.GLStates do
  201. begin
  202. Disable(stLighting);
  203. Disable(stFog);
  204. Disable(stCullFace);
  205. Disable(stColorMaterial);
  206. Disable(stDepthTest);
  207. Disable(stLineSmooth);
  208. Disable(stLineStipple);
  209. Disable(stPointSmooth);
  210. Enable(stBlend);
  211. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  212. // Setup and backup pen stuff
  213. FPenColor := clBlack;
  214. SetVector(FCurrentPenColorVector, NullHmgPoint);
  215. gl.Color4fv(@FCurrentPenColorVector);
  216. FPenWidth := 1;
  217. LineWidth := 1;
  218. PointSize := 1;
  219. end;
  220. end;
  221. procedure TGLCanvas.StartPrimitive(const primitiveType: Integer);
  222. begin
  223. if primitiveType <> FLastPrimitive then
  224. begin
  225. if FLastPrimitive <> cNoPrimitive then
  226. gl.End_;
  227. if primitiveType <> cNoPrimitive then
  228. gl.Begin_(primitiveType);
  229. FLastPrimitive := primitiveType;
  230. end;
  231. end;
  232. procedure TGLCanvas.StopPrimitive;
  233. begin
  234. StartPrimitive(cNoPrimitive);
  235. end;
  236. procedure TGLCanvas.InvertYAxis;
  237. var
  238. mat: TGLMatrix;
  239. begin
  240. mat := IdentityHmgMatrix;
  241. mat.Y.Y := -1;
  242. mat.W.Y := FBufferSizeY;
  243. gl.MultMatrixf(@mat);
  244. end;
  245. procedure TGLCanvas.SetPenColor(const val: TColor);
  246. begin
  247. SetVector(FCurrentPenColorVector, ConvertWinColor(val,
  248. FCurrentPenColorVector.W));
  249. FPenColor := val;
  250. gl.Color4fv(@FCurrentPenColorVector);
  251. end;
  252. procedure TGLCanvas.SetPenAlpha(const val: Single);
  253. begin
  254. FCurrentPenColorVector.W := val;
  255. gl.Color4fv(@FCurrentPenColorVector);
  256. end;
  257. procedure TGLCanvas.SetPenWidth(const val: Integer);
  258. begin
  259. if val < 1 then
  260. Exit;
  261. if val <> FPenWidth then
  262. with CurrentGLContext.GLStates do
  263. begin
  264. FPenWidth := val;
  265. StopPrimitive;
  266. LineWidth := val;
  267. PointSize := val;
  268. end;
  269. end;
  270. procedure TGLCanvas.MoveTo(const x, y: Integer);
  271. begin
  272. FCurrentPos.X := x;
  273. FCurrentPos.Y := y;
  274. end;
  275. procedure TGLCanvas.MoveTo(const x, y: Single);
  276. begin
  277. FCurrentPos.X := x;
  278. FCurrentPos.Y := y;
  279. end;
  280. procedure TGLCanvas.MoveToRel(const x, y: Integer);
  281. begin
  282. FCurrentPos.X := FCurrentPos.X + x;
  283. FCurrentPos.Y := FCurrentPos.Y + y;
  284. end;
  285. procedure TGLCanvas.MoveToRel(const x, y: Single);
  286. begin
  287. FCurrentPos.X := FCurrentPos.X + x;
  288. FCurrentPos.Y := FCurrentPos.Y + y;
  289. end;
  290. procedure TGLCanvas.LineTo(const x, y: Integer);
  291. begin
  292. StartPrimitive(GL_LINES);
  293. gl.Vertex2fv(@FCurrentPos);
  294. MoveTo(x, y);
  295. gl.Vertex2fv(@FCurrentPos);
  296. end;
  297. procedure TGLCanvas.LineTo(const x, y: Single);
  298. begin
  299. StartPrimitive(GL_LINES);
  300. gl.Vertex2fv(@FCurrentPos);
  301. MoveTo(x, y);
  302. gl.Vertex2fv(@FCurrentPos);
  303. end;
  304. procedure TGLCanvas.LineToRel(const x, y: Integer);
  305. begin
  306. LineTo(FCurrentPos.X + x, FCurrentPos.Y + y);
  307. end;
  308. procedure TGLCanvas.LineToRel(const x, y: Single);
  309. begin
  310. LineTo(FCurrentPos.X + x, FCurrentPos.Y + y);
  311. end;
  312. procedure TGLCanvas.Line(const x1, y1, x2, y2: Integer);
  313. begin
  314. StartPrimitive(GL_LINES);
  315. gl.Vertex2i(x1, y1);
  316. gl.Vertex2i(x2, y2);
  317. end;
  318. procedure TGLCanvas.Line(const x1, y1, x2, y2: Single);
  319. begin
  320. StartPrimitive(GL_LINES);
  321. gl.Vertex2f(x1, y1);
  322. gl.Vertex2f(x2, y2);
  323. end;
  324. procedure TGLCanvas.Polyline(const points: array of TPoint);
  325. var
  326. i, n: Integer;
  327. begin
  328. n := Length(Points);
  329. if n > 1 then
  330. begin
  331. StartPrimitive(GL_LINE_STRIP);
  332. gl.Vertex2iv(@points[Low(points)]);
  333. for i := Low(points) + 1 to High(points) do
  334. gl.Vertex2iv(@points[i]);
  335. StopPrimitive;
  336. end;
  337. end;
  338. procedure TGLCanvas.Polygon(const points: array of TPoint);
  339. var
  340. i, n: Integer;
  341. begin
  342. n := Length(Points);
  343. if n > 1 then
  344. begin
  345. StartPrimitive(GL_LINE_LOOP);
  346. gl.Vertex2iv(@points[Low(points)]);
  347. for i := Low(points) + 1 to High(points) do
  348. gl.Vertex2iv(@points[i]);
  349. StopPrimitive;
  350. end;
  351. end;
  352. procedure TGLCanvas.PlotPixel(const x, y: Integer);
  353. begin
  354. StartPrimitive(GL_POINTS);
  355. gl.Vertex2i(x, y);
  356. end;
  357. procedure TGLCanvas.PlotPixel(const x, y: Single);
  358. begin
  359. StartPrimitive(GL_POINTS);
  360. gl.Vertex2f(x, y);
  361. end;
  362. procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Integer);
  363. begin
  364. StartPrimitive(GL_LINE_LOOP);
  365. gl.Vertex2i(x1, y1);
  366. gl.Vertex2i(x2, y1);
  367. gl.Vertex2i(x2, y2);
  368. gl.Vertex2i(x1, y2);
  369. StopPrimitive;
  370. end;
  371. procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Single);
  372. begin
  373. StartPrimitive(GL_LINE_LOOP);
  374. gl.Vertex2f(x1, y1);
  375. gl.Vertex2f(x2, y1);
  376. gl.Vertex2f(x2, y2);
  377. gl.Vertex2f(x1, y2);
  378. StopPrimitive;
  379. end;
  380. function TGLCanvas.GetPenAlpha: Single;
  381. begin
  382. Result := FCurrentPenColorVector.W;
  383. end;
  384. procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Integer);
  385. begin
  386. StartPrimitive(GL_QUADS);
  387. gl.Vertex2i(x1, y1);
  388. gl.Vertex2i(x2, y1);
  389. gl.Vertex2i(x2, y2);
  390. gl.Vertex2i(x1, y2);
  391. StopPrimitive;
  392. end;
  393. procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Single);
  394. begin
  395. StartPrimitive(GL_QUADS);
  396. gl.Vertex2f(x1, y1);
  397. gl.Vertex2f(x2, y1);
  398. gl.Vertex2f(x2, y2);
  399. gl.Vertex2f(x1, y2);
  400. StopPrimitive;
  401. end;
  402. procedure TGLCanvas.EllipseVertices(x, y, xRadius, yRadius: Single);
  403. var
  404. i, n: Integer;
  405. s, c: TSingleArray;
  406. begin
  407. n := Round(MaxFloat(xRadius, yRadius) * 0.1) + 5;
  408. SetLength(s, n);
  409. SetLength(c, n);
  410. Dec(n);
  411. PrepareSinCosCache(s, c, 0, 90);
  412. ScaleFloatArray(s, yRadius);
  413. ScaleFloatArray(c, xRadius);
  414. // first quadrant (top right)
  415. for i := 0 to n do
  416. gl.Vertex2f(x + c[i], y - s[i]);
  417. // second quadrant (top left)
  418. for i := n - 1 downto 0 do
  419. gl.Vertex2f(x - c[i], y - s[i]);
  420. // third quadrant (bottom left)
  421. for i := 1 to n do
  422. gl.Vertex2f(x - c[i], y + s[i]);
  423. // fourth quadrant (bottom right)
  424. for i := n - 1 downto 0 do
  425. gl.Vertex2f(x + c[i], y + s[i]);
  426. end;
  427. procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Integer);
  428. begin
  429. Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
  430. 0.5);
  431. end;
  432. procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Single);
  433. begin
  434. Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
  435. 0.5);
  436. end;
  437. procedure TGLCanvas.Ellipse(const x, y: Single; const Radius: Single);
  438. begin
  439. Ellipse(x, y, Radius, Radius);
  440. end;
  441. procedure TGLCanvas.Ellipse(const x, y: Integer; const xRadius, yRadius:
  442. Single);
  443. var
  444. sx, sy: Single;
  445. begin
  446. sx := x;
  447. sy := y;
  448. Ellipse(sx, sy, xRadius, yRadius);
  449. end;
  450. procedure TGLCanvas.Ellipse(const x, y: Single; const xRadius, yRadius: Single);
  451. begin
  452. StartPrimitive(GL_LINE_STRIP);
  453. EllipseVertices(x, y, xRadius, yRadius);
  454. StopPrimitive;
  455. end;
  456. procedure TGLCanvas.FillEllipse(const x, y: Integer; const xRadius, yRadius:
  457. Single);
  458. begin
  459. StartPrimitive(GL_TRIANGLE_FAN);
  460. gl.Vertex2f(x, y); // not really necessary, but may help with memory stride
  461. EllipseVertices(x, y, xRadius, yRadius);
  462. StopPrimitive;
  463. end;
  464. procedure TGLCanvas.FillEllipse(const x, y, xRadius, yRadius: Single);
  465. begin
  466. StartPrimitive(GL_TRIANGLE_FAN);
  467. gl.Vertex2f(x, y); // not really necessary, but may help with memory stride
  468. EllipseVertices(x, y, xRadius, yRadius);
  469. StopPrimitive;
  470. end;
  471. procedure TGLCanvas.FillEllipse(const x, y, Radius: Single);
  472. begin
  473. FillEllipse(x, y, Radius, Radius);
  474. end;
  475. procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Single;
  476. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector);
  477. begin
  478. StartPrimitive(GL_QUADS);
  479. gl.Color4f(x1y1Color.X, x1y1Color.Y, x1y1Color.Z, x1y1Color.W);
  480. gl.Vertex2f(x1, y1);
  481. gl.Color4f(x2y1Color.X, x2y1Color.Y, x2y1Color.Z, x2y1Color.W);
  482. gl.Vertex2f(x2, y1);
  483. gl.Color4f(x2y2Color.X, x2y2Color.Y, x2y2Color.Z, x2y2Color.W);
  484. gl.Vertex2f(x2, y2);
  485. gl.Color4f(x1y2Color.X, x1y2Color.Y, x1y2Color.Z, x1y2Color.W);
  486. gl.Vertex2f(x1, y2);
  487. StopPrimitive;
  488. // restore pen color
  489. gl.Color4fv(@FCurrentPenColorVector);
  490. end;
  491. procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Integer;
  492. const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TGLColorVector);
  493. begin
  494. StartPrimitive(GL_QUADS);
  495. gl.Color4f(x1y1Color.X, x1y1Color.Y, x1y1Color.Z, x1y1Color.W);
  496. gl.Vertex2i(x1, y1);
  497. gl.Color4f(x2y1Color.X, x2y1Color.Y, x2y1Color.Z, x2y1Color.W);
  498. gl.Vertex2i(x2, y1);
  499. gl.Color4f(x2y2Color.X, x2y2Color.Y, x2y2Color.Z, x2y2Color.W);
  500. gl.Vertex2i(x2, y2);
  501. gl.Color4f(x1y2Color.X, x1y2Color.Y, x1y2Color.Z, x1y2Color.W);
  502. gl.Vertex2i(x1, y2);
  503. StopPrimitive;
  504. // restore pen color
  505. gl.Color4fv(@FCurrentPenColorVector);
  506. end;
  507. procedure TGLCanvas.FillEllipseGradient(const x, y: Integer; const xRadius, yRadius: Integer; const edgeColor: TGLColorVector);
  508. begin
  509. StartPrimitive(GL_TRIANGLE_FAN);
  510. // the center will use the last set PenColor and PenAlpha
  511. gl.Vertex2f(x, y); // really necessary now :)
  512. // then OpenGL will do a gradient from the center to the edge using the edgeColor
  513. gl.Color4f(edgeColor.X, edgeColor.Y, edgeColor.Z, edgeColor.W);
  514. EllipseVertices(x, y, xRadius, yRadius);
  515. StopPrimitive;
  516. // restore pen color
  517. gl.Color4fv(@FCurrentPenColorVector);
  518. end;
  519. procedure TGLCanvas.FillEllipseGradient(const x, y, xRadius, yRadius: Single; const edgeColor: TGLColorVector);
  520. begin
  521. StartPrimitive(GL_TRIANGLE_FAN);
  522. gl.Vertex2f(x, y); // really necessary now :)
  523. gl.Color4f(edgeColor.X, edgeColor.Y, edgeColor.Z, edgeColor.W);
  524. EllipseVertices(x, y, xRadius, yRadius);
  525. StopPrimitive;
  526. // restore pen color
  527. gl.Color4fv(@FCurrentPenColorVector);
  528. end;
  529. procedure TGLCanvas.FillEllipseGradient(const x, y, Radius: Single; const edgeColor: TGLColorVector);
  530. begin
  531. FillEllipseGradient(x, y, Radius, Radius, edgeColor);
  532. end;
  533. procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
  534. begin
  535. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
  536. end;
  537. procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
  538. begin
  539. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
  540. end;
  541. procedure TGLCanvas.Arc(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
  542. begin
  543. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, False);
  544. end;
  545. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
  546. begin
  547. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
  548. end;
  549. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
  550. begin
  551. DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
  552. end;
  553. procedure TGLCanvas.ArcTo(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
  554. begin
  555. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, True);
  556. end;
  557. procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Integer);
  558. var
  559. x2r, y2r, x, y: integer;
  560. begin
  561. x2r := 2*xr;
  562. y2r := 2*yr;
  563. x := x1 -1;
  564. y := y2 +1;
  565. Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
  566. Line(x1, y1 + yr, x1, y - yr);
  567. Arc(x, y, x + x2r, y - y2r, pi, pion2);
  568. Line(x + xr, y2, x2 - xr, y2);
  569. Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
  570. Line(x2, y1 + yr, x2, y - yr);
  571. Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
  572. Line(x + xr, y1, x2 - xr, y1);
  573. end;
  574. procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Single);
  575. var
  576. x2r, y2r, x, y: Single;
  577. begin
  578. x2r := 2*xr;
  579. y2r := 2*yr;
  580. x := x1 -1;
  581. y := y2 +1;
  582. Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
  583. Line(x1, y1 + yr, x1, y - yr);
  584. Arc(x, y, x + x2r, y - y2r, pi, pion2);
  585. Line(x + xr, y2, x2 - xr, y2);
  586. Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
  587. Line(x2, y1 + yr, x2, y - yr);
  588. Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
  589. Line(x + xr, y1, x2 - xr, y1);
  590. end;
  591. // wrapper from "ByPoints" method
  592. procedure TGLCanvas.DrawArc(x1, y1, x2, y2, x3, y3, x4, y4: Single; UpdateCurrentPos: Boolean);
  593. var
  594. x, y: Single;
  595. AngleBegin, AngleEnd: Single;
  596. begin
  597. if x1 > x2 then
  598. SwapSingle(@x1, @x2);
  599. if y1 > y2 then
  600. SwapSingle(@y1, @y2);
  601. NormalizePoint(x1, y1, x2, y2, x3, y3, @x, @y);
  602. AngleBegin := ArcTan2(y, x);
  603. NormalizePoint(x1, y1, x2, y2, x4, y4, @x, @y);
  604. AngleEnd := ArcTan2(y, x);
  605. DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, UpdateCurrentPos);
  606. end;
  607. // Real work is here
  608. procedure TGLCanvas.DrawArc(x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single; UpdateCurrentPos: Boolean);
  609. var
  610. Xc, Yc, Rx, Ry, x, y, s, c: Single;
  611. AngleCurrent, AngleDiff, AngleStep: Single;
  612. begin
  613. // check that our box is well set (as the original Arc function do)
  614. if x1 > x2 then
  615. SwapSingle(@x1, @x2);
  616. if y1 > y2 then
  617. SwapSingle(@y1, @y2);
  618. if (x1 = x2) or (y1 = y2) then
  619. exit;
  620. Xc := (x1 + x2) * 0.5;
  621. Yc := (y1 + y2) * 0.5;
  622. Rx := Abs(x2 - x1) * 0.5;
  623. Ry := Abs(y2 - y1) * 0.5;
  624. // if ClockWise then swap AngleBegin and AngleEnd to simulate it.
  625. if FArcDirection = adClockWise then
  626. begin
  627. AngleCurrent := AngleBegin;
  628. AngleBegin := AngleEnd;
  629. AngleEnd := AngleCurrent;
  630. end;
  631. if (AngleEnd >= AngleBegin) then
  632. begin // if end sup to begin, remove 2*Pi (360°)
  633. AngleEnd := AngleEnd - 2 * Pi;
  634. end;
  635. AngleDiff := Abs(AngleEnd - AngleBegin); // the amount radian to travel
  636. AngleStep := AngleDiff / Round(MaxFloat(Rx, Ry) * 0.1 + 5); // granulity of drawing, not too much, not too less
  637. AngleCurrent := AngleBegin;
  638. StartPrimitive(GL_LINE_STRIP);
  639. while AngleCurrent >= AngleBegin - AngleDiff do
  640. begin
  641. SinCosine(AngleCurrent, s, c);
  642. x := Xc + (Rx * c);
  643. y := Yc + (Ry * s);
  644. gl.Vertex2f(x, y);
  645. AngleCurrent := AngleCurrent - AngleStep; // always step down, rotate only one way to draw it
  646. end;
  647. SinCosine(AngleEnd, s, c);
  648. x := Xc + (Rx * c);
  649. y := Yc + (Ry * s);
  650. gl.Vertex2f(x, y);
  651. StopPrimitive();
  652. if UpdateCurrentPos then
  653. MoveTo(x, y); //FCurrentPos := CurrentPos;
  654. end;
  655. // for internal need
  656. procedure TGLCanvas.NormalizePoint(const x1, y1, x2, y2: Single; const x, y: Single; pX, pY: PSingle);
  657. begin
  658. pX^ := (x - x1) / (x2 - x1) * 2.0 - 1.0;
  659. pY^ := (y - y1) / (y2 - y1) * 2.0 - 1.0;
  660. end;
  661. procedure TGLCanvas.SwapSingle(pX, pY: PSingle);
  662. var
  663. tmp: Single;
  664. begin
  665. tmp := pX^;
  666. pX^ := pY^;
  667. pY^ := tmp;
  668. end;
  669. //---------------------------------------------------------------------------
  670. end.