00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #ifdef _MSC_VER
00040 # pragma warning (disable: 4786)
00041 #endif
00042
00043 #include "OSGTextFace.h"
00044 #include "OSGTextLayoutParam.h"
00045 #include "OSGTextLayoutResult.h"
00046 #ifdef __sgi
00047 # include <assert.h>
00048 # include <float.h>
00049 #else
00050 # include <cassert>
00051 # include <cfloat>
00052 #endif
00053
00054
00055 using namespace std;
00056
00057
00058 OSG_BEGIN_NAMESPACE
00059
00060
00061
00062
00063
00064
00065 TextFace::~TextFace() {}
00066
00067
00068
00069
00070
00071
00072 void TextFace::layout(const string &utf8Text,
00073 const TextLayoutParam ¶m,
00074 TextLayoutResult &result)
00075 {
00076
00077 wstring text;
00078 convertUTF8ToUnicode(utf8Text, text);
00079
00080
00081 layout(text, param, result);
00082 }
00083
00084
00085
00086
00087
00088
00089 void TextFace::layout(const vector<string> &lines,
00090 const TextLayoutParam ¶m,
00091 TextLayoutResult &result)
00092 {
00093
00094 vector<wstring> unicodeLines(lines.size());
00095 vector<wstring>::iterator unicodeLineIt;
00096 vector<string>::const_iterator lineIt;
00097 for (lineIt = lines.begin(), unicodeLineIt = unicodeLines.begin();
00098 lineIt != lines.end(); ++lineIt, ++unicodeLineIt)
00099 convertUTF8ToUnicode(*lineIt, *unicodeLineIt);
00100
00101
00102 layout(unicodeLines, param, result);
00103 }
00104
00105
00106
00107
00108
00109
00110 void TextFace::layout(const vector<wstring> &lines,
00111 const TextLayoutParam ¶m,
00112 TextLayoutResult &result)
00113 {
00114
00115 result.clear();
00116
00117 TextLayoutParam lineParam = param;
00118 Vec2f offset, lineOffset;
00119 if (param.horizontal == true)
00120 {
00121 Real32 lineHeight = _horiAscent - _horiDescent;
00122 Real32 spacing = param.spacing * lineHeight;
00123 Real32 leading = spacing - lineHeight;
00124 Real32 fullHeight = spacing * lines.size() - leading;
00125 result.textBounds[1] = fullHeight;
00126 lineParam.minorAlignment = TextLayoutParam::ALIGN_FIRST;
00127 if (param.topToBottom == true)
00128 {
00129 lineOffset[1] = -spacing;
00130 switch (param.minorAlignment)
00131 {
00132 default:
00133 case TextLayoutParam::ALIGN_FIRST:
00134 break;
00135 case TextLayoutParam::ALIGN_BEGIN:
00136 offset[1] = -_horiAscent;
00137 break;
00138 case TextLayoutParam::ALIGN_MIDDLE:
00139 offset[1] = -_horiAscent + fullHeight / 2.f;
00140 break;
00141 case TextLayoutParam::ALIGN_END:
00142 offset[1] = -_horiAscent + fullHeight;
00143 break;
00144 }
00145 }
00146 else
00147 {
00148 lineOffset[1] = spacing;
00149 switch (param.minorAlignment)
00150 {
00151 default:
00152 case TextLayoutParam::ALIGN_FIRST:
00153 break;
00154 case TextLayoutParam::ALIGN_BEGIN:
00155 offset[1] = -_horiDescent;
00156 break;
00157 case TextLayoutParam::ALIGN_MIDDLE:
00158 offset[1] = -_horiDescent - fullHeight / 2.f;
00159 break;
00160 case TextLayoutParam::ALIGN_END:
00161 offset[1] = -_horiDescent - fullHeight;
00162 break;
00163 }
00164 }
00165 }
00166 else
00167 {
00168 Real32 lineHeight = _vertDescent - _vertAscent;
00169 Real32 spacing = param.spacing * lineHeight;
00170 Real32 leading = spacing - lineHeight;
00171 Real32 fullHeight = spacing * lines.size() - leading;
00172 result.textBounds[0] = fullHeight;
00173 lineParam.minorAlignment = TextLayoutParam::ALIGN_FIRST;
00174 if (param.leftToRight == true)
00175 {
00176 lineOffset[0] = spacing;
00177 switch (param.minorAlignment)
00178 {
00179 default:
00180 case TextLayoutParam::ALIGN_FIRST:
00181 break;
00182 case TextLayoutParam::ALIGN_BEGIN:
00183 offset[0] = -_vertAscent;
00184 break;
00185 case TextLayoutParam::ALIGN_MIDDLE:
00186 offset[0] = -_vertAscent - fullHeight / 2.f;
00187 break;
00188 case TextLayoutParam::ALIGN_END:
00189 offset[0] = -_vertAscent - fullHeight;
00190 break;
00191 }
00192 }
00193 else
00194 {
00195 lineOffset[0] = -spacing;
00196 switch (param.minorAlignment)
00197 {
00198 default:
00199 case TextLayoutParam::ALIGN_FIRST:
00200 break;
00201 case TextLayoutParam::ALIGN_BEGIN:
00202 offset[0] = -_vertDescent;
00203 break;
00204 case TextLayoutParam::ALIGN_MIDDLE:
00205 offset[0] = -_vertDescent + fullHeight / 2.f;
00206 break;
00207 case TextLayoutParam::ALIGN_END:
00208 offset[0] = -_vertDescent + fullHeight;
00209 break;
00210 }
00211 }
00212 }
00213
00214 result.lineBounds.reserve(lines.size());
00215 TextLayoutResult lineResult;
00216 Real32 maxExtend = 0.f;
00217 UInt32 lineIndex;
00218 for (lineIndex = 0; lineIndex < lines.size(); ++lineIndex)
00219 {
00220 lineParam.setLength(param.getLength(lineIndex));
00221 layout(lines[lineIndex], lineParam, lineResult);
00222 UInt32 i, numGlyphs = lineResult.getNumGlyphs();
00223 for (i = 0; i < numGlyphs; ++i)
00224 {
00225 result.indices.push_back(lineResult.indices[i]);
00226 result.positions.push_back(lineResult.positions[i] + offset);
00227 }
00228 assert(lineResult.lineBounds.empty() == false);
00229 result.lineBounds.push_back(lineResult.lineBounds.front());
00230 Real32 extend = lineResult.lineBounds.front()[param.horizontal == true ? 0 : 1];
00231 if (extend > maxExtend)
00232 maxExtend = extend;
00233 offset += lineOffset;
00234 }
00235 result.textBounds[param.horizontal == true ? 0 : 1] = maxExtend;
00236 }
00237
00238
00239
00240
00241
00242
00243 void TextFace::calculateBoundingBox(const TextLayoutResult &layoutResult, Vec2f &lowerLeft, Vec2f &upperRight)
00244 {
00245
00246 lowerLeft.setValues(FLT_MAX, FLT_MAX);
00247 upperRight.setValues(-FLT_MAX, -FLT_MAX);
00248
00249 UInt32 i, numGlyphs = layoutResult.getNumGlyphs();
00250 for (i = 0; i < numGlyphs; ++i)
00251 {
00252 const TextGlyph &glyph = getGlyph(layoutResult.indices[i]);
00253 Real32 width = glyph.getWidth();
00254 Real32 height = glyph.getHeight();
00255
00256 if ((width <= 0.f) || (height <= 0.f))
00257 continue;
00258
00259
00260 const Vec2f &pos = layoutResult.positions[i];
00261 Real32 left = pos.x();
00262 Real32 right = left + glyph.getWidth();
00263 Real32 top = pos.y();
00264 Real32 bottom = top - glyph.getHeight();
00265
00266
00267 if (lowerLeft[0] > left)
00268 lowerLeft[0] = left;
00269 if (upperRight[0] < right)
00270 upperRight[0] = right;
00271 if (upperRight[1] < top)
00272 upperRight[1] = top;
00273 if (lowerLeft[1] > bottom)
00274 lowerLeft[1] = bottom;
00275 }
00276 }
00277
00278
00279
00280
00281
00282
00283 static wchar_t utf8Char2Unicode(const char *&str)
00284 {
00285 char c = *str++;
00286 if ((c & 0x80) == 0)
00287 return c;
00288 if (((c & 0xc0) == 0x80) || ((c & 0xfe) == 0xfe))
00289 return (unsigned char)c;
00290 char mask = 0xe0, value = 0xc0;
00291 int i;
00292 for (i = 1; i < 6; ++i)
00293 {
00294 if ((c & mask) == value)
00295 break;
00296 value = mask;
00297 mask >>= 1;
00298 }
00299 wchar_t result = c & ~mask;
00300 for (; i > 0; --i)
00301 {
00302 c = *str++;
00303 if ((c & 0xc0) != 0x80)
00304 ;
00305 result <<= 6;
00306 result |= c & 0x3f;
00307 }
00308 return result;
00309 }
00310
00311
00312
00313
00314
00315
00316
00317
00318 void TextFace::convertUTF8ToUnicode(const string &utf8Text, wstring &text)
00319 {
00320
00321 text.erase();
00322 text.reserve(utf8Text.length());
00323
00324
00325 const char *pos = utf8Text.c_str();
00326 while (*pos != '\0')
00327 text.append(1, utf8Char2Unicode(pos));
00328 }
00329
00330
00331
00332
00333
00334
00335 void TextFace::justifyLine(const TextLayoutParam ¶m,
00336 const vector<UInt32> &spaceIndices,
00337 Vec2f &currPos, TextLayoutResult &layoutResult) const
00338 {
00339
00340 UInt32 numGlyphs = layoutResult.positions.size();
00341 if (numGlyphs < 2)
00342 return;
00343
00344
00345 Real32 actualLength = osgabs(param.horizontal == true ? currPos.x() : currPos.y());
00346 Real32 delta = param.getLength(0) - actualLength;
00347
00348
00349 UInt32 numSpaces = spaceIndices.size();
00350 numGlyphs -= 1 + numSpaces;
00351
00352 Real32 factor = 0.5;
00353 Real32 spaceDelta = delta / (static_cast<Real32>(numSpaces) + static_cast<Real32>(numGlyphs) * factor);
00354 Real32 glyphDelta = spaceDelta * factor;
00355 Vec2f spaceOffset, glyphOffset;
00356 if (param.horizontal == true)
00357 if (param.leftToRight == true)
00358 {
00359 spaceOffset.setValues(spaceDelta, 0.f);
00360 glyphOffset.setValues(glyphDelta, 0.f);
00361 }
00362 else
00363 {
00364 spaceOffset.setValues(-spaceDelta, 0.f);
00365 glyphOffset.setValues(-glyphDelta, 0.f);
00366 }
00367 else
00368 if (param.topToBottom == true)
00369 {
00370 spaceOffset.setValues(0.f, -spaceDelta);
00371 glyphOffset.setValues(0.f, -glyphDelta);
00372 }
00373 else
00374 {
00375 spaceOffset.setValues(0.f, spaceDelta);
00376 glyphOffset.setValues(0.f, glyphDelta);
00377 }
00378
00379
00380 Vec2f offset;
00381 vector<UInt32>::const_iterator it = spaceIndices.begin();
00382 UInt32 i;
00383 for (i = 1; i < layoutResult.positions.size(); ++i)
00384 {
00385 if ((it == spaceIndices.end()) || (i < *it))
00386 offset += glyphOffset;
00387 else
00388 {
00389 offset += spaceOffset;
00390 ++it;
00391 }
00392 layoutResult.positions[i] += offset;
00393 }
00394 currPos += offset;
00395 }
00396
00397
00398
00399
00400
00401
00402 void TextFace::adjustLineOrigin(const TextLayoutParam ¶m,
00403 const Vec2f &currPos,
00404 TextLayoutResult &layoutResult) const
00405 {
00406 Vec2f offset;
00407 if (param.horizontal == true)
00408 {
00409 switch (param.minorAlignment)
00410 {
00411 default:
00412 case TextLayoutParam::ALIGN_FIRST: offset[1] = 0.f; break;
00413 case TextLayoutParam::ALIGN_BEGIN: offset[1] = -_horiAscent; break;
00414 case TextLayoutParam::ALIGN_MIDDLE: offset[1] = -(_horiAscent + _horiDescent) / 2.f; break;
00415 case TextLayoutParam::ALIGN_END: offset[1] = -_horiDescent; break;
00416 }
00417 if (param.leftToRight == true)
00418 {
00419 switch (param.majorAlignment)
00420 {
00421 default:
00422 case TextLayoutParam::ALIGN_FIRST:
00423 case TextLayoutParam::ALIGN_BEGIN:
00424 if (currPos.x() < 0)
00425 offset[0] = -currPos.x();
00426 break;
00427 case TextLayoutParam::ALIGN_MIDDLE:
00428 offset[0] = -currPos.x() / 2.f;
00429 break;
00430 case TextLayoutParam::ALIGN_END:
00431 if (currPos.x() > 0)
00432 offset[0] = -currPos.x();
00433 break;
00434 }
00435 }
00436 else
00437 {
00438 switch (param.majorAlignment)
00439 {
00440 default:
00441 case TextLayoutParam::ALIGN_FIRST:
00442 case TextLayoutParam::ALIGN_BEGIN:
00443 if (currPos.x() > 0)
00444 offset[0] = -currPos.x();
00445 break;
00446 case TextLayoutParam::ALIGN_MIDDLE:
00447 offset[0] = -currPos.x() / 2.f;
00448 break;
00449 case TextLayoutParam::ALIGN_END:
00450 if (currPos.x() < 0)
00451 offset[0] = -currPos.x();
00452 break;
00453 }
00454 }
00455 }
00456 else
00457 {
00458 switch (param.minorAlignment)
00459 {
00460 default:
00461 case TextLayoutParam::ALIGN_FIRST: offset[0] = 0.f; break;
00462 case TextLayoutParam::ALIGN_BEGIN: offset[0] = -_vertAscent; break;
00463 case TextLayoutParam::ALIGN_MIDDLE: offset[0] = -(_vertAscent + _vertDescent) / 2.f; break;
00464 case TextLayoutParam::ALIGN_END: offset[0] = -_vertDescent; break;
00465 }
00466 if (param.topToBottom == true)
00467 {
00468 switch (param.majorAlignment)
00469 {
00470 default:
00471 case TextLayoutParam::ALIGN_FIRST:
00472 case TextLayoutParam::ALIGN_BEGIN:
00473 if (currPos.y() > 0)
00474 offset[1] = -currPos.y();
00475 break;
00476 case TextLayoutParam::ALIGN_MIDDLE:
00477 offset[1] = -currPos.y() / 2.f;
00478 break;
00479 case TextLayoutParam::ALIGN_END:
00480 if (currPos.y() < 0)
00481 offset[1] = -currPos.y();
00482 break;
00483 }
00484 }
00485 else
00486 {
00487 switch (param.majorAlignment)
00488 {
00489 default:
00490 case TextLayoutParam::ALIGN_FIRST:
00491 case TextLayoutParam::ALIGN_BEGIN:
00492 if (currPos.y() < 0)
00493 offset[1] = -currPos.y();
00494 break;
00495 case TextLayoutParam::ALIGN_MIDDLE:
00496 offset[1] = -currPos.y() / 2.f;
00497 break;
00498 case TextLayoutParam::ALIGN_END:
00499 if (currPos.y() > 0)
00500 offset[1] = -currPos.y();
00501 break;
00502 }
00503 }
00504 }
00505
00506
00507 if ((offset.x() != 0.f) || (offset.y() != 0.f))
00508 {
00509 vector<Vec2f>::iterator it;
00510 for (it = layoutResult.positions.begin(); it != layoutResult.positions.end(); ++it)
00511 *it += offset;
00512 }
00513 }
00514
00515
00516 OSG_END_NAMESPACE
00517
00518
00519
00520
00521
00522 #ifdef OSG_SGI_CC
00523 #pragma set woff 1174
00524 #endif
00525
00526 #ifdef OSG_LINUX_ICC
00527 #pragma warning( disable : 177 )
00528 #endif
00529
00530 namespace
00531 {
00532 static OSG::Char8 cvsid_cpp[] = "@(#)$Id: OSGTextFace.cpp,v 1.2 2005/06/01 10:42:15 pdaehne Exp $";
00533 static OSG::Char8 cvsid_hpp[] = OSGTEXTFACE_HEADER_CVSID;
00534 static OSG::Char8 cvsid_inl[] = OSGTEXTFACE_INLINE_CVSID;
00535 }
00536
00537 #ifdef __sgi
00538 #pragma reset woff 1174
00539 #endif