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 #include <vlGraphics/CoreText.hpp>
00033 #include <vlGraphics/OpenGLContext.hpp>
00034 #include <vlGraphics/Actor.hpp>
00035 #include <vlCore/Log.hpp>
00036
00037 #include <ft2build.h>
00038 #include FT_FREETYPE_H
00039
00040 using namespace vl;
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 void CoreText::render_Implementation(const Actor* actor, const Shader*, const Camera* camera, OpenGLContext* gl_context) const
00062 {
00063 gl_context->bindVAS(NULL, false, false);
00064
00065 VL_CHECK(font())
00066
00067 if (!font() || !font()->mFT_Face)
00068 return;
00069
00070 if ( text().empty() )
00071 return;
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094 GLboolean depth_mask=0;
00095 glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask);
00096 glDepthMask(GL_FALSE);
00097
00098
00099 if (backgroundEnabled())
00100 renderBackground( actor, camera );
00101
00102
00103 if (borderEnabled())
00104 renderBorder( actor, camera );
00105
00106
00107
00108
00109 if (shadowEnabled())
00110 renderText( actor, camera, shadowColor(), shadowVector() );
00111
00112 if (outlineEnabled())
00113 {
00114 renderText( actor, camera, outlineColor(), fvec2(-1,0) );
00115 renderText( actor, camera, outlineColor(), fvec2(+1,0) );
00116 renderText( actor, camera, outlineColor(), fvec2(0,-1) );
00117 renderText( actor, camera, outlineColor(), fvec2(0,+1) );
00118 }
00119
00120 renderText( actor, camera, color(), fvec2(0,0) );
00121
00122
00123
00124
00125
00126 glDepthMask(depth_mask);
00127
00128 if (depth_mask)
00129 {
00130
00131 GLboolean color_mask[4];
00132 glGetBooleanv(GL_COLOR_WRITEMASK, color_mask);
00133 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
00134
00135
00136 int stencil_front_mask=0;
00137 glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_front_mask);
00138 int stencil_back_mask=0;
00139 if (Has_GL_Version_2_0)
00140 glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &stencil_back_mask);
00141 glStencilMask(0);
00142
00143
00144 renderBackground( actor, camera );
00145
00146
00147 renderBorder( actor, camera );
00148
00149
00150 glColorMask(color_mask[0],color_mask[1],color_mask[2],color_mask[3]);
00151
00152
00153 glStencilMask(stencil_front_mask);
00154 if (Has_GL_Version_2_0)
00155 glStencilMaskSeparate(GL_BACK, stencil_back_mask);
00156 }
00157 }
00158
00159 void CoreText::renderText(const Actor*, const Camera*, const fvec4& color, const fvec2& offset) const
00160 {
00161 if(!mFont)
00162 {
00163 Log::error("CoreText::renderText() error: no Font assigned to the CoreText object.\n");
00164 VL_TRAP()
00165 return;
00166 }
00167
00168 if (!font()->mFT_Face)
00169 {
00170 Log::error("CoreText::renderText() error: invalid FT_Face: probably you tried to load an unsupported font format.\n");
00171 VL_TRAP()
00172 return;
00173 }
00174
00175
00176
00177 AABB rbbox = rawboundingRect( text() );
00178 VL_CHECK(rbbox.maxCorner().z() == 0)
00179 VL_CHECK(rbbox.minCorner().z() == 0)
00180 AABB bbox = rbbox;
00181 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*margin(), 2.0f*margin(), 0) );
00182 VL_CHECK(bbox.maxCorner().z() == 0)
00183 VL_CHECK(bbox.minCorner().z() == 0)
00184
00185
00186
00187
00188
00189 float texc[] = { 0,0,0,0,0,0,0,0 };
00190 VL_glActiveTexture( GL_TEXTURE0 );
00191 VL_glClientActiveTexture( GL_TEXTURE0 );
00192 glEnable(GL_TEXTURE_2D);
00193 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
00194 glTexCoordPointer(2, GL_FLOAT, 0, texc);
00195
00196
00197 glColor4fv(color.ptr());
00198
00199
00200 glNormal3fv( fvec3(0,0,1).ptr() );
00201
00202 fvec3 vect[4];
00203 glEnableClientState( GL_VERTEX_ARRAY );
00204 glVertexPointer(3, GL_FLOAT, 0, vect[0].ptr());
00205
00206 FT_Long use_kerning = FT_HAS_KERNING( font()->mFT_Face );
00207 FT_UInt previous = 0;
00208
00209 fvec2 pen(0,0);
00210
00211
00212
00213
00214
00215
00216
00217 VL_CHECK(text().length())
00218
00219 std::vector< String > lines;
00220 lines.push_back( String() );
00221 for(int i=0; i<text().length(); ++i)
00222 {
00223 if (text()[i] == '\n')
00224 {
00225
00226 lines.push_back( String() );
00227 }
00228 else
00229 lines.back() += text()[i];
00230 }
00231
00232 for(unsigned iline=0; iline<lines.size(); iline++)
00233 {
00234
00235 if (textAlignment() == TextAlignJustify)
00236 lines[iline].trim();
00237
00238 AABB linebox = rawboundingRect( lines[iline] );
00239 int horz_text_align = 0;
00240 int just_space = 0;
00241 int just_remained_space = 0;
00242 int space_count = 0;
00243 for(int c=0; c<(int)lines[iline].length(); c++)
00244 if ( lines[iline][c] == ' ' )
00245 space_count++;
00246
00247 if (space_count && textAlignment() == TextAlignJustify)
00248 {
00249 just_space = int(rbbox.width() - linebox.width()) / space_count;
00250 just_remained_space = int(rbbox.width() - linebox.width()) % space_count;
00251 }
00252
00253 if (layout() == RightToLeftText)
00254 {
00255 if (textAlignment() == TextAlignRight)
00256 horz_text_align = 0;
00257 else
00258 if (textAlignment() == TextAlignLeft)
00259 horz_text_align = - int(rbbox.width() - linebox.width());
00260 else
00261 if (textAlignment() == TextAlignCenter)
00262 horz_text_align = - int((rbbox.width() - linebox.width()) / 2.0f);
00263 }
00264 if (layout() == LeftToRightText)
00265 {
00266 if (textAlignment() == TextAlignRight)
00267 horz_text_align = int(rbbox.width() - linebox.width());
00268 else
00269 if (textAlignment() == TextAlignLeft)
00270 horz_text_align = 0;
00271 else
00272 if (textAlignment() == TextAlignCenter)
00273 horz_text_align = + int((rbbox.width() - linebox.width()) / 2.0f);
00274 }
00275
00276
00277
00278
00279 if (iline != 0 && !lines[iline].length())
00280 {
00281 pen.y() -= mFont->mHeight;
00282 pen.x() = 0;
00283 }
00284 else
00285 for(int c=0; c<(int)lines[iline].length(); c++)
00286 {
00287 if (c == 0 && iline != 0)
00288 {
00289 pen.y() -= mFont->mHeight;
00290 pen.x() = 0;
00291 }
00292
00293 const Glyph* glyph = mFont->glyph( lines[iline][c] );
00294
00295 if (!glyph)
00296 continue;
00297
00298 if ( kerningEnabled() && use_kerning && previous && glyph->glyphIndex() )
00299 {
00300 FT_Vector delta; delta.y = 0;
00301 if (layout() == LeftToRightText)
00302 {
00303 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta );
00304 pen.x() += delta.x / 64.0f;
00305 }
00306 else
00307 if (layout() == RightToLeftText)
00308 {
00309 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta );
00310 pen.x() -= delta.x / 64.0f;
00311 }
00312 pen.y() += delta.y / 64.0f;
00313 }
00314 previous = glyph->glyphIndex();
00315
00316 if (glyph->textureHandle())
00317 {
00318 glBindTexture( GL_TEXTURE_2D, glyph->textureHandle() );
00319
00320 texc[0] = glyph->s0();
00321 texc[1] = glyph->t1();
00322 texc[2] = glyph->s1();
00323 texc[3] = glyph->t1();
00324 texc[4] = glyph->s1();
00325 texc[5] = glyph->t0();
00326 texc[6] = glyph->s0();
00327 texc[7] = glyph->t0();
00328
00329 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left();
00330
00331 vect[0].x() = pen.x() + glyph->width()*0 + left -1;
00332 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
00333
00334 vect[1].x() = pen.x() + glyph->width()*1 + left +1;
00335 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
00336
00337 vect[2].x() = pen.x() + glyph->width()*1 + left +1;
00338 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
00339
00340 vect[3].x() = pen.x() + glyph->width()*0 + left -1;
00341 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
00342
00343 if (layout() == RightToLeftText)
00344 {
00345 vect[0].x() -= glyph->width()-1 +2;
00346 vect[1].x() -= glyph->width()-1 +2;
00347 vect[2].x() -= glyph->width()-1 +2;
00348 vect[3].x() -= glyph->width()-1 +2;
00349 }
00350
00351 vect[0].y() -= mFont->mHeight;
00352 vect[1].y() -= mFont->mHeight;
00353 vect[2].y() -= mFont->mHeight;
00354 vect[3].y() -= mFont->mHeight;
00355
00356
00357 vect[0] -= (fvec3)bbox.minCorner();
00358 vect[1] -= (fvec3)bbox.minCorner();
00359 vect[2] -= (fvec3)bbox.minCorner();
00360 vect[3] -= (fvec3)bbox.minCorner();
00361
00362
00363 vect[0].x() += margin() + horz_text_align;
00364 vect[0].y() += margin();
00365 vect[1].x() += margin() + horz_text_align;
00366 vect[1].y() += margin();
00367 vect[2].x() += margin() + horz_text_align;
00368 vect[2].y() += margin();
00369 vect[3].x() += margin() + horz_text_align;
00370 vect[3].y() += margin();
00371
00372
00373 vect[0].x() += offset.x();
00374 vect[0].y() += offset.y();
00375 vect[1].x() += offset.x();
00376 vect[1].y() += offset.y();
00377 vect[2].x() += offset.x();
00378 vect[2].y() += offset.y();
00379 vect[3].x() += offset.x();
00380 vect[3].y() += offset.y();
00381
00382
00383 for(int i=0; i<4; ++i)
00384 {
00385 if (textOrigin() & AlignHCenter)
00386 {
00387 VL_CHECK( !(textOrigin() & AlignRight) )
00388 VL_CHECK( !(textOrigin() & AlignLeft) )
00389 vect[i].x() -= (int)(bbox.width() / 2.0f);
00390 }
00391
00392 if (textOrigin() & AlignRight)
00393 {
00394 VL_CHECK( !(textOrigin() & AlignHCenter) )
00395 VL_CHECK( !(textOrigin() & AlignLeft) )
00396 vect[i].x() -= (int)bbox.width();
00397 }
00398
00399 if (textOrigin() & AlignTop)
00400 {
00401 VL_CHECK( !(textOrigin() & AlignBottom) )
00402 VL_CHECK( !(textOrigin() & AlignVCenter) )
00403 vect[i].y() -= (int)bbox.height();
00404 }
00405
00406 if (textOrigin() & AlignVCenter)
00407 {
00408 VL_CHECK( !(textOrigin() & AlignTop) )
00409 VL_CHECK( !(textOrigin() & AlignBottom) )
00410 vect[i].y() -= int(bbox.height() / 2.0);
00411 }
00412 }
00413
00414 glDrawArrays(GL_QUADS, 0, 4);
00415
00416 #if (0)
00417 glDisable(GL_TEXTURE_2D);
00418 glColor3fv(vec3(1,0,0).ptr());
00419 glDrawArrays(GL_LINE_LOOP, 0, 4);
00420 glColor4fv(color.ptr());
00421 glEnable(GL_TEXTURE_2D);
00422 #endif
00423 }
00424
00425 if (just_space && lines[iline][c] == ' ' && iline != lines.size()-1)
00426 {
00427 if (layout() == LeftToRightText)
00428 {
00429 pen.x() += just_space + (just_remained_space?1:0);
00430
00431 }
00432 else
00433 if (layout() == RightToLeftText)
00434 {
00435 pen.x() -= just_space + (just_remained_space?1:0);
00436
00437 }
00438 if(just_remained_space)
00439 just_remained_space--;
00440 }
00441
00442 if (layout() == LeftToRightText)
00443 {
00444 pen.x() += glyph->advance().x();
00445
00446 }
00447 else
00448 if (layout() == RightToLeftText)
00449 {
00450 pen.x() -= glyph->advance().x();
00451
00452 }
00453
00454 }
00455 }
00456
00457 glDisableClientState( GL_VERTEX_ARRAY );
00458 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
00459
00460 VL_CHECK_OGL();
00461
00462 glDisable(GL_TEXTURE_2D);
00463 glBindTexture(GL_TEXTURE_2D, 0);
00464 }
00465
00466
00467 AABB CoreText::rawboundingRect(const String& text) const
00468 {
00469 AABB aabb;
00470
00471 if(!font())
00472 {
00473 Log::error("CoreText::rawboundingRect() error: no Font assigned to the CoreText object.\n");
00474 VL_TRAP()
00475 return aabb;
00476 }
00477
00478 if (!font()->mFT_Face)
00479 {
00480 Log::error("CoreText::rawboundingRect() error: invalid FT_Face: probably you tried to load an unsupported font format.\n");
00481 VL_TRAP()
00482 return aabb;
00483 }
00484
00485 fvec2 pen(0,0);
00486 fvec3 vect[4];
00487
00488 FT_Long use_kerning = FT_HAS_KERNING( font()->mFT_Face );
00489 FT_UInt previous = 0;
00490
00491 for(int c=0; c<(int)text.length(); c++)
00492 {
00493 if (text[c] == '\n')
00494 {
00495 pen.y() -= mFont->mHeight ? mFont->mHeight : mFont->mSize;
00496 pen.x() = 0;
00497 continue;
00498 }
00499
00500 const ref<Glyph>& glyph = mFont->glyph(text[c]);
00501
00502
00503 if (glyph.get() == NULL)
00504 continue;
00505
00506 if ( kerningEnabled() && use_kerning && previous && glyph->glyphIndex())
00507 {
00508 FT_Vector delta; delta.y = 0;
00509 if (layout() == LeftToRightText)
00510 {
00511 FT_Get_Kerning( font()->mFT_Face, previous, glyph->glyphIndex(), FT_KERNING_DEFAULT, &delta );
00512 pen.x() += delta.x / 64.0f;
00513 }
00514 else
00515 if (layout() == RightToLeftText)
00516 {
00517 FT_Get_Kerning( font()->mFT_Face, glyph->glyphIndex(), previous, FT_KERNING_DEFAULT, &delta );
00518 pen.x() -= delta.x / 64.0f;
00519 }
00520 pen.y() += delta.y / 64.0f;
00521 }
00522 previous = glyph->glyphIndex();
00523
00524 if ( glyph->textureHandle() )
00525 {
00526 int left = layout() == RightToLeftText ? -glyph->left() : +glyph->left();
00527
00528 vect[0].x() = pen.x() + glyph->width()*0 + left -1;
00529 vect[0].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
00530
00531 vect[1].x() = pen.x() + glyph->width()*1 + left +1;
00532 vect[1].y() = pen.y() + glyph->height()*0 + glyph->top() - glyph->height() -1;
00533
00534 vect[2].x() = pen.x() + glyph->width()*1 + left +1;
00535 vect[2].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
00536
00537 vect[3].x() = pen.x() + glyph->width()*0 + left -1;
00538 vect[3].y() = pen.y() + glyph->height()*1 + glyph->top() - glyph->height() +1;
00539
00540 if (layout() == RightToLeftText)
00541 {
00542 vect[0].x() -= glyph->width()-1 +2;
00543 vect[1].x() -= glyph->width()-1 +2;
00544 vect[2].x() -= glyph->width()-1 +2;
00545 vect[3].x() -= glyph->width()-1 +2;
00546 }
00547
00548 vect[0].y() -= mFont->mHeight;
00549 vect[1].y() -= mFont->mHeight;
00550 vect[2].y() -= mFont->mHeight;
00551 vect[3].y() -= mFont->mHeight;
00552 }
00553
00554 aabb.addPoint( (vec3)vect[0] );
00555 aabb.addPoint( (vec3)vect[1] );
00556 aabb.addPoint( (vec3)vect[2] );
00557 aabb.addPoint( (vec3)vect[3] );
00558
00559 if (layout() == LeftToRightText)
00560 pen += glyph->advance();
00561 else
00562 if (layout() == RightToLeftText)
00563 pen -= glyph->advance();
00564 }
00565
00566 return aabb;
00567 }
00568
00569 void CoreText::renderBackground(const Actor*, const Camera*) const
00570 {
00571
00572
00573
00574
00575 glColor4fv(mBackgroundColor.ptr());
00576
00577
00578 glNormal3fv( fvec3(0,0,1).ptr() );
00579
00580 vec3 a,b,c,d;
00581 AABB bbox = boundingRect();
00582 a = bbox.minCorner();
00583 b.x() = (float)bbox.maxCorner().x();
00584 b.y() = (float)bbox.minCorner().y();
00585 c = bbox.maxCorner();
00586 d.x() = (float)bbox.minCorner().x();
00587 d.y() = (float)bbox.maxCorner().y();
00588
00589 a.z() = b.z() = c.z() = d.z() = 0;
00590
00591 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d };
00592 glEnableClientState( GL_VERTEX_ARRAY );
00593 glVertexPointer(3, GL_FLOAT, 0, vect);
00594
00595 glDrawArrays(GL_QUADS,0,4);
00596
00597 glDisableClientState( GL_VERTEX_ARRAY );
00598 }
00599
00600 void CoreText::renderBorder(const Actor*, const Camera*) const
00601 {
00602
00603
00604
00605
00606 glColor4fv(mBorderColor.ptr());
00607
00608
00609 glNormal3fv( fvec3(0,0,1).ptr() );
00610
00611 vec3 a,b,c,d;
00612 AABB bbox = boundingRect();
00613 a = bbox.minCorner();
00614 b.x() = (float)bbox.maxCorner().x();
00615 b.y() = (float)bbox.minCorner().y();
00616 c = bbox.maxCorner();
00617 d.x() = (float)bbox.minCorner().x();
00618 d.y() = (float)bbox.maxCorner().y();
00619
00620 a.z() = b.z() = c.z() = d.z() = 0;
00621
00622 fvec3 vect[] = { (fvec3)a, (fvec3)b, (fvec3)c, (fvec3)d };
00623 glEnableClientState( GL_VERTEX_ARRAY );
00624 glVertexPointer(3, GL_FLOAT, 0, vect);
00625
00626 glDrawArrays(GL_LINE_LOOP,0,4);
00627
00628 glDisableClientState( GL_VERTEX_ARRAY );
00629 }
00630
00631 AABB CoreText::boundingRect() const
00632 {
00633 return boundingRect(text());
00634 }
00635
00636 AABB CoreText::boundingRect(const String& text) const
00637 {
00638 AABB bbox = rawboundingRect( text );
00639 bbox.setMaxCorner( bbox.maxCorner() + vec3(2.0f*margin(), 2.0f*margin(), 0) );
00640
00641
00642 vec3 min = bbox.minCorner() - bbox.minCorner();
00643 vec3 max = bbox.maxCorner() - bbox.minCorner();
00644
00645
00646
00647 if (textOrigin() & AlignHCenter)
00648 {
00649 VL_CHECK( !(textOrigin() & AlignRight) )
00650 VL_CHECK( !(textOrigin() & AlignLeft) )
00651 min.x() -= int(bbox.width() / 2.0);
00652 max.x() -= int(bbox.width() / 2.0);
00653 }
00654
00655 if (textOrigin() & AlignRight)
00656 {
00657 VL_CHECK( !(textOrigin() & AlignHCenter) )
00658 VL_CHECK( !(textOrigin() & AlignLeft) )
00659 min.x() -= (int)bbox.width();
00660 max.x() -= (int)bbox.width();
00661 }
00662
00663 if (textOrigin() & AlignTop)
00664 {
00665 VL_CHECK( !(textOrigin() & AlignBottom) )
00666 VL_CHECK( !(textOrigin() & AlignVCenter) )
00667 min.y() -= (int)bbox.height();
00668 max.y() -= (int)bbox.height();
00669 }
00670
00671 if (textOrigin() & AlignVCenter)
00672 {
00673 VL_CHECK( !(textOrigin() & AlignTop) )
00674 VL_CHECK( !(textOrigin() & AlignBottom) )
00675 min.y() -= int(bbox.height() / 2.0);
00676 max.y() -= int(bbox.height() / 2.0);
00677 }
00678
00679 AABB aabb;
00680 aabb.setMinCorner(min);
00681 aabb.setMaxCorner(max);
00682 return aabb;
00683 }
00684