00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "xrb_asciifont.hpp"
00012
00013 #include <sstream>
00014
00015 #include "xrb_binaryfileserializer.hpp"
00016 #include "xrb_gl.hpp"
00017 #include "xrb_gltexture.hpp"
00018 #include "xrb_pal.hpp"
00019 #include "xrb_render.hpp"
00020 #include "xrb_rendercontext.hpp"
00021 #include "xrb_texture.hpp"
00022 #include "xrb_util.hpp"
00023
00024 namespace Xrb
00025 {
00026
00027
00028
00029
00030
00031 void AsciiFont::GlyphSpecification::Read (Serializer &serializer)
00032 {
00033 m_ascii = serializer.ReadSint8();
00034 m_size = serializer.ReadScreenCoordVector2();
00035 m_bearing_26_6[Dim::X] = serializer.ReadSint32();
00036 m_bearing_26_6[Dim::Y] = serializer.ReadSint32();
00037 m_advance_26_6 = serializer.ReadSint32();
00038 m_texture_coordinates = serializer.ReadScreenCoordVector2();
00039 }
00040
00041 void AsciiFont::GlyphSpecification::Write (Serializer &serializer) const
00042 {
00043 serializer.WriteSint8(m_ascii);
00044 serializer.WriteScreenCoordVector2(m_size);
00045 serializer.WriteSint32(m_bearing_26_6[Dim::X]);
00046 serializer.WriteSint32(m_bearing_26_6[Dim::Y]);
00047 serializer.WriteSint32(m_advance_26_6);
00048 serializer.WriteScreenCoordVector2(m_texture_coordinates);
00049 }
00050
00051 int AsciiFont::GlyphSpecification::SortByWidthFirst (
00052 void const *const left_operand,
00053 void const *const right_operand)
00054 {
00055 GlyphSpecification const *left_glyph =
00056 *static_cast<GlyphSpecification const *const *>(left_operand);
00057 GlyphSpecification const *right_glyph =
00058 *static_cast<GlyphSpecification const *const *>(right_operand);
00059
00060
00061 if (left_glyph->m_size[Dim::X] < right_glyph->m_size[Dim::X])
00062 return -1;
00063 else if (left_glyph->m_size[Dim::X] == right_glyph->m_size[Dim::X])
00064 return left_glyph->m_size[Dim::Y] - right_glyph->m_size[Dim::Y];
00065 else
00066 return 1;
00067 }
00068
00069 int AsciiFont::GlyphSpecification::SortByHeightFirst (
00070 void const *const left_operand,
00071 void const *const right_operand)
00072 {
00073 GlyphSpecification const *left_glyph =
00074 *static_cast<GlyphSpecification const *const *>(left_operand);
00075 GlyphSpecification const *right_glyph =
00076 *static_cast<GlyphSpecification const *const *>(right_operand);
00077
00078
00079 if (left_glyph->m_size[Dim::Y] < right_glyph->m_size[Dim::Y])
00080 return -1;
00081 else if (left_glyph->m_size[Dim::Y] == right_glyph->m_size[Dim::Y])
00082 return left_glyph->m_size[Dim::X] - right_glyph->m_size[Dim::X];
00083 else
00084 return 1;
00085 }
00086
00087
00088
00089
00090
00091 AsciiFont *AsciiFont::CreateFromCache (
00092 std::string const &font_face_path,
00093 ScreenCoord pixel_height)
00094 {
00095 AsciiFont *retval = NULL;
00096
00097 std::string font_metadata_path(FORMAT(font_face_path << '.' << pixel_height << ".data"));
00098 std::string font_bitmap_path(FORMAT(font_face_path << '.' << pixel_height << ".png"));
00099
00100
00101 Texture *texture = Singleton::Pal().LoadImage(font_bitmap_path.c_str());
00102
00103 if (texture == NULL)
00104 return retval;
00105
00106
00107 BinaryFileSerializer serializer;
00108 serializer.Open(font_metadata_path.c_str(), "rb");
00109
00110 if (!serializer.IsOpen())
00111 {
00112 Delete(texture);
00113 return retval;
00114 }
00115
00116
00117 retval = new AsciiFont(font_face_path, pixel_height);
00118
00119 Uint32 hash = serializer.ReadUint32();
00120 retval->m_has_kerning = serializer.ReadBool();
00121 retval->m_baseline_height = serializer.ReadScreenCoord();
00122 Uint32 rendered_glyph_count(serializer.ReadUint32());
00123
00124 if (rendered_glyph_count != RENDERED_GLYPH_COUNT)
00125 {
00126 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); glyph count mismatch (got %u, expected %u) -- delete the associated cache files\n", font_face_path.c_str(), pixel_height, rendered_glyph_count, RENDERED_GLYPH_COUNT);
00127 DeleteAndNullify(retval);
00128 }
00129 else
00130 {
00131 for (Uint32 i = 0; i < RENDERED_GLYPH_COUNT; ++i)
00132 retval->m_glyph_specification[i].Read(serializer);
00133 for (Uint32 left = 0; left < RENDERED_GLYPH_COUNT; ++left)
00134 for (Uint32 right = 0; right < RENDERED_GLYPH_COUNT; ++right)
00135 retval->m_kern_pair_26_6[left*RENDERED_GLYPH_COUNT + right] = serializer.ReadSint32();
00136
00137 if (hash != retval->Hash())
00138 {
00139 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); invalid font metadata -- delete the associated cache files\n", font_face_path.c_str(), pixel_height);
00140 DeleteAndNullify(retval);
00141 }
00142
00143 if (!serializer.IsAtEnd())
00144 {
00145 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); font metadata file is too long -- delete the associated cache files\n", font_face_path.c_str(), pixel_height);
00146 DeleteAndNullify(retval);
00147 }
00148 }
00149
00150 serializer.Close();
00151
00152 if (retval != NULL)
00153 {
00154 retval->m_gl_texture = GLTexture::Create(texture);
00155 ASSERT1(retval->m_gl_texture != NULL);
00156
00157 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); loaded cached font data\n", font_face_path.c_str(), pixel_height);
00158 }
00159
00160 Delete(texture);
00161 return retval;
00162 }
00163
00164 AsciiFont *AsciiFont::Create (
00165 std::string const &font_face_path,
00166 ScreenCoord pixel_height,
00167 bool has_kerning,
00168 ScreenCoord baseline_height,
00169 GlyphSpecification sorted_glyph_specification[RENDERED_GLYPH_COUNT],
00170 FontCoord kern_pair_26_6[RENDERED_GLYPH_COUNT*RENDERED_GLYPH_COUNT],
00171 Texture *font_texture)
00172 {
00173 ASSERT1(font_texture != NULL);
00174
00175 AsciiFont *retval = new AsciiFont(font_face_path, pixel_height);
00176
00177 retval->m_has_kerning = has_kerning;
00178 retval->m_baseline_height = baseline_height;
00179 memcpy(retval->m_glyph_specification, sorted_glyph_specification, sizeof(retval->m_glyph_specification));
00180 memcpy(retval->m_kern_pair_26_6, kern_pair_26_6, sizeof(retval->m_kern_pair_26_6));
00181 retval->m_gl_texture = GLTexture::Create(font_texture);
00182 ASSERT1(retval->m_gl_texture != NULL);
00183
00184 return retval;
00185 }
00186
00187 bool AsciiFont::CacheToDisk (Texture *font_texture) const
00188 {
00189 ASSERT1(font_texture != NULL);
00190
00191 std::string font_metadata_path(FORMAT(FontFacePath() << '.' << PixelHeight() << ".data"));
00192 std::string font_bitmap_path(FORMAT(FontFacePath() << '.' << PixelHeight() << ".png"));
00193
00194
00195 {
00196 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); ", FontFacePath().c_str(), PixelHeight());
00197
00198 BinaryFileSerializer serializer;
00199 serializer.Open(font_metadata_path.c_str(), "wb");
00200 if (serializer.IsOpen())
00201 {
00202 serializer.WriteUint32(Hash());
00203 serializer.WriteBool(m_has_kerning);
00204 serializer.WriteScreenCoord(m_baseline_height);
00205 serializer.WriteUint32(RENDERED_GLYPH_COUNT);
00206 for (Uint32 i = 0; i < RENDERED_GLYPH_COUNT; ++i)
00207 m_glyph_specification[i].Write(serializer);
00208 for (Uint32 left = 0; left < RENDERED_GLYPH_COUNT; ++left)
00209 for (Uint32 right = 0; right < RENDERED_GLYPH_COUNT; ++right)
00210 serializer.WriteSint32(m_kern_pair_26_6[left*RENDERED_GLYPH_COUNT + right]);
00211
00212 serializer.Close();
00213
00214 fprintf(stderr, "cached font data\n");
00215
00216
00217 }
00218 else
00219 {
00220 fprintf(stderr, "error caching font data\n");
00221 return false;
00222 }
00223 }
00224
00225
00226 {
00227 fprintf(stderr, "AsciiFont::Create(\"%s\", %d); ", FontFacePath().c_str(), PixelHeight());
00228 bool success = Singleton::Pal().SaveImage(font_bitmap_path.c_str(), *font_texture) == Pal::SUCCESS;
00229 if (success)
00230 fprintf(stderr, "cached font bitmap\n");
00231 else
00232 fprintf(stderr, "error caching font bitmap file\n");
00233 return success;
00234 }
00235 }
00236
00237 void AsciiFont::MoveThroughGlyph (
00238 FontCoordVector2 *const pen_position_26_6,
00239 ScreenCoordVector2 const &initial_pen_position,
00240 char const *const current_glyph,
00241 char const *const next_glyph,
00242 Uint32 *remaining_glyph_count,
00243 FontCoord *major_space_26_6) const
00244 {
00245 ASSERT1(current_glyph != NULL);
00246 ASSERT1(*current_glyph != '\0');
00247
00248 if (*current_glyph == '\n')
00249 {
00250 (*pen_position_26_6)[Dim::X] = ScreenToFontCoord(initial_pen_position[Dim::X]);
00251 (*pen_position_26_6)[Dim::Y] -= ScreenToFontCoord(PixelHeight());
00252 }
00253 else
00254 {
00255 if (*current_glyph == '\t')
00256 {
00257 (*pen_position_26_6)[Dim::X] += TAB_SIZE * m_glyph_specification[GlyphIndex(' ')].m_advance_26_6;
00258 }
00259 else
00260 {
00261 (*pen_position_26_6)[Dim::X] += m_glyph_specification[GlyphIndex(*current_glyph)].m_advance_26_6;
00262 if (next_glyph != NULL)
00263 (*pen_position_26_6)[Dim::X] += KernPair_26_6(*current_glyph, *next_glyph);
00264 }
00265
00266 if (remaining_glyph_count != NULL && *remaining_glyph_count > 1)
00267 {
00268 ASSERT1(major_space_26_6 != NULL);
00269 *remaining_glyph_count -= 1;
00270 ScreenCoord space_to_use_26_6 = *major_space_26_6 / *remaining_glyph_count;
00271 ASSERT1(space_to_use_26_6 <= *major_space_26_6);
00272 *major_space_26_6 -= space_to_use_26_6;
00273 (*pen_position_26_6)[Dim::X] += space_to_use_26_6;
00274 }
00275 }
00276 }
00277
00278 void AsciiFont::GenerateWordWrappedString (
00279 std::string const &source_string,
00280 std::string *const dest_string,
00281 ScreenCoordVector2 const &text_area_size) const
00282 {
00283 ASSERT1(dest_string != NULL);
00284
00285
00286 dest_string->clear();
00287
00288
00289 ScreenCoord wrap_width_26_6 = ScreenToFontCoord(text_area_size[Dim::X]);
00290
00291 bool forced_newline = false;
00292 bool line_start = true;
00293 char const *current_token;
00294 char const *next_token;
00295 FontCoord current_pos_26_6 = 0;
00296 FontCoord token_width_26_6;
00297 TokenClass next_token_class;
00298
00299 current_token = source_string.c_str();
00300 while (GetTokenClass(*current_token) != NULLCHAR)
00301 {
00302 if (line_start)
00303 {
00304 current_pos_26_6 = 0;
00305
00306 if (forced_newline)
00307 *dest_string += '\n';
00308 while (GetTokenClass(*current_token) == WHITESPACE)
00309 current_token = StartOfNextToken(current_token);
00310 line_start = false;
00311 forced_newline = false;
00312 }
00313
00314 switch (GetTokenClass(*current_token))
00315 {
00316 case WHITESPACE:
00317
00318
00319 next_token = StartOfNextToken(current_token);
00320 next_token_class = GetTokenClass(*next_token);
00321 token_width_26_6 = TokenWidth_26_6(" ") + TokenWidth_26_6(next_token);
00322 if (next_token_class == NULLCHAR)
00323 {
00324 }
00325 else if (next_token_class == NEWLINE)
00326 {
00327 }
00328 else if (next_token_class == WORD &&
00329 current_pos_26_6 + token_width_26_6 > wrap_width_26_6)
00330 {
00331 *dest_string += '\n';
00332 line_start = true;
00333 }
00334 else
00335 {
00336
00337 *dest_string += ' ';
00338 current_pos_26_6 += TokenWidth_26_6(" ");
00339 }
00340 current_token = next_token;
00341 break;
00342
00343 case NEWLINE:
00344 *dest_string += '\n';
00345 forced_newline = true;
00346 line_start = true;
00347 current_token = StartOfNextToken(current_token);
00348 break;
00349
00350 case NULLCHAR:
00351 break;
00352
00353 case WORD:
00354 token_width_26_6 = TokenWidth_26_6(current_token);
00355 next_token = StartOfNextToken(current_token);
00356 forced_newline = false;
00357 if (token_width_26_6 > wrap_width_26_6)
00358 {
00359 for (Sint32 i = 0; i < next_token - current_token; ++i)
00360 *dest_string += *(current_token + i);
00361 *dest_string += '\n';
00362 current_token = next_token;
00363 line_start = true;
00364 }
00365 else if (current_pos_26_6 + token_width_26_6 > wrap_width_26_6)
00366 {
00367 *dest_string += '\n';
00368 line_start = true;
00369 }
00370 else
00371 {
00372 for (Sint32 i = 0; i < next_token - current_token; ++i)
00373 *dest_string += *(current_token + i);
00374 current_pos_26_6 += token_width_26_6;
00375 current_token = next_token;
00376 }
00377 break;
00378 }
00379 }
00380 }
00381
00382 void AsciiFont::DrawGlyphSetup (RenderContext const &render_context) const
00383 {
00384 ASSERT1(m_gl_texture != NULL);
00385
00386 if (render_context.MaskAndBiasWouldResultInNoOp())
00387 return;
00388
00389 glMatrixMode(GL_MODELVIEW);
00390 glPushMatrix();
00391 glLoadIdentity();
00392
00393 Render::SetupTextureUnits(
00394 m_gl_texture->Handle(),
00395 render_context.ColorMask(),
00396 render_context.ColorBias());
00397
00398
00399
00400 glActiveTexture(GL_TEXTURE0);
00401
00402 glMatrixMode(GL_TEXTURE);
00403 glPushMatrix();
00404 glLoadIdentity();
00405 glScalef(
00406 1.0f / m_gl_texture->Width(),
00407 1.0f / m_gl_texture->Height(),
00408 1.0f);
00409
00410
00411 glEnableClientState(GL_VERTEX_ARRAY);
00412 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00413 }
00414
00415 void AsciiFont::DrawGlyphShutdown (RenderContext const &render_context) const
00416 {
00417
00418
00419
00420 glDisableClientState(GL_VERTEX_ARRAY);
00421 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
00422
00423
00424 ASSERT1(GL::Integer(GL_MATRIX_MODE) == GL_TEXTURE);
00425 glPopMatrix();
00426
00427
00428 glMatrixMode(GL_MODELVIEW);
00429 glPopMatrix();
00430 }
00431
00432 void AsciiFont::DrawGlyph (
00433 RenderContext const &render_context,
00434 char const *const glyph,
00435 FontCoordVector2 const &pen_position_26_6) const
00436 {
00437 ASSERT1(glyph != NULL);
00438 ASSERT1(*glyph != '\0');
00439
00440 if (*glyph == '\n' || *glyph == '\t')
00441 return;
00442
00443 Uint32 glyph_index = GlyphIndex(*glyph);
00444
00445 ScreenCoordRect glyph_texture_coordinates(m_glyph_specification[glyph_index].m_size);
00446 glyph_texture_coordinates += m_glyph_specification[glyph_index].m_texture_coordinates;
00447
00448
00449
00450 ScreenCoordVector2 pen_position(
00451 FontToScreenCoordVector2(
00452 pen_position_26_6 + FontCoordVector2(m_glyph_specification[glyph_index].m_bearing_26_6[Dim::X], 0)));
00453 ScreenCoordRect glyph_vertex_coordinates(m_glyph_specification[glyph_index].m_size);
00454 glyph_vertex_coordinates += pen_position;
00455 glyph_vertex_coordinates +=
00456 ScreenCoordVector2(
00457 0,
00458 m_baseline_height +
00459 FontToScreenCoord(m_glyph_specification[glyph_index].m_bearing_26_6[Dim::Y]) -
00460 m_glyph_specification[glyph_index].m_size[Dim::Y] -
00461 PixelHeight());
00462
00463
00464
00465
00466 {
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483 Sint16 glyph_texture_coordinate_array[8] =
00484 {
00485 glyph_texture_coordinates.TopLeft()[Dim::X], glyph_texture_coordinates.TopLeft()[Dim::Y],
00486 glyph_texture_coordinates.TopRight()[Dim::X], glyph_texture_coordinates.TopRight()[Dim::Y],
00487 glyph_texture_coordinates.BottomLeft()[Dim::X], glyph_texture_coordinates.BottomLeft()[Dim::Y],
00488 glyph_texture_coordinates.BottomRight()[Dim::X], glyph_texture_coordinates.BottomRight()[Dim::Y]
00489 };
00490 Sint16 glyph_vertex_coordinate_array[8] =
00491 {
00492 glyph_vertex_coordinates.BottomLeft()[Dim::X], glyph_vertex_coordinates.BottomLeft()[Dim::Y],
00493 glyph_vertex_coordinates.BottomRight()[Dim::X], glyph_vertex_coordinates.BottomRight()[Dim::Y],
00494 glyph_vertex_coordinates.TopLeft()[Dim::X], glyph_vertex_coordinates.TopLeft()[Dim::Y],
00495 glyph_vertex_coordinates.TopRight()[Dim::X], glyph_vertex_coordinates.TopRight()[Dim::Y]
00496 };
00497
00498 glVertexPointer(2, GL_SHORT, 0, glyph_vertex_coordinate_array);
00499
00500
00501 glClientActiveTexture(GL_TEXTURE0);
00502 glTexCoordPointer(2, GL_SHORT, 0, glyph_texture_coordinate_array);
00503
00504
00505 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
00506 }
00507 }
00508
00509 Uint32 AsciiFont::Hash () const
00510 {
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525 Uint32 retval = 0;
00526 retval ^= Uint32(PixelHeight());
00527 retval *= 11;
00528 retval ^= Uint32(m_has_kerning ? 1 : 0);
00529 retval *= 11;
00530 retval ^= Uint32(m_baseline_height);
00531 retval *= 11;
00532 for (Uint32 i = 0; i < RENDERED_GLYPH_COUNT; ++i)
00533 {
00534 retval ^= Uint32(m_glyph_specification[i].m_ascii);
00535 retval *= 11;
00536 retval ^= Uint32(m_glyph_specification[i].m_size[Dim::X]);
00537 retval *= 11;
00538 retval ^= Uint32(m_glyph_specification[i].m_size[Dim::Y]);
00539 retval *= 11;
00540 retval ^= Uint32(m_glyph_specification[i].m_bearing_26_6[Dim::X]);
00541 retval *= 11;
00542 retval ^= Uint32(m_glyph_specification[i].m_bearing_26_6[Dim::Y]);
00543 retval *= 11;
00544 retval ^= Uint32(m_glyph_specification[i].m_advance_26_6);
00545 retval *= 11;
00546 retval ^= Uint32(m_glyph_specification[i].m_texture_coordinates[Dim::X]);
00547 retval *= 11;
00548 retval ^= Uint32(m_glyph_specification[i].m_texture_coordinates[Dim::Y]);
00549 retval *= 11;
00550 }
00551 for (Uint32 i = 0; i < RENDERED_GLYPH_COUNT*RENDERED_GLYPH_COUNT; ++i)
00552 {
00553 retval ^= Uint32(m_kern_pair_26_6[i]);
00554 retval *= 11;
00555 }
00556 return retval;
00557 }
00558
00559 FontCoord AsciiFont::KernPair_26_6 (char left, char right) const
00560 {
00561 Uint32 glyph_index_left = GlyphIndex(left);
00562 Uint32 glyph_index_right = GlyphIndex(right);
00563 ASSERT1(glyph_index_left < RENDERED_GLYPH_COUNT);
00564 ASSERT1(glyph_index_right < RENDERED_GLYPH_COUNT);
00565 return m_kern_pair_26_6[glyph_index_left*RENDERED_GLYPH_COUNT + glyph_index_right];
00566 }
00567
00568 AsciiFont::TokenClass AsciiFont::GetTokenClass (char const c)
00569 {
00570 switch (c)
00571 {
00572 case ' ' :
00573 case '\t': return WHITESPACE;
00574 case '\n': return NEWLINE;
00575 case '\0': return NULLCHAR;
00576 default : return WORD;
00577 }
00578 }
00579
00580 char const *AsciiFont::StartOfNextToken (char const *string)
00581 {
00582 ASSERT1(string != NULL);
00583
00584 Sint32 token_class = GetTokenClass(*string);
00585
00586 if (token_class == NEWLINE)
00587 return ++string;
00588 else if (token_class == NULLCHAR)
00589 return string;
00590
00591 while (*string != '\0' && GetTokenClass(*string) == token_class)
00592 ++string;
00593
00594 return string;
00595 }
00596
00597 FontCoord AsciiFont::TokenWidth_26_6 (char const *const string) const
00598 {
00599 ASSERT1(string != NULL);
00600
00601 char const *current_glyph = string;
00602 char const *next_glyph;
00603 char const *const end_glyph = StartOfNextToken(current_glyph);
00604 FontCoordVector2 pen_position_26_6(FontCoordVector2::ms_zero);
00605 while (current_glyph != end_glyph)
00606 {
00607 next_glyph = current_glyph + 1;
00608 if (*string != '\n')
00609 MoveThroughGlyph(
00610 &pen_position_26_6,
00611 ScreenCoordVector2::ms_zero,
00612 current_glyph,
00613 next_glyph);
00614 current_glyph = next_glyph;
00615 }
00616
00617 return pen_position_26_6[Dim::X];
00618 }
00619
00620 }
00621