/*---------------------------------------------------------------------------* Project: NintendoWare File: font_TextWriterBase.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER >= 1500 #include #define GET_FORMAT_BUFFER() \ reinterpret_cast((s_FormatBuffer != NULL) ? \ s_FormatBuffer: \ _alloca(s_FormatBufferSize)) #else #include #define GET_FORMAT_BUFFER() \ reinterpret_cast((s_FormatBuffer != NULL) ? \ s_FormatBuffer: \ alloca(s_FormatBufferSize)) #endif namespace nw { namespace font { /* ------------------------------------------------------------------------ クラス変数定義 ------------------------------------------------------------------------ */ template CharType* TextWriterBase::s_FormatBuffer = NULL; template std::size_t TextWriterBase::s_FormatBufferSize = DEFAULT_FORMAT_BUFFER_SIZE; template TagProcessorBase TextWriterBase::s_DefaultTagProcessor; namespace { /*!--------------------------------------------------------------------------* @brief 基点が中央であるときのカーソル位置補正の値を求めます。 半分の値が浮動小数点にならないように整数値に切り上げます。 @param[in] value 考慮する幅/高さ @return 基点が中央であるときのカーソル位置補正の値を返します。 *---------------------------------------------------------------------------*/ inline f32 AdjustCenterValue(f32 value) { return math::FCeil(value * 0.5f); } } // namespace /* ======================================================================= public ======================================================================== */ /* ------------------------------------------------------------------------ コンストラクタ/デストラクタ ------------------------------------------------------------------------ */ template TextWriterBase::TextWriterBase() : CharWriter(), m_WidthLimit(FLT_MAX), m_CharSpace(0), m_LineSpace(0), m_TabWidth (4), m_DrawFlag(DEFAULT_DRAWFLAG), m_TagProcessor(&s_DefaultTagProcessor) { } template TextWriterBase::~TextWriterBase() { } /* ------------------------------------------------------------------------ 行間/文字間/タブ幅 ------------------------------------------------------------------------ */ template void TextWriterBase::SetLineHeight(f32 height) { const Font* font = GetFont(); const int linefeed = font != NULL ? font->GetLineFeed(): 0; m_LineSpace = height - linefeed * GetScaleV(); } template f32 TextWriterBase::GetLineHeight() const { const Font* font = GetFont(); const int linefeed = font != NULL ? font->GetLineFeed(): 0; return linefeed * GetScaleV() + m_LineSpace; } /* ------------------------------------------------------------------------ 文字列サイズ計算 ------------------------------------------------------------------------ */ template f32 TextWriterBase::CalcFormatStringWidth( StreamType format, ... ) const { NN_POINTER_ASSERT(format); ut::Rect rect; std::va_list vargs; va_start(vargs, format); CalcVStringRect(&rect, format, vargs); va_end(vargs); return rect.GetWidth(); } template f32 TextWriterBase::CalcFormatStringHeight( StreamType format, ... ) const { NN_POINTER_ASSERT(format); ut::Rect rect; std::va_list vargs; va_start(vargs, format); CalcVStringRect(&rect, format, vargs); va_end(vargs); return rect.GetHeight(); } template void TextWriterBase::CalcFormatStringRect( ut::Rect* pRect, StreamType format, ... ) const { NN_POINTER_ASSERT(pRect); NN_POINTER_ASSERT(format); std::va_list vargs; va_start(vargs, format); CalcVStringRect(pRect, format, vargs); va_end(vargs); } template void TextWriterBase::CalcVStringRect( ut::Rect* pRect, StreamType format, std::va_list args ) const { NN_POINTER_ASSERT(pRect); NN_POINTER_ASSERT(format); CharType* buffer = GET_FORMAT_BUFFER(); int length = VSNPrintf(buffer, s_FormatBufferSize, format, args); length = math::Min(length, int(s_FormatBufferSize - 1)); CalcStringRect(pRect, buffer, length); } template f32 TextWriterBase::CalcStringWidth( StreamType str, int length ) const { NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); ut::Rect rect; CalcStringRect(&rect, str, length); return rect.GetWidth(); } template f32 TextWriterBase::CalcStringHeight( StreamType str, int length ) const { NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); ut::Rect rect; CalcStringRect(&rect, str, length); return rect.GetHeight(); } template void TextWriterBase::CalcStringRect( ut::Rect* pRect, StreamType str, int length ) const { NN_POINTER_ASSERT(pRect); NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); TextWriterBase myCopy = *this; myCopy.CalcStringRectImpl(pRect, str, length); #if defined(NW_FONT_PROFILE) this->CopyProfileData(myCopy); #endif } /* ------------------------------------------------------------------------ 文字列描画 ------------------------------------------------------------------------ */ template f32 TextWriterBase::Printf( StreamType format, ... ) { NN_POINTER_ASSERT(format); std::va_list vargs; va_start(vargs, format); f32 width = VPrintf(format, vargs); va_end(vargs); return width; } template f32 TextWriterBase::VPrintf( StreamType format, std::va_list args ) { NN_POINTER_ASSERT(format); CharType* buffer = GET_FORMAT_BUFFER(); int length = VSNPrintf(buffer, s_FormatBufferSize, format, args); length = math::Min(length, int(s_FormatBufferSize - 1)); f32 width = Print(buffer, length); return width; } template f32 TextWriterBase::Print( StreamType str, int length ) { NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); NW_FONT_COUNTUP(m_PrintTextCount); NW_FONT_STOPWATCH_START(m_PrintTextSw); TextWriterBase myCopy = *this; f32 width = myCopy.PrintImpl(str, length); SetCursor( myCopy.GetCursorX(), myCopy.GetCursorY() ); #if defined(NW_FONT_PROFILE) this->CopyProfileData(myCopy); #endif NW_FONT_STOPWATCH_STOP(m_PrintTextSw); return width; } /* ======================================================================= private ======================================================================== */ /* ------------------------------------------------------------------------ 文字列処理 ------------------------------------------------------------------------ */ template f32 TextWriterBase::CalcLineWidth( StreamType str, int length ) { NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); ut::Rect rect; TextWriterBase myCopy = *this; myCopy.SetCursor(0, 0); myCopy.CalcLineRectImpl(&rect, &str, length); #if defined(NW_FONT_PROFILE) this->CopyProfileData(myCopy); #endif return rect.GetWidth(); } template bool TextWriterBase::CalcLineRectImpl( ut::Rect* pRect, StreamType* pStr, int length ) { NN_POINTER_ASSERT(pRect); NN_POINTER_ASSERT(pStr); NN_POINTER_ASSERT(*pStr); NW_FONT_MIN_ASSERT(length, 0); const StreamType str = *pStr; const StreamType end = str + length; const bool bUseLimit = m_WidthLimit < FLT_MAX; PrintContext context(this, str, 0, 0, 0); f32 limitLeft = 0; f32 limitRight = 0; bool bCharSpace = false; bool bOverLimit = false; StreamType prevStreamPos = NULL; ut::Rect prevRect; /* 文字列の途中でエンコーディングが変わる事は想定していない */ NN_POINTER_ASSERT(GetFont()); CharStrmReader reader = GetFont()->GetCharStrmReader(CharType(0)); // 1行の高さにはLineSpaceを含めない。 f32 lineFeed = GetLineHeight() - GetLineSpace(); pRect->left = 0; pRect->right = 0; pRect->top = math::Min(0.0f, lineFeed); pRect->bottom = math::Max(0.0f, lineFeed); prevRect = *pRect; reader.Set(str); prevStreamPos = NULL; for ( CharCode code = reader.Next(); reinterpret_cast(reader.GetCurrentPos()) <= end; ) { if (code < ' ') { //---- 制御文字(タグの開始) typename TagProcessor::Operation operation; ut::Rect rect(limitRight, 0, 0, 0); context.str = reinterpret_cast(reader.GetCurrentPos()); context.flags = 0; context.flags |= bCharSpace ? 0: CONTEXT_NO_CHAR_SPACE; SetCursorX(limitRight); //---- 折り返しの判定 if ( bUseLimit && code != '\n' && prevStreamPos != NULL ) { PrintContext context2 = context; TextWriterBase myCopy = *this; ut::Rect rect2; context2.writer = &myCopy; operation = m_TagProcessor->CalcRect(&rect2, code, &context2); #if defined(NW_FONT_PROFILE) this->CopyProfileData(myCopy); #endif if ( rect2.GetWidth() > 0.0f && (myCopy.GetCursorX() - context.xOrigin > m_WidthLimit) ) { bOverLimit = true; code = '\n'; reader.Set(prevStreamPos); continue; } } //---- タグ処理 operation = m_TagProcessor->CalcRect(&rect, code, &context); NN_POINTER_ASSERT(context.str); reader.Set(context.str); pRect->left = math::Min(pRect->left, rect.left); pRect->top = math::Min(pRect->top, rect.top); pRect->right = math::Max(pRect->right, rect.right); pRect->bottom = math::Max(pRect->bottom, rect.bottom); limitRight = GetCursorX(); if (operation == TagProcessor::OPERATION_END_DRAW) { //---- 全部読み進んだ事にする *pStr += length; return false; } else if (operation == TagProcessor::OPERATION_NO_CHAR_SPACE) { bCharSpace = false; } else if (operation == TagProcessor::OPERATION_CHAR_SPACE) { bCharSpace = true; } else if (operation == TagProcessor::OPERATION_NEXT_LINE) { break; } } else { //---- 通常の文字 f32 crntRight = limitRight; if (bCharSpace) { crntRight += GetCharSpace(); } if (IsWidthFixed()) { crntRight += GetFixedWidth(); } else { crntRight += GetFont()->GetCharWidth(code) * GetScaleH(); } //---- 折り返しの判定 if (bUseLimit && prevStreamPos != NULL) { f32 width = crntRight - limitLeft; if (width > m_WidthLimit) { bOverLimit = true; code = '\n'; reader.Set(prevStreamPos); continue; } } limitRight = crntRight; pRect->left = math::Min(pRect->left, limitRight); pRect->right = math::Max(pRect->right, limitRight); bCharSpace = true; } if (bUseLimit) { prevStreamPos = reinterpret_cast(reader.GetCurrentPos()); } code = reader.Next(); } *pStr = reinterpret_cast(reader.GetCurrentPos()); return bOverLimit; } template void TextWriterBase::CalcStringRectImpl( ut::Rect* pRect, StreamType str, int length ) { NN_POINTER_ASSERT(pRect); NN_POINTER_ASSERT(str); NW_FONT_MIN_ASSERT(length, 0); const StreamType end = str + length; int remain = length; StreamType pos = str; pRect->left = 0; pRect->right = 0; pRect->top = 0; pRect->bottom = 0; SetCursor(0, 0); do { ut::Rect rect; CalcLineRectImpl(&rect, &pos, remain); remain = (end - pos); pRect->left = math::Min(pRect->left, rect.left); pRect->top = math::Min(pRect->top, rect.top); pRect->right = math::Max(pRect->right, rect.right); pRect->bottom = math::Max(pRect->bottom, rect.bottom); } while (remain > 0); } template f32 TextWriterBase::PrintImpl( StreamType str, int length ) { NN_POINTER_ASSERT(str); NN_POINTER_ASSERT(GetFont()); NW_FONT_MIN_ASSERT(length, 0); f32 xOrigin = GetCursorX(); f32 yOrigin = GetCursorY(); f32 limitLeft = 0; f32 limitRight = 0; const bool bUseLimit = m_WidthLimit < FLT_MAX; const f32 orgCursorY = yOrigin; bool bCharSpace = false; StreamType prevStreamPos = str; StreamType prevNewLinePos = str; f32 textWidth = AdjustCursor(&xOrigin, &yOrigin, str, length); f32 yCursorAdj = orgCursorY - GetCursorY(); PrintContext context(this, str, xOrigin, yOrigin, 0); CharStrmReader reader = GetFont()->GetCharStrmReader(CharType(0)); reader.Set(str); for ( CharCode code = reader.Next(); reinterpret_cast(reader.GetCurrentPos()) - str <= length; ) { if (code < ' ') { //---- 制御文字(タグの開始) typename TagProcessor::Operation operation; context.str = reinterpret_cast(reader.GetCurrentPos()); context.flags = 0; context.flags |= bCharSpace ? 0: CONTEXT_NO_CHAR_SPACE; //---- 折り返しの判定 if ( bUseLimit && code != '\n' && prevStreamPos != prevNewLinePos ) { PrintContext context2 = context; TextWriterBase myCopy = *this; ut::Rect rect; context2.writer = &myCopy; operation = m_TagProcessor->CalcRect(&rect, code, &context2); #if defined(NW_FONT_PROFILE) this->CopyProfileData(myCopy); #endif if ( rect.GetWidth() > 0.0f && myCopy.GetCursorX() - context.xOrigin > m_WidthLimit ) { code = '\n'; reader.Set(prevStreamPos); continue; } } //---- タグ処理 operation = m_TagProcessor->Process(code, &context); limitRight = GetCursorX() - xOrigin; if (operation == TagProcessor::OPERATION_NEXT_LINE) { NN_POINTER_ASSERT(context.str); //---- 次行描画開始位置Xの補正 if (IsDrawFlagSet(HORIZONTAL_ALIGN_MASK, HORIZONTAL_ALIGN_CENTER)) { const int remain = length - (context.str - str); const f32 width = CalcLineWidth( context.str, remain ); const f32 offset = AdjustCenterValue(textWidth) - AdjustCenterValue(width); SetCursorX( context.xOrigin + offset ); } else if (IsDrawFlagSet(HORIZONTAL_ALIGN_MASK, HORIZONTAL_ALIGN_RIGHT)) { const int remain = length - (context.str - str); const f32 width = CalcLineWidth( context.str, remain ); const f32 offset = textWidth - width; SetCursorX( context.xOrigin + offset ); } else { //---- 最大幅の更新 const f32 width = GetCursorX() - context.xOrigin; textWidth = math::Max(textWidth, width); SetCursorX( context.xOrigin ); } if (bUseLimit) { prevNewLinePos = reinterpret_cast(reader.GetCurrentPos()); } bCharSpace = false; } else if (operation == TagProcessor::OPERATION_NO_CHAR_SPACE) { bCharSpace = false; } else if (operation == TagProcessor::OPERATION_CHAR_SPACE) { bCharSpace = true; } else if (operation == TagProcessor::OPERATION_END_DRAW) { break; } NN_POINTER_ASSERT(context.str); reader.Set(context.str); } else { //---- 通常の文字 const f32 baseY = GetCursorY(); f32 crntRight = limitRight; if (bCharSpace) { crntRight += GetCharSpace(); } if (IsWidthFixed()) { crntRight += GetFixedWidth(); } else { crntRight += GetFont()->GetCharWidth(code) * GetScaleH(); } //---- 折り返しの判定 if (bUseLimit && prevStreamPos != prevNewLinePos) { f32 width = crntRight - limitLeft; if (width > m_WidthLimit) { code = '\n'; reader.Set(prevStreamPos); continue; } } limitRight = crntRight; if (bCharSpace) { MoveCursorX( GetCharSpace() ); } bCharSpace = true; //---- カーソルはベースラインにあるのでセル上端へ移動する { const Font* pFont = GetFont(); const f32 adj = - pFont->GetBaselinePos() * GetScaleV(); MoveCursorY( adj ); } Print(code); // 戻す SetCursorY( baseY ); } if (bUseLimit) { prevStreamPos = reinterpret_cast(reader.GetCurrentPos()); } code = reader.Next(); } //---- 最大幅の更新 { const f32 width = GetCursorX() - context.xOrigin; textWidth = math::Max(textWidth, width); } //---- カーソル位置Yを描画フラグに従って移動 // AdjustCursorでベースライン上に移動したカーソル位置を元に戻す if ( IsDrawFlagSet(VERTICAL_ORIGIN_MASK, VERTICAL_ORIGIN_MIDDLE) || IsDrawFlagSet(VERTICAL_ORIGIN_MASK, VERTICAL_ORIGIN_BOTTOM) ) { SetCursorY(orgCursorY); } else { MoveCursorY(yCursorAdj); } return textWidth; } template f32 TextWriterBase::AdjustCursor( f32* pXOrigin, f32* pYOrigin, StreamType str, int length ) { f32 textWidth = 0; f32 textHeight = 0; { u32 flagMask = HORIZONTAL_ALIGN_MASK | HORIZONTAL_ORIGIN_MASK | VERTICAL_ORIGIN_MASK; u32 blineFlag = HORIZONTAL_ALIGN_LEFT | HORIZONTAL_ORIGIN_LEFT | VERTICAL_ORIGIN_BASELINE; u32 topFlag = HORIZONTAL_ALIGN_LEFT | HORIZONTAL_ORIGIN_LEFT | VERTICAL_ORIGIN_TOP; if ( ! IsDrawFlagSet(flagMask, blineFlag) && ! IsDrawFlagSet(flagMask, topFlag) ) { ut::Rect textRect; CalcStringRect(&textRect, str, length); textWidth = textRect.left + textRect.right; textHeight = textRect.top + textRect.bottom; } } //---- 描画開始位置Xの補正 // 各々の位置から文字列の左端に統一 if (IsDrawFlagSet(HORIZONTAL_ORIGIN_MASK, HORIZONTAL_ORIGIN_CENTER)) { *pXOrigin -= AdjustCenterValue(textWidth); } else if (IsDrawFlagSet(HORIZONTAL_ORIGIN_MASK, HORIZONTAL_ORIGIN_RIGHT)) { *pXOrigin -= textWidth; } //---- 描画開始位置Yの補正 // 各々の位置から 1 行目のアセンダラインに統一 if (IsDrawFlagSet(VERTICAL_ORIGIN_MASK, VERTICAL_ORIGIN_MIDDLE)) { *pYOrigin -= AdjustCenterValue(textHeight); } else if (IsDrawFlagSet(VERTICAL_ORIGIN_MASK, VERTICAL_ORIGIN_BOTTOM)) { *pYOrigin -= textHeight; } //---- 1行目描画開始位置Xの補正 // 文字列の左端からそれぞれの寄せ位置へカーソルを移動 if (IsDrawFlagSet(HORIZONTAL_ALIGN_MASK, HORIZONTAL_ALIGN_CENTER)) { const f32 width = CalcLineWidth(str, length); const f32 offset = AdjustCenterValue(textWidth) - AdjustCenterValue(width); SetCursorX( *pXOrigin + offset ); } else if (IsDrawFlagSet(HORIZONTAL_ALIGN_MASK, HORIZONTAL_ALIGN_RIGHT)) { const f32 width = CalcLineWidth(str, length); const f32 offset = textWidth - width; SetCursorX( *pXOrigin + offset ); } else { SetCursorX( *pXOrigin ); } //---- 1行目描画開始位置Yの補正 // 1 行目のベースライン位置にカーソルを移動 if (IsDrawFlagSet(VERTICAL_ORIGIN_MASK, VERTICAL_ORIGIN_BASELINE)) { // pYOrigin はベースラインであるのでそのままセット SetCursorY( *pYOrigin ); } else { // pYOrigin はアセンダラインであるのでベースラインにカーソルを移動 SetCursorY( *pYOrigin + GetFontAscent() ); } return textWidth; } #if defined(NW_FONT_PROFILE) template void TextWriterBase::PrintProfile() { using namespace nn::fnd; CharWriter::PrintProfile(); TimeSpan timeSpan = m_PrintTextSw.GetElapsedTime(); NN_LOG("TextWriter Print time %d micros, count %d.\n", (int)timeSpan.GetMilliSeconds(), m_PrintTextCount); } template void TextWriterBase::CopyProfileData(const TextWriterBase& other) const { CharWriter::CopyProfileData(other); m_PrintTextSw = other.m_PrintTextSw; m_PrintTextCount = other.m_PrintTextCount; } #endif /* ======================================================================= 明示的実体化 ======================================================================== */ template class TagProcessorBase; template class TagProcessorBase; template class TextWriterBase; template class TextWriterBase; } // namespace font } // namespace nw