OpenTypeScan.Mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. MODULE OpenTypeScan; (** AUTHOR "eos, PL"; PURPOSE "Bluebottle port of OpenType"; *)
  2. (**
  3. Scan conversion for TrueType contours
  4. **)
  5. (*
  6. 7.12.1999 - don't let dropout control move coordinates out of bounding box
  7. *)
  8. IMPORT
  9. OpenTypeInt;
  10. CONST
  11. (** enumeration rules **)
  12. Round* = 0; (** if included, coordinates are rounded to integer grid (leave out for grey scales) **)
  13. Dropouts* = 1; (** if included, dropouts are detected and fixed **)
  14. Stubs* = 2; (** if included, only dropouts which intersect other pixels are fixed **)
  15. Smart* = 3; (** if included, dropout pixels are positioned more intelligently **)
  16. X = OpenTypeInt.X; Y = OpenTypeInt.Y;
  17. TYPE
  18. F26D6 = OpenTypeInt.F26D6;
  19. Fixed = OpenTypeInt.Fixed;
  20. Intersection = POINTER TO IntersectionDesc;
  21. IntersectionDesc = RECORD
  22. xy: F26D6; (* coordinate on corresponding scanline *)
  23. up: INTEGER; (* contour direction at point *)
  24. param: Fixed; (* curve parameter *)
  25. next: Intersection; (* next intersection on same scanline *)
  26. link: Intersection; (* next intersection on same contour *)
  27. END;
  28. Scanline = POINTER TO ScanlineDesc;
  29. ScanlineDesc = RECORD
  30. next, prev: Scanline; (* next and previous scanline *)
  31. yx: F26D6; (* scanline coordinate *)
  32. isect: Intersection; (* list of intersections *)
  33. END;
  34. Rasterizer* = RECORD
  35. width*, height*: INTEGER; (** dimensions of pixel matrix **)
  36. xmin*, ymin*, xmax*, ymax*: F26D6; (** bounding box **)
  37. rules: SET; (* scan conversion rules *)
  38. hor, ver: Scanline; (* horizontal and vertical scanlines *)
  39. END;
  40. EnumData* = RECORD END;
  41. Enumerator* = PROCEDURE (rowcol: INTEGER; beg, end: F26D6; VAR data: EnumData);
  42. (**
  43. When enumerating rows, 'rowcol' is the row number and 'beg' and 'end' are subpixel values
  44. representing a horizontal interval in that row.
  45. When enumerating columns, 'rowcol' is the column number and 'beg' and 'end' are subpixel
  46. values representing a vertical interval in that column
  47. **)
  48. VAR
  49. scanlinePool: Scanline;
  50. intersectionPool: Intersection;
  51. (*--- pools ----*)
  52. PROCEDURE NewScanline(): Scanline;
  53. VAR scanline: Scanline;
  54. BEGIN{EXCLUSIVE}
  55. IF scanlinePool = NIL THEN
  56. NEW(scanline);
  57. ELSE
  58. scanline := scanlinePool; scanlinePool := scanline.next;
  59. scanline.next := NIL;
  60. END;
  61. RETURN scanline
  62. END NewScanline;
  63. PROCEDURE NewIntersection(): Intersection;
  64. VAR intersection: Intersection;
  65. BEGIN{EXCLUSIVE}
  66. IF intersectionPool = NIL THEN
  67. NEW(intersection)
  68. ELSE
  69. intersection := intersectionPool; intersectionPool := intersection.next;
  70. intersection.next := NIL;
  71. END;
  72. RETURN intersection
  73. END NewIntersection;
  74. PROCEDURE DisposeIntersections(VAR first: Intersection);
  75. VAR sentinel, next: Intersection;
  76. BEGIN (* only called from DisposeScanlines, no EXCLUSIVE possible here *)
  77. sentinel := first; next := NIL;
  78. WHILE (first # NIL) & (next # sentinel) DO
  79. next := first.next;
  80. first.next := intersectionPool;
  81. first.link := NIL;
  82. intersectionPool := first;
  83. first := next;
  84. END;
  85. END DisposeIntersections;
  86. PROCEDURE DisposeScanlines(VAR first: Scanline);
  87. VAR next, sentinel: Scanline;
  88. BEGIN{EXCLUSIVE}
  89. sentinel := first; next := NIL;
  90. WHILE (first # NIL) & (next # sentinel) DO
  91. next := first.next;
  92. DisposeIntersections(first.isect);
  93. first.prev := NIL;
  94. first.next := scanlinePool;
  95. scanlinePool := first;
  96. first := next;
  97. END;
  98. first := NIL;
  99. END DisposeScanlines;
  100. PROCEDURE DisposeRasterizer*(VAR ras: Rasterizer);
  101. BEGIN
  102. DisposeScanlines(ras.hor);
  103. DisposeScanlines(ras.ver);
  104. END DisposeRasterizer;
  105. (*--- Conversion of Outline to Scanlines ---*)
  106. PROCEDURE Init (VAR first: Scanline);
  107. VAR last: Scanline;
  108. BEGIN
  109. first := NewScanline(); last := NewScanline();
  110. first.yx := MIN(F26D6); first.next := last; first.prev := last; first.isect := NIL;
  111. last.yx := MAX(F26D6); last.prev := first; last.next := first; last.isect := NIL
  112. END Init;
  113. PROCEDURE Insert (VAR scan: Scanline; yx, xy: F26D6; up: INTEGER; hint: Intersection; t: Fixed);
  114. VAR sl: Scanline; is, prev: Intersection;
  115. BEGIN
  116. (* find correct scanline *)
  117. WHILE scan.(*next.*)yx <= yx DO scan := scan.next END;
  118. WHILE scan.yx > yx DO scan := scan.prev END;
  119. (* allocate new scanline if on new coordinate *)
  120. IF scan.yx < yx THEN
  121. sl := NewScanline();sl.yx := yx;
  122. sl.next := scan.next; sl.prev := scan;
  123. sl.next.prev := sl; scan.next := sl;
  124. is := NewIntersection(); is.xy := MAX(F26D6); is.next := is;
  125. sl.isect := is;
  126. scan := sl
  127. END;
  128. (* search place to insert and insert new intersection in scanline *)
  129. prev := scan.isect; WHILE prev.next.xy < xy DO prev := prev.next END;
  130. is := NewIntersection();
  131. is.xy := xy;
  132. is.up := up;
  133. is.next := prev.next; prev.next := is;
  134. (* insert intersection within contour sequence *)
  135. is.param := t;
  136. WHILE hint.link.param < t DO hint := hint.link END;
  137. is.link := hint.link; hint.link := is
  138. END Insert;
  139. PROCEDURE IntersectLine (x0, y0, x1, y1: F26D6; VAR scans: Scanline; hint: Intersection; t, dt: Fixed);
  140. VAR y, yend, ystep, dy, dy0, dy2, dx, xstep, q, r, x, xr: F26D6; up: LONGINT; qt, rt, tr: Fixed; times: LONGINT;
  141. BEGIN
  142. IF y0 # y1 THEN
  143. y := y0+20H; yend := y1+20H;
  144. y := y - y MOD 40H + 20H;
  145. yend := yend - yend MOD 40H + 20H;
  146. IF y0 < y1 THEN
  147. up := 1; ystep := 40H; dy := y1 - y0; dy0 := y - y0
  148. ELSE
  149. up := -1; ystep := -40H; dy := y0 - y1; DEC(y, 40H); DEC(yend, 40H); dy0 := y0 - y
  150. END;
  151. IF y # yend THEN
  152. dy2 := 2*dy;
  153. IF x0 <= x1 THEN dx := x1 - x0; xstep := 1
  154. ELSE dx := x0 - x1; xstep := -1
  155. END;
  156. q := xstep * (dx DIV dy); r := 2*(dx MOD dy);
  157. x := x0 + q * dy0; xr := r * dy0;
  158. IF xr >= dy THEN
  159. times := (xr-dy) DIV dy2 + 1;
  160. INC(x,xstep*times);
  161. DEC(xr, dy2*times);
  162. END;
  163. (*
  164. WHILE xr >= dy DO INC(x, xstep); DEC(xr, dy2) END;
  165. *)
  166. q := 40H*q; r := 40H*r;
  167. (*
  168. WHILE r >= dy DO INC(q, xstep); DEC(r, dy2) END;
  169. *)
  170. IF r >= dy THEN
  171. times := (r-dy) DIV dy2 + 1;
  172. INC(q,xstep*times);
  173. DEC(r, dy2*times);
  174. END;
  175. qt := dt DIV dy; rt := 2*(dt MOD dy);
  176. INC(t, qt * dy0); tr := rt * dy0;
  177. IF tr >= dy THEN
  178. times := (tr-dy) DIV dy2 + 1;
  179. INC(t,times);
  180. DEC(tr, dy2*times);
  181. END;
  182. (*
  183. WHILE tr >= dy DO INC(t); DEC(tr, dy2) END;
  184. *)
  185. qt := 40H*qt; rt := 40H*rt;
  186. (*
  187. WHILE rt >= dy DO INC(qt); DEC(rt, dy2) END;
  188. *)
  189. IF rt >= dy THEN
  190. times := (rt-dy) DIV dy2 + 1;
  191. INC(qt,times);
  192. DEC(rt, dy2*times);
  193. END;
  194. REPEAT
  195. Insert(scans, y, x, SHORT(up), hint, t);
  196. INC(x, q); INC(xr, r);
  197. IF xr >= dy THEN INC(x, xstep); DEC(xr, dy2) END;
  198. INC(t, qt); INC(tr, rt);
  199. IF tr >= dy THEN INC(t); DEC(tr, dy2) END;
  200. INC(y, ystep)
  201. UNTIL y = yend
  202. END
  203. END
  204. END IntersectLine;
  205. PROCEDURE IntersectBezier (x0, y0, x1, y1, x2, y2: F26D6; VAR scans: Scanline; hint: Intersection; t, dt: Fixed);
  206. VAR dx, dy, d, x01, y01, x12, y12, xm, ym: F26D6;
  207. CONST Resolution = 10H;
  208. BEGIN
  209. dx := x1 - x0; dy := y1 - y0;
  210. d := dx * dx + dy * dy;
  211. dx := x2 - x1; dy := y2 - y1;
  212. INC(d, dx * dx + dy * dy);
  213. IF d < (Resolution * 40H) THEN (* total curve length is smaller 40, an eighth a pixel *)
  214. IntersectLine(x0, y0, x2, y2, scans, hint, t, dt)
  215. ELSE
  216. dt := dt DIV 2;
  217. x01 := (x0 + x1 + 1) DIV 2; y01 := (y0 + y1 + 1) DIV 2;
  218. x12 := (x1 + x2 + 1) DIV 2; y12 := (y1 + y2 + 1) DIV 2;
  219. xm := (x01 + x12 + 1) DIV 2; ym := (y01 + y12 + 1) DIV 2;
  220. IntersectBezier(x0, y0, x01, y01, xm, ym, scans, hint, t, dt);
  221. IntersectBezier(xm, ym, x12, y12, x2, y2, scans, hint, t + dt, dt)
  222. END
  223. END IntersectBezier;
  224. (** convert outline to scanlines **)
  225. PROCEDURE Convert* (outline: OpenTypeInt.Zone; rules: SET; VAR r: Rasterizer);
  226. VAR
  227. hor, ver: Scanline; last, hint, is: Intersection; pt: OpenTypeInt.Points; cont, first, points, i, j, p, q: INTEGER; t: Fixed;
  228. c0, c2, c1: OpenTypeInt.Coord;
  229. BEGIN
  230. DisposeRasterizer(r);
  231. r.rules := rules;
  232. IF outline.contours = 0 THEN
  233. r.width := 0; r.height := 0;
  234. r.xmin := 0; r.ymin := 0; r.xmax := 0; r.ymax := 0;
  235. RETURN
  236. END;
  237. Init(r.hor); Init(r.ver); hor := r.hor; ver := r.ver;
  238. last := NewIntersection(); last.param := MAX(Fixed); (* sentinel for contour list *)
  239. pt := outline.pt;
  240. cont := 0;
  241. WHILE cont < outline.contours DO
  242. first := outline.first[cont]; points := outline.first[cont+1] - first;
  243. i := 0; WHILE (i < points) & ~pt[first + i].onCurve DO INC(i) END; (* find first point that is on contour *)
  244. IF i = points THEN (* found no point on curve *)
  245. i := 0 (* else in some fonts this character is left blank! *)
  246. END;
  247. IF i < points THEN
  248. j := i; p := first + j;
  249. hint := last; hint.link := last;
  250. t := 0; (* contour parameter *)
  251. c0 := pt[p].cur;
  252. REPEAT
  253. j := (j+1) MOD points; p := first + j;
  254. IF pt[p].onCurve THEN (* line *)
  255. c2 := pt[p].cur;
  256. IntersectLine(c0[X], c0[Y], c2[X], c2[Y], hor, hint, t, 10000H);
  257. IntersectLine(c0[Y], c0[X], c2[Y], c2[X], ver, hint, t, 10000H)
  258. ELSE
  259. c1 := pt[p].cur;
  260. q := first + (j+1) MOD points;
  261. IF pt[q].onCurve THEN (* Bezier with explicit end point *)
  262. j := q - first; p := q;
  263. c2 := pt[p].cur
  264. ELSE (* Bezier with implicit end-point *)
  265. c2[X] := (pt[p].cur[X] + pt[q].cur[X]) DIV 2;
  266. c2[Y] := (pt[p].cur[Y] + pt[q].cur[Y]) DIV 2
  267. END;
  268. IntersectBezier(c0[X], c0[Y], c1[X], c1[Y], c2[X], c2[Y], hor, hint, t, 10000H);
  269. IntersectBezier(c0[Y], c0[X], c1[Y], c1[X], c2[Y], c2[X], ver, hint, t, 10000H)
  270. END;
  271. c0 := c2; INC(t, 10000H);
  272. WHILE hint.link # last DO hint := hint.link END
  273. UNTIL j = i;
  274. hint.link := last.link (* unlink sentinel of contour list *)
  275. END;
  276. INC(cont)
  277. END;
  278. (*
  279. r.xmin := r.ver.next.yx DIV 40H * 40H; r.xmax := (r.ver.prev.prev.yx DIV 40H + 1) * 40H;
  280. r.ymin := r.hor.next.yx DIV 40H * 40H; r.ymax := (r.hor.prev.prev.yx DIV 40H + 1) * 40H;
  281. *)
  282. (* calculate bounding box *)
  283. (*
  284. r.xmin := MAX(F26D6); r.ymin := MAX(F26D6);
  285. r.xmax := MIN(F26D6); r.ymax := MIN(F26D6);
  286. *)
  287. (* *)
  288. r.xmin := r.ver.next.yx; r.xmax := r.ver.prev.prev.yx;
  289. r.ymin := r.hor.next.yx; r.ymax := r.hor.prev.prev.yx;
  290. (* *)
  291. hor := r.hor.next;
  292. WHILE hor # r.hor.prev DO
  293. is := hor.isect.next;
  294. IF is.xy < r.xmin THEN r.xmin := is.xy END;
  295. WHILE is.next # hor.isect DO is := is.next END;
  296. IF is.xy > r.xmax THEN r.xmax := is.xy END;
  297. hor := hor.next
  298. END;
  299. ver := r.ver.next;
  300. WHILE ver # r.ver.prev DO
  301. is := ver.isect.next;
  302. IF is.xy < r.ymin THEN r.ymin := is.xy END;
  303. WHILE is.next # ver.isect DO is := is.next END;
  304. IF is.xy > r.ymax THEN r.ymax := is.xy END;
  305. ver := ver.next
  306. END;
  307. r.xmin := r.xmin - r.xmin MOD 40H;
  308. r.ymin := r.ymin - r.ymin MOD 40H;
  309. r.xmax := r.xmax + 40H - r.xmax MOD 40H;
  310. r.ymax := r.ymax + 40H - r.ymax MOD 40H;
  311. (* *)
  312. IF r.xmin < r.xmax THEN r.width := SHORT((r.xmax - r.xmin) DIV 40H) ELSE r.width := 0 END;
  313. IF r.ymin < r.ymax THEN r.height := SHORT((r.ymax - r.ymin) DIV 40H) ELSE r.height := 0 END
  314. END Convert;
  315. (** enumerate rows or columns of scan-converted outline **)
  316. PROCEDURE EnumerateRows* (VAR r: Rasterizer; enum: Enumerator; VAR data: EnumData);
  317. VAR hor: Scanline; is, is0: Intersection; x0, r0, x1, r1: F26D6; sum: INTEGER;
  318. BEGIN
  319. IF r.width * r.height = 0 THEN RETURN END;
  320. hor := r.hor.next;
  321. WHILE hor.next # r.hor DO
  322. is := hor.isect.next;
  323. WHILE is # hor.isect DO
  324. is0 := is; x0 := is0.xy;
  325. IF Round IN r.rules THEN
  326. r0 := x0 MOD 40H; DEC(x0, r0);
  327. IF r0 > 20H THEN INC(x0, 40H) END
  328. END;
  329. sum := is.up; REPEAT is := is.next; INC(sum, is.up)
  330. UNTIL (is = hor.isect) OR (sum = 0);
  331. IF is # hor.isect THEN (* found opposite intersection *)
  332. x1 := is.xy;
  333. IF Round IN r.rules THEN
  334. r1 := x1 MOD 40H; DEC(x1, r1);
  335. IF r1 >= 20H THEN INC(x1, 40H) END (* greater or equal to include pixel center *)
  336. END;
  337. IF (x0 = x1) & (Dropouts IN r.rules) & (~(Stubs IN r.rules) OR (is0.link # is) & (is.link # is0)) THEN
  338. IF Round IN r.rules THEN
  339. IF x0 = r.xmin THEN
  340. x1 := x0 + 40H
  341. ELSIF x1 = r.xmax THEN
  342. x0 := x1 - 40H
  343. ELSIF Smart IN r.rules THEN
  344. r0 := ((is0.xy + is.xy) DIV 2) MOD 40H; (* use middle of both points to choose pixel *)
  345. IF (r0 = 0) OR (r0 > 20H) THEN x0 := x1 - 40H (* midpoint in upper half of lower pixel *)
  346. ELSE x1 := x0 + 40H (* midpoint in lower half of upper pixel *)
  347. END
  348. ELSE
  349. x0 := x1 - 40H (* choose left pixel *)
  350. END
  351. ELSE (* create 1/64 pixel distance between x0 and x1 *)
  352. IF x0 MOD 40H > 20H THEN DEC(x0) ELSE INC(x1) END
  353. END
  354. END;
  355. IF x0 <= x1 THEN
  356. ASSERT((r.xmin <= x0) & (x1 <= r.xmax), 110);
  357. enum(SHORT((hor.yx - r.ymin) DIV 40H), x0 - r.xmin, x1 - r.xmin, data)
  358. END;
  359. is := is.next
  360. END
  361. END;
  362. hor := hor.next
  363. END
  364. END EnumerateRows;
  365. PROCEDURE EnumerateColumns* (VAR r: Rasterizer; enum: Enumerator; VAR data: EnumData);
  366. VAR ver: Scanline; is, is0: Intersection; y0, r0, y1, r1: F26D6; sum: INTEGER;
  367. BEGIN
  368. IF r.width * r.height = 0 THEN RETURN END;
  369. ver := r.ver.next;
  370. WHILE ver.next # r.ver DO
  371. is := ver.isect.next;
  372. WHILE is # ver.isect DO
  373. is0 := is; y0 := is0.xy;
  374. IF Round IN r.rules THEN
  375. r0 := y0 MOD 40H; DEC(y0, r0);
  376. IF r0 > 20H THEN INC(y0, 40H) END
  377. END;
  378. sum := is.up; REPEAT is := is.next; INC(sum, is.up) UNTIL (is = ver.isect) OR (sum = 0);
  379. IF is # ver.isect THEN (* found opposite intersection *)
  380. y1 := is.xy;
  381. IF Round IN r.rules THEN
  382. r1 := y1 MOD 40H; DEC(y1, r1);
  383. IF r1 >= 20H THEN INC(y1, 40H) END (* greater or equal to include pixel center *)
  384. END;
  385. IF (y0 = y1) & (Dropouts IN r.rules) & (~(Stubs IN r.rules) OR (is0.link # is) & (is.link # is0)) THEN
  386. IF Round IN r.rules THEN
  387. IF y0 = r.ymin THEN
  388. y1 := y0 + 40H
  389. ELSIF y1 = r.ymax THEN
  390. y0 := y1 - 40H
  391. ELSIF Smart IN r.rules THEN
  392. r0 := ((is0.xy + is.xy) DIV 2) MOD 40H; (* use middle of both points to choose pixel *)
  393. IF (r0 = 0) OR (r0 > 20H) THEN y0 := y1 - 40H (* midpoint in upper half of lower pixel *)
  394. ELSE y1 := y0 + 40H (* midpoint in lower half of upper pixel *)
  395. END
  396. ELSE
  397. y0 := y1 - 40H (* choose lower pixel *)
  398. END
  399. ELSE (* create 1/64 pixel distance between y0 and y1 *)
  400. IF y0 MOD 40H > 20H THEN DEC(y0) ELSE INC(y1) END
  401. END
  402. END;
  403. IF y0 <= y1 THEN
  404. ASSERT((r.ymin <= y0) & (y1 <= r.ymax), 110);
  405. enum(SHORT((ver.yx - r.xmin) DIV 40H), y0 - r.ymin, y1 - r.ymin, data)
  406. END;
  407. is := is.next
  408. END
  409. END;
  410. ver := ver.next
  411. END
  412. END EnumerateColumns;
  413. END OpenTypeScan.