WMGraphics.Mod 29 KB

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