From 8898ad9b25fca6afe2374d293a981db02a83d7e9 Mon Sep 17 00:00:00 2001 From: "FWoltermann@gmail.com" Date: Thu, 31 May 2012 14:46:27 +0000 Subject: Committing the documentation to svn to have it accessible online --- Doc/doxygen/html/_font_8cpp_source.html | 1348 +++++++++++++++++++++++++++++++ 1 file changed, 1348 insertions(+) create mode 100644 Doc/doxygen/html/_font_8cpp_source.html (limited to 'Doc/doxygen/html/_font_8cpp_source.html') diff --git a/Doc/doxygen/html/_font_8cpp_source.html b/Doc/doxygen/html/_font_8cpp_source.html new file mode 100644 index 0000000..082be72 --- /dev/null +++ b/Doc/doxygen/html/_font_8cpp_source.html @@ -0,0 +1,1348 @@ + + + + + +Starshatter_Open: D:/SRC/StarshatterSVN/nGenEx/Font.cpp Source File + + + + + + + + + + + + + +
+
+ + + + + + +
+
Starshatter_Open +
+
Open source Starshatter engine
+
+
+ + + + + +
+
+ +
+
+
+ +
+ + + + +
+ +
+ +
+
+
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 
+
+
+ + + + -- cgit v1.1