/*---------------------------------------------------------------------------* Project: NintendoWare File: lyt_TextBox.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. 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. $Revision: 25594 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #include #include #include namespace nw { namespace lyt { namespace internal { namespace { inline u8 ClampColor(s16 colVal) { return u8(colVal < 0 ? 0: (colVal > 255 ? 255: colVal)); } /*!--------------------------------------------------------------------------* @brief 基点が中央であるときのカーソル位置の値を求めます。 @param[in] value 考慮する幅/高さ @param[in] isCeil 半分の値が浮動小数点にならないように整数値に 切り上げる場合は真。 @return 基点が中央であるときのカーソル位置補正の値を返します。 *---------------------------------------------------------------------------*/ inline f32 AdjustCenterValue( f32 value, bool isCeil ) { f32 ret = value / 2; return isCeil ? math::FCeil(ret): ret; } } // namespace nw::lyt::internal::{anonymous} } // namespace nw::lyt::internal using namespace math; NW_UT_RUNTIME_TYPEINFO_DEFINITION(TextBox, TextBox::Base); // 実行時型情報の実体を定義 TextBox::TextBox(u16 allocStrLen) { Init(allocStrLen); InitMaterial(); } TextBox::TextBox( u16 allocStrLen, const wchar_t* str, const font::Font* pFont ) { Init(allocStrLen); SetFont(pFont); SetString(str); InitMaterial(); } TextBox::TextBox( u16 allocStrLen, const wchar_t* str, u16 strLen, const font::Font* pFont ) { Init(allocStrLen); SetFont(pFont); SetString(str, 0, strLen); InitMaterial(); } TextBox::TextBox( const res::TextBox* pBlock, const ResBlockSet& resBlockSet ) : Pane(pBlock) { u16 allocStrBufLen = static_cast(pBlock->textBufBytes / sizeof(wchar_t)); if (allocStrBufLen > 0) { allocStrBufLen -= 1; } Init(allocStrBufLen); for (int i = 0; i < TEXTCOLOR_MAX; ++i) { m_TextColors[i] = pBlock->textCols[i]; } m_FontSize = pBlock->fontSize; m_TextPosition = pBlock->textPosition; m_Bits.textAlignment = pBlock->textAlignment; m_CharSpace = pBlock->charSpace; m_LineSpace = pBlock->lineSpace; // Fontの構築 NW_NULL_ASSERT(resBlockSet.pFontList); NW_ASSERT(pBlock->fontIdx < resBlockSet.pFontList->fontNum); const res::Font *const fonts = internal::ConvertOffsToPtr(resBlockSet.pFontList, sizeof(*resBlockSet.pFontList)); const char *const fontName = internal::ConvertOffsToPtr(fonts, fonts[pBlock->fontIdx].nameStrOffset); m_pFont = resBlockSet.pResAccessor->GetFont(fontName); // 初期文字列のコピー if (pBlock->textStrBytes >= sizeof(wchar_t) && m_TextBuf) { const wchar_t *const pBlockText = internal::ConvertOffsToPtr(pBlock, pBlock->textStrOffset); const u16 resStrLen = static_cast(pBlock->textStrBytes / sizeof(wchar_t) - 1); SetString(pBlockText, 0, resStrLen); } // マテリアルの作成 { NW_NULL_ASSERT(resBlockSet.pMaterialList); const u32 *const matOffsTbl = internal::ConvertOffsToPtr(resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList)); const res::Material *const pResMaterial = internal::ConvertOffsToPtr(resBlockSet.pMaterialList, matOffsTbl[pBlock->materialIdx]); m_pMaterial = Layout::NewObj(pResMaterial, resBlockSet); } } void TextBox::Init(u16 allocStrLen) { m_TextBuf = 0; m_TextBufBytes = 0; m_TextLen = 0; m_pFont = 0; m_FontSize = Size(0, 0); SetTextPositionH(HORIZONTALPOSITION_CENTER); SetTextPositionV(VERTICALPOSITION_CENTER); m_LineSpace = 0; m_CharSpace = 0; m_pTagProcessor = 0; m_pDispStringBuf = 0; std::memset(&m_Bits, 0, sizeof(m_Bits)); if (allocStrLen > 0) { AllocStringBuffer(allocStrLen); } } void TextBox::InitMaterial() { // マテリアルの作成 m_pMaterial = Layout::NewObj(); if (m_pMaterial) { m_pMaterial->ReserveMem(0, 0, 0); } } TextBox::~TextBox() { // OSReport("TextBox::~TextBox()\n"); if (m_pMaterial && !m_pMaterial->IsUserAllocated()) { Layout::DeleteObj(m_pMaterial); m_pMaterial = 0; } FreeStringBuffer(); } u8 TextBox::GetMaterialNum() const { return m_pMaterial? 1 : 0; } Material* TextBox::GetMaterial(u32 idx) const { NW_WARNING(idx < GetMaterialNum(), "idx >= GetMaterialNum() : %d >= %d", idx, GetMaterialNum()); return idx == 0 ? m_pMaterial : 0; } void TextBox::SetMaterial(Material* pMaterial) { if (m_pMaterial && !m_pMaterial->IsUserAllocated()) { Layout::DeleteObj(m_pMaterial); } m_pMaterial = pMaterial; } const ut::Color8 TextBox::GetVtxColor(u32 idx) const { NW_ASSERT(idx < VERTEXCOLOR_MAX); return GetTextColor(idx / 2); } void TextBox::SetVtxColor( u32 idx, ut::Color8 value ) { NW_ASSERT(idx < VERTEXCOLOR_MAX); SetTextColor(idx / 2, value); } u8 TextBox::GetVtxColorElement(u32 idx) const { NW_ASSERT(idx < ANIMTARGET_VERTEXCOLOR_MAX); return reinterpret_cast(&m_TextColors[idx / (2 * sizeof(ut::Color8))])[idx % sizeof(ut::Color8)]; } void TextBox::SetVtxColorElement(u32 idx, u8 value) { NW_ASSERT(idx < ANIMTARGET_VERTEXCOLOR_MAX); u8& elm = reinterpret_cast(&m_TextColors[idx / (2 * sizeof(ut::Color8))])[idx % sizeof(ut::Color8)]; elm = value; } const ut::Rect TextBox::GetTextDrawRect() const { if (m_pFont == NULL) { NW_WARNING(false, "m_pFont is NULL"); return ut::Rect(); } font::WideTextWriter writer; writer.SetCursor(0, 0); SetFontInfo(&writer); ut::Rect textRect; writer.CalcStringRect(&textRect, m_TextBuf, m_TextLen); const Size textSize(textRect.GetWidth(), textRect.GetHeight()); VEC2 ltPos = GetVtxPos(); const VEC2 curPos = AdjustTextPos(GetSize(), false); const VEC2 textPos = AdjustTextPos(textSize, true); ltPos.x += curPos.x - textPos.x; ltPos.y -= curPos.y - textPos.y; textRect.left = ltPos.x; textRect.top = ltPos.y; textRect.right = ltPos.x + textSize.width; textRect.bottom = ltPos.y - textSize.height; return textRect; } #ifdef NW_LYT_DMPGL_ENABLED void TextBox::DrawSelf(const DrawInfo& drawInfo) { NW_LYT_STOPWATCH_MEASURE(-400, "nw::lyt::TextBox::DrawSelf"); if (m_TextLen <= 0 || !m_pFont || !m_pMaterial) { return; } // lytの描画設定がfontに影響しないように。 internal::FinalizeGraphics(); GraphicsResource& graphicsResource = *drawInfo.GetGraphicsResource(); graphicsResource.ResetGlState(); font::TextWriterResource& writerResource = graphicsResource.GetTextWriterResource(); font::WideTextWriter writer; writer.SetTextWriterResource(&writerResource); SetupTextWriter(&writer); writerResource.ActiveGlProgram(); NW_GL_ASSERT(); // 行列 LoadMtx(drawInfo); ut::Color8 minCol = m_pMaterial->GetColor(INTERPOLATECOLOR_BLACK); ut::Color8 maxCol = m_pMaterial->GetColor(INTERPOLATECOLOR_WHITE); writer.SetColorMapping(minCol, maxCol); writer.SetAlpha(GetGlobalAlpha()); writer.SetupGX(); (void)writer.Print(m_TextBuf, m_TextLen); // fontの描画設定がlytに影響しないように。 writer.FinalizeGX(); } #endif // NW_LYT_DMPGL_ENABLED u16 TextBox::GetStringBufferLength() const { if (m_TextBufBytes == 0) { return 0; } NW_ASSERT(m_TextBufBytes >= sizeof(wchar_t)); return static_cast(m_TextBufBytes / sizeof(wchar_t) - 1); } void TextBox::AllocStringBuffer(u16 minLen) { if (minLen == 0) { return; } u32 allocLen = minLen; ++allocLen; // 終端文字分インクリメント const u32 textBufBytes = allocLen * sizeof(wchar_t); if (textBufBytes >= 0x10000) { NW_WARNING(false, "minLen is too large. - %d\n", minLen); } // 要求する長さのバッファを既に確保している場合は何もしない if (textBufBytes <= m_TextBufBytes) { return; } FreeStringBuffer(); // 指定した文字数分VBOバッファを確保 const u32 drawBufSize = font::CharWriter::GetDispStringBufferSize(minLen); wchar_t* textBuf = Layout::NewArray(allocLen); void* pDispStringBuf = Layout::AllocMemory(drawBufSize); if (NULL == textBuf || NULL == pDispStringBuf) { if (NULL != textBuf) { Layout::DeletePrimArray(textBuf); } if (NULL != pDispStringBuf) { Layout::FreeMemory(pDispStringBuf); } return; } m_TextBuf = textBuf; m_TextBufBytes = static_cast(textBufBytes); // 表示文字列バッファを初期化 m_pDispStringBuf = font::CharWriter::InitDispStringBuffer( pDispStringBuf, minLen); } void TextBox::FreeStringBuffer() { if (m_TextBuf) { Layout::FreeMemory(m_pDispStringBuf); Layout::DeletePrimArray(m_TextBuf); m_pDispStringBuf = 0; m_TextBuf = 0; m_TextBufBytes = 0; m_TextLen = 0; } } u16 TextBox::SetString( const wchar_t* str, u16 dstIdx ) { return SetStringImpl(str, dstIdx, std::wcslen(str)); } u16 TextBox::SetString( const wchar_t* str, u16 dstIdx, u16 strLen ) { return SetStringImpl(str, dstIdx, strLen); } u16 TextBox::SetStringImpl( const wchar_t* str, u16 dstIdx, u32 strLen ) { if (m_pFont == 0) { NW_WARNING(false, "m_pFont is NULL.\n"); return 0; } if (m_TextBuf == 0) { NW_WARNING(false, "m_TextBuf is NULL.\n"); return 0; } const u16 bufLen = GetStringBufferLength(); if (dstIdx >= bufLen) // バッファを超えているためコピーしない { NW_WARNING(false, "dstIdx is out of range.\n"); return 0; } u32 cpLen = bufLen; cpLen -= dstIdx; // コピー可能な文字数 cpLen = ut::Min(strLen, cpLen); NW_WARNING(cpLen >= strLen, "%d character(s) droped.\n", strLen - cpLen); std::memcpy(m_TextBuf + dstIdx, str, cpLen * sizeof(wchar_t)); // m_TextLen にセットする値は 16bitの bufLen 以下の値。 m_TextLen = static_cast(dstIdx + cpLen); m_TextBuf[m_TextLen] = 0; UpdatePTDirty(true); return static_cast(cpLen); } const font::Font* TextBox::GetFont() const { return m_pFont; } void TextBox::SetFont(const font::Font* pFont) { if (pFont && pFont->GetCharacterCode() != font::CHARACTER_CODE_UNICODE) { NW_WARNING(false, "pFont is not uincode encoding."); return; } if (UpdatePTDirty(m_pFont != pFont)) { m_pFont = pFont; if (m_pFont) { SetFontSize(Size(static_cast(m_pFont->GetWidth()), static_cast(m_pFont->GetHeight()))); } else { SetFontSize(Size(0.f, 0.f)); } } } #ifdef NW_LYT_DMPGL_ENABLED void TextBox::LoadMtx(const DrawInfo& drawInfo) { MTX34 mtx; GetTextGlobalMtx(&mtx); drawInfo.GetGraphicsResource()->GetTextWriterResource().SetViewMtx(mtx.a); NW_GL_ASSERT(); } #endif void TextBox::SetFontInfo(font::WideTextWriter* pWriter) const { pWriter->SetFont(m_pFont); if (m_pFont != NULL) { pWriter->SetFontSize(m_FontSize.width, m_FontSize.height); pWriter->SetLineSpace(m_LineSpace); pWriter->SetCharSpace(m_CharSpace); pWriter->SetWidthLimit(GetSize().width); } if (m_pTagProcessor) { pWriter->SetTagProcessor(m_pTagProcessor); } } void TextBox::SetTextPos(font::WideTextWriter* pWriter) const { u32 value = 0; switch (GetTextAlignment()) { case TEXTALIGNMENT_SYNCHRONOUS: default: switch (GetTextPositionH()) { case HORIZONTALPOSITION_LEFT: default: value = font::WideTextWriter::HORIZONTAL_ALIGN_LEFT; break; case HORIZONTALPOSITION_CENTER: value = font::WideTextWriter::HORIZONTAL_ALIGN_CENTER; break; case HORIZONTALPOSITION_RIGHT: value = font::WideTextWriter::HORIZONTAL_ALIGN_RIGHT; break; } break; case TEXTALIGNMENT_LEFT: value = font::WideTextWriter::HORIZONTAL_ALIGN_LEFT; break; case TEXTALIGNMENT_CENTER: value = font::WideTextWriter::HORIZONTAL_ALIGN_CENTER; break; case TEXTALIGNMENT_RIGHT: value = font::WideTextWriter::HORIZONTAL_ALIGN_RIGHT; break; } switch (GetTextPositionH()) { case HORIZONTALPOSITION_LEFT: default: value |= font::WideTextWriter::HORIZONTAL_ORIGIN_LEFT; break; case HORIZONTALPOSITION_CENTER: value |= font::WideTextWriter::HORIZONTAL_ORIGIN_CENTER; break; case HORIZONTALPOSITION_RIGHT: value |= font::WideTextWriter::HORIZONTAL_ORIGIN_RIGHT; break; } switch (GetTextPositionV()) { case VERTICALPOSITION_TOP: default: value |= font::WideTextWriter::VERTICAL_ORIGIN_TOP; break; case VERTICALPOSITION_CENTER: value |= font::WideTextWriter::VERTICAL_ORIGIN_MIDDLE; break; case VERTICALPOSITION_BOTTOM: value |= font::WideTextWriter::VERTICAL_ORIGIN_BOTTOM; break; } pWriter->SetDrawFlag(value); } VEC2 TextBox::AdjustTextPos( const Size& size, bool isCeil ) const { VEC2 pos; switch (GetTextPositionH()) { case HORIZONTALPOSITION_LEFT: default: pos.x = 0.f; break; case HORIZONTALPOSITION_CENTER: pos.x = internal::AdjustCenterValue(size.width, isCeil); break; case HORIZONTALPOSITION_RIGHT: pos.x = size.width; break; } switch (GetTextPositionV()) { case VERTICALPOSITION_TOP: default: pos.y = 0.f; break; case VERTICALPOSITION_CENTER: pos.y = internal::AdjustCenterValue(size.height, isCeil); break; case VERTICALPOSITION_BOTTOM: pos.y = size.height; break; } return pos; } void TextBox::GetTextGlobalMtx(nw::math::MTX34* pMtx) const { MTX34Copy(pMtx, &GetGlobalMtx()); // テキストの描画開始位置の調整をViewModel行列で行う。 VEC2 pos = GetVtxPos(); const VEC2 txtPos = AdjustTextPos(GetSize(), false); pos.x += txtPos.x; pos.y -= txtPos.y; pMtx->m[0][3] += pMtx->m[0][0] * pos.x + pMtx->m[0][1] * pos.y; pMtx->m[1][3] += pMtx->m[1][0] * pos.x + pMtx->m[1][1] * pos.y; pMtx->m[2][3] += pMtx->m[2][0] * pos.x + pMtx->m[2][1] * pos.y; // TextWriterはY軸正を下にしているので、YとZを反転させる。 // Yの反転。 pMtx->m[0][1] = - pMtx->m[0][1]; pMtx->m[1][1] = - pMtx->m[1][1]; pMtx->m[2][1] = - pMtx->m[2][1]; // Zの反転。 // Zは影響が無いので計算を省略。 /* pMtx->m[0][2] = - pMtx->m[0][2]; pMtx->m[1][2] = - pMtx->m[1][2]; pMtx->m[2][2] = - pMtx->m[2][2]; */ } #ifdef NW_LYT_DRAWER_ENABLE void TextBox::SetupDrawCharData(Drawer* pDrawer) { font::WideTextWriter writer; writer.SetDispStringBuffer(m_pDispStringBuf); SetupTextWriter(&writer); if (m_Bits.isPTDirty) { writer.StartPrint(); (void)writer.Print(m_TextBuf, m_TextLen); writer.EndPrint(); m_Bits.isPTDirty = false; } // CharWriter::StartPrint() 後は、DispStringBuf::IsGeneratedCommand() は false になる。 if (! m_pDispStringBuf->IsGeneratedCommand() && pDrawer) { // 文字列のコマンドキャッシュを作成 pDrawer->BuildTextCommand(&writer); } } #endif void TextBox::SetupTextWriter(font::WideTextWriter* pWriter) { SetFontInfo(pWriter); SetTextPos(pWriter); ut::Color8 topCol = m_TextColors[TEXTCOLOR_TOP]; ut::Color8 btmCol = m_TextColors[TEXTCOLOR_BOTTOM]; pWriter->SetGradationMode(topCol != btmCol ? font::CharWriter::GRADMODE_V: font::CharWriter::GRADMODE_NONE); pWriter->SetTextColor(topCol, btmCol); } } // namespace nw::lyt } // namespace nw