GXS.Canvas.pas 23 KB

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