/escape-src/font.cc

# · C++ · 429 lines · 312 code · 84 blank · 33 comment · 85 complexity · 0e0aa975301242ac7d794dc787c5ab73 MD5 · raw file

  1. #include "font.h"
  2. #include "extent.h"
  3. enum attr { COLOR, ALPHA, };
  4. struct font_attrlist {
  5. attr what;
  6. int value;
  7. font_attrlist * next;
  8. font_attrlist(attr w, int h, font_attrlist * n) : what(w), value(h), next(n) {}
  9. /* PERF: we often have to look at the entire stack. */
  10. /* sets the current color and alpha according to the stack */
  11. static void set(font_attrlist * cs, int & c, int & a) {
  12. c = 0;
  13. a = 0;
  14. bool gotc = false, gota = false;
  15. while(cs && !(gotc && gota)) {
  16. switch(cs->what) {
  17. case COLOR:
  18. if (!gotc) { c = cs->value; gotc = true; }
  19. break;
  20. case ALPHA:
  21. if (!gota) { a = cs->value; gota = true; }
  22. break;
  23. }
  24. cs = cs -> next;
  25. }
  26. }
  27. static int pop(font_attrlist *& il) {
  28. if (il) {
  29. font_attrlist * tmp = il;
  30. int t = tmp->value;
  31. il = il->next;
  32. delete tmp;
  33. return t;
  34. } else return 0;
  35. }
  36. };
  37. struct fontreal : public font {
  38. /* data is an array of font images, differing in
  39. their alpha transparency */
  40. SDL_Surface ** data;
  41. int ndim;
  42. unsigned char chars[255];
  43. /* 9 x 16 */
  44. static fontreal * create(string file,
  45. string charmap,
  46. int width,
  47. int height,
  48. int styles=1,
  49. int overlap=0,
  50. int ndim_=2);
  51. virtual int sizex(const string &);
  52. virtual int sizex_plain(const string &);
  53. virtual void draw(int x, int y, string s);
  54. virtual void drawto(SDL_Surface *, int x, int y, string s);
  55. virtual void draw_plain(int x, int y, string s);
  56. virtual void drawto_plain(SDL_Surface *, int x, int y, string s);
  57. virtual void destroy() {
  58. if (data) {
  59. for(int i = 0; i < ndim; i ++) {
  60. if (data[i]) SDL_FreeSurface(data[i]);
  61. }
  62. free (data);
  63. }
  64. delete this;
  65. }
  66. virtual int drawlinesc(int x, int y, string, bool);
  67. virtual int drawlines(int x, int y, string);
  68. virtual int drawcenter(int x, int y, string);
  69. virtual ~fontreal();
  70. };
  71. font::~font() {}
  72. fontreal::~fontreal() {}
  73. font * font::create(string f, string c,
  74. int w, int h, int s, int o, int d) {
  75. return fontreal::create(f, c, w, h, s, o, d);
  76. }
  77. fontreal * fontreal::create (string file,
  78. string charmap,
  79. int width,
  80. int height,
  81. int styles,
  82. int overlap,
  83. int ndim) {
  84. fontreal * f = new fontreal();
  85. if (!f) return 0;
  86. extent<fontreal> fe(f);
  87. f->width = width;
  88. f->height = height;
  89. f->styles = styles;
  90. f->overlap = overlap;
  91. f->data = 0;
  92. f->ndim = ndim;
  93. if (!ndim) return 0;
  94. f->data = (SDL_Surface **)malloc(sizeof (SDL_Surface *) * ndim);
  95. if (!f->data) return 0;
  96. for(int z = 0; z < ndim; z++) f->data[z] = 0;
  97. f->data[0] = sdlutil::imgload(file.c_str());
  98. if (!f->data[0]) return 0;
  99. int last = 0;
  100. while(last < (ndim - 1)) {
  101. last ++;
  102. f->data[last] = sdlutil::alphadim(f->data[last - 1]);
  103. if (!f->data[last]) return 0;
  104. }
  105. for(int j = 0; j < 255; j ++) {
  106. f->chars[j] = 0;
  107. }
  108. for(unsigned int i = 0; i < charmap.length (); i ++) {
  109. int idx = (unsigned char)charmap[i];
  110. f->chars[idx] = i;
  111. }
  112. fe.release ();
  113. return f;
  114. }
  115. void fontreal::draw(int x, int y, string s) {
  116. drawto(screen, x, y, s);
  117. }
  118. void fontreal::draw_plain(int x, int y, string s) {
  119. drawto_plain(screen, x, y, s);
  120. }
  121. void fontreal::drawto_plain(SDL_Surface * surf, int x, int y, string s) {
  122. SDL_Rect src, dest;
  123. dest.x = x;
  124. dest.y = y;
  125. src.w = width;
  126. src.h = height;
  127. for(unsigned int i = 0; i < s.length(); i ++) {
  128. int idx = (unsigned char)s[i];
  129. src.x = chars[idx] * width;
  130. src.y = 0;
  131. SDL_BlitSurface(data[0], &src, surf, &dest);
  132. dest.x += (width - overlap);
  133. }
  134. }
  135. void fontreal::drawto(SDL_Surface * surf, int x, int y, string s) {
  136. SDL_Rect src, dest;
  137. /* XXX can't init these once, since blit can side-effect fields
  138. (try drawing off the screen) */
  139. dest.x = x;
  140. dest.y = y;
  141. src.w = width;
  142. src.h = height;
  143. /* keep track of our color and alpha settings */
  144. font_attrlist * cstack = 0;
  145. int color, alpha;
  146. font_attrlist::set(cstack, color, alpha);
  147. for(unsigned int i = 0; i < s.length(); i ++) {
  148. if ((unsigned char)s[i] == '^') {
  149. if (i < s.length()) {
  150. i++;
  151. switch((unsigned char)s[i]) {
  152. case '^': break; /* quoted... keep going */
  153. case '<': /* pop */
  154. if (cstack) font_attrlist::pop(cstack);
  155. font_attrlist::set(cstack, color, alpha);
  156. continue;
  157. default:
  158. if (s[i] >= '#' && s[i] <= '\'') {
  159. /* alpha */
  160. cstack =
  161. new font_attrlist(ALPHA,
  162. abs((unsigned char)s[i] - '#') % ndim,
  163. cstack);
  164. } else {
  165. /* color */
  166. cstack =
  167. new font_attrlist(COLOR,
  168. abs(((unsigned char)s[i] - '0')
  169. % styles),
  170. cstack);
  171. }
  172. font_attrlist::set(cstack, color, alpha);
  173. continue;
  174. }
  175. }
  176. }
  177. /* current color */
  178. int idx = (unsigned char)s[i];
  179. src.x = chars[idx] * width;
  180. src.y = color * height;
  181. SDL_BlitSurface(data[alpha], &src, surf, &dest);
  182. dest.x += (width - overlap);
  183. }
  184. /* empty list */
  185. while(cstack) font_attrlist::pop(cstack);
  186. }
  187. int fontreal::sizex_plain(const string & s) {
  188. return s.length () * (width - overlap);
  189. }
  190. int fontreal::sizex(const string & s) {
  191. return font::length(s) * (width - overlap);
  192. }
  193. int fontreal::drawlines(int x, int y, string s) {
  194. return drawlinesc(x, y, s, false);
  195. }
  196. int fontreal::drawcenter(int x, int y, string s) {
  197. return drawlinesc(x, y, s, true);
  198. }
  199. /* XXX doesn't handle color codes that span lines. */
  200. int fontreal::drawlinesc(int x, int y, string s, bool center) {
  201. int start = 0;
  202. unsigned int idx = 0;
  203. /* draw every non-empty string delimited by \n */
  204. int offset = 0;
  205. int wroteany = 0;
  206. for(;;idx ++) {
  207. again:;
  208. /* reached end of string? */
  209. if (idx >= s.length()) {
  210. if (wroteany) {
  211. int xx;
  212. string sub = s.substr(start, idx - start);
  213. if (center) {
  214. xx = x - (sizex(sub) >> 1);
  215. } else xx = x;
  216. draw(xx, y + offset, sub);
  217. return offset + height;
  218. } else return offset;
  219. }
  220. if (s[idx] == '\n') {
  221. int xx;
  222. string sub = s.substr(start, idx - start);
  223. if (center) {
  224. xx = x - (sizex(sub) >> 1);
  225. } else xx = x;
  226. draw(xx, y + offset, sub);
  227. offset += height;
  228. start = idx + 1;
  229. idx = idx + 1;
  230. wroteany = 0;
  231. goto again;
  232. } else wroteany = 1;
  233. }
  234. }
  235. /* by example:
  236. "" returns 0
  237. "\n" returns 1
  238. "hello" returns 1
  239. "hello\n" returns 1
  240. "hello\nworld" returns 2
  241. "hello\nworld\n" returns 2
  242. */
  243. int font::lines(const string & s) {
  244. unsigned int idx = 0;
  245. int sofar = 0;
  246. enum mode { M_FINDANY, M_STEADY, };
  247. mode m = M_FINDANY;
  248. for(;;idx++) {
  249. if (idx >= s.length()) return sofar;
  250. switch(m) {
  251. case M_FINDANY:
  252. if (s[idx] == '\n') {
  253. sofar++;
  254. continue;
  255. } else {
  256. sofar++;
  257. m = M_STEADY;
  258. continue;
  259. }
  260. case M_STEADY:
  261. if (s[idx] == '\n') {
  262. m = M_FINDANY;
  263. continue;
  264. }
  265. }
  266. }
  267. }
  268. string font::substr(const string & s, unsigned int start, unsigned int len) {
  269. /* skip 'start' chars */
  270. unsigned int i = 0; /* pos in fontstring */
  271. unsigned int j = 0; /* number of actual chars seen */
  272. for(; i < s.length() && j < start; i ++) {
  273. if ((unsigned char)s[i] == '^') {
  274. if (i < s.length()) {
  275. i++;
  276. if ((unsigned char)s[i] == '^') j++;
  277. } else j ++; /* ??? */
  278. } else j ++;
  279. }
  280. j = i;
  281. /* substring will start at j; now count
  282. 'len' chars to find end */
  283. unsigned int k = 0;
  284. /* XXX should also add any trailing
  285. control codes */
  286. for(; i < s.length() && k < len; i ++) {
  287. if ((unsigned char)s[i] == '^') {
  288. if (i < s.length()) {
  289. i++;
  290. if ((unsigned char)s[i] == '^') j++;
  291. } else k ++; /* ??? */
  292. } else k ++;
  293. }
  294. return s.substr(j, i - j);
  295. }
  296. /* assume n <= font::length(s) */
  297. string font::prefix(const string & s, unsigned int n) {
  298. return font::substr(s, 0, n);
  299. }
  300. /* assume n <= font::length (s) */
  301. string font::suffix(const string & s, unsigned int n) {
  302. return font::substr(s, font::length(s) - n, n);
  303. }
  304. unsigned int font::length(string s) {
  305. unsigned int i, n=0;
  306. for(i = 0; i < s.length(); i ++) {
  307. if ((unsigned char)s[i] == '^') {
  308. if (i < s.length()) {
  309. i++;
  310. if ((unsigned char)s[i] == '^') n++;
  311. } else n ++; /* ??? */
  312. } else n ++;
  313. }
  314. return n;
  315. }
  316. /* XXX should go to fontutil */
  317. #include "chars.h"
  318. string font::pad(const string & s, int ns) {
  319. unsigned int l = font::length(s);
  320. unsigned int n = abs(ns);
  321. if (l > n) {
  322. return truncate(s, ns);
  323. // return font::prefix(s, n - 3) + (string)ALPHA50 "..." POP;
  324. } else {
  325. string ou;
  326. /* PERF there's definitely a faster way to do this */
  327. for(unsigned int j = 0; j < (n - l); j ++) ou += " ";
  328. if (ns > 0) {
  329. return s + ou;
  330. } else {
  331. return ou + s;
  332. }
  333. }
  334. }
  335. string font::truncate(const string & s, int ns) {
  336. unsigned int l = font::length(s);
  337. unsigned int n = abs(ns);
  338. if (l > n) {
  339. if (ns > 0) {
  340. return font::prefix(s, n - 3) + (string)ALPHA50 "..." POP;
  341. } else {
  342. return (string)ALPHA50 "..." POP + font::suffix(s, n - 3);
  343. }
  344. } else return s;
  345. }