WMGraphics.Mod 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. MODULE WMGraphics; (** AUTHOR "TF"; PURPOSE "Generic Graphic Support"; *)
  2. IMPORT
  3. Kernel, Rectangles := WMRectangles, Raster, KernelLog, UTF8Strings, Strings, RasterScale := WMRasterScale,
  4. Codecs, Files, Streams;
  5. CONST
  6. (** Copy Modes *)
  7. ModeCopy* = RasterScale.ModeCopy; ModeSrcOverDst* = RasterScale.ModeSrcOverDst;
  8. (** Scale Modes *)
  9. ScaleBox* = RasterScale.ScaleBox; ScaleBilinear* = RasterScale.ScaleBilinear;
  10. (** Clip Modes *)
  11. ClipNone* = 0; ClipRect* = 1; (*ClipStencil* = 2;*)
  12. (** FontStyles *)
  13. FontBold* = 0; FontItalic* = 1;
  14. Black* = 0FFH; White* = LONGINT(0FFFFFFFFH); Transparent*=0H;
  15. Gray*=LONGINT(0777777FFH);
  16. Red* = LONGINT(0FF0000FFH);
  17. DarkRed* = LONGINT(08C0000FFH);
  18. Green* = 000FF00FFH; Blue* = 0FFFFH;
  19. Yellow* = LONGINT(0FFFF00FFH);
  20. Magenta* = LONGINT(0FF00FFFFH);
  21. Cyan* = 00FFFFFFH;
  22. Gold* = LONGINT(0FFD700FFH);
  23. TYPE
  24. Real* = REAL;
  25. Char32 = LONGINT;
  26. Point2d* = RECORD x*, y* : LONGINT END;
  27. Image* = OBJECT(Raster.Image)
  28. VAR
  29. key* : POINTER TO ARRAY OF CHAR;
  30. END Image;
  31. Rectangle* = Rectangles.Rectangle;
  32. Color* = LONGINT;
  33. GlyphSpacings* = RECORD
  34. bearing* : Rectangle;
  35. width*, height*, ascent*, descent* : LONGINT;
  36. dx*, dy* : LONGINT; (** Delta position where the bitmap returned by GetGlyphMap has to be placed relatively to
  37. x, y on the base line *)
  38. END;
  39. (* Bearings are the blank spaces left an right of a character.
  40. bearing.l is the left, bearing.r is the right, bearing.t top and bearing.b the bottom side - bearing of the character
  41. hadvance = bearing.l + width + bearing.r --> the distance to the next character on the line without --> kerning
  42. vadvance = bearing.t + height + bearing.b --> the baseline to baseline distance of two lines of this font
  43. When rendering a character at the position (x, y), y refers to the y position of the baseline, x refers to .
  44. --> Kerning pairs
  45. *)
  46. (* ascent is the height of the font above the base line in units of the destination canvas *)
  47. (* descent is the height of the font below the base line in units of the destination canvas *)
  48. (* basetobasedist is the suggested distance between two lines of this font *)
  49. Font* = OBJECT
  50. VAR
  51. ascent*, descent* : LONGINT;
  52. name* : ARRAY 256 OF CHAR;
  53. size* : LONGINT;
  54. style* : SET;
  55. PROCEDURE &Init*;
  56. END Init;
  57. PROCEDURE GetHeight*():LONGINT;
  58. BEGIN
  59. RETURN ascent + descent
  60. END GetHeight;
  61. PROCEDURE GetAscent*():LONGINT;
  62. BEGIN
  63. RETURN ascent
  64. END GetAscent;
  65. PROCEDURE GetDescent*():LONGINT;
  66. BEGIN
  67. RETURN descent
  68. END GetDescent;
  69. (* return TRUE if the font can render the character *)
  70. PROCEDURE HasChar*(char : Char32) : BOOLEAN;
  71. BEGIN
  72. RETURN FALSE
  73. END HasChar;
  74. (** Render an UTF8 string to a canvas *)
  75. PROCEDURE RenderString*(canvas : Canvas ; x, y : Real; CONST text : ARRAY OF CHAR);
  76. VAR i, len, code : LONGINT; g : GlyphSpacings;
  77. BEGIN
  78. len := LEN(text); i := 0;
  79. WHILE (i < len) & (text[i] # 0X) DO
  80. IF UTF8Strings.DecodeChar(text, i, code) THEN
  81. IF HasChar(code) THEN
  82. GetGlyphSpacings(code, g);
  83. RenderChar(canvas, x, y, code)
  84. ELSE
  85. FBGetGlyphSpacings(code, g);
  86. FBRenderChar(canvas, x, y, code)
  87. END;
  88. x := x + g.bearing.l + g.width + g.bearing.r
  89. ELSE INC(i) (* avoid endless loop *)
  90. END
  91. END
  92. END RenderString;
  93. (** Render an UTF8 string to a canvas *)
  94. PROCEDURE GetStringSize*(CONST text : ARRAY OF CHAR; VAR dx, dy : LONGINT);
  95. VAR i, len, code : LONGINT; g : GlyphSpacings;
  96. BEGIN
  97. len := LEN(text); i := 0; dx := 0; dy := GetHeight();
  98. WHILE (i < len) & (text[i] # 0X) DO
  99. IF UTF8Strings.DecodeChar(text, i, code) THEN
  100. IF HasChar(code) THEN GetGlyphSpacings(code, g);
  101. ELSE FBGetGlyphSpacings(code, g)
  102. END;
  103. dy := MAX(dy, g.height);
  104. dx := dx + g.bearing.l + g.width + g.bearing.r
  105. ELSE INC(i) (* avoid endless loop *)
  106. END
  107. END
  108. END GetStringSize;
  109. (** Render character char to canvas at x, y (baseline) *)
  110. PROCEDURE RenderChar*(canvas : Canvas ; x, y : Real; char : Char32);
  111. VAR g : GlyphSpacings; img : Image;
  112. BEGIN
  113. GetGlyphSpacings(char, g);
  114. GetGlyphMap(char, img);
  115. canvas.DrawImage(ENTIER(x + g.bearing.l) + g.dx, ENTIER(y - ascent) + g.dy, img, ModeSrcOverDst)
  116. END RenderChar;
  117. (** return a bitmap of character code *)
  118. PROCEDURE GetGlyphMap*(code : LONGINT; VAR map : Image);
  119. END GetGlyphMap;
  120. (** return spacing of character code *)
  121. PROCEDURE GetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : GlyphSpacings);
  122. END GetGlyphSpacings;
  123. END Font;
  124. FontManager* = OBJECT
  125. PROCEDURE GetFont*(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : Font;
  126. BEGIN
  127. RETURN NIL
  128. END GetFont;
  129. END FontManager;
  130. CanvasState* = RECORD
  131. clipMode : SET;
  132. clipRect : Rectangle;
  133. limits : Rectangle;
  134. dx, dy : LONGINT;
  135. font : Font;
  136. color : Color;
  137. END;
  138. Canvas* = OBJECT
  139. VAR
  140. limits*, (* The limits to which the clip Rect can be set *)
  141. clipRect* : Rectangle; (* The current clip rectangle *)
  142. dx*, dy* : LONGINT;
  143. color* : Color;
  144. clipMode* : SET;
  145. generator*: Strings.String;
  146. font- : Font;
  147. (** IF cs is NIL a new canvas state object is created for this canvas, otherwise cs is reused *)
  148. PROCEDURE SaveState*(VAR cs : CanvasState);
  149. BEGIN
  150. cs.clipMode := clipMode;
  151. cs.limits := limits;
  152. cs.dx := dx; cs.dy := dy;
  153. cs.font := font; cs.color := color;
  154. GetClipRect(cs.clipRect)
  155. END SaveState;
  156. (** Restore a previously saved canvas state *)
  157. PROCEDURE RestoreState*(CONST cs : CanvasState);
  158. BEGIN
  159. clipMode := cs.clipMode;
  160. limits := cs.limits;
  161. dx := cs.dx; dy := cs.dy;
  162. font := cs.font; color := cs.color;
  163. SetClipRect(cs.clipRect)
  164. END RestoreState;
  165. (** set the current clipping rectangle as the limit for new SetClipRect operations.
  166. ddx and ddy specify a coordinate shift. *)
  167. PROCEDURE ClipRectAsNewLimits*(ddx, ddy : LONGINT);
  168. BEGIN
  169. limits := clipRect;
  170. SetDelta(dx + ddx, dy + ddy)
  171. END ClipRectAsNewLimits;
  172. (** in user coordinates *)
  173. PROCEDURE SetClipRect*(rect : Rectangle);
  174. BEGIN
  175. INCL(clipMode, ClipRect);
  176. rect.r := Max(rect.r, rect.l); rect.b := Max(rect.b, rect.t);
  177. Rectangles.MoveRel(rect, dx, dy);
  178. Rectangles.ClipRect(rect, limits);
  179. clipRect := rect
  180. END SetClipRect;
  181. (** return the current Clipping rectangle in user coordinates; Clients may use this to avoid drawing that is
  182. clipped away for sure *)
  183. PROCEDURE GetClipRect*(VAR rect : Rectangle);
  184. BEGIN
  185. rect := clipRect;
  186. Rectangles.MoveRel(rect, -dx, -dy)
  187. END GetClipRect;
  188. (** *)
  189. PROCEDURE SetClipMode*(mode : SET);
  190. BEGIN
  191. clipMode := mode
  192. END SetClipMode;
  193. (** Set color for fonts *)
  194. PROCEDURE SetColor*(x : Color);
  195. BEGIN
  196. color := x
  197. END SetColor;
  198. PROCEDURE GetColor*() : Color;
  199. BEGIN
  200. RETURN color;
  201. END GetColor;
  202. (** Set the current font. IF f is NIL, GetFont will search for the system default font. *)
  203. PROCEDURE SetFont*(f: Font);
  204. BEGIN
  205. font := f
  206. END SetFont;
  207. (** Return the font currently set for this canvas. If no font is set, return the system default font. If no
  208. system default font is set, block until a default font is set *)
  209. PROCEDURE GetFont*():Font;
  210. BEGIN
  211. IF font = NIL THEN font := GetDefaultFont() END;
  212. RETURN font
  213. END GetFont;
  214. (** Draw an UTF8 String at position x, y (base line)
  215. The currently set font and color is used
  216. *)
  217. PROCEDURE DrawString*(x, y: LONGINT; CONST text : ARRAY OF CHAR);
  218. BEGIN
  219. IF font # NIL THEN
  220. font.RenderString(SELF, x, y, text)
  221. END
  222. END DrawString;
  223. PROCEDURE SetLineWidth*(w : Real);
  224. BEGIN
  225. (* Dummy. But is implemented in WMGraphicsGfx *)
  226. END SetLineWidth;
  227. (** draw a line within the current clipping rectangle *)
  228. (** Override for improved speed *)
  229. PROCEDURE Line*(x0, y0, x1, y1 : LONGINT; color : Color; mode : LONGINT);
  230. VAR t, xi, mi, xf, mf, dt2, ds2 : LONGINT; r: Rectangles.Rectangle;
  231. (* clipped bresenham algorithm according to
  232. Bresenham's Line Generation Algorithm with Built-in Clipping, Yevgeny P. Kuzmin, 1995
  233. *)
  234. PROCEDURE ClippedLine(x0,y0,x1,y1: LONGINT; r: Rectangle; color : Color; mode : LONGINT);
  235. VAR
  236. dsx,dsy,stx,sty,xd,yd,dx2,dy2,rem,term,e: LONGINT;
  237. tmp: HUGEINT;
  238. rev,setx: BOOLEAN;
  239. BEGIN
  240. (* standardization && trivial reject *)
  241. IF( x1 - x0 > 0 ) THEN
  242. IF ( x0 > r.r) OR (x1 < r.l ) THEN RETURN END;
  243. stx := 1;
  244. ELSE
  245. IF ( x1 > r.r) OR ( x0 < r.l ) THEN RETURN END;
  246. stx := -1;
  247. x0 :=-x0; x1 :=-x1;
  248. r.l :=-r.l; r.r :=-r.r;
  249. Swap(r.l,r.r);
  250. END;
  251. IF ( y1 - y0 > 0 ) THEN
  252. IF ( y0 > r.b) OR (y1 < r.t ) THEN RETURN END;
  253. sty:=1;
  254. ELSE
  255. IF ( y1 > r.b) OR (y0 < r.t ) THEN RETURN END;
  256. sty :=-1;
  257. y0 :=-y0; y1 :=-y1;
  258. r.t :=-r.t; r.b :=-r.b;
  259. Swap(r.t,r.b);
  260. END;
  261. dsx := x1-x0; dsy := y1-y0;
  262. IF ( dsx < dsy ) THEN
  263. rev := TRUE;
  264. Swap(x0,y0); Swap(x1,y1); Swap(dsx,dsy);
  265. Swap(r.l,r.t); Swap(r.r,r.b); Swap(stx,sty);
  266. ELSE
  267. rev := FALSE;
  268. END;
  269. (* Bresenham's set up *)
  270. dx2 := 2*dsx; dy2 := 2*dsy;
  271. xd := x0; yd :=y0;
  272. e := 2*dsy-dsx; term := x1;
  273. setx := TRUE;
  274. IF (y0 < r.t) THEN
  275. (* window horizontal entry *)
  276. tmp := HUGEINT(dx2) *(r.t-y0)-dsx;
  277. INC(xd,LONGINT(tmp DIV dy2));
  278. rem := LONGINT(tmp MOD dy2);
  279. IF ( xd>r.r ) THEN RETURN END;
  280. IF ( xd+1>=r.l ) THEN
  281. yd := r.t; DEC(e,rem+dsx);
  282. IF (rem>0 ) THEN INC(xd); INC(e,dy2) END;
  283. setx := FALSE;
  284. END;
  285. END;
  286. IF setx & ( x0 < r.l ) THEN
  287. (* window vertical entry *)
  288. tmp := HUGEINT(dy2) * (r.l-x0);
  289. INC(yd, LONGINT(tmp DIV dx2));
  290. rem := LONGINT(tmp MOD dx2);
  291. IF ( yd>r.b) OR (yd=r.b) & (rem>=dsx) THEN RETURN END;
  292. xd :=r.l; INC(e,rem);
  293. IF( rem>=dsx ) THEN INC(yd); DEC(e,dx2) END;
  294. END;
  295. IF ( y1 > r.b ) THEN
  296. (* window exit *)
  297. tmp := HUGEINT(dx2)*(r.b-y0)+dsx;
  298. term := x0+LONGINT(tmp DIV dy2);
  299. rem := LONGINT(tmp MOD dy2);
  300. IF ( rem=0 ) THEN DEC(term) END;
  301. END;
  302. IF ( term>r.r) THEN term := r.r; END;
  303. INC(term);
  304. IF ( sty =-1 ) THEN yd := -yd END;
  305. (* reverse transformation *)
  306. IF ( stx =-1 ) THEN xd := -xd; term := -term; END;
  307. DEC(dx2,dy2);
  308. (* Bresenham's line drawing *)
  309. IF rev THEN
  310. WHILE ( xd # term ) DO
  311. SetPixel(yd, xd, color, mode);
  312. IF ( e >= 0 ) THEN INC(xd, stx); INC(yd, sty); DEC(e,dx2)
  313. ELSE INC(xd, stx); INC(e, dy2);
  314. END;
  315. END;
  316. ELSE
  317. WHILE ( xd # term ) DO (* Bresenham's line drawing *)
  318. SetPixel(xd,yd, color, mode);
  319. IF ( e >= 0 ) THEN INC(xd, stx); INC(yd, sty); DEC(e,dx2)
  320. ELSE INC(xd, stx); INC(e, dy2);
  321. END;
  322. END;
  323. END;
  324. END ClippedLine;
  325. BEGIN
  326. IF y0 = y1 THEN (* horizontal case *)
  327. IF x0 > x1 THEN t := x0; x0 := x1; x1 := t END;
  328. Fill(Rectangles.MakeRect(x0, y0, x1 + 1, y0 + 1), color, mode)
  329. ELSIF x0 = x1 THEN (* vertical case *)
  330. IF y0 > y1 THEN t := y0; y0 := y1; y1 := t END;
  331. Fill(Rectangles.MakeRect(x0, y0, x0 + 1, y1 + 1), color, mode)
  332. ELSE (* general case *)
  333. GetClipRect(r);
  334. ClippedLine(x0,y0,x1,y1,r,color,mode);
  335. (*
  336. IF ABS(y1 - y0) > ABS(x1 - x0) THEN
  337. IF y0 > y1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
  338. xi := x0; xf := y0 - y1; mi := (x1 - x0) DIV (y1 - y0); mf := 2 * ( (x1 - x0) MOD (y1 - y0)); dt2 := 2 * (y1 - y0);
  339. GetClipRect(r);
  340. IF y0 < r.t THEN
  341. (* this loop should be replaced by a closed expression *)
  342. FOR t := y0 TO r.t-1 DO
  343. INC(xi, mi); INC(xf, mf);
  344. IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
  345. END;
  346. y0 := r.t;
  347. END;
  348. IF y1 > r.b THEN
  349. y1 := r.b;
  350. END;
  351. FOR t := y0 TO y1 DO
  352. SetPixel(xi, t, color, mode);
  353. INC(xi, mi); INC(xf, mf);
  354. IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
  355. END
  356. ELSE
  357. IF x0 > x1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
  358. xi := y0; xf := x0 - x1; mi := (y1 - y0) DIV (x1 - x0); mf := 2 * ( (y1 - y0) MOD (x1 - x0)); dt2 := 2 * (x1 - x0);
  359. GetClipRect(r);
  360. IF x0 < r.l THEN
  361. (* this loop should be replaced by a closed expression *)
  362. FOR t := x0 TO r.l-1 DO
  363. INC(xi, mi); INC(xf, mf);
  364. IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
  365. END;
  366. x0 := r.l;
  367. END;
  368. IF x1 > r.r THEN
  369. x1 := r.r;
  370. END;
  371. FOR t := x0 TO x1 DO
  372. SetPixel(t, xi, color, mode);
  373. INC(xi, mi); INC(xf, mf);
  374. IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
  375. END
  376. END
  377. *)
  378. END
  379. END Line;
  380. PROCEDURE LineReal*(x0, y0, x1, y1 : Real; color : Color; mode : LONGINT);
  381. BEGIN
  382. Line(ENTIER(x0),ENTIER(y0),ENTIER(x1),ENTIER(y1),color,mode);
  383. END LineReal;
  384. (** set a pixel within the current clipping rectangle *)
  385. PROCEDURE SetPixel*(x, y : LONGINT; color : Color; mode : LONGINT);
  386. BEGIN
  387. Fill(MakeRectangle(x, y, x + 1, y + 1), color, mode)
  388. END SetPixel;
  389. (** fill a rectangle within the current clipping rectangle *)
  390. PROCEDURE Fill*(rect : Rectangle; color : Color; mode : LONGINT);
  391. END Fill;
  392. (** fill a polygon given by points *)
  393. PROCEDURE FillPolygonFlat*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; color : Color; mode : LONGINT);
  394. END FillPolygonFlat;
  395. PROCEDURE FillPolygonCB*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; callBack : FillLineCallBack);
  396. END FillPolygonCB;
  397. PROCEDURE PolyLine*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; closed : BOOLEAN; color : Color; mode : LONGINT);
  398. VAR i : LONGINT;
  399. BEGIN
  400. FOR i := 1 TO nofPoints - 1 DO
  401. Line(points[i-1].x, points[i-1].y, points[i].x, points[i].y, color, mode)
  402. END;
  403. IF closed THEN
  404. Line(points[nofPoints-1].x, points[nofPoints-1].y, points[0].x, points[0].y, color, mode)
  405. END
  406. END PolyLine;
  407. (** draw an image within the current clipping rectangle *)
  408. PROCEDURE DrawImage*(x, y: LONGINT; image: Raster.Image; mode : LONGINT);
  409. END DrawImage;
  410. PROCEDURE ScaleImage*(src : Raster.Image; sr, dr : Rectangle; copyMode, scaleMode : LONGINT);
  411. END ScaleImage;
  412. (** Set coordinate shift *)
  413. PROCEDURE SetDelta*(dx, dy: LONGINT);
  414. BEGIN
  415. SELF.dx := dx; SELF.dy := dy
  416. END SetDelta;
  417. (** Set the available range in the super drawing space *)
  418. PROCEDURE SetLimits*(r : Rectangle);
  419. BEGIN
  420. limits := r
  421. END SetLimits;
  422. (** Get the avalilable range in the super drawing space, like the range set but clipped *)
  423. PROCEDURE GetLimits*(): Rectangle;
  424. BEGIN
  425. RETURN limits
  426. END GetLimits;
  427. END Canvas;
  428. TYPE
  429. FillPosEntry = RECORD pos, next : LONGINT END;
  430. FillHeap = POINTER TO ARRAY OF FillPosEntry;
  431. FillLineCallBack* = PROCEDURE {DELEGATE} (canvas : Canvas; y, x0, x1 : LONGINT);
  432. CanvasGenerator* = PROCEDURE(img:Raster.Image):BufferCanvas;
  433. TYPE
  434. BufferCanvas* = OBJECT(Canvas)
  435. VAR img- : Raster.Image;
  436. bounds : Rectangle; (* real limiting img bounds *)
  437. (* filling *)
  438. fillHeap : FillHeap;
  439. heapSize, topHeap : LONGINT;
  440. height : LONGINT;
  441. edges : POINTER TO ARRAY OF LONGINT;
  442. PROCEDURE &New*(img : Raster.Image);
  443. BEGIN
  444. SELF.img := img;
  445. bounds := MakeRectangle(0, 0, img.width, img.height);
  446. SetLimits(bounds);
  447. clipRect := bounds;
  448. clipMode := { ClipRect };
  449. (* filling *)
  450. height := img.height; NEW(edges, height);
  451. SetFont(GetDefaultFont());
  452. generator:=Strings.NewString("WMGraphics.GenCanvas");
  453. END New;
  454. (* Not thread-safe!!! *)
  455. PROCEDURE GetImage*() : Raster.Image;
  456. BEGIN
  457. RETURN img;
  458. END GetImage;
  459. PROCEDURE SetLimits*(r : Rectangle);
  460. BEGIN
  461. r.r := Max(r.r, r.l); r.b := Max(r.t, r.b);
  462. Rectangles.ClipRect(r, bounds); SetLimits^(r)
  463. END SetLimits;
  464. (* PROCEDURE Line*(x0, y0, x1, y1 : LONGINT; color : Color; mode : LONGINT);
  465. BEGIN
  466. END Line; *)
  467. PROCEDURE Fill*(rect : Rectangle; color : Color; mode : LONGINT);
  468. VAR rm : Raster.Mode; pix : Raster.Pixel;
  469. BEGIN
  470. (* convert to super coordinates *)
  471. Rectangles.MoveRel(rect, dx, dy);
  472. IF ClipRect IN clipMode THEN Rectangles.ClipRect(rect, clipRect) END;
  473. Rectangles.ClipRect(rect, limits);
  474. IF ~Rectangles.RectEmpty(rect) THEN
  475. Raster.SetRGBA(pix, ((color DIV 65536) DIV 256) MOD 256, (color DIV 65536) MOD 256,
  476. (color DIV 256) MOD 256, color MOD 256);
  477. IF mode = ModeCopy THEN Raster.InitMode(rm, Raster.srcCopy) ELSE Raster.InitMode(rm, Raster.srcOverDst) END;
  478. Raster.Fill(SELF.img, rect.l, rect.t, rect.r, rect.b, pix, rm);
  479. END
  480. END Fill;
  481. (* Polygon filling *)
  482. (** fill a polygon given by points *)
  483. PROCEDURE FillPolygonFlat*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; color : Color; mode : LONGINT);
  484. VAR i : LONGINT;
  485. BEGIN
  486. IF nofPoints < 3 THEN RETURN END;
  487. ASSERT(nofPoints <= LEN(points));
  488. ClearHeap;
  489. FOR i := 1 TO nofPoints - 1 DO AddLine(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y) END;
  490. AddLine(points[nofPoints - 1].x, points[nofPoints - 1].y, points[0].x, points[0].y);
  491. FillFlat(color, mode)
  492. END FillPolygonFlat;
  493. (** fill a polygon given by points *)
  494. PROCEDURE FillPolygonCB*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; callBack : FillLineCallBack);
  495. VAR i : LONGINT;
  496. BEGIN
  497. IF nofPoints < 3 THEN RETURN END;
  498. ASSERT(nofPoints <= LEN(points));
  499. ClearHeap;
  500. FOR i := 1 TO nofPoints - 1 DO AddLine(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y) END;
  501. AddLine(points[nofPoints - 1].x, points[nofPoints - 1].y, points[0].x, points[0].y);
  502. FillCB(callBack)
  503. END FillPolygonCB;
  504. PROCEDURE ClearHeap;
  505. VAR i : LONGINT;
  506. BEGIN
  507. topHeap := 0;
  508. FOR i := 0 TO height - 1 DO edges[i] := 0 END;
  509. IF fillHeap = NIL THEN NEW(fillHeap, 1024); heapSize := 1024 END
  510. END ClearHeap;
  511. PROCEDURE NewFillPos(pos : LONGINT) : LONGINT;
  512. VAR newHeap : FillHeap;
  513. i : LONGINT;
  514. BEGIN
  515. INC(topHeap);
  516. IF topHeap >= heapSize THEN (* grow heap *)
  517. NEW(newHeap, heapSize * 2);
  518. FOR i := 0 TO heapSize - 1 DO newHeap[i] := fillHeap[i] END;
  519. heapSize := heapSize * 2;
  520. fillHeap := newHeap
  521. END;
  522. fillHeap[topHeap].pos := pos;
  523. fillHeap[topHeap].next := 0;
  524. RETURN topHeap
  525. END NewFillPos;
  526. PROCEDURE AddIntersection(y, pos : LONGINT);
  527. VAR new, cur : LONGINT;
  528. BEGIN
  529. IF (y < 0) OR (y >= height) THEN RETURN END;
  530. new := NewFillPos(pos);
  531. IF edges[y] = 0 THEN edges[y] := new
  532. ELSE
  533. cur := edges[y];
  534. IF fillHeap[cur].pos > pos THEN
  535. fillHeap[new].next := cur;
  536. edges[y] := new
  537. ELSE
  538. WHILE (fillHeap[cur].next # 0) & (fillHeap[fillHeap[cur].next].pos < pos) DO cur := fillHeap[cur].next END;
  539. fillHeap[new].next := fillHeap[cur].next;
  540. fillHeap[cur].next := new
  541. END;
  542. END;
  543. END AddIntersection;
  544. PROCEDURE AddLine(x0, y0, x1, y1 : LONGINT);
  545. VAR t, xi, xf, mi, mf, dt2 : LONGINT ;
  546. BEGIN
  547. IF (y0 = y1) THEN RETURN END;
  548. IF y0 > y1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
  549. xi := x0; xf := y0 - y1; mi := (x1 - x0) DIV (y1 - y0); mf := 2 * ( (x1 - x0) MOD (y1 - y0)); dt2 := 2 * (y1 - y0);
  550. FOR t := y0 TO y1 - 1 DO
  551. AddIntersection(t, xi);
  552. INC(xi, mi); INC(xf, mf);
  553. IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
  554. END
  555. END AddLine;
  556. PROCEDURE FillFlat(color : Color; mode : LONGINT);
  557. VAR i, sp, cur : LONGINT;
  558. in : BOOLEAN;
  559. BEGIN
  560. FOR i := 0 TO height - 1 DO
  561. cur := edges[i];
  562. in := FALSE;
  563. WHILE cur # 0 DO
  564. in := ~in;
  565. IF in THEN sp := fillHeap[cur].pos
  566. ELSE Fill(Rectangles.MakeRect(sp, i, fillHeap[cur].pos, i + 1), color, mode)
  567. END;
  568. cur := fillHeap[cur].next
  569. END
  570. END
  571. END FillFlat;
  572. PROCEDURE FillCB(cb : FillLineCallBack);
  573. VAR i, sp, cur : LONGINT;
  574. in : BOOLEAN;
  575. BEGIN
  576. FOR i := 0 TO height - 1 DO
  577. cur := edges[i];
  578. in := FALSE;
  579. WHILE cur # 0 DO
  580. in := ~in;
  581. IF in THEN sp := fillHeap[cur].pos
  582. ELSE cb(SELF, i, sp, fillHeap[cur].pos)
  583. END;
  584. cur := fillHeap[cur].next
  585. END
  586. END
  587. END FillCB;
  588. PROCEDURE DrawImage*(x, y: LONGINT; img: Raster.Image; mode : LONGINT);
  589. VAR imgBounds : Rectangle;
  590. rm : Raster.Mode;
  591. dx, dy: LONGINT;
  592. BEGIN
  593. IF (img = NIL) OR (img.adr = NIL) THEN RETURN END;
  594. dx := SELF.dx; dy := SELF.dy; (* avoid race below *)
  595. imgBounds := MakeRectangle(0, 0, img.width, img.height);
  596. (* to super coordinates *)
  597. Rectangles.MoveRel(imgBounds, x + dx, y + dy);
  598. IF ClipRect IN clipMode THEN Rectangles.ClipRect(imgBounds, clipRect) END;
  599. Rectangles.ClipRect(imgBounds, limits);
  600. IF ~Rectangles.RectEmpty(imgBounds) THEN
  601. IF mode = ModeCopy THEN Raster.InitMode(rm, Raster.srcCopy) ELSE Raster.InitMode(rm, Raster.srcOverDst) END;
  602. Raster.SetRGBA(rm.col, (color DIV 1000000H) MOD 100H, (color DIV 10000H) MOD 100H,
  603. (color DIV 100H) MOD 100H, color MOD 100H);
  604. IF imgBounds.l - (x + dx) < 0 THEN
  605. KernelLog.String("Error...");
  606. KernelLog.String("x + dx = "); KernelLog.Int(x + dx, 4); KernelLog.Ln;
  607. KernelLog.String("x = "); KernelLog.Int(x, 4); KernelLog.Ln;
  608. KernelLog.String("dx = "); KernelLog.Int(dx, 4); KernelLog.Ln;
  609. KernelLog.String("clip = "); KernelLog.Int(clipRect.l, 4); KernelLog.Int(clipRect.t, 4);
  610. KernelLog.Int(clipRect.r, 4); KernelLog.Int(clipRect.b, 4);KernelLog.Ln;
  611. KernelLog.String("imgBounds = ");
  612. KernelLog.Int(imgBounds.l, 4); KernelLog.Int(imgBounds.t, 4); KernelLog.Int(imgBounds.r, 4); KernelLog.Int(imgBounds.b, 4);KernelLog.Ln;
  613. KernelLog.String("limits = "); KernelLog.Int(limits.l, 4); KernelLog.Int(limits.t, 4);
  614. KernelLog.Int(limits.r, 4); KernelLog.Int(limits.b, 4);KernelLog.Ln;
  615. RETURN
  616. END;
  617. Raster.Copy(img, SELF.img, imgBounds.l - (x + dx), imgBounds.t - (y + dy),
  618. imgBounds.r - imgBounds.l + (imgBounds.l - (x + dx)), imgBounds.b - imgBounds.t + (imgBounds.t - (y + dy)),
  619. imgBounds.l, imgBounds.t, rm);
  620. END;
  621. END DrawImage;
  622. PROCEDURE ScaleImage*(src : Raster.Image; sr , dr : Rectangle; copyMode, scaleMode : LONGINT);
  623. BEGIN
  624. Rectangles.MoveRel(dr, dx, dy);
  625. RasterScale.Scale(src, sr, img, dr, clipRect, copyMode, scaleMode);
  626. END ScaleImage;
  627. END BufferCanvas;
  628. VAR imgCache : Kernel.FinalizedCollection;
  629. searchName : ARRAY 128 OF CHAR;
  630. foundImg : Image;
  631. defaultFont : Font;
  632. fontManager : FontManager;
  633. fallbackFonts* : ARRAY 5 OF Font;
  634. nofFallbackFonts : LONGINT;
  635. CONST
  636. AlignLeft* = 0; AlignCenter* = 1; AlignRight* = 2;
  637. AlignTop* = 0; AlignBottom* = 2;
  638. PROCEDURE Max(a, b:LONGINT):LONGINT;
  639. BEGIN
  640. IF a>b THEN RETURN a ELSE RETURN b END
  641. END Max;
  642. PROCEDURE Swap(VAR a,b: LONGINT);
  643. VAR t: LONGINT;
  644. BEGIN
  645. t := a; a := b; b := t;
  646. END Swap;
  647. (* Tool Functions *)
  648. PROCEDURE MakeRectangle*(l, t, r, b: LONGINT):Rectangle;
  649. VAR result : Rectangle;
  650. BEGIN
  651. result.l := l; result.t := t; result.r := r; result.b := b; RETURN result
  652. END MakeRectangle;
  653. PROCEDURE ColorToRGBA*(color : Color; VAR r, g, b, a : LONGINT);
  654. BEGIN
  655. r := (color DIV 1000000H) MOD 100H;
  656. g := (color DIV 10000H) MOD 100H;
  657. b := (color DIV 100H) MOD 100H;
  658. a := color MOD 100H
  659. END ColorToRGBA;
  660. PROCEDURE RGBAToColor*(r, g, b, a: LONGINT): Color;
  661. BEGIN
  662. RETURN r * 1000000H + g * 10000H + b * 100H + a
  663. END RGBAToColor;
  664. PROCEDURE Dark*(color:Color):Color;
  665. VAR c:Color;
  666. BEGIN
  667. c := MAX(0, (color DIV 1000000H) MOD 100H-40H);
  668. c := 100H * c + MAX(0, (color DIV 10000H) MOD 100H - 40H);
  669. c := 100H * c +MAX(0, (color DIV 100H) MOD 100H-40H);
  670. c := 100H * c + color MOD 100H;
  671. RETURN LONGINT(c);
  672. END Dark;
  673. PROCEDURE Light*(color:Color):Color;
  674. VAR c:Color;
  675. BEGIN
  676. c := MIN(0FFH, (color DIV 1000000H) MOD 100H + 40H);
  677. c := 100H * c + MIN(0FFH, (color DIV 10000H) MOD 100H + 40H);
  678. c := 100H * c +MIN(0FFH, (color DIV 100H) MOD 100H + 40H);
  679. c := 100H * c + color MOD 100H;
  680. RETURN LONGINT(c);
  681. END Light;
  682. PROCEDURE CheckImage(obj: ANY; VAR cont: BOOLEAN);
  683. BEGIN
  684. IF obj IS Image THEN
  685. IF obj(Image).key # NIL THEN
  686. IF obj(Image).key^ = searchName THEN
  687. foundImg := obj(Image);
  688. cont := FALSE
  689. END
  690. END
  691. END
  692. END CheckImage;
  693. PROCEDURE GetExtension (CONST name : ARRAY OF CHAR;VAR ext: ARRAY OF CHAR);
  694. VAR i, j: LONGINT; ch: CHAR;
  695. BEGIN
  696. i := 0; j := 0;
  697. WHILE name[i] # 0X DO
  698. IF name[i] = "." THEN j := i+1 END;
  699. INC(i)
  700. END;
  701. i := 0;
  702. REPEAT
  703. ch := name[j]; ext[i] := ch; INC(i); INC(j)
  704. UNTIL (ch = 0X) OR (i = LEN(ext));
  705. ext[i-1] := 0X
  706. END GetExtension;
  707. (** loads an image and returns a BGRA8888 bitmap if successful, NIL otherwise.
  708. If shared is TRUE, the image will not be reloaded if it is already in memory.
  709. *)
  710. PROCEDURE LoadImage*(CONST name : ARRAY OF CHAR; shared : BOOLEAN): Image;
  711. VAR img : Image;
  712. res: WORD; w, h, x : LONGINT;
  713. decoder : Codecs.ImageDecoder;
  714. in : Streams.Reader;
  715. ext : ARRAY 16 OF CHAR;
  716. BEGIN
  717. IF name = "" THEN RETURN NIL END;
  718. BEGIN {EXCLUSIVE}
  719. IF shared THEN
  720. foundImg := NIL; COPY(name, searchName);
  721. imgCache.Enumerate(CheckImage);
  722. IF foundImg # NIL THEN RETURN foundImg END
  723. END;
  724. END;
  725. GetExtension(name, ext);
  726. Strings.UpperCase(ext);
  727. decoder := Codecs.GetImageDecoder(ext);
  728. IF decoder = NIL THEN
  729. KernelLog.String("No decoder found for "); KernelLog.String(ext); KernelLog.Ln;
  730. RETURN NIL
  731. END;
  732. in := Codecs.OpenInputStream(name);
  733. IF in # NIL THEN
  734. decoder.Open(in, res);
  735. IF res = 0 THEN
  736. decoder.GetImageInfo(w, h, x, x);
  737. NEW(img);
  738. Raster.Create(img, w, h, Raster.BGRA8888);
  739. decoder.Render(img);
  740. NEW(img.key, LEN(name)); COPY(name, img.key^);
  741. IF shared THEN imgCache.Add(img, NIL) END
  742. END
  743. END;
  744. RETURN img
  745. END LoadImage;
  746. PROCEDURE StoreImage*(img : Raster.Image; CONST name : ARRAY OF CHAR; VAR res : WORD);
  747. VAR encoder : Codecs.ImageEncoder;
  748. f : Files.File;
  749. w : Files.Writer;
  750. ext : ARRAY 16 OF CHAR;
  751. BEGIN
  752. res := -1;
  753. GetExtension(name, ext);
  754. Strings.UpperCase(ext);
  755. encoder := Codecs.GetImageEncoder(ext);
  756. IF encoder = NIL THEN
  757. KernelLog.String("No encoder found for "); KernelLog.String(ext); KernelLog.Ln;
  758. RETURN
  759. END;
  760. f := Files.New(name);
  761. IF f # NIL THEN
  762. Files.OpenWriter(w, f, 0);
  763. END;
  764. IF w # NIL THEN
  765. encoder.Open(w);
  766. encoder.WriteImage(img, res);
  767. Files.Register(f);
  768. END
  769. END StoreImage;
  770. (** Draw an UTF8 String in a rectangle *)
  771. PROCEDURE DrawStringInRect*(canvas : Canvas; rect : Rectangle; wrap : BOOLEAN; hAlign, vAlign : LONGINT;
  772. CONST text : ARRAY OF CHAR);
  773. VAR tw, th, xPos, yPos : LONGINT;
  774. font : Font;
  775. BEGIN
  776. font := canvas.GetFont();
  777. IF font # NIL THEN
  778. font.GetStringSize(text, tw, th);
  779. END;
  780. xPos := rect.l; yPos := rect.t + font.GetAscent();
  781. IF ~wrap THEN
  782. IF hAlign = AlignCenter THEN xPos := ((rect.l + rect.r) - tw) DIV 2
  783. ELSIF hAlign = AlignRight THEN xPos := rect.r - tw
  784. END;
  785. IF vAlign = AlignCenter THEN yPos := (rect.t + rect.b - font.GetDescent() - font.GetAscent() ) DIV 2 + font.GetAscent() ;
  786. ELSIF vAlign = AlignBottom THEN yPos := rect.b - font.GetDescent();
  787. END;
  788. canvas.DrawString(xPos, yPos, text);
  789. ELSE
  790. (* not implemented *)
  791. END
  792. END DrawStringInRect;
  793. PROCEDURE GenCanvas*(img:Raster.Image):BufferCanvas;
  794. VAR c:BufferCanvas;
  795. BEGIN
  796. NEW(c,img); RETURN c
  797. END GenCanvas;
  798. PROCEDURE InstallDefaultFont*(f : Font);
  799. BEGIN { EXCLUSIVE }
  800. defaultFont := f;
  801. fallbackFonts[0] := defaultFont
  802. END InstallDefaultFont;
  803. PROCEDURE GetDefaultFont*() : Font;
  804. BEGIN { EXCLUSIVE }
  805. AWAIT(defaultFont # NIL);
  806. RETURN defaultFont
  807. END GetDefaultFont;
  808. PROCEDURE InstallFontManager*(fm : FontManager);
  809. BEGIN { EXCLUSIVE }
  810. fontManager := fm;
  811. IF fontManager # NIL THEN
  812. fallbackFonts[1] := fontManager.GetFont("Single", 20, {});
  813. END
  814. END InstallFontManager;
  815. PROCEDURE GetFont*(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : Font;
  816. VAR f : Font;
  817. BEGIN { EXCLUSIVE }
  818. f := NIL;
  819. IF fontManager # NIL THEN f := fontManager.GetFont(name, size, style) END;
  820. IF f = NIL THEN AWAIT(defaultFont # NIL); f := defaultFont END;
  821. RETURN f
  822. END GetFont;
  823. (** Render the fallback case of the character char to canvas at x, y (baseline) *)
  824. PROCEDURE FBRenderChar*(canvas : Canvas ; x, y : Real; char : Char32);
  825. VAR i, w, h : LONGINT; f : Font; found : BOOLEAN; str : ARRAY 16 OF CHAR; r: Rectangles.Rectangle;
  826. BEGIN
  827. i := 0; found := FALSE;
  828. WHILE ~found & (i < nofFallbackFonts) DO
  829. f := fallbackFonts[i];
  830. IF (f # NIL) & f.HasChar(char) THEN found := TRUE END;
  831. INC(i)
  832. END;
  833. IF f # NIL THEN f.RenderChar(canvas, x, y, char)
  834. ELSE
  835. f := GetDefaultFont();
  836. Strings.IntToStr(char,str); Strings.Concat("U", str, str);
  837. f.GetStringSize(str, w, h);
  838. r := Rectangles.MakeRect(ENTIER(x), ENTIER(y) - f.ascent, ENTIER(x) + w, ENTIER(y) + f.descent);
  839. canvas.Fill(r, LONGINT(0CCCC00FFH), ModeCopy);
  840. f.RenderString(canvas, x, y, str)
  841. END
  842. END FBRenderChar;
  843. (** return the fallback spacing of character code *)
  844. PROCEDURE FBGetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : GlyphSpacings);
  845. VAR i : LONGINT; f : Font; found : BOOLEAN; str : ARRAY 16 OF CHAR;
  846. BEGIN
  847. i := 0; found := FALSE;
  848. WHILE ~found & (i < nofFallbackFonts) DO
  849. f := fallbackFonts[i];
  850. IF (f # NIL) & f.HasChar(code) THEN found := TRUE END;
  851. INC(i)
  852. END;
  853. IF f # NIL THEN f.GetGlyphSpacings(code, glyphSpacings)
  854. ELSE
  855. f := GetDefaultFont();
  856. Strings.IntToStr(code, str); Strings.Concat("U", str, str);
  857. glyphSpacings.bearing := Rectangles.MakeRect(0, 0, 0, 0);
  858. f.GetStringSize(str, glyphSpacings.width, glyphSpacings.height);
  859. glyphSpacings.ascent := f.ascent; glyphSpacings.descent := f.descent;
  860. glyphSpacings.dx := 0; glyphSpacings.dy := 0
  861. END
  862. END FBGetGlyphSpacings;
  863. (** Tools *)
  864. (* Return true if the alpha value at pos x, y in img is >= threshold. Returns false if x, y are out of image *)
  865. PROCEDURE IsBitmapHit*(x, y, threshold: LONGINT; img: Raster.Image) : BOOLEAN;
  866. VAR pix : Raster.Pixel;
  867. mode : Raster.Mode;
  868. BEGIN
  869. IF (img # NIL) & (x >= 0) & (y >= 0) & (x < img.width) & (y < img.height) THEN
  870. Raster.InitMode(mode, Raster.srcCopy);
  871. Raster.Get(img, x, y, pix, mode);
  872. RETURN (ORD(pix[Raster.a]) >= threshold)
  873. ELSE RETURN FALSE
  874. END
  875. END IsBitmapHit;
  876. PROCEDURE IsScaledBitmapHit*(x,y,w,h,threshold: LONGINT; img: Raster.Image): BOOLEAN;
  877. BEGIN
  878. RETURN IsBitmapHit(x*img.width DIV w, y*img.height DIV h, threshold,img);
  879. END IsScaledBitmapHit;
  880. PROCEDURE ClearCache*;
  881. BEGIN
  882. imgCache.Clear;
  883. END ClearCache;
  884. BEGIN
  885. nofFallbackFonts := 3;
  886. NEW(imgCache)
  887. END WMGraphics.