Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Font.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Font.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Font Resource class implementation
13 */
14 
15 #include "MemDebug.h"
16 #include "Font.h"
17 #include "Polygon.h"
18 #include "Bitmap.h"
19 #include "DataLoader.h"
20 #include "ParseUtil.h"
21 #include "Video.h"
22 
23 DWORD GetRealTime();
24 
25 // +--------------------------------------------------------------------+
26 
28 : flags(0), height(0), baseline(0), interspace(0), spacewidth(0),
29 imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
30 scale(1), material(0), vset(0), polys(0), npolys(0),
31 caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
32 {
33  ZeroMemory(name, sizeof(name));
34  ZeroMemory(glyph, sizeof(glyph));
35  ZeroMemory(kern, sizeof(kern));
36 }
37 
38 Font::Font(const char* n)
39 : flags(0), height(0), baseline(0), interspace(0), spacewidth(4),
40 imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
41 scale(1), material(0), vset(0), polys(0), npolys(0),
42 caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
43 {
44  ZeroMemory(glyph, sizeof(glyph));
45  ZeroMemory(kern, sizeof(kern));
46  CopyMemory(name, n, sizeof(name));
47 
48  if (!Load(name)) {
49  flags = 0;
50  height = 0;
51  baseline = 0;
52  interspace = 0;
53  spacewidth = 0;
54  imagewidth = 0;
55  image = 0;
56 
57  ZeroMemory(glyph, sizeof(glyph));
58  ZeroMemory(kern, sizeof(kern));
59  }
60 }
61 
62 // +--------------------------------------------------------------------+
63 
65 {
66  if (image) delete [] image;
67  if (vset) delete vset;
68  if (polys) delete [] polys;
69  if (material) delete material;
70 }
71 
72 // +--------------------------------------------------------------------+
73 
74 static char kern_tweak[256][256];
75 
76 bool
77 Font::Load(const char* name)
78 {
79  if (!name || !name[0])
80  return false;
81 
82  char imgname[256];
83  char defname[256];
84  wsprintf(defname, "%s.def", name);
85  wsprintf(imgname, "%s.pcx", name);
86 
88  if (!loader)
89  return false;
90 
91  LoadDef(defname, imgname);
92 
93  for (int i = 0; i < 256; i++) {
94  glyph[i].offset = GlyphOffset(i);
95  glyph[i].width = 0;
96  }
97 
98  if (loader->LoadBitmap(imgname, bitmap)) {
99  if (!bitmap.Pixels() && !bitmap.HiPixels())
100  return false;
101 
102  scale = bitmap.Width() / 256;
103  imagewidth = bitmap.Width();
104  if (height > bitmap.Height())
105  height = bitmap.Height();
106 
107  int imgsize = bitmap.Width() * bitmap.Height();
108  image = new(__FILE__,__LINE__) BYTE[imgsize];
109 
110  if (image) {
111  if (bitmap.Pixels()) {
112  CopyMemory(image, bitmap.Pixels(), imgsize);
113  }
114 
115  else {
116  for (int i = 0; i < imgsize; i++)
117  image[i] = (BYTE) bitmap.HiPixels()[i].Alpha();
118  }
119  }
120 
121  material = new(__FILE__,__LINE__) Material;
122  material->tex_diffuse = &bitmap;
123  }
124  else {
125  return false;
126  }
127 
128  for (int i = 0; i < 256; i++) {
129  glyph[i].width = CalcWidth(i);
130  }
131 
132  color = Color::White;
133 
134  if (!(flags & (FONT_FIXED_PITCH | FONT_NO_KERN)))
135  AutoKern();
136 
137  for (int i = 0; i < 256; i++) {
138  for (int j = 0; j < 256; j++) {
139  if (kern_tweak[i][j] < 100) {
140  kern[i][j] = kern_tweak[i][j];
141  }
142  }
143  }
144 
145  return true;
146 }
147 
148 void
149 Font::LoadDef(char* defname, char* imgname)
150 {
151  for (int i = 0; i < 256; i++)
152  for (int j = 0; j < 256; j++)
153  kern_tweak[i][j] = 111;
154 
155  DataLoader* loader = DataLoader::GetLoader();
156  if (!loader)
157  return;
158 
159  BYTE* block;
160  int blocklen = loader->LoadBuffer(defname, block, true);
161 
162  if (!block || blocklen < 4)
163  return;
164 
165  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
166  Term* term = parser.ParseTerm();
167 
168  if (!term) {
169  Print("WARNING: could not parse '%s'\n", defname);
170  return;
171  }
172  else {
173  TermText* file_type = term->isText();
174  if (!file_type || file_type->value() != "FONT") {
175  Print("WARNING: invalid font def file '%s'\n", defname);
176  return;
177  }
178  }
179 
180  do {
181  delete term;
182 
183  term = parser.ParseTerm();
184 
185  if (term) {
186  TermDef* def = term->isDef();
187  if (def) {
188  if (def->name()->value().indexOf("image") == 0) {
189  GetDefText(imgname, def, defname);
190  }
191 
192  else if (def->name()->value() == "height") {
193  int h=0;
194  GetDefNumber(h, def, defname);
195 
196  if (h >= 0 && h <= 32)
197  height = (BYTE) h;
198  }
199 
200  else if (def->name()->value() == "baseline") {
201  int b=0;
202  GetDefNumber(b, def, defname);
203 
204  if (b >= 0 && b <= 32)
205  baseline = (BYTE) b;
206  }
207 
208  else if (def->name()->value() == "flags") {
209  if (def->term()->isText()) {
210  Text buf;
211  GetDefText(buf, def, defname);
212  buf.setSensitive(false);
213 
214  flags = 0;
215 
216  if (buf.contains("caps"))
217  flags = flags | FONT_ALL_CAPS;
218 
219  if (buf.contains("kern"))
220  flags = flags | FONT_NO_KERN;
221 
222  if (buf.contains("fixed"))
223  flags = flags | FONT_FIXED_PITCH;
224  }
225 
226  else {
227  int f=0;
228  GetDefNumber(f, def, defname);
229  flags = (WORD) f;
230  }
231  }
232 
233  else if (def->name()->value() == "interspace") {
234  int n=0;
235  GetDefNumber(n, def, defname);
236 
237  if (n >= 0 && n <= 100)
238  interspace = (BYTE) n;
239  }
240 
241  else if (def->name()->value() == "spacewidth") {
242  int n=0;
243  GetDefNumber(n, def, defname);
244 
245  if (n >= 0 && n <= 100)
246  spacewidth = (BYTE) n;
247  }
248 
249  else if (def->name()->value() == "expansion") {
250  GetDefNumber(expansion, def, defname);
251  }
252 
253  else if (def->name()->value() == "kern") {
254  TermStruct* val = def->term()->isStruct();
255 
256  char a[8], b[8];
257  int k=111;
258 
259  a[0] = 0;
260  b[0] = 0;
261 
262  for (int i = 0; i < val->elements()->size(); i++) {
263  TermDef* pdef = val->elements()->at(i)->isDef();
264  if (pdef) {
265  if (pdef->name()->value() == "left" || pdef->name()->value() == "a")
266  GetDefText(a, pdef, defname);
267 
268  else if (pdef->name()->value() == "right" || pdef->name()->value() == "b")
269  GetDefText(b, pdef, defname);
270 
271  else if (pdef->name()->value() == "kern" || pdef->name()->value() == "k")
272  GetDefNumber(k, pdef, defname);
273  }
274  }
275 
276  if (k < 100)
277  kern_tweak[a[0]][b[0]] = k;
278  }
279 
280  else {
281  Print("WARNING: unknown object '%s' in '%s'\n",
282  def->name()->value().data(), defname);
283  }
284  }
285  else {
286  Print("WARNING: term ignored in '%s'\n", defname);
287  term->print();
288  }
289  }
290  }
291  while (term);
292 
293  loader->ReleaseBuffer(block);
294 
295 }
296 
297 // +--------------------------------------------------------------------+
298 
299 static const int pipe_width = 16;
300 static const int char_width = 16;
301 static const int char_height = 16;
302 static const int row_items = 16;
303 static const int row_width = row_items * char_width;
304 static const int row_size = char_height * row_width;
305 
306 int
307 Font::GlyphOffset(BYTE c) const
308 {
309  if (flags & FONT_ALL_CAPS)
310  if (islower(c))
311  c = toupper(c);
312 
313  return (c/row_items * row_size * scale * scale +
314  c%row_items * char_width * scale);
315 }
316 
317 int
318 Font::GlyphLocationX(BYTE c) const
319 {
320  if (flags & FONT_ALL_CAPS)
321  if (islower(c))
322  c = toupper(c);
323 
324  return c%row_items * char_width;
325 }
326 
327 int
328 Font::GlyphLocationY(BYTE c) const
329 {
330  if (flags & FONT_ALL_CAPS)
331  if (islower(c))
332  c = toupper(c);
333 
334  return c/row_items * char_height;
335 }
336 
337 // +--------------------------------------------------------------------+
338 
339 int
340 Font::CalcWidth(BYTE c) const
341 {
342  if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
343  return pipe_width;
344 
345  if (c >= 128 || !image)
346  return 0;
347 
348  // all digits should be same size:
349  if (isdigit(c))
350  c = '0';
351 
352  int result = 0;
353  int w = 16 * scale;
354  int h = 16 * scale;
355 
356  BYTE* src = image + GlyphOffset(c);
357 
358  for (int y = 0; y < h; y++) {
359  BYTE* pleft = src;
360 
361  for (int x = 0; x < w; x++) {
362  if (*pleft++ > 0 && x > result)
363  result = x;
364  }
365 
366  src += imagewidth;
367  }
368 
369  return result + 2;
370 }
371 
372 // +--------------------------------------------------------------------+
373 
375 {
376  double l[32];
377  double r[32];
378 };
379 
380 void
381 Font::FindEdges(BYTE c, double* l, double* r)
382 {
383  if (!image)
384  return;
385 
386  int w = glyph[c].width;
387  int h = height;
388 
389  if (h > 32)
390  h = 32;
391 
392  BYTE* src = image + GlyphOffset(c);
393 
394  for (int y = 0; y < h; y++) {
395  BYTE* pleft = src;
396  BYTE* pright = src+w-1;
397 
398  *l = -1;
399  *r = -1;
400 
401  for (int x = 0; x < w; x++) {
402  if (*l == -1 && *pleft != 0)
403  *l = x + 1 - (double) *pleft/255.0;
404  if (*r == -1 && *pright != 0)
405  *r = x + 1 - (double) *pright/255.0;
406 
407  pleft++;
408  pright--;
409  }
410 
411  src += imagewidth;
412  l++;
413  r++;
414  }
415 }
416 
417 static bool nokern(char c)
418 {
419  if (c <= Font::ARROW_RIGHT)
420  return true;
421 
422  const char* nokernchars = "0123456789+=<>-.,:;?'\"";
423 
424  if (strchr(nokernchars, c))
425  return true;
426 
427  return false;
428 }
429 
430 void
431 Font::AutoKern()
432 {
433  FontKernData* data = new(__FILE__,__LINE__) FontKernData[256];
434 
435  if (!data)
436  return;
437 
438  int h = height;
439  if (h > 32) h = 32;
440 
441  int i, j;
442 
443  // first, compute row edges for each glyph:
444 
445  for (i = 0; i < 256; i++) {
446  ZeroMemory(&data[i], sizeof(FontKernData));
447 
448  char c = i;
449 
450  if ((flags & FONT_ALL_CAPS) && islower(c))
451  c = toupper(c);
452 
453  if (glyph[(BYTE) c].width > 0) {
454  FindEdges((BYTE) c, data[i].l, data[i].r);
455  }
456  }
457 
458  // then, compute the appropriate kern for each pair.
459  // use a desired average distance of one pixel,
460  // with a desired minimum distance of more than half a pixel:
461 
462  double desired_avg = 2.5 + expansion;
463  double desired_min = 1;
464 
465  for (i = 0; i < 256; i++) {
466  for (j = 0; j < 256; j++) {
467  // no kerning between digits or dashes:
468  if (nokern(i) || nokern(j)) {
469  kern[i][j] = (char) 0;
470  }
471 
472  else {
473  double delta = 0;
474  double avg = 0;
475  double min = 2500;
476  int n = 0;
477 
478  for (int y = 0; y < h; y++) {
479  if (data[i].r[y] >= 0 && data[j].l[y] >= 0) {
480  delta = data[i].r[y] + data[j].l[y];
481  avg += delta;
482  if (delta < min)
483  min = delta;
484 
485  n++;
486  }
487  }
488 
489  if (n > 0) {
490  avg /= n;
491 
492  delta = desired_avg - avg;
493 
494  if (delta < desired_min - min) {
495  delta = ceil(desired_min - min);
496 
497  if (i == 'T' && islower(j) && !(flags & FONT_ALL_CAPS))
498  delta += 1;
499  }
500  }
501  else {
502  delta = 0;
503  }
504 
505  kern[i][j] = (char) delta;
506  }
507  }
508  }
509 
510  delete [] data;
511 }
512 
513 // +--------------------------------------------------------------------+
514 
515 int
516 Font::CharWidth(char c) const
517 {
518  if (flags & FONT_ALL_CAPS)
519  if (islower(c))
520  c = toupper(c);
521 
522  int result = 0;
523 
524  if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
525  result = pipe_width;
526 
527  else if (c < 0 || isspace(c))
528  result = spacewidth;
529 
530  else
531  result = glyph[c].width + interspace;
532 
533  return result;
534 }
535 
536 int
538 {
539  return spacewidth;
540 }
541 
542 int
543 Font::KernWidth(char a, char b) const
544 {
545  if (flags & FONT_ALL_CAPS) {
546  if (islower(a)) a = toupper(a);
547  if (islower(b)) b = toupper(b);
548  }
549 
550  return kern[a][b];
551 }
552 
553 void
554 Font::SetKern(char a, char b, int k)
555 {
556  if (k < -100 || k > 100)
557  return;
558 
559  if (flags & FONT_ALL_CAPS) {
560  if (islower(a)) a = toupper(a);
561  if (islower(b)) b = toupper(b);
562  }
563 
564  kern[a][b] = (char) k;
565 }
566 
567 // +--------------------------------------------------------------------+
568 
569 int
570 Font::StringWidth(const char* str, int len) const
571 {
572  int result = 0;
573 
574  if (!str)
575  return result;
576 
577  if (!len)
578  len = strlen(str);
579 
580  const char* c = str;
581  for (int i = 0; i < len; i++) {
582  if (isspace(*c) && (*c < PIPE_NBSP || *c > ARROW_RIGHT))
583  result += spacewidth;
584  else {
585  int cc = *c;
586  if (flags & FONT_ALL_CAPS)
587  if (islower(cc))
588  cc = toupper(cc);
589 
590  int k = 0;
591  if (i < len-1)
592  k = kern[cc][str[i+1]];
593 
594  result += glyph[cc].width + interspace + k;
595  }
596  c++;
597  }
598 
599  return result;
600 }
601 
602 // +--------------------------------------------------------------------+
603 
604 void
605 Font::DrawText(const char* text, int count, Rect& text_rect, DWORD flags, Bitmap* tgt)
606 {
607  Rect clip_rect = text_rect;
608 
609  if (clip_rect.w < 1 || clip_rect.h < 1)
610  return;
611 
612  tgt_bitmap = tgt;
613 
614  if (text && text[0]) {
615  if (count < 1)
616  count = strlen(text);
617 
618  // single line:
619  if (flags & DT_SINGLELINE) {
620  DrawTextSingle(text, count, text_rect, clip_rect, flags);
621  }
622 
623  // multi-line with word wrap:
624  else if (flags & DT_WORDBREAK) {
625  DrawTextWrap(text, count, text_rect, clip_rect, flags);
626  }
627 
628  // multi-line with clip:
629  else {
630  DrawTextMulti(text, count, text_rect, clip_rect, flags);
631  }
632  }
633  else {
634  caret_x = text_rect.x + 2;
635  caret_y = text_rect.y + 2;
636  }
637 
638  // if calc only, update the rectangle:
639  if (flags & DT_CALCRECT) {
640  text_rect.h = clip_rect.h;
641  text_rect.w = clip_rect.w;
642  }
643 
644  // otherwise, draw caret if requested:
645  else if (caret_index >= 0 && caret_y >= text_rect.y && caret_y <= text_rect.y + text_rect.h) {//caret_y + height < text_rect.y + text_rect.h) {
646  Video* video = Video::GetInstance();
647 
648  if (video && (GetRealTime() / 500) & 1) {
649  float v[4];
650  v[0] = (float) (caret_x + 1);
651  v[1] = (float) (caret_y);
652  v[2] = (float) (caret_x + 1);
653  v[3] = (float) (caret_y + height);
654 
655  video->DrawScreenLines(1, v, color, blend);
656  }
657 
658  caret_index = -1;
659  }
660 
661  tgt_bitmap = 0;
662 }
663 
664 // +--------------------------------------------------------------------+
665 
666 static int find_next_word_start(const char* text, int index)
667 {
668  // step through intra-word space:
669  while (text[index] && isspace(text[index]) && text[index] != '\n')
670  index++;
671 
672  return index;
673 }
674 
675 static int find_next_word_end(const char* text, int index)
676 {
677  if (index < 0)
678  return index;
679 
680  // check for leading newline:
681  if (text[index] == '\n')
682  return index;
683 
684  // step through intra-word space:
685  while (text[index] && isspace(text[index]))
686  index++;
687 
688  // step through word:
689  while (text[index] && !isspace(text[index]))
690  index++;
691 
692  return index-1;
693 }
694 
695 // +--------------------------------------------------------------------+
696 
697 void
698 Font::DrawTextSingle(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
699 {
700  // parse the format flags:
701  bool nodraw = (flags & DT_CALCRECT) ?true:false;
702 
703  int align = DT_LEFT;
704  if (flags & DT_RIGHT)
705  align = DT_RIGHT;
706  else if (flags & DT_CENTER)
707  align = DT_CENTER;
708 
709  int max_width = 0;
710 
711  int valign = DT_TOP;
712  if (flags & DT_BOTTOM) valign = DT_BOTTOM;
713  else if (flags & DT_VCENTER) valign = DT_VCENTER;
714 
715  int xoffset = 0;
716  int yoffset = 0;
717 
718  int length = StringWidth(text, count);
719  if (length < text_rect.w) {
720  switch (align) {
721  default:
722  case DT_LEFT: break;
723  case DT_RIGHT: xoffset = text_rect.w - length; break;
724  case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
725  }
726  }
727 
728  if (Height() < text_rect.h) {
729  switch (valign) {
730  default:
731  case DT_TOP: break;
732  case DT_BOTTOM: yoffset = text_rect.h - Height(); break;
733  case DT_VCENTER: yoffset = (text_rect.h - Height())/2; break;
734  }
735  }
736 
737  max_width = length;
738 
739  // if calc only, update the rectangle:
740  if (nodraw) {
741  clip_rect.h = Height();
742  clip_rect.w = max_width;
743  }
744 
745  // otherwise, draw the string now:
746  else {
747  int x1 = text_rect.x + xoffset;
748  int y1 = text_rect.y + yoffset;
749 
750  DrawString(text, count, x1, y1, text_rect);
751  }
752 
753  if (caret_index >= 0 && caret_index <= count) {
754  caret_x = text_rect.x + xoffset;
755  caret_y = text_rect.y + yoffset;
756 
757  if (caret_index > 0)
758  caret_x += StringWidth(text, caret_index);
759  }
760 
761  else {
762  caret_x = text_rect.x + 0;
763  caret_y = text_rect.y + 0;
764  }
765 }
766 
767 // +--------------------------------------------------------------------+
768 
769 void
770 Font::DrawTextWrap(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
771 {
772  // parse the format flags:
773  bool nodraw = (flags & DT_CALCRECT) ?true:false;
774 
775  int align = DT_LEFT;
776  if (flags & DT_RIGHT)
777  align = DT_RIGHT;
778  else if (flags & DT_CENTER)
779  align = DT_CENTER;
780 
781  int nlines = 0;
782  int max_width = 0;
783 
784  int line_start = 0;
785  int line_count = 0;
786  int count_remaining = count;
787  int curr_word_end = -1;
788  int next_word_end = 0;
789  int eol_index = 0;
790 
791  int xoffset = 0;
792  int yoffset = 0;
793 
794  caret_x = -1;
795  caret_y = -1;
796 
797  // repeat for each line of text:
798  while (count_remaining > 0) {
799  int length = 0;
800 
801  // find the end of the last whole word that fits on the line:
802  for (;;) {
803  next_word_end = find_next_word_end(text, curr_word_end+1);
804 
805  if (next_word_end < 0 || next_word_end == curr_word_end)
806  break;
807 
808  if (text[next_word_end] == '\n') {
809  eol_index = curr_word_end = next_word_end;
810  break;
811  }
812 
813  int word_len = next_word_end - line_start + 1;
814 
815  length = StringWidth(text+line_start, word_len);
816 
817  if (length < text_rect.w) {
818  curr_word_end = next_word_end;
819 
820  // check for a newline in the next block of white space:
821  eol_index = 0;
822  const char* eol = &text[curr_word_end+1];
823  while (*eol && isspace(*eol) && *eol != '\n')
824  eol++;
825 
826  if (*eol == '\n') {
827  eol_index = eol - text;
828  break;
829  }
830  }
831  else
832  break;
833  }
834 
835  line_count = curr_word_end - line_start + 1;
836 
837  if (line_count > 0) {
838  length = StringWidth(text+line_start, line_count);
839  }
840 
841  // there was a single word longer than the entire line:
842  else {
843  line_count = next_word_end - line_start + 1;
844  length = StringWidth(text+line_start, line_count);
845  curr_word_end = next_word_end;
846  }
847 
848  xoffset = 0;
849  if (length < text_rect.w) {
850  switch (align) {
851  default:
852  case DT_LEFT: break;
853  case DT_RIGHT: xoffset = text_rect.w - length; break;
854  case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
855  }
856  }
857 
858  if (length > max_width) max_width = length;
859 
860  if (eol_index > 0)
861  curr_word_end = eol_index;
862 
863  int next_line_start = find_next_word_start(text, curr_word_end+1);
864 
865  if (length > 0 && !nodraw) {
866  int x1 = text_rect.x + xoffset;
867  int y1 = text_rect.y + yoffset;
868 
869  DrawString(text+line_start, line_count, x1, y1, text_rect);
870 
871  if (caret_index == line_start) {
872  caret_x = x1 - 2;
873  caret_y = y1;
874  }
875  else if (caret_index > line_start && caret_index < next_line_start) {
876  caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
877  caret_y = text_rect.y + yoffset;
878  }
879  else if (caret_index == count) {
880  if (text[count-1] == '\n') {
881  caret_x = x1 - 2;
882  caret_y = y1 + height;
883  }
884  else {
885  caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
886  caret_y = text_rect.y + yoffset;
887  }
888  }
889  }
890 
891  nlines++;
892  yoffset += Height();
893  if (eol_index > 0)
894  curr_word_end = eol_index;
895  line_start = find_next_word_start(text, curr_word_end+1);
896  count_remaining = count - line_start;
897  }
898 
899  // if calc only, update the rectangle:
900  if (nodraw) {
901  clip_rect.h = nlines * Height();
902  clip_rect.w = max_width;
903  }
904 }
905 
906 // +--------------------------------------------------------------------+
907 
908 void
909 Font::DrawTextMulti(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
910 {
911  // parse the format flags:
912  bool nodraw = (flags & DT_CALCRECT) ?true:false;
913 
914  int align = DT_LEFT;
915  if (flags & DT_RIGHT)
916  align = DT_RIGHT;
917  else if (flags & DT_CENTER)
918  align = DT_CENTER;
919 
920  int max_width = 0;
921  int line_start = 0;
922  int count_remaining = count;
923 
924  int xoffset = 0;
925  int yoffset = 0;
926  int nlines = 0;
927 
928  // repeat for each line of text:
929  while (count_remaining > 0) {
930  int length = 0;
931  int line_count = 0;
932 
933  // find the end of line:
934  while (line_count < count_remaining) {
935  char c = text[line_start+line_count];
936  if (!c || c == '\n')
937  break;
938 
939  line_count++;
940  }
941 
942  if (line_count > 0) {
943  length = StringWidth(text+line_start, line_count);
944  }
945 
946  xoffset = 0;
947  if (length < text_rect.w) {
948  switch (align) {
949  default:
950  case DT_LEFT: break;
951  case DT_RIGHT: xoffset = text_rect.w - length; break;
952  case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
953  }
954  }
955 
956  if (length > max_width) max_width = length;
957 
958  if (length && !nodraw) {
959  int x1 = text_rect.x + xoffset;
960  int y1 = text_rect.y + yoffset;
961 
962  DrawString(text+line_start, line_count, x1, y1, text_rect);
963  }
964 
965  nlines++;
966  yoffset += Height();
967 
968  if (line_start+line_count+1 < count) {
969  line_start = find_next_word_start(text, line_start+line_count+1);
970  count_remaining = count - line_start;
971  }
972  else {
973  count_remaining = 0;
974  }
975  }
976 
977  // if calc only, update the rectangle:
978  if (nodraw) {
979  clip_rect.h = nlines * Height();
980  clip_rect.w = max_width;
981  }
982 }
983 
984 // +--------------------------------------------------------------------+
985 
986 int
987 Font::DrawString(const char* str, int len, int x1, int y1, const Rect& clip, Bitmap* tgt)
988 {
989  Video* video = Video::GetInstance();
990  int count = 0;
991  int maxw = clip.w;
992  int maxh = clip.h;
993 
994  if (len < 1 || !video)
995  return count;
996 
997  // vertical clip
998  if ((y1 < clip.y) || (y1 > clip.y + clip.h))
999  return count;
1000 
1001  // RENDER TO BITMAP
1002 
1003  if (!tgt)
1004  tgt = tgt_bitmap;
1005 
1006  if (tgt) {
1007  for (int i = 0; i < len; i++) {
1008  char c = str[i];
1009 
1010  if ((flags & FONT_ALL_CAPS) && islower(c))
1011  c = toupper(c);
1012 
1013  int cw = glyph[c].width + interspace;
1014  int ch = height;
1015  int k = 0;
1016 
1017  if (i < len-1)
1018  k = kern[c][str[i+1]];
1019 
1020  // horizontal clip:
1021  if (x1 < clip.x) {
1022  if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
1023  x1 += spacewidth;
1024  maxw -= spacewidth;
1025  }
1026  else {
1027  x1 += cw+k;
1028  maxw -= cw+k;
1029  }
1030  }
1031  else if (x1+cw > clip.x+clip.w) {
1032  return count;
1033  }
1034  else {
1035  if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
1036  x1 += spacewidth;
1037  maxw -= spacewidth;
1038  }
1039  else {
1040  int sx = GlyphLocationX(c);
1041  int sy = GlyphLocationY(c);
1042 
1043  Color* srcpix = bitmap.HiPixels();
1044  Color* dstpix = tgt->HiPixels();
1045  if (srcpix && dstpix) {
1046  int spitch = bitmap.Width();
1047  int dpitch = tgt->Width();
1048 
1049  Color* dst = dstpix + (y1*dpitch) + x1;
1050  Color* src = srcpix + (sy*spitch) + sx;
1051 
1052  for (int i = 0; i < ch; i++) {
1053  Color* ps = src;
1054  Color* pd = dst;
1055 
1056  for (int n = 0; n < cw; n++) {
1057  DWORD alpha = ps->Alpha();
1058  if (alpha) {
1059  *pd = color.dim(alpha / 240.0);
1060  }
1061  ps++;
1062  pd++;
1063  }
1064 
1065  dst += dpitch;
1066  src += spitch;
1067  }
1068  }
1069  else {
1070  // this probably won't work...
1071  tgt->BitBlt(x1, y1, bitmap, sx, sy, cw, ch, true);
1072  }
1073 
1074  x1 += cw + k;
1075  maxw -= cw + k;
1076  }
1077 
1078  count++;
1079  }
1080  }
1081  return count;
1082  }
1083 
1084  // RENDER TO VIDEO
1085 
1086  // allocate verts, if necessary
1087  int nverts = 4*len;
1088  if (!vset) {
1089  vset = new(__FILE__,__LINE__) VertexSet(nverts);
1090 
1091  if (!vset)
1092  return false;
1093 
1095 
1096  for (int v = 0; v < vset->nverts; v++) {
1097  vset->s_loc[v].z = 0.0f;
1098  vset->rw[v] = 1.0f;
1099  }
1100  }
1101  else if (vset->nverts < nverts) {
1102  vset->Resize(nverts);
1103 
1104  for (int v = 0; v < vset->nverts; v++) {
1105  vset->s_loc[v].z = 0.0f;
1106  vset->rw[v] = 1.0f;
1107  }
1108  }
1109 
1110  if (vset->nverts < nverts)
1111  return count;
1112 
1113  if (alpha < 1)
1114  color.SetAlpha((BYTE) (alpha * 255.0f));
1115  else
1116  color.SetAlpha(255);
1117 
1118  for (int i = 0; i < len; i++) {
1119  char c = str[i];
1120 
1121  if ((flags & FONT_ALL_CAPS) && islower(c))
1122  c = toupper(c);
1123 
1124  int cw = glyph[c].width + interspace;
1125  int k = 0;
1126 
1127  if (i < len-1)
1128  k = kern[c][str[i+1]];
1129 
1130  // horizontal clip:
1131  if (x1 < clip.x) {
1132  if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
1133  x1 += spacewidth;
1134  maxw -= spacewidth;
1135  }
1136  else {
1137  x1 += cw+k;
1138  maxw -= cw+k;
1139  }
1140  }
1141  else if (x1+cw > clip.x+clip.w) {
1142  break;
1143  }
1144  else {
1145  if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
1146  x1 += spacewidth;
1147  maxw -= spacewidth;
1148  }
1149  else {
1150  // create four verts for this character:
1151  int v = count*4;
1152  double char_x = GlyphLocationX(c);
1153  double char_y = GlyphLocationY(c);
1154  double char_w = glyph[c].width;
1155  double char_h = height;
1156 
1157  if (y1 + char_h > clip.y + clip.h) {
1158  char_h = clip.y + clip.h - y1;
1159  }
1160 
1161  vset->s_loc[v+0].x = (float) (x1 - 0.5);
1162  vset->s_loc[v+0].y = (float) (y1 - 0.5);
1163  vset->tu[v+0] = (float) (char_x / 256);
1164  vset->tv[v+0] = (float) (char_y / 256);
1165  vset->diffuse[v+0] = color.Value();
1166 
1167  vset->s_loc[v+1].x = (float) (x1 + char_w - 0.5);
1168  vset->s_loc[v+1].y = (float) (y1 - 0.5);
1169  vset->tu[v+1] = (float) (char_x / 256 + char_w / 256);
1170  vset->tv[v+1] = (float) (char_y / 256);
1171  vset->diffuse[v+1] = color.Value();
1172 
1173  vset->s_loc[v+2].x = (float) (x1 + char_w - 0.5);
1174  vset->s_loc[v+2].y = (float) (y1 + char_h - 0.5);
1175  vset->tu[v+2] = (float) (char_x / 256 + char_w / 256);
1176  vset->tv[v+2] = (float) (char_y / 256 + char_h / 256);
1177  vset->diffuse[v+2] = color.Value();
1178 
1179  vset->s_loc[v+3].x = (float) (x1 - 0.5);
1180  vset->s_loc[v+3].y = (float) (y1 + char_h - 0.5);
1181  vset->tu[v+3] = (float) (char_x / 256);
1182  vset->tv[v+3] = (float) (char_y / 256 + char_h / 256);
1183  vset->diffuse[v+3] = color.Value();
1184 
1185  x1 += cw + k;
1186  maxw -= cw + k;
1187 
1188  count++;
1189  }
1190  }
1191  }
1192 
1193  if (count) {
1194  // this small hack is an optimization to reduce the
1195  // size of vertex buffer needed for font rendering:
1196 
1197  int old_nverts = vset->nverts;
1198  vset->nverts = 4 * count;
1199 
1200  // create a larger poly array, if necessary:
1201  if (count > npolys) {
1202  if (polys)
1203  delete [] polys;
1204 
1205  npolys = count;
1206  polys = new(__FILE__,__LINE__) Poly[npolys];
1207  Poly* p = polys;
1208  int index = 0;
1209 
1210  for (int i = 0; i < npolys; i++) {
1211  p->nverts = 4;
1212  p->vertex_set = vset;
1213  p->material = material;
1214  p->verts[0] = index++;
1215  p->verts[1] = index++;
1216  p->verts[2] = index++;
1217  p->verts[3] = index++;
1218 
1219  p++;
1220  }
1221  }
1222 
1223  video->DrawScreenPolys(count, polys, blend);
1224 
1225  // remember to restore the proper size of the vertex set:
1226  vset->nverts = old_nverts;
1227  }
1228 
1229  return count;
1230 }
1231