WMGraphics.Mod 26 KB

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