WMGraphics.Mod 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  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(Rectangles.MakeRect(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 := Rectangles.MakeRect(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 := Rectangles.MakeRect(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 Swap(VAR a,b: LONGINT);
  639. VAR t: LONGINT;
  640. BEGIN
  641. t := a; a := b; b := t;
  642. END Swap;
  643. (* Tool Functions *)
  644. PROCEDURE MakeRectangle*(l, t, r, b: LONGINT):Rectangle;
  645. VAR result : Rectangle;
  646. BEGIN
  647. result.l := l; result.t := t; result.r := r; result.b := b; RETURN result
  648. END MakeRectangle;
  649. PROCEDURE ColorToRGBA*(color : Color; VAR r, g, b, a : LONGINT);
  650. BEGIN
  651. r := (color DIV 1000000H) MOD 100H;
  652. g := (color DIV 10000H) MOD 100H;
  653. b := (color DIV 100H) MOD 100H;
  654. a := color MOD 100H
  655. END ColorToRGBA;
  656. PROCEDURE RGBAToColor*(r, g, b, a: LONGINT): Color;
  657. BEGIN
  658. RETURN r * 1000000H + g * 10000H + b * 100H + a
  659. END RGBAToColor;
  660. PROCEDURE Dark*(color:Color):Color;
  661. VAR c:Color;
  662. BEGIN
  663. c := MAX(0, (color DIV 1000000H) MOD 100H-40H);
  664. c := 100H * c + MAX(0, (color DIV 10000H) MOD 100H - 40H);
  665. c := 100H * c +MAX(0, (color DIV 100H) MOD 100H-40H);
  666. c := 100H * c + color MOD 100H;
  667. RETURN LONGINT(c);
  668. END Dark;
  669. PROCEDURE Light*(color:Color):Color;
  670. VAR c:Color;
  671. BEGIN
  672. c := MIN(0FFH, (color DIV 1000000H) MOD 100H + 40H);
  673. c := 100H * c + MIN(0FFH, (color DIV 10000H) MOD 100H + 40H);
  674. c := 100H * c +MIN(0FFH, (color DIV 100H) MOD 100H + 40H);
  675. c := 100H * c + color MOD 100H;
  676. RETURN LONGINT(c);
  677. END Light;
  678. PROCEDURE CheckImage(obj: ANY; VAR cont: BOOLEAN);
  679. BEGIN
  680. IF obj IS Image THEN
  681. IF obj(Image).key # NIL THEN
  682. IF obj(Image).key^ = searchName THEN
  683. foundImg := obj(Image);
  684. cont := FALSE
  685. END
  686. END
  687. END
  688. END CheckImage;
  689. PROCEDURE GetExtension (CONST name : ARRAY OF CHAR;VAR ext: ARRAY OF CHAR);
  690. VAR i, j: LONGINT; ch: CHAR;
  691. BEGIN
  692. i := 0; j := 0;
  693. WHILE name[i] # 0X DO
  694. IF name[i] = "." THEN j := i+1 END;
  695. INC(i)
  696. END;
  697. i := 0;
  698. REPEAT
  699. ch := name[j]; ext[i] := ch; INC(i); INC(j)
  700. UNTIL (ch = 0X) OR (i = LEN(ext));
  701. ext[i-1] := 0X
  702. END GetExtension;
  703. (** loads an image and returns a BGRA8888 bitmap if successful, NIL otherwise.
  704. If shared is TRUE, the image will not be reloaded if it is already in memory.
  705. *)
  706. PROCEDURE LoadImage*(CONST name : ARRAY OF CHAR; shared : BOOLEAN): Image;
  707. VAR img : Image;
  708. res: WORD; w, h, x : LONGINT;
  709. decoder : Codecs.ImageDecoder;
  710. in : Streams.Reader;
  711. ext : ARRAY 16 OF CHAR;
  712. BEGIN
  713. IF name = "" THEN RETURN NIL END;
  714. BEGIN {EXCLUSIVE}
  715. IF shared THEN
  716. foundImg := NIL; COPY(name, searchName);
  717. imgCache.Enumerate(CheckImage);
  718. IF foundImg # NIL THEN RETURN foundImg END
  719. END;
  720. END;
  721. GetExtension(name, ext);
  722. Strings.UpperCase(ext);
  723. decoder := Codecs.GetImageDecoder(ext);
  724. IF decoder = NIL THEN
  725. KernelLog.String("No decoder found for "); KernelLog.String(ext); KernelLog.Ln;
  726. RETURN NIL
  727. END;
  728. in := Codecs.OpenInputStream(name);
  729. IF in # NIL THEN
  730. decoder.Open(in, res);
  731. IF res = 0 THEN
  732. decoder.GetImageInfo(w, h, x, x);
  733. NEW(img);
  734. Raster.Create(img, w, h, Raster.BGRA8888);
  735. decoder.Render(img);
  736. NEW(img.key, LEN(name)); COPY(name, img.key^);
  737. IF shared THEN imgCache.Add(img, NIL) END
  738. END
  739. END;
  740. RETURN img
  741. END LoadImage;
  742. PROCEDURE StoreImage*(img : Raster.Image; CONST name : ARRAY OF CHAR; VAR res : WORD);
  743. VAR encoder : Codecs.ImageEncoder;
  744. f : Files.File;
  745. w : Files.Writer;
  746. ext : ARRAY 16 OF CHAR;
  747. BEGIN
  748. res := -1;
  749. GetExtension(name, ext);
  750. Strings.UpperCase(ext);
  751. encoder := Codecs.GetImageEncoder(ext);
  752. IF encoder = NIL THEN
  753. KernelLog.String("No encoder found for "); KernelLog.String(ext); KernelLog.Ln;
  754. RETURN
  755. END;
  756. f := Files.New(name);
  757. IF f # NIL THEN
  758. Files.OpenWriter(w, f, 0);
  759. END;
  760. IF w # NIL THEN
  761. encoder.Open(w);
  762. encoder.WriteImage(img, res);
  763. Files.Register(f);
  764. END
  765. END StoreImage;
  766. (** Draw an UTF8 String in a rectangle *)
  767. PROCEDURE DrawStringInRect*(canvas : Canvas; rect : Rectangle; wrap : BOOLEAN; hAlign, vAlign : LONGINT;
  768. CONST text : ARRAY OF CHAR);
  769. VAR tw, th, xPos, yPos : LONGINT;
  770. font : Font;
  771. BEGIN
  772. font := canvas.GetFont();
  773. IF font # NIL THEN
  774. font.GetStringSize(text, tw, th);
  775. END;
  776. xPos := rect.l; yPos := rect.t + font.GetAscent();
  777. IF ~wrap THEN
  778. IF hAlign = AlignCenter THEN xPos := ((rect.l + rect.r) - tw) DIV 2
  779. ELSIF hAlign = AlignRight THEN xPos := rect.r - tw
  780. END;
  781. IF vAlign = AlignCenter THEN yPos := (rect.t + rect.b - font.GetDescent() - font.GetAscent() ) DIV 2 + font.GetAscent() ;
  782. ELSIF vAlign = AlignBottom THEN yPos := rect.b - font.GetDescent();
  783. END;
  784. canvas.DrawString(xPos, yPos, text);
  785. ELSE
  786. (* not implemented *)
  787. END
  788. END DrawStringInRect;
  789. PROCEDURE GenCanvas*(img:Raster.Image):BufferCanvas;
  790. VAR c:BufferCanvas;
  791. BEGIN
  792. NEW(c,img); RETURN c
  793. END GenCanvas;
  794. PROCEDURE InstallDefaultFont*(f : Font);
  795. BEGIN { EXCLUSIVE }
  796. defaultFont := f;
  797. fallbackFonts[0] := defaultFont
  798. END InstallDefaultFont;
  799. PROCEDURE GetDefaultFont*() : Font;
  800. BEGIN { EXCLUSIVE }
  801. AWAIT(defaultFont # NIL);
  802. RETURN defaultFont
  803. END GetDefaultFont;
  804. PROCEDURE InstallFontManager*(fm : FontManager);
  805. BEGIN { EXCLUSIVE }
  806. fontManager := fm;
  807. IF fontManager # NIL THEN
  808. fallbackFonts[1] := fontManager.GetFont("Single", 20, {});
  809. END
  810. END InstallFontManager;
  811. (*Workaround:
  812. If the font with a weight other than Bold is required, use a full name of the file (for ttf-fonts) with empty style.
  813. For example: GetFont( "IBMPlexSans-SemiBold", 24, {} );
  814. *)
  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.