00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "xrb_font.hpp"
00012
00013 #include "xrb_math.hpp"
00014 #include "xrb_pal.hpp"
00015 #include "xrb_rendercontext.hpp"
00016 #include "xrb_utf8.hpp"
00017
00018 namespace Xrb
00019 {
00020
00021 FontCoordVector2 ScreenToFontCoordVector2 (ScreenCoordVector2 const &v)
00022 {
00023 return FontCoordVector2(ScreenToFontCoord(v[Dim::X]), ScreenToFontCoord(v[Dim::Y]));
00024 }
00025
00026 ScreenCoordVector2 FontToScreenCoordVector2 (FontCoordVector2 const &v)
00027 {
00028 return ScreenCoordVector2(FontToScreenCoord(v[Dim::X]), FontToScreenCoord(v[Dim::Y]));
00029 }
00030
00031
00032
00033
00034
00035 Font *Font::Create (std::string const &font_face_path, Sint32 pixel_height)
00036 {
00037 return Singleton::Pal().LoadFont(font_face_path.c_str(), pixel_height);
00038 }
00039
00040 ScreenCoordRect Font::StringRect (char const *const string) const
00041 {
00042 ASSERT1(string != NULL);
00043
00044 FontCoordVector2 pen_position_26_6(FontCoordVector2::ms_zero);
00045 FontCoordVector2 pen_position_span_26_6(pen_position_26_6);
00046
00047 char const *current_glyph = string;
00048 char const *next_glyph;
00049
00050 while (*current_glyph != '\0')
00051 {
00052
00053 next_glyph = UTF8::NextCharacter(current_glyph);
00054
00055 MoveThroughGlyph(
00056 &pen_position_26_6,
00057 ScreenCoordVector2::ms_zero,
00058 current_glyph,
00059 next_glyph,
00060 NULL,
00061 NULL);
00062
00063 current_glyph = next_glyph;
00064
00065 TrackBoundingBox(&pen_position_span_26_6, pen_position_26_6);
00066 }
00067
00068
00069 MoveThroughGlyph(
00070 &pen_position_26_6,
00071 ScreenCoordVector2::ms_zero,
00072 "\n",
00073 NULL,
00074 NULL,
00075 NULL);
00076
00077 TrackBoundingBox(&pen_position_span_26_6, pen_position_26_6);
00078
00079
00080 pen_position_span_26_6[Dim::X] = Math::FixedPointRound<6>(pen_position_span_26_6[Dim::X]);
00081 pen_position_span_26_6[Dim::Y] = Math::FixedPointRound<6>(pen_position_span_26_6[Dim::Y]);
00082
00083 return ScreenCoordRect(
00084 Min(FontToScreenCoord(pen_position_span_26_6[Dim::X]), ScreenCoord(0)),
00085 Min(FontToScreenCoord(pen_position_span_26_6[Dim::Y]), ScreenCoord(0)),
00086 Max(FontToScreenCoord(pen_position_span_26_6[Dim::X]), ScreenCoord(0)),
00087 Max(FontToScreenCoord(pen_position_span_26_6[Dim::Y]), ScreenCoord(0)));
00088 }
00089
00090 ScreenCoordRect Font::StringRect (LineFormatVector const &line_format_vector) const
00091 {
00092 ASSERT1(!line_format_vector.empty());
00093
00094 ScreenCoord width = 0;
00095 for (Font::LineFormatVector::const_iterator it = line_format_vector.begin(),
00096 it_end = line_format_vector.end();
00097 it != it_end;
00098 ++it)
00099 {
00100 width = Max(width, it->m_width);
00101 }
00102 return ScreenCoordRect(ScreenCoordVector2(width, line_format_vector.size() * PixelHeight()));
00103 }
00104
00105 void Font::DrawString (
00106 RenderContext const &render_context,
00107 ScreenCoordVector2 const &initial_pen_position,
00108 char const *const string) const
00109 {
00110
00111 DrawGlyphSetup(render_context);
00112
00113 DrawStringPrivate(render_context, initial_pen_position, string);
00114
00115 DrawGlyphShutdown(render_context);
00116 }
00117
00118 void Font::GenerateLineFormatVector (
00119 char const *const source_string,
00120 LineFormatVector *const dest_line_format_vector) const
00121 {
00122 ASSERT1(source_string != NULL);
00123 ASSERT1(dest_line_format_vector != NULL);
00124
00125 dest_line_format_vector->clear();
00126
00127 FontCoordVector2 pen_position_26_6(FontCoordVector2::ms_zero);
00128 LineFormat line_format;
00129 line_format.m_ptr = source_string;
00130 line_format.m_glyph_count = 0;
00131 bool line_start = true;
00132
00133 char const *current_glyph = source_string;
00134 char const *next_glyph;
00135 while (*current_glyph != '\0')
00136 {
00137
00138 next_glyph = UTF8::NextCharacter(current_glyph);
00139
00140
00141 if (line_start)
00142 {
00143 pen_position_26_6 = FontCoordVector2::ms_zero;
00144 line_format.m_ptr = current_glyph;
00145 line_format.m_glyph_count = 0;
00146 line_start = false;
00147 }
00148
00149
00150
00151 if (*current_glyph == '\n')
00152 {
00153 line_start = true;
00154 line_format.m_width = FontToScreenCoord(pen_position_26_6[Dim::X]);
00155 dest_line_format_vector->push_back(line_format);
00156 }
00157
00158 else
00159 ++line_format.m_glyph_count;
00160
00161
00162
00163 MoveThroughGlyph(
00164 &pen_position_26_6,
00165 ScreenCoordVector2::ms_zero,
00166 current_glyph,
00167 next_glyph);
00168
00169
00170 current_glyph = next_glyph;
00171 }
00172
00173 line_format.m_width = FontToScreenCoord(Abs(pen_position_26_6[Dim::X]));
00174 dest_line_format_vector->push_back(line_format);
00175 }
00176
00177 void Font::DrawLineFormattedText (
00178 RenderContext const &render_context,
00179 ScreenCoordRect const &draw_rect,
00180 char const *const source_string,
00181 LineFormatVector const &line_format_vector,
00182 Alignment2 const &alignment) const
00183 {
00184 ASSERT1(line_format_vector.size() > 0);
00185
00186
00187 if (alignment[Dim::X] == LEFT && alignment[Dim::Y] == TOP)
00188 {
00189 DrawString(render_context, draw_rect.TopLeft(), source_string);
00190 return;
00191 }
00192
00193 ScreenCoord text_height =
00194 line_format_vector.size() * PixelHeight();
00195 ScreenCoordVector2 total_spacing = ScreenCoordVector2::ms_zero;
00196 ScreenCoordVector2 initial_pen_position(draw_rect.TopLeft());
00197 ScreenCoordVector2 pen_position(initial_pen_position);
00198
00199 switch (alignment[Dim::Y])
00200 {
00201 case TOP: break;
00202
00203 case CENTER:
00204 pen_position[Dim::Y] = initial_pen_position[Dim::Y] - (draw_rect.Height() - text_height) / 2;
00205
00206 break;
00207
00208 case BOTTOM:
00209 pen_position[Dim::Y] = initial_pen_position[Dim::Y] - (draw_rect.Height() - text_height);
00210
00211 break;
00212
00213 case SPACED:
00214 if (line_format_vector.size() == 1)
00215 pen_position[Dim::Y] = initial_pen_position[Dim::Y] - (draw_rect.Height() - text_height) / 2;
00216
00217 else
00218
00219 total_spacing[Dim::Y] = draw_rect.Height() - text_height;
00220
00221 if (total_spacing[Dim::Y] < 0)
00222 total_spacing[Dim::Y] = 0;
00223 break;
00224
00225 default: ASSERT0(false && "Invalid Alignment"); break;
00226 }
00227
00228
00229 DrawGlyphSetup(render_context);
00230
00231 Uint32 spacing_lines_left = line_format_vector.size() - 1;
00232 for (Uint32 line = 0; line < line_format_vector.size(); ++line)
00233 {
00234
00235 switch (alignment[Dim::X])
00236 {
00237 case LEFT:
00238 pen_position[Dim::X] = initial_pen_position[Dim::X];
00239 break;
00240
00241 case CENTER:
00242 pen_position[Dim::X] = initial_pen_position[Dim::X] + (draw_rect.Width() - line_format_vector[line].m_width) / 2;
00243 break;
00244
00245 case RIGHT:
00246 pen_position[Dim::X] = initial_pen_position[Dim::X] + draw_rect.Width() - line_format_vector[line].m_width;
00247 break;
00248
00249 case SPACED:
00250 pen_position[Dim::X] = initial_pen_position[Dim::X];
00251 total_spacing[Dim::X] = draw_rect.Width() - line_format_vector[line].m_width;
00252
00253 if (total_spacing[Dim::X] > 4 * m_pixel_height)
00254 total_spacing[Dim::X] = 0;
00255
00256 else if (total_spacing[Dim::X] < 0)
00257 total_spacing[Dim::X] = 0;
00258 break;
00259
00260 default:
00261 ASSERT0(false && "Invalid Alignment");
00262 break;
00263 }
00264
00265 DrawStringPrivate(
00266 render_context,
00267 pen_position,
00268 line_format_vector[line].m_ptr,
00269 "\n",
00270 line_format_vector[line].m_glyph_count,
00271 total_spacing[Dim::X]);
00272
00273 pen_position[Dim::Y] -= PixelHeight();
00274
00275 if (spacing_lines_left > 0)
00276 {
00277 ScreenCoord spacing_to_use = total_spacing[Dim::Y] / spacing_lines_left--;
00278 pen_position[Dim::Y] -= spacing_to_use;
00279 total_spacing[Dim::Y] -= spacing_to_use;
00280 }
00281 }
00282
00283
00284 DrawGlyphShutdown(render_context);
00285 }
00286
00287 void Font::DrawStringPrivate (
00288 RenderContext const &render_context,
00289 ScreenCoordVector2 const &initial_pen_position,
00290 char const *const string,
00291 char const *const string_terminator,
00292 Uint32 remaining_glyph_count,
00293 ScreenCoord const remaining_space) const
00294 {
00295 ASSERT1(string != NULL);
00296
00297 FontCoordVector2 pen_position_26_6(ScreenToFontCoordVector2(initial_pen_position));
00298 FontCoord major_space_26_6 = ScreenToFontCoord(remaining_space);
00299
00300 char const *current_glyph = string;
00301 char const *next_glyph;
00302
00303 while (*current_glyph != '\0' &&
00304 !UTF8::AreCharactersEqual(current_glyph, string_terminator))
00305 {
00306
00307 next_glyph = UTF8::NextCharacter(current_glyph);
00308
00309 DrawGlyph(render_context, current_glyph, pen_position_26_6);
00310
00311 MoveThroughGlyph(
00312 &pen_position_26_6,
00313 initial_pen_position,
00314 current_glyph,
00315 next_glyph,
00316 &remaining_glyph_count,
00317 &major_space_26_6);
00318
00319 current_glyph = next_glyph;
00320 }
00321 }
00322
00323 void Font::TrackBoundingBox (
00324 FontCoordVector2 *const pen_position_span_26_6,
00325 FontCoordVector2 const &pen_position_26_6) const
00326 {
00327 ASSERT1(pen_position_span_26_6 != NULL);
00328
00329 (*pen_position_span_26_6)[Dim::X] = Max((*pen_position_span_26_6)[Dim::X], pen_position_26_6[Dim::X]);
00330 (*pen_position_span_26_6)[Dim::Y] = Min((*pen_position_span_26_6)[Dim::Y], pen_position_26_6[Dim::Y]);
00331 }
00332
00333 }
00334